GIStemp STEP345_toSBBXgrid

Overview

We will be looking at the toSBBXgrid.f program. This FORTRAN program is compiled and run by the top level script do_comb_step3.sh and has no wrapper script. Yet another variation on a coding theme… I’ve chosen to leave the program listings as one large block rather than interlieve comments. This is so that experienced programmers can just “see the code” and read it. It also means that “newbies” can just read the commentary and skip the code.

It will only frustrate the folks who sort of remember the programming language and would really like the description interlieved so they would not need to keep hopping back and forth. The code is short enough that that ought not to be much of a problem.

The FORTRAN toSBBXgrid.f

Here is the listing. The explanation will follow below, after the === bar.

C*********************************************************************
C *** PROGRAM READS NCAR FILES
C *** Input files:  31-36 NEWUPD.1-NEWUPD.6
C ***
C *** Output files: 10    subbox.data
C ***               11    box.data
C ***
C *** This program grids the station data
C*********************************************************************
C****
C**** This program interpolates the given station data or their
C**** ANOMALIES with respect to 1951-1980 to a prescribed grid.
C****
C**** Input files:    units 31,32,...,30+INM (see NCARSURF TODISK)
C****
C****  Record 1: M1,INFOI(2),...,INFOI(8),M1L,TITLEI  header record
C****  Record 2: IDATA(M1-->M1L),LT,LN,ID,HT,NAME,M2,M2L  station 1
C****  Record 3: IDATA(M2-->M2L),LT,LN,ID,HT,NAME,M3,M3L  station 2
C****            etc.
C****  Set ITRIM=0 if the input files are not trimmed (i.e. M1L=
C****  M2L=...= INFOI(4) , INFOI(9) and trailing M2L,... missing)
C****
C**** Output files:     units 10 and 11 (regional means)
C****
C****  10: Record 1: INFO(1),...,INFO(8),TITLEO   header record
C****      Record 2: AVG(1 --> MONM),LTS,LTN,LNW,LNE  grid point 1
C****      Record 3: AVG(1 --> MONM),LTS,LTN,LNW,LNE  grid point 2
C****            etc.
C****  11: Record 1: INFOI(1),...,INFOI(8),TITLER     header record
C****      Record 2: AVGR(1-->M0),WTR(1-->M0),NG,LTS,LTN,LNW,LNE box 1
C****      Record 3: AVGR(1-->M0),WTR(1-->M0),NG,LTS,LTN,LNW,LNE box 2
C****            etc.
C****  AVG(1-->MONM) is a full time series, starting at January
C****  of year IYRBEG and ending at December of year IYREND.
C****  IDATA(1) and AVGR(1) refer to Jan of year IYRBG0 which may
C****  be less than IYRBEG, M0=MONM0 is the max. length of an input
C****  time series, and WTR(M) is the area of the part of the region
C****  that contained valid data for month M (in square meters).
C****
C****  NG is the total number of non-missing data on that record.
C****  LTS,LTN is the latitude of the southern,northern edge of the
C****  (sub)box, LNW,LNE the longitude of the western,eastern edge
C****  in hundredths of degrees (all 4-byte integers).
C****
C****  INFO(1),...,INFO(8)  are 4-byte integers,
C****  TITLEO is an 80-byte character string,
C****  AVG,AVGR,WTR are 4-byte reals.
C****      INFO 1 = 1 (indicates that time series are not trimmed)
C****           2 = KQ (quantity flag, see below)
C****           3 = MAVG (time avg flag: 1 - 4 DJF - SON, 5 ANN,
C****                     6 MONTHLY, 7 SEAS, 8 - 19 JAN - DEC  )
C****           4 = MONM  (length of each time record)
C****           5 = MONM+4 (size of data record length)
C****           6 = IYRBEG (first year of each time record)
C****           7 = flag for missing data
C****           8 = flag for precipitation trace
C****  INFO(I)=INFOI(I) for I=2,3,7,8
C****  In the output file missing data are flagged by
C****  the real number  XBAD = FLOAT( INFO(7) )
C****
C**** The spatial averaging is done as follows:
C**** Stations within RCRIT km of the grid point P contribute
C**** to the mean at P with weight  1.- d/1200,  (d = distance
C**** between station and grid point in  km). To remove the station
C**** bias, station data are shifted before combining them with the
C**** current mean. The shift is such that the means over the time
C**** period they have in common remains unchanged (individually
C**** for each month). If that common period is less than 20(NCRIT)
C**** years, the station is disregarded. To decrease that chance,
C**** stations are combined successively in order of the length of
C**** their time record. A final shift then reverses the mean shift
C**** OR (to get anomalies) causes the 1951-1980 mean to become
C**** zero for each month.
C****
C**** Regional means are computed similarly except that the weight
C**** of a grid box with valid data is set to its area.
C**** Separate programs were written to combine regional data in
C**** the same way, but using the weights saved on unit 11.
C****
C?*** Input parameters (# of input files, time period)
      PARAMETER (INM=6,KM0=12,ITRIM=1,KQM=20)
C?*** Out put parameters (output time period, base period)
      PARAMETER (IYBASE=1951,LYBASE=1980)
C?*** Grid dimensions
      PARAMETER (NRM=80,NCM=100)
C?*** Earth radius and limits
      PARAMETER (REARTH=6375.,NCRIT=20,RMAX=999.,RMIN=-999.)
C?*** Work array sizes
      PARAMETER (NSTAM=6000,nStaYR=300000)
      CHARACTER*80 TITLEI,TITLEO
C**** Input arrays
      DIMENSION INFO(8),INFOI(8+ITRIM),ITRL(14+ITRIM)
      integer, allocatable :: idata(:)
      real*4, allocatable :: rdata(:)
C**** Station dependent arrays
      DIMENSION I1S(NSTAM+1),MFS(NSTAM),IORD(NSTAM),ISOFI(NSTAM),
     *   LEN(NSTAM),WTI(NSTAM),ID(NSTAM),ID0(NSTAM),nuseid(nstam)
      REAL*4 CSLONS(NSTAM),CSLATS(NSTAM),SNLONS(NSTAM),SNLATS(NSTAM)
      REAL*4 LONS(NSTAM),LATS(NSTAM) ! lon/lat in degrees
C**** Output-related arrays (dim of output array = MONM < MONM0)
      real*4, allocatable :: avg(:,:),dnew(:),wt(:),avgr(:),wtr(:)
      DIMENSION WTM(12),IORDR(NCM),LENC(NCM)
      COMMON/DIAG/NSTCMB,NSTMNS
C?*** SKIPR and output grid dependent arrays (regions and centers)
      LOGICAL SKIPR/.false./,SKIP(INM,NRM)
      INTEGER INFOR(8)
      REAL*4 PI180,SCALE(KQM)/3*.1,9*0.,.1,7*0./,
     *  CSLONC(NCM,NRM),CSLATC(NCM,NRM),SNLONC(NCM,NRM),SNLATC(NCM,NRM)
      COMMON/GRIDC/PI180,XS(NRM),XN(NRM),XE(NRM),XW(NRM),SKIP,
     *  CSLONC,CSLATC,SNLONC,SNLATC,AREA(NCM,NRM),LATLON(4,NCM+1,NRM)
      COMMON/LIMIT/XBAD,NOVRLP,BIAS(12)
      PI180=DATAN(1.D0)/45.D0
      TOMETR=4.*180.*PI180 * REARTH**2/8000.
      RCRIT=1200.
      IF(IARGC().GT.1) THEN
        CALL GETARG(2,TITLEI)
        READ(TITLEI,*) ICRIT
        RCRIT=ICRIT
      END IF
      RBYRC=REARTH/RCRIT
CSRG  RBYRC0=9004.32/RCRIT
      CSCRIT=COS(RCRIT/REARTH)
      NOVRLP=NCRIT
      IYRBEG=1880
      IF(IARGC().GT.0) THEN
        CALL GETARG(1,TITLEI)
        READ(TITLEI,*) IYRBEG
      END IF
C****
C**** Read and use the header record of an input file
C****
      READ (31) INFOI,TITLEI
      KQ=INFOI(2)
      SCL=1.
      IF(KQ.LE.KQM) SCL=SCALE(KQ)
      IF(SCL.EQ.0.) THEN
         WRITE(6,*) ' PROGRAM NOT READY FOR QUANTITY ',KQ
         STOP 'INAPPROPRIATE QUANTITY'
      END IF
C     KM = the number of time frames per year
      KM=1
      IF(INFOI(3).EQ.6) KM=12
      IF(INFOI(3).EQ.7) KM=4
      MSIZE = km*nStaYR
      allocate ( rdata(msize),idata(msize) )
      ML=INFOI(4)
      allocate ( avg(ML,ncm),dnew(ML),wt(ML),avgr(ML),wtr(ML) )
      IYREND = ML/KM + INFOI(6) - 1
      MONM0=ML
      NYRSIN=INFOI(4)/KM
      IF(IYRBEG.LT.INFOI(6)) IYRBEG=INFOI(6)
      MONM=(IYREND-IYRBEG+1)*KM
      MFOUT=1+(IYRBEG-INFOI(6))*KM
      NFB=1+IYBASE-INFOI(6)
      NLB=1+LYBASE-INFOI(6)
      MBAD=INFOI(7)
      LAST=INFOI(7)
      XBAD=MBAD
      TRACE=INFOI(8)
C**** Create and write out the header record of the output file
      DO 10 I=1,8
      INFOR(I)=INFOI(I)
   10 INFO(I)=INFOI(I)
      INFO(1)=1
      INFOR(1)=1
      INFO(4)=MONM
      INFO(5)=INFO(4)+7
      INFOR(5)=2*INFOI(4)+1+4
      INFO(6)=IYRBEG
      TITLEO=TITLEI
      TITLEO(20:39)='ANOM (C)  CR     KM '
      WRITE(TITLEO(33:36),'(I4)') ICRIT
      IF(KQ.EQ.2) TITLEO(25:28)='(MM)'
C?    IF(KQ.EQ.3) TITLEO(31:44)='(MB)          '
C?    IF(KQ.EQ.13) TITLEO(32:50)='(PERCENT)          '
C?    IF(IYBASE.GT.0) TITLEO(61:69)='ANOMALIES'
      WRITE (TITLEO(46:57),'(I4,A)') IYRBEG,'-present'
      WRITE(6,'(1X,A80)') TITLEI,TITLEO
      WRITE(6,'('' INFO BOX   '',8I10)') INFOR
      WRITE(6,'('' INFO SUBBOX'',8I10)') INFO
      WRITE (10) INFO,TITLEO
      IF(.NOT.SKIPR) WRITE(11) INFOR,TITLEO(1:39),TITLEI(40:80)
C**** Find grid-dependent arrays (GRIDC-COMMON)
      CALL GRIDEA (NRM,NCM, INM, RBYRC)
C**** Convert areas to units of square-meters
      DO 20 NR=1,NRM
      DO 20 NC=1,NCM
   20 AREA(NC,NR)=AREA(NC,NR)*TOMETR
C****
C**** Loop over NRM large regions
C****
      DO 300 NR=1,NRM
C**** Collect the station data needed for region NR
      IS=0
      I1S(1)=1
      DO 90 IN=1,INM
      IF(SKIP(IN,NR)) GO TO 90
      REWIND 30+IN
      READ (30+IN) INFOI
      MF=INFOI(1)
      IF(ITRIM.GT.0) ML=INFOI(8+ITRIM)
   50 IF(MF.GE.LAST) GO TO 90
      IF(I1S(IS+1)+ML-MF.GT.MSIZE) STOP 'ERROR: NStaYR TOO SMALL'
      CALL SREAD (30+IN,ML+1-MF,IDATA(I1S(IS+1)),ITRL,14+ITRIM)
      XLAT=.1*ITRL(1)
      XLON=.1*ITRL(2)
      MFS(IS+1)=MF
      LENGTH=ML-MF+1
      MF=ITRL(14)
      IF(ITRIM.GT.0) ML=ITRL(14+ITRIM)
      IF(XLAT.LE.XS(NR).OR.XLAT.GE.XN(NR)) GO TO 50
      XLONR=XLON
      IF(XLON.GT.XE(NR)) XLONR=XLON-360.
      IF(XLON.LT.XW(NR)) XLONR=XLON+360.
      IF(XLONR.LE.XW(NR).OR.XLONR.GE.XE(NR)) GO TO 50
      IS=IS+1
      nuseid(is)=0
      IF(IS.GT.NSTAM) STOP 'ERROR: NStaM TOO SMALL'
      I1S(IS+1)=I1S(IS)+LENGTH
      LATS(IS)=XLAT
      LONS(IS)=XLON
      CSLATS(IS)=COS(XLAT*PI180)
      SNLATS(IS)=SIN(XLAT*PI180)
      CSLONS(IS)=COS(XLON*PI180)
      SNLONS(IS)=SIN(XLON*PI180)
      ID(IS)=ITRL(3)
      GO TO 50
   90 CONTINUE
      NSTA=IS
      MSIZE0=I1S(NSTA+1)
      IF(NSTA.EQ.0) THEN
         NGOOD=0
         DO 100 M=1,MONM0
         WTR(M)=0.
  100    AVGR(M)=XBAD
         DO 110 N=1,NCM
  110    CALL SWRITE(10,MONM,AVGR,LATLON(1,NC,NR),XBAD)
         GO TO 290
      END IF
C**** Convert data to real numbers if necessary (KQ<100)
      IF(KQ.LT.100) THEN
         DO 115 N=1,I1S(NSTA+1)-1
  115    RDATA(N)=IDATA(N)
      END IF
C**** Order the NSTA stations according to length of time record
      DO 120 IS=1,NSTA
C 120 LEN(IS)=I1S(IS+1)-I1S(IS)
      LEN(IS)=0
      DO 120 M=I1S(IS),I1S(IS+1)-1
C?LIM IF(RDATA(M) >RMAX or  <RMIN) RDATA(M)=XBAD
      IF(RDATA(M).EQ.XBAD) GO TO 120
      LEN(IS)=LEN(IS)+1
C?*** Change data if necessary (e.g. trace flag for precip)
C?PRC IF(RDATA(M).EQ.TRACE) RDATA(M)=0.
C?LIM IF(RDATA(M).GT.RMAX or <RMIN) RDATA(M)=RMAX or RMIN
  120 CONTINUE
      CALL SORT (IORD,NSTA,LEN)
C****
C**** Find the time series for all NCM centers in region NR
C****
      DO 200 NC=1,NCM
C**** the subbox edges
      Esou=.01*latlon(1,nc,nr)
      Enor=.01*latlon(2,nc,nr)
      Ewst=.01*latlon(3,nc,nr)
      Eest=.01*latlon(4,nc,nr)
C**** Loop over all stations in memory
      IS0=0
      DO 130 N=1,NSTA
      IS=IORD(N)
C**** Find distance between center and station
      CSDBYR=SNLATS(IS)*SNLATC(NC,NR)+CSLATS(IS)*CSLATC(NC,NR)*
     *  (CSLONS(IS)*CSLONC(NC,NR)+SNLONS(IS)*SNLONC(NC,NR))
CSRG  The next line is replaced by 'IF D/RC>1' below in the orig code
      IF(CSDBYR.LE.CSCRIT) THEN
C****   Keep stations in the current subbox even if D>Rcrit
        dbyrc=1
        if(lats(is).gt.Enor) go to 130
        if(lats(is).lt.Esou) go to 130
        if(abs(lats(is)).lt.89..and.lons(is).gt.Eest) go to 130
        if(abs(lats(is)).lt.89..and.lons(is).lt.Ewst) go to 130
      else
        DBYRC=0.
C**** The arc is replaced by the smaller chord (to avoid using ACOS)
        IF(CSDBYR.LT.1.) DBYRC=RBYRC*SQRT(2.*(1.-CSDBYR))
CSRG    IF(CSDBYR.LT.1.) DBYRC=RBYRC0*SQRT(1.-CSDBYR)
CSRG    IF(DBYRC.GT.1.) GO TO 130
      end if
      IS0=IS0+1
      WTI(IS0)=1.-DBYRC
      ISOFI(IS0)=IS
  130 CONTINUE
C**** Set the weight of the stations outside Rcrit to min(insiders)
      wmin=1.
      do is=1,is0
        if(wti(is).gt.0..and.wti(is).lt.wmin) wmin=wti(is)
      end do
      do is=1,is0
        if(wti(is).le.0.) wti(is)=wmin ! *fac (0<fac<1) ???
      end do
C****
C**** Combine the station data
C****
      NSTCMB=0
      NSTMNS=0
      DO 150 M=1,MONM0
      WT(M)=0.
  150 AVG(M,NC)=XBAD
      IF(IS0.EQ.0) THEN
      LENC(NC)=0
         CALL SWRITE(10,MONM,AVG(1,NC),LATLON(1,NC,NR),XBAD)
         WRITE(6,'('' NO STATIONS FOR CENTER '',2I6)') NR,NC
         GO TO 200
      END IF
C**** Start with the station with the longest time record
      IS=ISOFI(1)
      IOFF=MFS(IS)-I1S(IS)
      NSTMNS=LEN(IS)
      NSTCMB=1
      ID0(1)=ID(IS)
      nuseid(is)=nuseid(is)+1
C**** First update of full data and weight arrays
      WMAX=WTI(1)
      DO 155 K=1,KM
      WTM(K)=WTI(1)
  155 BIAS(K)=0.
      DO 160 M=I1S(IS),I1S(IS+1)-1
      AVG(M+IOFF,NC)=RDATA(M)
      IF(RDATA(M).LT.XBAD) WT(M+IOFF)=WTI(1)
  160 CONTINUE
C**** Add in the remaining stations
      DO 190 I=2,IS0
      IS=ISOFI(I)
      IOFF=MFS(IS)-I1S(IS)
C**** Extend the new data into a full series
      DO 170 M=1,MONM0
  170 DNEW(M)=XBAD
      DO 180 M=I1S(IS),I1S(IS+1)-1
  180 DNEW(M+IOFF)=RDATA(M)
      NF1=1+(MFS(IS)-1)/KM
      NL1=1+(I1S(IS+1)-2+IOFF)/KM
C**** Shift new data, then combine them with current mean
C**** Save the average shifts in the array BIAS
      CALL CMBINE (AVG(1,NC),WT, DNEW,NF1,NL1,WTI(I),WTM, KM,
     *  ID(IS),NSM)
      NSTMNS=NSTMNS+NSM
      IF(NSM.EQ.0) GO TO 190
      NSTCMB=NSTCMB+1
      ID0(NSTCMB)=ID(IS)
      nuseid(is)=nuseid(is)+1
      IF(WMAX.LT.WTI(I)) WMAX=WTI(I)
  190 CONTINUE
C**** Set BIAS=time average over the base period if IYBASE > 0
      IF(NFB.GT.0) CALL TAVG(AVG(1,NC),KM,NYRSIN, NFB,NLB, NR,NC,0.)
C**** Subtract BIAS, then scale and write the result to disk
      LENC(NC)=0
      M=0
      DO 195 IY=1,MONM0/KM
      DO 195 K=1,KM
      M=M+1
      IF(AVG(M,NC).EQ.XBAD) GO TO 195
      AVG(M,NC)=SCL*(AVG(M,NC)-BIAS(K))
      LENC(NC)=LENC(NC)+1
  195 CONTINUE
      CALL SWRITE(10,MONM,AVG(MFOUT,NC),LATLON(1,NC,NR),RCRIT*(1.-WMAX))
CW    WRITE(6,'('' CENTER '',3I6,'' STATIONS USED'')') NR,NC,IS0
      IF(NSTCMB.EQ.0.OR.NSTCMB.GT.8) GO TO 200
CW    WRITE(6,'(19I7)') (ID(ISOFI(I)),I=1,IS0)
CW    WRITE(6,'(19I7)') (LEN0(ISOFI(I)),I=1,IS0)
      LATC=.5*(LATLON(1,NC,NR)+LATLON(2,NC,NR))
      LONC=.5*(LATLON(3,NC,NR)+LATLON(4,NC,NR))
      WRITE(6,'('' LAT,LON,STN-MNTHS,STNS,IDS'',3I6,I8,2X,8I10)')
     *  LATC,LONC,NSTMNS,NSTCMB,(ID0(I),I=1,NSTCMB)
CW    WRITE(6,'(19I7)') (NINT(1.E6*WTI(I)),I=1,IS0)
CW    WRITE(6,'(1X,12I4,5X,12I4)')(NINT(10.*AVG(MFOUT-1+M,NC)),M=1,MONM)
  200 CONTINUE
      nuse=0
      do n=1,nstam
      if(nuseid(n).gt.0) then
          write(77,*) 'used station ',id(n),nuseid(n),' times'
          nuse=nuse+1
      end if
      end do
      write(77,*) nuse,' stations used for region', nr
C****
C**** Find the regionally averaged time series
C****
      IF(SKIPR) GO TO 300
      CALL SORT (IORDR,NCM,LENC)
      NC=IORDR(1)
      DO 205 K=1,KM
      WTM(K)=AREA(NC,NR)
  205 BIAS(K)=0.
      DO 210 M=1,MONM0
      WTR(M)=0.
      IF(AVG(M,NC).LT.XBAD) WTR(M)=AREA(NC,NR)
  210 AVGR(M)=AVG(M,NC)
C**** Add in the series of the remaining centers in region NR
      DO 220 N=2,NCM
      NC=IORDR(N)
      IF(LENC(N).EQ.0) GO TO 230
  220 CALL CMBINE (AVGR,WTR,AVG(1,NC),1,MONM0/KM,AREA(NC,NR),WTM,KM,
     *  NC,NSM)
  230 CONTINUE
C**** Set BIAS=time average over the base period if IYBASE > 0
      IF(NFB.GT.0) CALL TAVG (AVGR,KM,NYRSIN, NFB,NLB, NR,0, 0.)
      NGOOD=0
      M=0
      DO 240 IY=1,MONM0/KM
      DO 240 K=1,KM
      M=M+1
      IF(AVGR(M).EQ.XBAD) GO TO 240
      AVGR(M)=AVGR(M)-BIAS(K)
      NGOOD=NGOOD+1
  240 CONTINUE
CW    WRITE(6,'(1X,12I4,5X,12I4)')(NINT(10.*AVGR(M)),M=1,MONM0)
CW    WRITE(6,'(1X,12I4,5X,12I4)')(NINT(WTR(M)/TOMETR),M=1,MONM0)
  290 IF(.NOT.SKIPR) WRITE(11) AVGR,WTR,NGOOD,(LATLON(I,NCM+1,NR),I=1,4)
  300 WRITE(6,'('' REGION'',3I9,'' STATIONS USED'')') NR,MSIZE0,NSTA
      STOP
      END

      SUBROUTINE GRIDEA (NRM1,NCM1, INM1, RBYRC)
C****
C**** This output grid dependent routine sets up the latitude and
C**** longitude limits for the Rcrit km hull of the large regions
C**** and computes the relevant grid quantities.
C****
C**** Current order of boxes:   1-4   north , west->east ...
C****                            .      to
C****                          76-80  south , west->east
C**** Order of subboxes:        1-10  south , west->east ...
C****                            .      to
C****                         91-100  north , west->east
C****
      PARAMETER (NRM=80,ICM=10,JCM=10, INM=6,ISHORT=0, NCM=ICM*JCM)
      LOGICAL SKIP(INM,NRM)
      REAL*4 PI180,SNLATJ(JCM),CSLATJ(JCM),LT100S(JCM),LT100N(JCM),
     *  CSLONC(NCM,NRM),CSLATC(NCM,NRM),SNLONC(NCM,NRM),SNLATC(NCM,NRM)
      COMMON/GRIDC/PI180,XS(NRM),XN(NRM),XE(NRM),XW(NRM),SKIP,
     *  CSLONC,CSLATC,SNLONC,SNLATC,AREA(NCM,NRM),LATLON(4,NCM+1,NRM)
C****
C**** Sergej's equal area grid
C****
C**** Grid constants for latitude zones
CCCCC REAL BANDS(9)/90.,64.2,44.4,23.6,0.,-23.6,-44.4,-64.2,-90./
C**** We need only the SINEs of the (northern) band edges
      REAL SNNEDG(9)/1.,.9,.7,.4,0.,-.4,-.7,-.9,-1./
C     Input data sets - 1: 90N-60N  2: 60N-30N ... 6: 60S-90S 7:short
      INTEGER INFRST(8)/1,1,2,2,3,4,5,5/,NUMJ(8)/4,8,12,16,16,12,8,4/
      INTEGER INLAST(8)/2,2,3,4,5,5,6,6/
C**** Check grid dimensions
      IF(NRM1.NE.NRM.OR.ICM*JCM.NE.NCM1.OR.INM1.NE.INM) THEN
         WRITE(6,'(6I9)') NRM1,NRM, ICM*JCM,NCM1, INM1,INM
         STOP 'ERROR: GRID SIZES INCONSISTENT'
      END IF
C**** Don't skip any short files if ISHORT=1
      DO 10 NR=1,NRM
      SKIP(INM,NR)=.FALSE.
      DO 10 IN=1,INM-ISHORT
   10 SKIP(IN,NR)=.TRUE.
C**** Find DDLAT to extend each box R km in north and south direction
      DDLAT=1./(PI180*RBYRC)
C**** Loop over all BOXES (large regions)
      NR=0
      DO 50 J=1,8
C**** Find the sin of the latitudes of the centers in band J
      SNN=SNNEDG(J)
      SNS=SNNEDG(J+1)
      DSLATJ=(SNN-SNS)/JCM
      DO 20 JC=1,JCM
      LT100S(JC)=NINT(100./PI180*ASIN(SNS+(JC-1)*DSLATJ))
      LT100N(JC)=NINT(100./PI180*ASIN(SNS+JC*DSLATJ))
      SNLATJ(JC)=SNS+(JC-.5)*DSLATJ
   20 CSLATJ(JC)=SQRT(1.-SNLATJ(JC)**2)
      DO 50 I=1,NUMJ(J)
      NR=NR+1
C**** Complete definition of SKIP-array
      DO 30 IN=INFRST(J),INLAST(J)
   30 SKIP(IN,NR)=.FALSE.
      DLON=360./NUMJ(J)
      XEAST=-180.+I*DLON
      XWEST=XEAST-DLON
C**** Extend each box by half a box in each direction (>Rcrit km ?)
      XW(NR)=XWEST-.5*DLON
      IF(J.EQ.1.OR.J.EQ.8) XW(NR)=-180.
      XE(NR)=XEAST+.5*DLON
      IF(J.EQ.1.OR.J.EQ.8) XE(NR)=180.
      XN(NR)=ASIN(SNN)/PI180+DDLAT
      XS(NR)=ASIN(SNS)/PI180-DDLAT
C**** Find sine and cosine of the center latitudes and longitudes
      DLONC=DLON/ICM
      DO 40 IC=1,ICM
      LN100W=NINT(100.*(XWEST+(IC-1)*DLONC))
      LN100E=NINT(100.*(XWEST+IC*DLONC))
      RLONI=PI180*(XWEST+(IC-.5)*DLONC)
      SNLONI=SIN(RLONI)
      CSLONI=COS(RLONI)
      DO 40 JC=1,JCM
CSRG  ICJC=JC+JCM*(IC-1)
      ICJC=IC+ICM*(JC-1)
      LATLON(1,ICJC,NR)=LT100S(JC)
      LATLON(2,ICJC,NR)=LT100N(JC)
      LATLON(3,ICJC,NR)=LN100W
      LATLON(4,ICJC,NR)=LN100E
      AREA(ICJC,NR)=1.
      SNLONC(ICJC,NR)=SNLONI
      CSLONC(ICJC,NR)=CSLONI
      SNLATC(ICJC,NR)=SNLATJ(JC)
   40 CSLATC(ICJC,NR)=CSLATJ(JC)
      LATLON(1,NCM+1,NR)=LT100S(1)
      LATLON(2,NCM+1,NR)=LT100N(JCM)
      LATLON(3,NCM+1,NR)=NINT(100.*XWEST)
      LATLON(4,NCM+1,NR)=NINT(100.*(XWEST+DLON))
   50 CONTINUE
      RETURN
      END

      SUBROUTINE CMBINE (AVG,WT, DNEW,NF1,NL1,WT1,WTM, KM, ID,NSM)
C****
C**** Bias of new data is removed by subtracting the difference
C**** over the common domain. Then the new data are averaged in.
C****
      COMMON/LIMIT/XBAD,NOVRLP,BIAS(12)
      DIMENSION AVG(KM,*),DNEW(KM,*),WT(KM,*),WTM(*),MISSNG(12)
C**** Loop over months or seasons if appropriate
      NSM=0
      MISSED=KM
      DO 50 K=1,KM
      MISSNG(K)=1
C**** Find means over common domain to compute bias
      SUMN=0
      SUM=0
      NCOM=0
      DO 10 N=NF1,NL1
      IF(AVG(K,N).GE.XBAD.OR.DNEW(K,N).GE.XBAD) GO TO 10
      NCOM=NCOM+1
      SUM=SUM+AVG(K,N)
      SUMN=SUMN+DNEW(K,N)
   10 CONTINUE
      IF(NCOM.LT.NOVRLP) GO TO 50
      BIASK=(SUM-SUMN)/FLOAT(NCOM)
C**** Find mean bias
      WTMNEW=WTM(K)+WT1
      BIAS(K)=(WTM(K)*BIAS(K)+WT1*BIASK)/WTMNEW
      WTM(K)=WTMNEW
C**** Update period of valid data, averages and weights
      DO 20 N=NF1,NL1
      IF(DNEW(K,N).GE.XBAD) GO TO 20
      WTNEW=WT(K,N)+WT1
      AVG(K,N)=(WT(K,N)*AVG(K,N)+WT1*(DNEW(K,N)+BIASK))/WTNEW
      WT(K,N)=WTNEW
      NSM=NSM+1
   20 CONTINUE
      MISSED=MISSED-1
      MISSNG(K)=0
   50 CONTINUE
CLOG? IF(MISSED.GT.0) WRITE(6,90) ID,WT1,MISSNG
   90 FORMAT(' UNUSED DATA - ID/SUBBOX,WT',I8,F5.2,12I2)
      RETURN
      END

      SUBROUTINE TAVG (DATA,KM,NYRS, NFB,NLB, NR,NC, DEFLT)
C****
C**** TAVG computes the time averages (separately for each calendar
C**** month if KM=12) over the base period (year NFB to NLB) and
C**** saves them in BIAS. In case of no data, the average is set to
C**** DEFLT if NR=0 or computed over the whole period if NR>0.
C****
      COMMON/LIMIT/XBAD,NOVRLP,BIAS(12)
      DIMENSION DATA(KM,*),LEN(12)
      MISSED=KM
      DO 50 K=1,KM
      BIAS(K)=DEFLT
      SUM=0.
      M=0
      DO 10 N=NFB,NLB
      IF(DATA(K,N).GE.XBAD) GO TO 10
      M=M+1
      SUM=SUM+DATA(K,N)
   10 CONTINUE
      LEN(K)=M
      IF(M.EQ.0) GO TO 50
      BIAS(K)=SUM/FLOAT(M)
      MISSED=MISSED-1
   50 CONTINUE
      IF(NR*MISSED.EQ.0) RETURN
C**** If base period is data free, use bias with respect to whole series
      DO 100 K=1,KM
      IF(LEN(K).GT.0) GO TO 100
      WRITE(6,'(''0NO DATA IN BASE PERIOD - MONTH,NR,NC'',3I9)') K,NR,NC
      SUM=0.
      M=0
      DO 60 N=1,NYRS
      IF(DATA(K,N).GE.XBAD) GO TO 60
      M=M+1
      SUM=SUM+DATA(K,N)
   60 CONTINUE
      IF(M.EQ.0) GO TO 100
      BIAS(K)=SUM/FLOAT(M)
  100 CONTINUE
      RETURN
      END

      SUBROUTINE SORT (INDEX,NDIM,LNGTH)
C**** Sorts INDEX and LNGTH such that LNGTH becomes decreasing
      DIMENSION INDEX(NDIM),LNGTH(NDIM)
      DO 10 N=1,NDIM
   10 INDEX(N)=N
      DO 30 N=1,NDIM-1
C**** Find maximum of LNGTH(N),...LNGTH(NDIM)
      NLMAX=N
      DO 20 NN=N+1,NDIM
      IF(LNGTH(NN).GT.LNGTH(NLMAX)) NLMAX=NN
   20 CONTINUE
C**** Switch positions N and NLMAX
      LMAX=LNGTH(NLMAX)
      LNGTH(NLMAX)=LNGTH(N)
      LNGTH(N)=LMAX
      IMAX=INDEX(NLMAX)
      INDEX(NLMAX)=INDEX(N)
   30 INDEX(N)=IMAX
      RETURN
      END

      SUBROUTINE SREAD (IN,LEN,IDATA,ITRL,NTRL)
C**** Speed read routine for input records
      DIMENSION IDATA(LEN),ITRL(NTRL)
      READ (IN) IDATA,ITRL
      RETURN
      END

      SUBROUTINE SWRITE (IOUT,NDIM,ARRAY,LATLON,DMIN)
      COMMON/DIAG/NSTNS,NSTMNS
      DIMENSION ARRAY(NDIM),LATLON(4)
      WRITE(IOUT) ARRAY,LATLON,NSTNS,NSTMNS,DMIN
      RETURN
      END

      SUBROUTINE LNFIT(DATA,NS,BAD, TREND, IGFRST,IGLAST,NGOOD)
      REAL*8 F1,F2,AX,A1,A2,A22
C**   Determines linear fit using regression analysis
C**   DATA(I) - time series to be fitted
C**   NS - length of the time series
C**   TREND = slope * total number of years
C**   IGFRST,IGLAST = first and last non-missing position
C**   NGOOD = number of good data (trend BAD if NGOOD<2)
      DIMENSION DATA(NS)
      IGFRST=0
      NGOOD=0
      NGSUM=0
      NGSUM2=0
      F1=0.
      F2=0.
      DO 10 I=1,NS
      IF(DATA(I).EQ.BAD) GO TO 10
      IF(IGFRST.EQ.0) IGFRST=I
      IGLAST=I
      NGOOD=NGOOD+1
      NGSUM=NGSUM+I
      NGSUM2=NGSUM2+I**2
      F1=F1+DATA(I)
      F2=F2+DATA(I)*I
   10 CONTINUE
      TREND=BAD
      IF(NGOOD.LT.2) RETURN
      A1=NGOOD
      A2=NGSUM
      A22=NGSUM2
      AX=A1*A22-A2**2
      TREND=NS*(F2*A1-F1*A2)/AX
C     A=(NGSUM2*F1-F2*NGSUM)/AX
      RETURN
      END

=========================================================

Almost painfully long, given the old school ALL CAPS style. The style of the “do loops” is more f77 (or even FORTRAN IV). This is probably one of the older parts of the “system”.

The analysis of this program will have to wait a bit as I’m still finishing an earlier step. It looks like it mostly just smears data over time and space to fill in missing chunks. I suspect that is fraught with “issues”.

About E.M.Smith

A technical managerial sort interested in things from Stonehenge to computer science. My present "hot buttons' are the mythology of Climate Change and ancient metrology; but things change...
This entry was posted in GISStemp Technical and Source Code and tagged , , , , , . Bookmark the permalink.

1 Response to GIStemp STEP345_toSBBXgrid

  1. Findor says:

    EM

    I am fairly new to your sight and as such I am trying to approach your views as a bit of an agnostic.

    My starting point was to read the early paper outlining the GISS methodology Hansen & Lebedeff 1987, particularly section 3 where they outline their methodology

    As such, the following points from your post

    “C**** The spatial averaging is done as follows:
    C**** Stations within RCRIT km of the grid point P contribute
    C**** to the mean at P with weight 1.- d/1200, (d = distance
    C**** between station and grid point in km). To remove the station
    C**** bias, station data are shifted before combining them with the
    C**** current mean. The shift is such that the means over the time
    C**** period they have in common remains unchanged (individually
    C**** for each month). If that common period is less than 20(NCRIT)
    C**** years, the station is disregarded. To decrease that chance,
    C**** stations are combined successively in order of the length of
    C**** their time record. A final shift then reverses the mean shift
    C**** OR (to get anomalies) causes the 1951-1980 mean to become
    C**** zero for each month.

    This sounds similar to the method they describe.

    “This is probably one of the older parts of the “system”.”

    Yes I strongly suspect so.

    “The analysis of this program will have to wait a bit as I’m still finishing an earlier step. It looks like it mostly just smears data over time and space to fill in missing chunks. I suspect that is fraught with “issues”.”

    Do you have a later analysis? Because I suspect that your view that “it mostly just smears data over time and space to fill in missing chunks” may be wide of the mark. This looks like the heart of the methodology.

    REPLY [ I don’t have anything new on this page yet. I got side tracked into the data analysis. The stuff with “C***” at the start of the lines are comments directly from the code so I would expect it to match the paper they wrote. And yes, it does “smear data”. That is the heart of what it is supposed to do. It tries to do it in a rational way, but it is unknown (yet) it if does it well (that is, I’ve not worked through this bit fully yet, so can not have a full opinion of it, just guesses.) -E.M.Smith ]

Comments are closed.