subroutine setupno2lay(lunin,mype,stats_no2,nlevs,nreal,nobs,&

     obstype,isis,is,no2_diagsave,init_pass,last_pass)

!$$$  subprogram documentation block
!                .      .    .
! subprogram:    setupno2lay --- Compute rhs of oi for omi no2 obs
!
!   prgrmmr:     parrish          org: np22                date: 1990-10-06
!
! abstract:      For sbuv ozone observations (layer amounts and total 
!                column, this routine 
!                  a) reads obs assigned to given mpi task (geographic region),
!                  b) simulates obs from guess,
!                  c) apply some quality control to obs,
!                  d) load weight and innovation arrays used in minimization
!                  e) collects statistics for runtime diagnostic output
!                  f) writes additional diagnostic information to output file
!
! program history log:
!   1990-10-06  parrish
!   1998-04-10  weiyu yang
!   1999-03-01  wu, ozone processing moved into setuprhs from setupoz
!   1999-08-24  derber, j., treadon, r., yang, w., first frozen mpp version
!   2003-12-23  kleist - modify to use pressure as vertical coordinate
!   2004-05-28  kleist - subroutine call update
!   2004-06-17  treadon - update documentation
!   2004-07-08  todling - added only's; removed gridmod; bug fix in diag
!   2004-07-15  todling - protex-compliant prologue; added intent's
!   2004-10-06  parrish - increase size of stats_no2 for nonlinear qc,
!                         add nonlin qc penalty calc and obs count                 
!   2004-11-22  derber - remove weight, add logical for boundary point
!   2004-12-22  treadon - add outer loop number to name of diagnostic file
!   2005-03-02  dee - reorganize diagnostic file writes so that
!                         concatenated files are self-contained
!   2005-03-09  parrish - nonlinear qc change to account for inflated obs error
!   2005-03-16  derber  - change call to sproz to save observation time
!   2005-04-11  treadon - add logical to toggle on/off nonlinear qc code
!   2005-05-18  wu - add use of OMI total ozone data
!   2005-09-22  derber - modify extensively - combine with sproz - no change
!   2005-09-28  derber  - combine with prep,spr,remove tran and clean up
!   2005-10-07  treadon - fix bug in increment of ii
!   2005-10-14  derber  - input grid location and fix regional lat/lon
!   2006-01-09  treadon - remove unused variables
!   2006-02-03  derber  - modify for new obs control
!   2006-02-17  treadon - correct bug when processing data not assimilated
!   2006-03-21  treadon - add option to perturb observation
!   2006-06-06  su - move to wgtlim to constants module
!   2006-07-28  derber  - modify to use new inner loop obs data structure
!                       - unify NL qc
!   2007-03-09      su  - remove option to perturb observation
!   2007-03-19  tremolet - binning of observations
!   2007-05-30  h.liu   - include rozcon with interpolation weights
!   2007-06-08  kleist/treadon - add prefix (task id or path) to diag_no2_file
!   2007-06-05  tremolet - add observation diagnostics structure
!   2008-05-23  safford - add subprogram doc block, rm unused uses and vars
!   2008-01-20  todling - add obsdiag info to diag files
!   2009-01-08  todling - re-implemented obsdiag/tail
!   2009-10-19  guo     - changed for multi-pass setup with dtime_check() and new
!			  arguments init_pass and last_pass.
!   2009-12-08  guo     - cleaned diag output rewind with open(position='rewind')
!   2013-01-26  parrish - change from grdcrd to grdcrd1, tintrp2a to tintrp2a1, intrp2a to intrp2a1,
!                           intrp3no2 to intrp3no21. (to allow successful debug compile on WCOSS)
!
!   input argument list:
!     lunin          - unit from which to read observations
!     mype           - mpi task id
!     nlevs          - number of levels (layer amounts + total column) per obs   
!     nreal          - number of pieces of non-ozone info (location, time, etc) per obs
!     nobs           - number of observations
!     isis           - sensor/instrument/satellite id
!     is             - integer(i_kind) counter for number of obs types to process
!     obstype        - type of ozone obs
!     no2_diagsave - switch on diagnostic output (.false.=no output)
!     stats_no2       - sums for various statistics as a function of level
!
!   output argument list:
!     stats_no2       - sums for various statistics as a function of level
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP; SGI Origin 2000; Compaq HP
!
!$$$ end documentation block
     
! !USES:

  use mpeu_util, only: die,perr
  use kinds, only: r_kind,r_single,i_kind

  use constants, only : zero,half,one,two,tiny_r_kind
  use constants, only : rozcon,cg_term,wgtlim,h300,r10
  use constants, only : rno2con

  use obsmod, only : no2head,no2tail,i_no2_ob_type,dplat,nobskeep
  use obsmod, only : mype_diaghdr,dirname,time_offset,ianldate
  use obsmod, only : obsdiags,lobsdiag_allocated,lobsdiagsave
  use obsmod, only : no2_ob_type
  use obsmod, only : obs_diag

  use gsi_4dvar, only: nobs_bins,hr_obsbin

  use gridmod, only : get_ij,nsig

  use guess_grids, only : nfldsig,ges_prsi,ntguessig,ges_no2,hrdifsig

  use no2info, only : jpch_no2,error_no2,pob_no2,gross_no2,nusis_no2
  use no2info, only : iuse_no2,b_no2,pg_no2

  use jfunc, only : jiter,last,miter
  
  use m_dtime, only: dtime_setup, dtime_check, dtime_show
  use gridmod, only : raqms ! ajl
  use raqmsmod, only : raqmsptrop ! ajl
  use omimod, only : nlevscatwt,presscatwt,bcastpresscatwt,no2pe,pintscatwt
  use mpimod, only : mpi_comm_world,mpi_rtype,mpi_integer4,npe
  implicit none
  
! !INPUT PARAMETERS:
  integer(i_kind)           ::        nno2pe(1:npe)
  integer(i_kind)                  , intent(in   ) :: lunin  ! unit from which to read observations
  integer(i_kind)                  , intent(in   ) :: mype   ! mpi task id
  integer(i_kind)                  , intent(in   ) :: nlevs  ! number of levels (layer amounts + total column) per obs   
  integer(i_kind)                  , intent(in   ) :: nreal  ! number of pieces of non-no2 info (location, time, etc) per obs
  integer(i_kind)                  , intent(in   ) :: nobs   ! number of observations
  character(20)                    , intent(in   ) :: isis   ! sensor/instrument/satellite id
  integer(i_kind)                  , intent(in   ) :: is     ! integer(i_kind) counter for number of obs types to process

  character(10)                    , intent(in   ) :: obstype          ! type of no2 obs
  logical                          , intent(in   ) :: no2_diagsave   ! switch on diagnostic output (.false.=no output)
  logical                          , intent(in   ) :: init_pass,last_pass	! state of "setup" processing

! !INPUT/OUTPUT PARAMETERS:

  real(r_kind),dimension(9,jpch_no2), intent(inout) :: stats_no2 ! sums for various statistics as 
                                                               ! a function of level
!-------------------------------------------------------------------------

! Declare local parameters  
  integer(i_kind),parameter:: iint=1
  integer(i_kind),parameter:: ireal=3
  real(r_kind),parameter:: rmiss = -9999.9_r_kind
  character(len=*),parameter:: myname="setupno2lay"

! Declare external calls for code analysis
  external:: intrp2a1
  external:: tintrp2a1
  external:: intrp3no21
  external:: grdcrd1
  external:: stop2

! Declare local variables  
  
  real(r_kind) no2obs,omg,rat_err2,dlat,dtime,dlon
  real(r_kind) cg_no2,wgross,wnotgross,wgt,arg,exp_arg,term
  real(r_kind) psi,errorinv
  real(r_kind),dimension(nlevs):: no2ges,varinv3,no2_inv
  real(r_kind),dimension(nlevs):: ratio_errors,error
  real(r_kind),dimension(nlevs-1):: ozp
  real(r_kind),dimension(nlevs):: pobs,gross,tnoise
  real(r_kind),dimension(nreal+nlevs,nobs):: data
  real(r_kind),dimension(nsig+1)::prsitmp
  real(r_kind) :: ptropobs
!  integer(i_kind),parameter :: nlevscatwt=35
  real(r_kind),dimension(nlevscatwt) :: scatwt
  real(r_kind),dimension(nsig) :: scatwtmodel
  real(r_single),dimension(nlevs):: pob4,grs4,err4
  real(r_single),dimension(ireal,nobs):: diagbuf
  real(r_single),allocatable,dimension(:,:,:)::rdiagbuf

  integer(i_kind) i,nlev,ii,jj,iextra,istat,ibin
  integer(i_kind) k,j,nz,jc,idia,irdim1,istatus,kk
  integer(i_kind) ioff,itoss,ikeep,nkeep,ierror_toq,ierror_poq
  integer(i_kind) isolz,ifovn,itoqf
  integer(i_kind) icldf,ivcqflags
  integer(i_kind) mm1,itime,ilat,ilon,ilate,ilone,ipoq
  integer(i_kind) ino2trop,ino2tropstd,iamftrop,iamftropstd,itroppres
  integer(i_kind),dimension(iint,nobs):: idiagbuf
  integer(i_kind),dimension(nlevs):: ipos,iouse,ikeepk
  integer(i_kind) :: ntrop

  real(r_kind),dimension(4):: tempwij
  integer(i_kind) nlevp,ierr
  
  character(12) string
  character(10) filex
  character(128) diag_no2_file

  logical,dimension(nobs):: luse
  logical:: l_may_be_passive

  logical:: in_curbin, in_anybin
  integer(i_kind),dimension(nobs_bins) :: n_alloc
  integer(i_kind),dimension(nobs_bins) :: m_alloc
  type(no2_ob_type),pointer:: my_head
  type(obs_diag),pointer:: my_diag
! ajl
  real(r_kind), dimension(nsig) :: dp,dpno2
  real(r_kind) :: sfcp
!  do k=1,nlevscatwt
!    write(540+mype,*),'presscatwt',k,presscatwt(k)
!  end do
!  do k=0,nlevscatwt
!    write(540+mype,*),'pintscatwt',k,pintscatwt(k)
!  enddo

  n_alloc(:)=0
  m_alloc(:)=0

  mm1=mype+1
!  write(6,*)mype,'top of setupno2lay'
!  call flush(6)


!
!*********************************************************************************
! Initialize arrays
  do j=1,nlevs
     ipos(j)=0
     iouse(j)=-2
     tnoise(j)=1.e10_r_kind
     gross(j)=1.e10_r_kind
     pobs(j)=1.e10_r_kind
  end do

  if(no2_diagsave)then
     irdim1=6
     if(lobsdiagsave) irdim1=irdim1+4*miter+1
     allocate(rdiagbuf(irdim1,nlevs,nobs))
  end if

! Locate data for satellite in no2info arrays
  itoss =1
  l_may_be_passive=.false.
  jc=0
  do j=1,jpch_no2
     if (isis == nusis_no2(j)) then
        jc=jc+1
        if (jc > nlevs) then
           write(6,*)'SETUPOZ:  ***ERROR*** in level numbers, jc,nlevs=',jc,nlevs,&
                ' ***STOP IN SETUPOZ***'
           call stop2(71)
        endif
        ipos(jc)=j

        iouse(jc)=iuse_no2(j)
!   for omi ozone error=5 gross=6 r10 is 10 h300 is 300
        tnoise(jc)=error_no2(j)
        gross(jc)=min(r10*gross_no2(j),h300)
!   for omi ozone gross =100 pob =0
        pobs(jc)=pob_no2(j)

        if (iouse(jc)<-1 .or. (iouse(jc)==-1 .and. &
             .not.no2_diagsave)) then
           tnoise(jc)=1.e10_r_kind
           gross(jc) =1.e10_r_kind
        endif
        if (iouse(jc)>-1) l_may_be_passive=.true.
        if (tnoise(jc)<1.e4_r_kind) itoss=0
!        write(860+mype,*)'jc',jc,'iouse',iouse(jc),'tnoise',tnoise(jc),'gross',gross(jc),'pobs',pobs(jc)
     endif
  end do
  nlev=jc

! Handle error conditions
  if (nlevs>nlev) write(6,*)'SETUPOZ:  level number reduced for ',obstype,' ', &
       nlevs,' --> ',nlev
  if (nlev == 0) then
     if (mype==0) write(6,*)'SETUPOZ:  no levels found for ',isis
     if (nobs>0) read(lunin) 
     goto 135
  endif
  if (itoss==1) then
     if (mype==0) write(6,*)'SETUPOZ:  all obs variances > 1.e4.  Do not use ',&
          'data from satellite ',isis
     if (nobs>0) read(lunin)
     goto 135
  endif

! Initialize variables used in no2 processing
  nkeep=0
  do i=1,nobs
     ikeep=0
     do k=1,nlev
        if (iouse(k)>0 .or. no2_diagsave) ikeep=1
     end do
     nkeep=nkeep+ikeep
  end do

! Read and transform no2 data
  read(lunin) data,luse

! If none of the data will be assimilated and don't need diagnostics,
! return to calling program
  if (nkeep==0) return

!    index information for data array (see reading routine)
  itime=2     ! index of analysis relative obs time
  ilon=3      ! index of grid relative obs location (x)
  ilat=4      ! index of grid relative obs location (y)
  ilone=5     ! index of earth relative longitude (degrees)
  ilate=6     ! index of earth relative latitude (degrees)
  ivcqflags=7   !  vertical column quality flag
!  itoq=7      ! index of total ozone error flag (sbuv2 only)
!  ipoq=8      ! index of profile ozone error flag (sbuv2 only)
  isolz=8     ! index of solar zenith angle   (gome and omi only)
  itoqf=9     ! index of row anomaly           (omi only) should be bit 4
  icldf=10    !  cloud fraction
  
!  ifovn=14    ! index of scan position (gome and omi only)
  ino2tropstd=12
  iamftrop=15
  iamftropstd=16
  itroppres=17
  ino2trop=17+nlevscatwt+1
  


! If requested, save data for diagnostic ouput
  if(no2_diagsave)ii=0

! Convert observation (lat,lon) from earth to grid relative values
  call dtime_setup()
!  if(mype.eq.0)write(6,*)'nlevs lay omi',nlevs
!  write(6,*)mype,'nlevs lay omi',nlevs,'nobs',nobs
!  call flush(6)
  do i=1,nobs
     dtime=data(itime,i)

     call dtime_check(dtime, in_curbin, in_anybin)
     if(.not.in_anybin) cycle

     if(in_curbin) then
        dlat=data(ilat,i)
        dlon=data(ilon,i)
        dtime=data(itime,i)
        if(i<100)then
          do k=1,nlevscatwt
            scatwt(k)=data(17+k,i)
!            write(500+mype,*)'psca',k,presscatwt(k),' wt ',scatwt(k)
          end do
        endif
 
!       ajl add for omi apriori
        if(raqms)then
!         need to integrate scatter weight * delta pressure * mixing rato
!         and adjust for tropospheric air mass factor
          call tintrp2a1(ges_prsi,prsitmp,dlat,dlon,dtime,hrdifsig,&
                   nsig+1,mype,nfldsig)
          call intrp3no2dp1(ges_no2,dpno2,dlat,dlon,dtime,mype)
!         this should return minidobson
!          dpno2=dpno2*rno2con ! make minidobson
!          if(i<10)then
           
!           write(500+mype,*)'dpno2',dpno2
!           write(500+mype,*)'prsitmp',prsitmp,'call raqmsno2col'
!          endif
              call intrp2a11(raqmsptrop,ptropobs,dlat,dlon,mype)
!              write(660+mype,*)'ptropobs',ptropobs,'pbufr',data(itroppres,i)
          !call raqmsno2col(dpno2,no2ges,mype,data(itroppres,i),prsitmp,scatwt,data(iamftrop,i),nsig)
!          write(500+mype,*)'scatwtmodel',shape(scatwtmodel)
!          call flush(500+mype)
          call raqmsno2col(dpno2,no2ges,mype,ptropobs,prsitmp,scatwt,data(iamftrop,i),nsig,ntrop,scatwtmodel)
!          if(mype.eq.0)then
!            write(6,*)'intrp3ozdp1'
!            call flush(6)
!          endif
!          call intrp3ozdp1(ges_no2,no2ges,dlat,dlon,dtime,&
!             dpno2,dp,sfcp,mype)
!          write(6,*)mype,'call omiadjust',no2ges
!          call omiadjust(no2ges,data(ilate,i),dpno2,dp,sfcp)
        else

!          call intrp3oz1(ges_no2,no2ges,dlat,dlon,ozp,dtime,&
          call intrp3no21(ges_no2,no2ges,dlat,dlon,ozp,dtime,&
             nlevs,mype)
        endif

        if(no2_diagsave .and. luse(i))then
           ii=ii+1
           idiagbuf(1,ii)=mype                  ! mpi task number
           diagbuf(1,ii) = data(ilate,i)        ! lat (degree)
           diagbuf(2,ii) = data(ilone,i)        ! lon (degree)
           diagbuf(3,ii) = data(itime,i)-time_offset ! time (hours relative to analysis)
        endif

!       Interpolate interface pressure to obs location
!       Calculate innovations, perform gross checks, and accumualte
!       numbers for statistics
!  if(mype.eq.0)write(6,*)'omi no2ges',no2ges,'nlev',nlev,'nlevs',nlevs
!  if(mype.eq.0)write(6,*)'dpno2',dpno2
!  if(mype.eq.0)write(6,*)'dp',dp
!  if(mype.eq.0)write(6,*)'sfcp',sfcp

!       For OMI/GOME, nlev=1 
        do k=1,nlev
           j=ipos(k)
           ioff=nreal+k

!          Compute innovation and load obs error into local array
!           no2obs = data(ioff,i)*1000. ! make minidobson
!          try no2trop instead
           no2obs=data(ino2trop,i)*1000.
           no2_inv(k) = no2obs-no2ges(k)
!           if(i<10)then
!             write(600+mype,*)'no2_inv',no2_inv(k),'no2ges',no2ges(k)
!             write(600+mype,*)'no2obs trop',k,no2obs,'std',data(ino2tropstd,i)*1000.
!             write(600+mype,*)mype,'amftrop',data(iamftrop,i),'troppres',data(itroppres,i)
!             do kk=1,ntrop
!               write(600+mype,*)'scatwtmodel',kk,scatwtmodel(kk)
!             end do
!             write(6,*)mype,'no2obs trop',k,no2obs,'std',data(ino2tropstd,i)*1000.
!             write(6,*)mype,'iamftrop',data(iamftrop,i),'std',data(iamftropstd,i)
!             write(6,*)mype,'amftrop',data(iamftrop,i),'troppres',data(itroppres,i)
!           endif
!           write(6,*)mype,'no2_inv',no2_inv(k),'obs',no2obs,no2ges(k)
!           error(k)     = tnoise(k)
           error(k) = data(ino2tropstd,i)*1000. ! make millidobson

!          Set inverse obs error squared and ratio_errors
           if (error(k)<1.e4_r_kind) then
              varinv3(k) = one/(error(k)**2)
!         for ozone error=5 so varinv=.04
              ratio_errors(k) = one
           else
              varinv3(k) = zero
              ratio_errors(k) = zero
           endif
!          check ajl to avoid out of bounds on j ajl
           if(j>jpch_no2)then
             write(6,*)'ajl error j too big',j,jpch_no2
             call stop2('j big')
           endif
!          end ajl test

!          Perform gross check
!          gross is test for too big a difference 100 for ozone
           if(abs(no2_inv(k)) > gross(k) .or. no2obs > 1000._r_kind .or. &
                no2ges(k)<tiny_r_kind) then
              varinv3(k)=zero
              ratio_errors(k)=zero
!             write(6,*)'SETUPOZ:  reset O3 varinv3=',varinv3(k)
              if(luse(i))stats_no2(2,j) = stats_no2(2,j) + one ! number of obs tossed
           endif

!          Accumulate numbers for statistics
           rat_err2 = ratio_errors(k)**2
           if (varinv3(k)>tiny_r_kind .or. &
                (iouse(k)==-1 .and. no2_diagsave)) then
              if(luse(i))then
                 omg=no2_inv(k)
                 stats_no2(1,j) = stats_no2(1,j) + one                          ! # obs
                 stats_no2(3,j) = stats_no2(3,j) + omg                          ! (o-g)
                 stats_no2(4,j) = stats_no2(4,j) + omg*omg                      ! (o-g)**2
!            for ozone rat_errs2=1. varinv=.04
                 stats_no2(5,j) = stats_no2(5,j) + omg*omg*varinv3(k)*rat_err2  ! penalty
                 stats_no2(6,j) = stats_no2(6,j) + no2obs                        ! obs

                 exp_arg = -half*varinv3(k)*omg**2
                 errorinv = sqrt(varinv3(k))
!            for ozone errorinv=.2
                 if (pg_no2(j) > tiny_r_kind .and. errorinv > tiny_r_kind) then
                    arg  = exp(exp_arg)
                    wnotgross= one-pg_no2(j)
                    cg_no2=b_no2(j)*errorinv
                    wgross = cg_term*pg_no2(j)/(cg_no2*wnotgross)
                    term = log((arg+wgross)/(one+wgross))
                    wgt  = one-wgross/(arg+wgross)
                 else
                    term = exp_arg
                    wgt  = one
                 endif
                 stats_no2(8,j) = stats_no2(8,j) -two*rat_err2*term
                 if(wgt < wgtlim) stats_no2(9,j)=stats_no2(9,j)+one
              end if
           endif

!          If not assimilating this observation, reset inverse variance to zero
           if (iouse(k)<1) then
              varinv3(k)=zero
              ratio_errors(k)=zero
              rat_err2 = zero
           end if
           if (rat_err2*varinv3(k)>tiny_r_kind .and. luse(i)) &
              stats_no2(7,j) = stats_no2(7,j) + one

!          Optionally save data for diagnostics
           if (no2_diagsave .and. luse(i)) then
              rdiagbuf(1,k,ii) = no2obs
              rdiagbuf(2,k,ii) = no2_inv(k)           ! obs-ges
              errorinv = sqrt(varinv3(k)*rat_err2)
              rdiagbuf(3,k,ii) = errorinv               ! inverse observation error
              if (obstype == 'gome' .or. obstype == 'omi' ) then
                 rdiagbuf(4,k,ii) = data(isolz,i)       ! solar zenith angle
                 rdiagbuf(5,k,ii) = data(ifovn,i)       ! field of view number
              else
                 rdiagbuf(4,k,ii) = rmiss                
                 rdiagbuf(5,k,ii) = rmiss               
              endif
              if (obstype == 'omi' ) then
                 rdiagbuf(6,k,ii) = data(itoqf,i)       ! row anomaly index
              else
                 rdiagbuf(6,k,ii) = rmiss                
              endif
           endif
!           if(abs(no2_inv(k))>no2ges(k).and.ratio_errors(k)>zero)then
!             !write(520+mype,*)'no2_inv',no2_inv(k),'no2ges',no2ges(k), &
!             ' ob ',no2obs,'gross',gross(k),'ratio_erros',ratio_errors(k), &
!             !'varinv3',varinv3(k)
!           endif

        end do
!       Check all information for obs.  If there is at least one piece of
!       information that passed quality control, use this observation.
        ikeepk=0
        do k=1,nlevs
           if ((ratio_errors(k)**2)*varinv3(k)>1.e-10_r_kind) ikeepk(k)=1
        end do
        ikeep=maxval(ikeepk)
     endif ! (in_curbin)
!    ajl code to output diagnostics
!     if(jiter.eq.1.and.init_pass.and.ikeep==1)then
!       write(500+mype,'(8f8.2)')data(ilone,i),data(ilate,i),no2obs,no2ges(1),no2_inv(1),dtime,data(ilon,i),data(ilat,i)
!     endif

!    In principle, we want ALL obs in the diagnostics structure but for
!    passive obs (monitoring), it is difficult to do if rad_diagsave
!    is not on in the first outer loop. For now we use l_may_be_passive...
     if (l_may_be_passive) then
!       Link observation to appropriate observation bin
        if (nobs_bins>1) then
           ibin = NINT( dtime/hr_obsbin ) + 1
        else
           ibin = 1
        endif
        IF (ibin<1.OR.ibin>nobs_bins) write(6,*)'SETUPOZLAY: ',mype,'Error nobs_bins,ibin= ',nobs_bins,ibin

        if(in_curbin) then
!          Process obs have at least one piece of information that passed qc checks
           if (.not. last .and. ikeep==1) then
 
              if(.not. associated(no2head(ibin)%head))then
                 allocate(no2head(ibin)%head,stat=istat)
                 if(istat /= 0)write(6,*)' failure to write no2head '
                 no2tail(ibin)%head => no2head(ibin)%head
              else
                 allocate(no2tail(ibin)%head%llpoint,stat=istat)
                 if(istat /= 0)write(6,*)' failure to write no2tail%llpoint '
                 no2tail(ibin)%head => no2tail(ibin)%head%llpoint
              end if
 
              m_alloc(ibin) = m_alloc(ibin) +1
              my_head => no2tail(ibin)%head
              my_head%idv = is
              my_head%iob = i

              nlevp=max(nlev-1,1)
              allocate(no2tail(ibin)%head%res(nlev),no2tail(ibin)%head%diags(nlev),&
                       no2tail(ibin)%head%err2(nlev),no2tail(ibin)%head%raterr2(nlev),&
                       no2tail(ibin)%head%prs(nlevp), &
                       no2tail(ibin)%head%wij(4,nsig), &
                       no2tail(ibin)%head%ipos(nlev),stat=istatus)
              if (istatus/=0) write(6,*)'SETUPNO2:  allocate error for no2_point, istatus=',istatus

!             Set number of levels for this obs
              no2tail(ibin)%head%nlno2 = nlev-1  ! NOTE: for OMI/GOME, nlno2=0
              no2tail(ibin)%head%nltrop = ntrop  ! NOTE: for OMI/GOME, nlno2=0

!             Set (i,j) indices of guess gridpoint that bound obs location
              call get_ij(mm1,dlat,dlon,no2tail(ibin)%head%ij(1),tempwij(1))

              call tintrp2a1(ges_prsi,prsitmp,dlat,dlon,dtime,hrdifsig,&
                   nsig+1,mype,nfldsig)

              do k = 1,nsig
                 no2tail(ibin)%head%wij(1,k)=tempwij(1)*rno2con*(prsitmp(k)-prsitmp(k+1))*scatwtmodel(k)
                 no2tail(ibin)%head%wij(2,k)=tempwij(2)*rno2con*(prsitmp(k)-prsitmp(k+1))*scatwtmodel(k)
                 no2tail(ibin)%head%wij(3,k)=tempwij(3)*rno2con*(prsitmp(k)-prsitmp(k+1))*scatwtmodel(k)
                 no2tail(ibin)%head%wij(4,k)=tempwij(4)*rno2con*(prsitmp(k)-prsitmp(k+1))*scatwtmodel(k)
              end do

!             Increment data counter and save information used in
!             inner loop minimization (int* and stp* routines)
 
              no2tail(ibin)%head%luse=luse(i)
              no2tail(ibin)%head%time=dtime

              no2tail(ibin)%head%prs(1) = zero   ! any value is OK, never used

           endif ! < .not.last >
        endif ! (in_curbin)

!       Link obs to diagnostics structure
        do k=1,nlevs
           if (.not.lobsdiag_allocated) then
              if (.not.associated(obsdiags(i_no2_ob_type,ibin)%head)) then
                 allocate(obsdiags(i_no2_ob_type,ibin)%head,stat=istat)
                 if (istat/=0) then
                    write(6,*)'setupno2lay: failure to allocate obsdiags',istat
                    call stop2(260)
                 end if
                 obsdiags(i_no2_ob_type,ibin)%tail => obsdiags(i_no2_ob_type,ibin)%head
              else
                 allocate(obsdiags(i_no2_ob_type,ibin)%tail%next,stat=istat)
                 if (istat/=0) then
                    write(6,*)'setupno2lay: failure to allocate obsdiags',istat
                    call stop2(261)
                 end if
                 obsdiags(i_no2_ob_type,ibin)%tail => obsdiags(i_no2_ob_type,ibin)%tail%next
              end if

              allocate(obsdiags(i_no2_ob_type,ibin)%tail%muse(miter+1))
              allocate(obsdiags(i_no2_ob_type,ibin)%tail%nldepart(miter+1))
              allocate(obsdiags(i_no2_ob_type,ibin)%tail%tldepart(miter))
              allocate(obsdiags(i_no2_ob_type,ibin)%tail%obssen(miter))
              obsdiags(i_no2_ob_type,ibin)%tail%indxglb=i
              obsdiags(i_no2_ob_type,ibin)%tail%nchnperobs=-99999
              obsdiags(i_no2_ob_type,ibin)%tail%luse=.false.
              obsdiags(i_no2_ob_type,ibin)%tail%muse(:)=.false.
              obsdiags(i_no2_ob_type,ibin)%tail%nldepart(:)=-huge(zero)
              obsdiags(i_no2_ob_type,ibin)%tail%tldepart(:)=zero
              obsdiags(i_no2_ob_type,ibin)%tail%wgtjo=-huge(zero)
              obsdiags(i_no2_ob_type,ibin)%tail%obssen(:)=zero

              n_alloc(ibin) = n_alloc(ibin) +1
              my_diag => obsdiags(i_no2_ob_type,ibin)%tail
              my_diag%idv = is
              my_diag%iob = i
              my_diag%ich = k
           else
              if (.not.associated(obsdiags(i_no2_ob_type,ibin)%tail)) then
                 obsdiags(i_no2_ob_type,ibin)%tail => obsdiags(i_no2_ob_type,ibin)%head
              else
                 obsdiags(i_no2_ob_type,ibin)%tail => obsdiags(i_no2_ob_type,ibin)%tail%next
              end if
              if (obsdiags(i_no2_ob_type,ibin)%tail%indxglb/=i) then
                 write(6,*)'setupno2lay: index error'
                 call stop2(262)
              end if
           endif

           if(in_curbin) then
              obsdiags(i_no2_ob_type,ibin)%tail%luse=luse(i)
              obsdiags(i_no2_ob_type,ibin)%tail%muse(jiter)= (ikeepk(k)==1)
              obsdiags(i_no2_ob_type,ibin)%tail%nldepart(jiter)=no2_inv(k)
              obsdiags(i_no2_ob_type,ibin)%tail%wgtjo= varinv3(k)*ratio_errors(k)**2
 
              if (.not. last .and. ikeep==1) then
                 no2tail(ibin)%head%ipos(k)    = ipos(k)
                 no2tail(ibin)%head%res(k)     = no2_inv(k)
                 no2tail(ibin)%head%err2(k)    = varinv3(k)
                 no2tail(ibin)%head%raterr2(k) = ratio_errors(k)**2
!             write(500+mype,*)'ipos',k,ipos(k),'inv',no2_inv(k),'varinv3',varinv3(k),'raterr2',ratio_errors(k)**2
                 no2tail(ibin)%head%diags(k)%ptr => obsdiags(i_no2_ob_type,ibin)%tail
 
                 my_head => no2tail(ibin)%head
                 my_diag => no2tail(ibin)%head%diags(k)%ptr
                 if(my_head%idv /= my_diag%idv .or. &
                    my_head%iob /= my_diag%iob .or. &
                              k /= my_diag%ich ) then
                    call perr(myname,'mismatching %[head,diags]%(idv,iob,ich,ibin) =', &
                          (/is,i,k,ibin/))
                    call perr(myname,'my_head%(idv,iob,ich) =',(/my_head%idv,my_head%iob,k/))
                    call perr(myname,'my_diag%(idv,iob,ich) =',(/my_diag%idv,my_diag%iob,my_diag%ich/))
                    call die(myname)
                 endif
              endif

              if (no2_diagsave.and.lobsdiagsave) then
                 idia=6
                 do jj=1,miter
                    idia=idia+1
                    if (obsdiags(i_no2_ob_type,ibin)%tail%muse(jj)) then
                       rdiagbuf(idia,k,ii) = one
                    else
                       rdiagbuf(idia,k,ii) = -one
                    endif
                 enddo
                 do jj=1,miter+1
                    idia=idia+1
                    rdiagbuf(idia,k,ii) = obsdiags(i_no2_ob_type,ibin)%tail%nldepart(jj)
                 enddo
                 do jj=1,miter
                    idia=idia+1
                    rdiagbuf(idia,k,ii) = obsdiags(i_no2_ob_type,ibin)%tail%tldepart(jj)
                 enddo
                 do jj=1,miter
                    idia=idia+1
                    rdiagbuf(idia,k,ii) = obsdiags(i_no2_ob_type,ibin)%tail%obssen(jj)
                 enddo
              endif
           endif ! (in_curbin)

        enddo ! < over nlevs >

     else

        if(in_curbin) then
           if (no2_diagsave.and.lobsdiagsave) then
              rdiagbuf(7:irdim1,1:nlevs,ii) = zero
           endif
        endif ! (in_curbin)
 
     endif ! < l_may_be_passive >

  end do   ! end do i=1,nobs

! If requested, write to diagnostic file
!  if(mype.eq.0)write(6,*)'find ajl ii',ii
  if (no2_diagsave .and. ii>0) then
     filex=obstype
     write(string,100) jiter
100  format('_',i2.2)
     diag_no2_file = trim(dirname) // trim(filex) // '_' // trim(dplat(is)) // (string)
     if(init_pass) then
        open(4,file=diag_no2_file,form='unformatted',status='unknown',position='rewind')
     else
        open(4,file=diag_no2_file,form='unformatted',status='old',position='append')
     endif
     iextra=0
     if (init_pass .and. mype==mype_diaghdr(is)) then
        write(4) isis,dplat(is),obstype,jiter,nlevs,ianldate,iint,ireal,iextra
        write(6,*)'SETUPOZ:   write header record for ',&
             isis,iint,ireal,iextra,' to file ',trim(diag_no2_file),' ',ianldate
        do i=1,nlevs
           pob4(i)=pobs(i)
           grs4(i)=gross(i)
           err4(i)=tnoise(i)
        end do
        write(4) pob4,grs4,err4,iouse
     endif
     write(4) ii
     write(4) idiagbuf(:,1:ii),diagbuf(:,1:ii),rdiagbuf(:,:,1:ii)
     close(4)
  endif

! Jump to this line if problem with data
135 continue        

! clean up
  call dtime_show('setuplay','diagsave:no2',i_no2_ob_type)

  if(no2_diagsave) deallocate(rdiagbuf)

! End of routine
  return
end subroutine setupno2lay
