! Utility functions in support of PIO io interface module cam_pio_utils 33,3 use pio, only : io_desc_t, iosystem_desc_t, file_desc_t, pio_double, pio_real, pio_freedecomp use shr_kind_mod, only : r8=>shr_kind_r8, i8=>shr_kind_i8, shr_kind_cl use cam_logfile, only: iulog use perf_mod, only: t_startf, t_stopf use spmd_utils, only: masterproc implicit none private public :: column_info, cam_pio_openfile, cam_pio_createfile public :: get_decomp public :: get_phys_decomp public :: get_dyn_decomp public :: init_pio_subsystem ! called from cam_comp public :: clean_iodesc_list integer, parameter, public :: dyn_stagger_decomp=102,dyn_decomp=101,phys_decomp=100 integer, parameter, public :: max_string_len = 256 ! Length of strings integer, parameter, public :: max_chars = shr_kind_cl ! max chars for char variables integer, parameter, public :: fieldname_len = 16 ! max chars for field name integer, parameter, public :: fieldname_suffix_len = 3 ! length of field name suffix ("&IC") integer, parameter, public :: fieldname_lenp2 = fieldname_len + 2 ! allow for extra characters integer, parameter, public :: max_fieldname_len = fieldname_len + fieldname_suffix_len ! max chars for field name (including suffix) type :: column_info character(len=max_chars) :: lat_name ! latitude name for this column or columns character(len=max_chars) :: lon_name ! latitude name for this column or columns integer :: num_lats ! number of lats in a group of contiguous columns integer :: num_lons ! number of lons in a group of contiguous columns integer :: columnlat(2) ! beginning and ending latitude (range) dimensioned by groups integer :: columnlon(2) ! beginning and ending longitude (range) dimensioned by groups end type column_info type, public :: field_info character(len=max_fieldname_len) :: name ! field name character(len=max_chars) :: long_name ! long name character(len=max_chars) :: units ! units character(len=max_chars) :: sampling_seq ! sampling sequence - if not every timestep, how often field is sampled ! (i.e., how often "outfld" is called): every other; only during LW/SW ! radiation calcs; etc. logical :: flag_xyfill ! non-applicable xy points flagged with fillvalue logical :: flag_isccplev ! levels dimension is ISCCP not CAM integer :: numlev ! vertical dimension (.nc file and internal arr) integer :: begdim1 ! on-node dim1 start index integer :: enddim1 ! on-node dim1 end index integer :: begdim2 ! on-node dim2 start index integer :: enddim2 ! on-node dim2 end index integer :: begdim3 ! on-node chunk or lat start index integer :: enddim3 ! on-node chunk or lat end index integer :: decomp_type ! type of decomposition (physics or dynamics) integer, pointer :: colperdim3(:) ! number of valid elements per chunk or lat end type field_info real(r8), parameter, public :: fillvalue = 1.e36_r8 ! fill value for netcdf fields integer, private :: io_stride, num_iotasks, io_type ! This variable should be private ? type(iosystem_desc_t), public :: pio_subsystem type iodesc_list integer(i8) :: tag type(io_desc_t), pointer :: iodesc type(iodesc_list), pointer :: next end type iodesc_list type(iodesc_list), target :: iodesc_list_top ! logical :: dumpit=.false. ! for debugging only contains subroutine init_pio_subsystem(nlfilename) 1,2 use pio, only: pio_rearr_box, pio_init use spmd_utils, only : iam, mpicom character(len=*) nlfilename call read_namelist_pio(nlfilename) call PIO_init(iam, mpicom, num_iotasks, 1, io_stride, & PIO_Rearr_box, PIO_subsystem ) nullify(iodesc_list_top%iodesc) nullify(iodesc_list_top%next) end subroutine init_pio_subsystem subroutine read_namelist_pio(nlfilename) 1,10 use namelist_utils, only: find_group_name use units, only : getunit, freeunit use abortutils, only : endrun use spmd_utils, only : npes, mpicom use dycore, only : dycore_is include 'mpif.h' character(len=*) nlfilename character(len=80) iotype_name namelist /pio_ctl/ io_stride, num_iotasks, iotype_name integer :: unitn, ierr io_stride = -1 ! set based on num_iotasks value when initialized < 0 num_iotasks = -1 ! set based on io_stride value when initialized < 0 iotype_name = 'netcdf' if(masterproc) then unitn=getunit() open( unitn, file=trim(nlfilename), status='old' ) call find_group_name(unitn, 'pio_ctl', status=ierr) if ( ierr == 0 ) then read (unitn,pio_ctl,iostat=ierr) if (ierr /= 0) then call endrun(' pio_ctl namelist read returns an'// & ' end of file or end of record condition' ) end if end if close( unitn ) call freeunit( unitn ) call set_pio_parameters(iotype_name) write(iulog,*) 'CAM PIO parameter settings...' write(iulog,*) ' io_stride = ',io_stride write(iulog,*) ' iotype_name = ',iotype_name write(iulog,*) ' num_iotasks = ',num_iotasks end if call mpi_bcast(io_type, 1, mpi_integer, 0, mpicom, ierr) call mpi_bcast(io_stride, 1, mpi_integer, 0, mpicom, ierr) call mpi_bcast(num_iotasks, 1, mpi_integer, 0, mpicom, ierr) end subroutine read_namelist_pio subroutine get_decomp (iodesc, field, dtype, numlev_in, column) use dyn_grid, only : get_horiz_grid_dim_d use dycore, only : dycore_is type(field_info), intent(in) :: field integer, intent(in) :: dtype integer, intent(in), optional :: numlev_in type(column_info), intent(in), optional :: column character(len=3) :: memorder type(io_desc_t), pointer :: iodesc integer :: hdim1_d, hdim2_d, numlev call t_startf('get_decomp') if(present(numlev_in)) then numlev=numlev_in else numlev = field%numlev end if memorder = 'xzy' if(present(column)) then if(field%decomp_type==phys_decomp) then call get_phys_decomp(iodesc, 1,numlev,1,dtype,column=column) else call get_dyn_decomp( iodesc, column%num_lons, column%num_lats, numlev, 0, dtype, memorder_in='xzy',column_in=column) end if else call get_horiz_grid_dim_d(hdim1_d, hdim2_d) if(field%decomp_type==phys_decomp) then call get_phys_decomp(iodesc, 1,numlev,1,dtype) else if(field%decomp_type==dyn_decomp) then call get_dyn_decomp( iodesc, hdim1_d, hdim2_d, numlev, 0, dtype, memorder_in='xzy') else if(field%decomp_type==dyn_stagger_decomp) then call get_dyn_decomp( iodesc, hdim1_d, hdim2_d-1, numlev, 0, dtype, memorder_in='xzy') end if end if call t_stopf('get_decomp') end subroutine get_decomp subroutine get_phys_decomp(iodesc, fdim, mdim, ldim, dtype, fileorder_in, column) 20,8 use pio, only : pio_initdecomp, pio_offset, pio_setdebuglevel use pio_support, only : pio_writedof use dyn_grid, only : get_horiz_grid_dim_d use dycore, only : dycore_is use spmd_utils, only : iam, mpicom type(io_desc_t), pointer :: iodesc type(column_info), optional :: column integer, intent(in) :: fdim, mdim, ldim character(len=3),optional, intent(in) :: fileorder_in character(len=3) :: fileorder integer :: dimlens(5), dimcnt integer, intent(in) :: dtype integer :: hdim1_d, hdim2_d integer, pointer :: ldof(:) integer :: ierr, ldecomp logical :: twodhorizontal, found call t_startf('get_phys_decomp') twodhorizontal = .true. if(dycore_is('UNSTRUCTURED')) twodhorizontal = .false. if(present(fileorder_in)) then fileorder=fileorder_in else fileorder='xyz' end if if(present(column)) then hdim1_d=column%num_lons hdim2_d=column%num_lats else call get_horiz_grid_dim_d(hdim1_d, hdim2_d) end if dimlens(1)=hdim1_d dimcnt=1 if(fdim>1) then dimcnt=dimcnt+1 dimlens(dimcnt)=fdim end if if(fileorder=='xyz') then ldecomp=5 if(twodhorizontal) then dimcnt=dimcnt+1 dimlens(dimcnt)=hdim2_d end if if(mdim>1) then dimcnt=dimcnt+1 dimlens(dimcnt)=mdim end if else ldecomp=7 if(mdim>1) then dimcnt=dimcnt+1 dimlens(dimcnt)=mdim end if if(twodhorizontal) then dimcnt=dimcnt+1 dimlens(dimcnt)=hdim2_d end if end if if(ldim>1) then dimcnt=dimcnt+1 dimlens(dimcnt)=ldim end if found=.false. if(.not.present(column)) then call find_iodesc(dimcnt, dimlens(1:dimcnt), dtype, ldecomp, iodesc, found) end if if(.not.found) then if(present(column)) then ldof => get_column_ldof(phys_decomp, column, mdim, fileorder_in=fileorder) else ldof => get_phys_ldof(hdim1_d, hdim2_d, fdim,mdim,ldim, fileorder) end if call pio_initdecomp(pio_subsystem, dtype,dimlens(1:dimcnt), ldof, iodesc) deallocate(ldof) end if call t_stopf('get_phys_decomp') end subroutine get_phys_decomp subroutine find_iodesc(dimcnt, dimlens, dtype, decomptype, iodesc, found) 2 integer, intent(in) :: dimcnt integer, intent(in) :: dimlens(dimcnt) integer, intent(in) :: dtype integer, intent(in) :: decomptype type(io_desc_t), pointer :: iodesc logical,intent(out) :: found type(iodesc_list), pointer :: this, prev integer :: i integer(i8) :: tag, j found = .false. this => iodesc_list_top j=1 tag = 0 do i=1,dimcnt tag = tag+dimlens(i)*j j=j*1000 end do tag = tag+j*int((dimcnt*100+dtype*10+decomptype),i8) do while(associated(this) .and. .not. found) if(tag==this%tag) then found=.true. iodesc => this%iodesc else prev=>this this=>this%next end if end do if(.not.found) then this=>prev if(associated(this%iodesc)) then allocate(this%next) this=>this%next end if allocate(this%iodesc) this%tag = tag iodesc=>this%iodesc if(masterproc) write(iulog,*) 'Creating new decomp: ',this%tag nullify(this%next) end if end subroutine find_iodesc ! Deallocate all entries in the iodesc list subroutine clean_iodesc_list() 2 type(iodesc_list), pointer :: this, prev if(associated(iodesc_list_top%iodesc)) then this => iodesc_list_top iodesc_list_top%tag = -1 call pio_freedecomp(pio_subsystem, this%iodesc) deallocate(this%iodesc) nullify(this%iodesc) this => this%next nullify(iodesc_list_top%next) do while(associated(this)) call pio_freedecomp(pio_subsystem, this%iodesc) deallocate(this%iodesc) prev=>this this=>this%next deallocate(prev) end do end if end subroutine clean_iodesc_list subroutine get_dyn_decomp(iodesc, hdim1, hdim2, nlev, ncnst, dtype, memorder_in, fileorder_in,column_in) 9,7 use dycore, only : dycore_is use pio, only : pio_initdecomp, pio_setdebuglevel use abortutils, only : endrun type(io_desc_t), pointer :: iodesc integer, intent(in) :: hdim1, hdim2, nlev, ncnst integer, intent(in) :: dtype character(len=*), optional :: memorder_in character(len=*), optional :: fileorder_in type(column_info), optional :: column_in character(len=3) :: memorder, fileorder logical :: twodhorizontal, found integer :: dimcnt, dimlens(4) integer, pointer :: ldof(:) integer :: tdecomp call t_startf('get_dyn_decomp') found=.false. if(present(memorder_in)) then memorder=memorder_in else if(dycore_is('LR')) then memorder='xyz' else memorder='xzy' end if if(memorder.eq.'xyz') then tdecomp=1 else tdecomp=3 end if if(present(fileorder_in)) then fileorder = fileorder_in else fileorder = 'xyz' end if twodhorizontal = .true. if(dycore_is('UNSTRUCTURED')) twodhorizontal = .false. dimlens(1)=hdim1 dimcnt=1 if(fileorder=='xyz') then tdecomp=tdecomp+1 if(twodhorizontal) then dimcnt=dimcnt+1 dimlens(dimcnt)=hdim2 end if if(nlev>1) then dimcnt=dimcnt+1 dimlens(dimcnt)=nlev end if else if(nlev>1) then dimcnt=dimcnt+1 dimlens(dimcnt)=nlev end if if(twodhorizontal) then dimcnt=dimcnt+1 dimlens(dimcnt)=hdim2 end if end if if(ncnst>0) then dimcnt=dimcnt+1 dimlens(dimcnt)=ncnst end if found=.false. if(.not.present(column_in)) then call find_iodesc(dimcnt, dimlens(1:dimcnt), dtype, tdecomp, iodesc, found) end if if(.not. found) then if(present(column_in)) then ldof => get_column_ldof(dyn_decomp, column_in, nlev, fileorder, memorder) else ldof => get_dyn_ldof(hdim1, hdim2, nlev*max(1,ncnst),memorder_in=memorder,fileorder_in=fileorder) end if call pio_initdecomp(pio_subsystem, dtype, dimlens(1:dimcnt), ldof, iodesc) deallocate(ldof) end if call t_stopf('get_dyn_decomp') ! call print_memusage('get_dyn_decomp') end subroutine get_dyn_decomp ! ! Get the integer mapping of a variable in the dynamics decomp in memory. ! The canonical ordering is as on the file. A 0 value indicates that the ! variable is not on the file (eg halo or boundary values) ! function get_dyn_ldof(hdim1_d, hdim2_d, nlev, fileorder_in, memorder_in, column_in) result(ldof) 1,14 use dyn_grid, only : get_gcol_block_d, get_block_owner_d, get_dyn_grid_parm, & get_gcol_block_cnt_d, get_block_gcol_cnt_d, get_block_gcol_d, get_block_bounds_d use spmd_utils, only : iam use dycore, only : dycore_is integer, intent(in) :: hdim1_d, hdim2_d, nlev integer, pointer :: ldof(:) integer :: i, block_cnt, max_block_cnt, b, k, ii, j integer :: lcnt, ngcols integer, allocatable :: gcols(:) character(len=3),optional, intent(in) :: fileorder_in, memorder_in type(column_info), optional, intent(in) :: column_in character(len=3) :: fileorder, memorder integer :: bfirst, blast, ncols integer :: beglatxy, beglonxy, endlatxy, endlonxy, plat logical, allocatable :: myblock(:) beglonxy = get_dyn_grid_parm('beglonxy') endlonxy = get_dyn_grid_parm('endlonxy') if(hdim2_d>1) then beglatxy = get_dyn_grid_parm('beglatxy') endlatxy = get_dyn_grid_parm('endlatxy') else beglatxy = 1 endlatxy = 1 end if plat = get_dyn_grid_parm('plat') if(present(fileorder_in)) then fileorder=fileorder_in else fileorder='xyz' end if if(present(memorder_in)) then memorder=memorder_in else if(dycore_is('LR')) then memorder='xyz' else memorder='xzy' end if ngcols = hdim1_d*hdim2_d if(dycore_is('UNSTRUCTURED')) then call get_block_bounds_d(bfirst, blast) allocate(myblock(bfirst:blast)) myblock=.false. lcnt=0 do b=bfirst,blast if(iam.eq.get_block_owner_d(b)) then ncols = get_block_gcol_cnt_d(b) lcnt=lcnt+nlev*ncols myblock(b)=.true. end if end do allocate(ldof(lcnt)) lcnt=0 ldof(:)=0 do k=1,nlev do b=bfirst,blast if(myblock(b)) then ncols = get_block_gcol_cnt_d(b) allocate(gcols(ncols)) call get_block_gcol_d(b, ncols, gcols) do i=1,ncols lcnt=lcnt+1 ldof(lcnt)=gcols(i)+(k-1)*ngcols end do deallocate(gcols) end if end do end do deallocate(myblock) else lcnt=(endlatxy-beglatxy+1)*nlev*(endlonxy-beglonxy+1) allocate(ldof(lcnt)) lcnt=0 ldof(:)=0 if(memorder.eq.'xzy') then do j=beglatxy,endlatxy do k=1,nlev do i=beglonxy, endlonxy lcnt=lcnt+1 if(j.eq.1.and.hdim2_d==plat-1) then ldof(lcnt)=0 else if(fileorder.eq.'xyz') then ldof(lcnt)=i+(j-(plat-hdim2_d+1))*hdim1_d+(k-1)*hdim1_d*hdim2_d else if(fileorder.eq.'xzy') then ldof(lcnt)=i+(j-(plat-hdim2_d+1))*hdim1_d*nlev+(k-1)*hdim1_d end if end if end do end do end do else ! if(memorder.eq.'xyz') then do k=1,nlev do j=beglatxy,endlatxy do i=beglonxy, endlonxy lcnt=lcnt+1 if(j.eq.1.and.hdim2_d==plat-1) then ldof(lcnt)=0 else if(hdim2_d>1) then if(fileorder.eq.'xyz') then ldof(lcnt)=i+(j-(plat-hdim2_d+1))*hdim1_d+(k-1)*hdim1_d*hdim2_d else ldof(lcnt)=i+(j-(plat-hdim2_d+1))*hdim1_d*nlev+(k-1)*hdim1_d end if else ! lon, lev decomp used for history nacs ldof(lcnt)=i+(k-1)*hdim1_d end if end do end do end do end if end if end function get_dyn_ldof subroutine cam_pio_openfile(file, fname, mode, is_init) 15,2 use pio, only : pio_openfile, file_desc_t, pio_noerr, pio_noclobber use abortutils, only : endrun type(file_desc_t), intent(inout), target :: file character(len=*), intent(in) :: fname integer, intent(in) :: mode logical, optional, intent(in) :: is_init integer :: ierr ierr = pio_openfile(pio_subsystem, file, io_type, fname, mode) if(ierr/= PIO_NOERR) then call endrun('Failed to open restart file to read') else if(pio_subsystem%io_rank==0) then write(iulog,*) 'Opened existing file ', trim(fname), file%fh end if end subroutine cam_pio_openfile subroutine cam_pio_createfile(file, fname, mode) 5,3 use pio, only : pio_createfile, file_desc_t, pio_noerr, pio_clobber, pio_64bit_offset use cam_control_mod, only : use_64bit_nc use abortutils, only : endrun type(file_desc_t), intent(inout) :: file character(len=*), intent(in) :: fname integer, intent(in) :: mode integer :: ierr if(use_64bit_nc) then ierr = pio_createfile(pio_subsystem, file, io_type, fname, ior(PIO_CLOBBER,PIO_64BIT_OFFSET)) else ierr = pio_createfile(pio_subsystem, file, io_type, fname, PIO_CLOBBER) end if if(ierr/= PIO_NOERR) then call endrun('Failed to open restart file to write') else if(pio_subsystem%io_rank==0) then write(iulog,*) 'Opened file ', trim(fname), ' to write', file%fh end if end subroutine cam_pio_createfile subroutine set_pio_parameters(io_type_name) 1,4 use pio, only : iotype_netcdf, iotype_pnetcdf, iotype_binary, iotype_pbinary, & pio_iotype_netcdf4p, pio_iotype_netcdf4c use spmd_utils, only : nsmps, npes use cam_logfile, only: iulog use shr_string_mod, only: shr_string_toUpper character(len=*), intent(in) :: io_type_name character(len=16) :: tmpname tmpname = shr_string_toupper(io_type_name) if(tmpname .eq. 'NETCDF') then io_type = iotype_netcdf else if(tmpname .eq. 'PNETCDF') then io_type = iotype_pnetcdf else if(tmpname .eq. 'NETCDF4P') then io_type = pio_iotype_netcdf4p else if(tmpname .eq. 'NETCDF4C') then io_type = pio_iotype_netcdf4c else write(iulog,*) 'Bad io_type argument - using iotype_netcdf' io_type=iotype_netcdf end if if(io_stride>0.and.num_iotasks<0) then num_iotasks = npes/io_stride else if(num_iotasks>0 .and. io_stride<0) then io_stride = npes/num_iotasks else if(num_iotasks<0 .and. io_stride<0) then io_stride = max(min(npes,4),npes/8) num_iotasks = npes/io_stride end if if(io_stride*num_iotasks> npes .or. io_stride<=0 .or. num_iotasks<=0) then write(iulog,*) 'io_stride or num_iotasks out of bounds - resetting to defaults ',io_stride, num_iotasks io_stride = max(1,npes/(nsmps*4)) num_iotasks = npes/io_stride end if write(iulog,*) 'Using io_type ',tmpname,' with stride ',io_stride,' and ',num_iotasks,' io tasks' end subroutine set_pio_parameters ! ! Get the integer mapping of a variable in the physics decomp in memory. ! The canonical ordering is as on the file. A 0 value indicates that the ! variable is not on the file (eg halo or boundary values) ! function get_phys_ldof(hdim1_d,hdim2_d,fdim,mdim,ldim , fileorder_in) result(ldof) 1,9 use phys_grid, only : get_gcol_all_p, get_ncols_p, get_lon_all_p, get_lat_all_p use ppgrid, only : pcols, begchunk, endchunk use dycore, only : dycore_is use spmd_utils, only : mpicom, iam integer, intent(in) :: hdim1_d, hdim2_d, fdim,mdim,ldim character(len=3),optional, intent(in) :: fileorder_in integer, pointer :: ldof(:) integer :: hsize integer :: gcols(pcols), ilat(pcols), ilon(pcols) integer :: ncols, lchnk, i, k, lcnt, f, ii character(len=3) :: fileorder integer :: ierr if(present(fileorder_in)) then fileorder=fileorder_in else fileorder='xyz' end if hsize= hdim1_d*hdim2_d allocate(ldof(pcols*(endchunk-begchunk+1)*fdim*mdim*ldim)) if(fileorder.eq.'xzy') then lcnt=0 do lchnk=begchunk,endchunk ncols = get_ncols_p(lchnk) call get_lon_all_p(lchnk, pcols, ilon) call get_lat_all_p(lchnk, pcols, ilat) do f=1,fdim do k=1,mdim do i=1,pcols do ii=1,ldim lcnt=lcnt+1 if(i<=ncols) then ldof(lcnt) = (ilon(i)-1)*ldim+(k-1)*hdim1_d*ldim+ & (ilat(i)-1)*hdim1_d*mdim*ldim+(f-1)*hsize*mdim*ldim+ii else ldof(lcnt)=0 end if end do end do end do end do end do else lcnt=0 do ii=1,ldim do lchnk=begchunk,endchunk ncols = get_ncols_p(lchnk) call get_gcol_all_p(lchnk, pcols, gcols) do k=1,mdim do i=1,pcols do f=1,fdim lcnt=lcnt+1 if(i<=ncols) then ldof(lcnt) = (f-1)+fdim*(gcols(i)+(k-1)*hsize+(ii-1)*hsize*mdim) else ldof(lcnt)=0 end if end do end do end do end do end do end if end function get_phys_ldof function get_column_ldof(decomp_type, column, nlev, fileorder_in, memorder_in) result(ldof) 2,10 use dyn_grid, only : get_dyn_grid_parm use phys_grid, only : get_gcol_all_p, get_ncols_p, get_lon_all_p, get_lat_all_p use ppgrid, only : pcols, begchunk, endchunk integer, intent(in) :: decomp_type type(column_info), intent(in) :: column integer, intent(in) :: nlev integer :: hsize integer, pointer :: ldof(:) integer :: gcols(pcols), ilat(pcols), ilon(pcols) integer :: ncols, lchnk, i, j, k, lcnt, num_lats, num_lons integer :: lon1, lon2, lat1, lat2, beglatxy, endlatxy, beglonxy, endlonxy character(len=3),optional, intent(in) :: fileorder_in, memorder_in character(len=3) :: fileorder, memorder if(present(fileorder_in)) then fileorder=fileorder_in else fileorder='xyz' end if if(present(memorder_in)) then memorder=memorder_in else memorder='xzy' end if num_lons= column%num_lons num_lats= column%num_lats lat1=column%columnlat(1) lat2=column%columnlat(2) lon1=column%columnlon(1) lon2=column%columnlon(2) hsize= num_lats*num_lons lcnt=0 if(decomp_type==phys_decomp) then allocate(ldof(pcols*(endchunk-begchunk+1)*nlev)) do lchnk=begchunk,endchunk ncols = get_ncols_p(lchnk) call get_lon_all_p(lchnk, pcols, ilon) call get_lat_all_p(lchnk, pcols, ilat) if(fileorder.eq.'xzy') then do k=1,nlev do i=1,pcols lcnt=lcnt+1 if(i<=ncols.and.& ilat(i)>=lat1.and. & ilat(i)<=lat2.and. & ilon(i)>=lon1.and. & ilon(i)<=lon2) then ldof(lcnt) = 1+(ilon(i)-lon1)+(k-1)*num_lons+& (ilat(i)-lat1)*num_lons*nlev else ldof(lcnt)=0 end if end do end do else do k=1,nlev do i=1,pcols lcnt=lcnt+1 if(i<=ncols.and.& ilat(i)>=lat1.and. & ilat(i)<=lat2.and. & ilon(i)>=lon1.and. & ilon(i)<=lon2) then ldof(lcnt) = 1+(ilon(i)-lon1)+(k-1)*hsize+& (ilat(i)-lat1)*num_lons else ldof(lcnt)=0 end if end do end do end if end do else beglonxy = get_dyn_grid_parm('beglonxy') endlonxy = get_dyn_grid_parm('endlonxy') beglatxy = get_dyn_grid_parm('beglatxy') endlatxy = get_dyn_grid_parm('endlatxy') allocate(ldof((endlonxy-beglonxy+1)*(endlatxy-beglatxy+1)*nlev)) ldof = 0 if(memorder.eq.'xzy') then do j=beglatxy,endlatxy do k=1,nlev do i=beglonxy, endlonxy lcnt=lcnt+1 if( j>=lat1.and. & j<=lat2.and. & i>=lon1.and. & i<=lon2) then if(fileorder.eq.'xyz') then ldof(lcnt)=1+(i-lon1)+(j-lat1)*num_lons+(k-1)*hsize else if(fileorder.eq.'xzy') then ldof(lcnt)=1+(i-lon1)+(j-lat1)*num_lons*nlev+(k-1)*num_lons end if end if end do end do end do else do k=1,nlev do j=beglatxy,endlatxy do i=beglonxy, endlonxy lcnt=lcnt+1 if( j>=lat1.and. & j<=lat2.and. & i>=lon1.and. & i<=lon2) then if(fileorder.eq.'xyz') then ldof(lcnt)=1+(i-lon1)+(j-lat1)*num_lons+(k-1)*hsize else ldof(lcnt)=1+(i-lon1)+(j-lat1)*num_lons*nlev+(k-1)*num_lons end if end if end do end do end do end if end if end function get_column_ldof end module cam_pio_utils