!======================================================================= !BOP ! ! !MODULE: ice_transport_driver - drivers for ice transport ! ! !DESCRIPTION: ! ! Drivers for remapping and upwind ice transport ! ! !REVISION HISTORY: ! SVN:$Id: ice_transport_upwind.F 28 2006-11-03 20:32:53Z eclare $ ! ! authors: Elizabeth C. Hunke and William H. Lipscomb, LANL ! ! 2004: Revised by William Lipscomb from ice_transport_mpdata. ! Stripped out mpdata, retained upwind, and added block structure. ! 2006: Incorporated remap transport driver and renamed from ! ice_transport_upwind. ! ! !INTERFACE: module ice_transport_driver 4,5 ! ! !USES: use ice_kinds_mod use ice_communicate, only: my_task, master_task use ice_domain_size use ice_constants use ice_fileunits ! !EOP ! implicit none save character (len=char_len) :: & advection ! type of advection scheme used ! 'upwind' => 1st order donor cell scheme ! 'remap' => remapping scheme logical, parameter :: & ! if true, prescribe area flux across each edge l_fixed_area = .false. ! NOTE: For remapping, hice, hsno, qice, and qsno are considered tracers. ! max_ntrace is not equal to max_ntrcr! ! ntrace is not equal to ntrcr! integer (kind=int_kind), parameter :: & max_ntrace = 2+max_ntrcr+nilyr+nslyr ! hice,hsno,qice,qsno,trcr integer (kind=int_kind) :: & ntrace ! number of tracers in use integer (kind=int_kind), dimension (:), allocatable :: & tracer_type ,&! = 1, 2, or 3 (see comments below) depend ! tracer dependencies (see below) logical (kind=log_kind), dimension (:), allocatable :: & has_dependents ! true if a tracer has dependent tracers integer (kind=int_kind), parameter :: & integral_order = 3 ! polynomial order of quadrature integrals ! linear=1, quadratic=2, cubic=3 logical (kind=log_kind), parameter :: & l_dp_midpt = .true. ! if true, find departure points using ! corrected midpoint velocity !======================================================================= contains !======================================================================= !BOP ! ! !IROUTINE: init_transport - initializations for horizontal transport ! ! !INTERFACE: ! subroutine init_transport 1,8 ! ! !DESCRIPTION: ! ! This subroutine is a wrapper for init_remap, which initializes the ! remapping transport scheme. If the model is run with upwind ! transport, no initializations are necessary. ! ! !REVISION HISTORY: ! ! authors William H. Lipscomb, LANL ! ! !USES: ! use ice_state, only: trcr_depend, ntrcr use ice_exit use ice_timers use ice_transport_remap, only: init_remap ! !EOP ! integer (kind=int_kind) :: & k, nt, nt1 ! tracer indices call ice_timer_start(timer_advect) ! advection ntrace = 2+ntrcr+nilyr+nslyr ! hice,hsno,qice,qsno,trcr allocate (tracer_type (ntrace), & depend (ntrace), & has_dependents(ntrace)) if (trim(advection)=='remap') then !lipscomb - two branches for now; consolidate later ! define tracer dependency arrays ! see comments in remapping routine depend(1:2) = 0 ! hice, hsno tracer_type(1:2) = 1 ! no dependency k = 2 do nt = 1, ntrcr depend(k+nt) = trcr_depend(nt) ! 0 for ice area tracers ! 1 for ice volume tracers ! 2 for snow volume tracers if (trcr_depend(nt) == 0) then tracer_type(k+nt) = 1 else ! trcr_depend = 1 or 2 tracer_type(k+nt) = 2 endif enddo k = k + ntrcr depend(k+1:k+nilyr) = 1 ! qice depends on hice tracer_type(k+1:k+nilyr) = 2 k = k + nilyr depend(k+1:k+nslyr) = 2 ! qsno depends on hsno tracer_type(k+1:k+nslyr) = 2 has_dependents = .false. do nt = 1, ntrace if (depend(nt) > 0) then nt1 = depend(nt) has_dependents(nt1) = .true. if (nt1 > nt) then write(nu_diag,*) & 'Tracer nt2 =',nt,' depends on tracer nt1 =',nt1 call abort_ice & ('ice: remap transport: Must have nt2 > nt1') endif endif enddo ! ntrace call init_remap ! grid quantities else ! upwind continue endif call ice_timer_stop(timer_advect) ! advection end subroutine init_transport !======================================================================= !BOP ! ! !IROUTINE: transport_remap - wrapper for remapping transport scheme ! ! !INTERFACE: ! subroutine transport_remap (dt) 1,37 ! ! !DESCRIPTION: ! ! This subroutine solves the transport equations for one timestep ! using the conservative remapping scheme developed by John Dukowicz ! and John Baumgardner (DB) and modified for sea ice by William ! Lipscomb and Elizabeth Hunke. ! ! This scheme preserves monotonicity of ice area and tracers. That is, ! it does not produce new extrema. It is second-order accurate in space, ! except where gradients are limited to preserve monotonicity. ! ! !REVISION HISTORY: ! ! authors William H. Lipscomb, LANL ! ! !USES: ! use ice_boundary use ice_global_reductions use ice_domain use ice_blocks use ice_state use ice_grid, only: tarea, HTE, HTN use ice_exit use ice_calendar, only: istep1, istep, diagfreq use ice_timers use ice_transport_remap, only: horizontal_remap, make_masks ! ! !INPUT/OUTPUT PARAMETERS: ! real (kind=dbl_kind), intent(in) :: & dt ! time step ! !EOP ! ! local variables integer (kind=int_kind) :: & i, j ,&! horizontal indices iblk ,&! block index ilo,ihi,jlo,jhi,&! beginning and end of physical domain n ,&! ice category index nt, nt1, nt2 ! tracer indices real (kind=dbl_kind), & dimension (nx_block,ny_block,0:ncat,max_blocks) :: & aim ,&! mean ice category areas in each grid cell aimask ! = 1. if ice is present, = 0. otherwise real (kind=dbl_kind), & dimension (nx_block,ny_block,max_ntrace,ncat,max_blocks) :: & trm ,&! mean tracer values in each grid cell trmask ! = 1. if tracer is present, = 0. otherwise logical (kind=log_kind) :: & l_stop ! if true, abort the model integer (kind=int_kind) :: & istop, jstop ! indices of grid cell where model aborts integer (kind=int_kind), dimension(0:ncat,max_blocks) :: & icellsnc ! number of cells with ice integer (kind=int_kind), & dimension(nx_block*ny_block,0:ncat,max_blocks) :: & indxinc, indxjnc ! compressed i/j indices type (block) :: & this_block ! block information for current block !------------------------------------------------------------------- ! If l_fixed_area is true, the area of each departure region is ! computed in advance (e.g., by taking the divergence of the ! velocity field and passed to locate_triangles. The departure ! regions are adjusted to obtain the desired area. ! If false, edgearea is computed in locate_triangles and passed out. !------------------------------------------------------------------- real (kind=dbl_kind), dimension(nx_block,ny_block,max_blocks) :: & edgearea_e ,&! area of departure regions for east edges edgearea_n ! area of departure regions for north edges ! variables related to optional bug checks logical (kind=log_kind), parameter :: & l_conservation_check = .true. ,&! if true, check conservation l_monotonicity_check = .true. ! if true, check monotonicity real (kind=dbl_kind), dimension(0:ncat) :: & asum_init ,&! initial global ice area asum_final ! final global ice area real (kind=dbl_kind), dimension(max_ntrace,ncat) :: & atsum_init ,&! initial global ice area*tracer atsum_final ! final global ice area*tracer real (kind=dbl_kind), dimension (:,:,:,:,:), allocatable :: & tmin ,&! local min tracer tmax ! local max tracer integer (kind=int_kind) :: alloc_error real (kind=dbl_kind), dimension(nx_block,ny_block,max_blocks) :: & work1 call ice_timer_start(timer_advect) ! advection !---!------------------------------------------------------------------- !---! Prepare for remapping. !---! Initialize, update ghost cells, fill tracer arrays. !---!------------------------------------------------------------------- l_stop = .false. istop = 0 jstop = 0 !------------------------------------------------------------------- ! Compute open water area in each grid cell. ! Note: An aggregate_area call is needed only if the open ! water area has changed since the previous call. ! Here we assume that aice0 is up to date. !------------------------------------------------------------------- ! do iblk = 1, nblocks ! call aggregate_area (nx_block, ny_block, ! iblk, & ! aicen(:,:,:,iblk), & ! aice (:,:, iblk), & ! aice0(:,:, iblk)) ! enddo !------------------------------------------------------------------- ! Ghost cell updates for state variables. ! Commented out because ghost cells are updated after cleanup_itd. !------------------------------------------------------------------- ! call ice_timer_start(timer_bound) ! call ice_HaloUpdate (aice0, halo_info, & ! field_loc_center, field_type_scalar) ! call bound_state (aicen, trcrn, & ! vicen, vsnon, & ! eicen, esnon) ! call ice_timer_stop(timer_bound) !------------------------------------------------------------------- ! Ghost cell updates for ice velocity. ! Commented out because ghost cell velocities are computed ! in ice_dyn_evp. !------------------------------------------------------------------- ! call ice_timer_start(timer_bound) ! call ice_HaloUpdate (uvel, halo_info, & ! field_loc_NEcorner, field_type_vector) ! call ice_HaloUpdate (vvel, halo_info, & ! field_loc_NEcorner, field_type_vector) ! call ice_timer_stop(timer_bound) !$OMP PARALLEL DO PRIVATE(iblk) do iblk = 1, nblocks !------------------------------------------------------------------- ! Fill arrays with fields to be remapped. !------------------------------------------------------------------- call state_to_tracers(nx_block, ny_block, & ntrcr, ntrace, & aice0(:,:, iblk), & aicen(:,:,:,iblk), trcrn(:,:,:,:,iblk), & vicen(:,:,:,iblk), vsnon(:,:, :,iblk), & eicen(:,:,:,iblk), esnon(:,:, :,iblk), & aim (:,:,:,iblk), trm (:,:,:,:,iblk)) enddo !$OMP END PARALLEL DO !---!------------------------------------------------------------------- !---! Optional conservation and monotonicity checks. !---!------------------------------------------------------------------- if (l_conservation_check .and. mod(istep,diagfreq) == 0) then !------------------------------------------------------------------- ! Compute initial values of globally conserved quantities. !------------------------------------------------------------------- do n = 0, ncat asum_init(n) = global_sum(aim(:,:,n,:), distrb_info, & field_loc_center, tarea) enddo do n = 1, ncat do nt = 1, ntrace if (tracer_type(nt)==1) then ! does not depend on another tracer atsum_init(nt,n) = & global_sum_prod(trm(:,:,nt,n,:), aim(:,:,n,:), & distrb_info, field_loc_center, & tarea) elseif (tracer_type(nt)==2) then ! depends on another tracer nt1 = depend(nt) do iblk = 1, nblocks do j= 1,ny_block do i = 1,nx_block work1(i,j,iblk) = trm(i,j,nt,n,iblk)*trm(i,j,nt1,n,iblk) end do end do end do atsum_init(nt,n) = & global_sum_prod(work1(:,:,:), aim(:,:,n,:), & distrb_info, field_loc_center, & tarea) elseif (tracer_type(nt)==3) then ! depends on two tracers nt1 = depend(nt) nt2 = depend(nt1) do iblk = 1, nblocks do j= 1,ny_block do i = 1,nx_block work1(i,j,iblk) = trm(i,j,nt,n,iblk)*trm(i,j,nt1,n,iblk) & *trm(i,j,nt2,n,iblk) end do end do end do atsum_init(nt,n) = & global_sum_prod(work1(:,:,:), aim(:,:,n,:), & distrb_info, field_loc_center, & tarea) endif ! tracer_type enddo ! nt enddo ! n endif ! l_conservation_check if (l_monotonicity_check .and. mod(istep,diagfreq) == 0) then allocate(tmin(nx_block,ny_block,ntrace,ncat,max_blocks), & tmax(nx_block,ny_block,ntrace,ncat,max_blocks), & STAT=alloc_error) if (alloc_error /= 0) & call abort_ice ('ice: allocation error') tmin(:,:,:,:,:) = c0 tmax(:,:,:,:,:) = c0 !$OMP PARALLEL DO PRIVATE(iblk,this_block,ilo,ihi,jlo,jhi,n) do iblk = 1, nblocks this_block = get_block(blocks_ice(iblk),iblk) ilo = this_block%ilo ihi = this_block%ihi jlo = this_block%jlo jhi = this_block%jhi !------------------------------------------------------------------- ! Compute masks. ! Masks are used to prevent tracer values in cells without ice ! from being used in the monotonicity check. !------------------------------------------------------------------- call make_masks (nx_block, ny_block, & ilo, ihi, jlo, jhi, & nghost, ntrace, & has_dependents, icellsnc(:,iblk), & indxinc(:,:,iblk), indxjnc(:,:,iblk), & aim(:,:,:,iblk), aimask(:,:,:,iblk), & trm(:,:,:,:,iblk), trmask(:,:,:,:,iblk)) !------------------------------------------------------------------- ! Compute local max and min of tracer fields. !------------------------------------------------------------------- do n = 1, ncat call local_max_min & (nx_block, ny_block, & ilo, ihi, jlo, jhi, & trm (:,:,:,n,iblk), & tmin(:,:,:,n,iblk), tmax (:,:,:,n,iblk), & aimask(:,:,n,iblk), trmask(:,:,:,n,iblk)) enddo enddo !$OMP END PARALLEL DO call ice_timer_start(timer_bound) call ice_HaloUpdate (tmin, halo_info, & field_loc_center, field_type_scalar) call ice_HaloUpdate (tmax, halo_info, & field_loc_center, field_type_scalar) call ice_timer_stop(timer_bound) !$OMP PARALLEL DO PRIVATE(iblk,this_block,ilo,ihi,jlo,jhi,n) do iblk = 1, nblocks this_block = get_block(blocks_ice(iblk),iblk) ilo = this_block%ilo ihi = this_block%ihi jlo = this_block%jlo jhi = this_block%jhi do n = 1, ncat call quasilocal_max_min (nx_block, ny_block, & ilo, ihi, jlo, jhi, & tmin(:,:,:,n,iblk), & tmax(:,:,:,n,iblk)) enddo enddo !$OMP END PARALLEL DO endif ! l_monotonicity_check !------------------------------------------------------------------- ! Main remapping routine: Step ice area and tracers forward in time. !------------------------------------------------------------------- ! If l_fixed_area is true, compute edgearea by taking the divergence ! of the velocity field. Otherwise, initialize edgearea. !------------------------------------------------------------------- !$OMP PARALLEL DO PRIVATE(iblk,i,j) do iblk = 1, nblocks do j = 1, ny_block do i = 1, nx_block edgearea_e(i,j,iblk) = c0 edgearea_n(i,j,iblk) = c0 enddo enddo enddo !$OMP END PARALLEL DO if (l_fixed_area) then !$OMP PARALLEL DO PRIVATE(iblk,this_block,i,j,ilo,ihi,jlo,jhi) do iblk = 1, nblocks this_block = get_block(blocks_ice(iblk),iblk) ilo = this_block%ilo ihi = this_block%ihi jlo = this_block%jlo jhi = this_block%jhi do j = jlo, jhi do i = ilo-1, ihi edgearea_e(i,j,iblk) = (uvel(i,j,iblk) + uvel(i,j-1,iblk)) & * p5 * HTE(i,j,iblk) * dt enddo enddo do j = jlo-1, jhi do i = ilo, ihi edgearea_n(i,j,iblk) = (vvel(i,j,iblk) + vvel(i-1,j,iblk)) & * p5 * HTN(i,j,iblk) * dt enddo enddo enddo ! iblk !$OMP END PARALLEL DO endif call horizontal_remap (dt, ntrace, & uvel (:,:,:), vvel (:,:,:), & aim (:,:,:,:), trm (:,:,:,:,:), & l_fixed_area, & edgearea_e(:,:,:), edgearea_n(:,:,:), & tracer_type, depend, & has_dependents, integral_order, & l_dp_midpt) !------------------------------------------------------------------- ! Given new fields, recompute state variables. !------------------------------------------------------------------- !$OMP PARALLEL DO PRIVATE(iblk) do iblk = 1, nblocks call tracers_to_state (nx_block, ny_block, & ntrcr, ntrace, & aim (:,:,:,iblk), trm (:,:,:,:,iblk), & aice0(:,:, iblk), & aicen(:,:,:,iblk), trcrn(:,:,:,:,iblk), & vicen(:,:,:,iblk), vsnon(:,:, :,iblk), & eicen(:,:,:,iblk), esnon(:,:, :,iblk)) enddo ! iblk !$OMP END PARALLEL DO !------------------------------------------------------------------- ! Ghost cell updates for state variables. !------------------------------------------------------------------- call ice_timer_start(timer_bound) call bound_state (aicen, trcrn, & vicen, vsnon, & eicen, esnon) call ice_timer_stop(timer_bound) !---!------------------------------------------------------------------- !---! Optional conservation and monotonicity checks !---!------------------------------------------------------------------- !------------------------------------------------------------------- ! Compute final values of globally conserved quantities. ! Check global conservation of area and area*tracers. (Optional) !------------------------------------------------------------------- if (l_conservation_check .and. mod(istep,diagfreq) == 0) then do n = 0, ncat asum_final(n) = global_sum(aim(:,:,n,:), distrb_info, & field_loc_center, tarea) enddo do n = 1, ncat do nt = 1, ntrace if (tracer_type(nt)==1) then ! does not depend on another tracer atsum_final(nt,n) = & global_sum_prod(trm(:,:,nt,n,:), aim(:,:,n,:), & distrb_info, field_loc_center, & tarea) elseif (tracer_type(nt)==2) then ! depends on another tracer nt1 = depend(nt) do iblk = 1, nblocks do j= 1,ny_block do i = 1,nx_block work1(i,j,iblk) = trm(i,j,nt,n,iblk)*trm(i,j,nt1,n,iblk) end do end do end do atsum_final(nt,n) = & global_sum_prod(work1(:,:,:), aim(:,:,n,:), & distrb_info, field_loc_center, & tarea) elseif (tracer_type(nt)==3) then ! depends on two tracers nt1 = depend(nt) nt2 = depend(nt1) do iblk = 1, nblocks do j= 1,ny_block do i = 1,nx_block work1(i,j,iblk) = trm(i,j,nt,n,iblk)*trm(i,j,nt1,n,iblk) & *trm(i,j,nt2,n,iblk) end do end do end do atsum_final(nt,n) = & global_sum_prod(work1(:,:,:), aim(:,:,n,:), & distrb_info, field_loc_center, & tarea) endif ! tracer_type enddo ! nt enddo ! n if (my_task == master_task) then call global_conservation (l_stop, & asum_init(0), asum_final(0)) if (l_stop) then write (nu_diag,*) 'istep1 =', istep1 write (nu_diag,*) 'transport: conservation error, cat 0' l_stop = .false. call abort_ice('ice remap transport: conservation error') endif do n = 1, ncat call global_conservation & (l_stop, & asum_init(n), asum_final(n), & atsum_init(:,n), atsum_final(:,n)) if (l_stop) then write (nu_diag,*) 'istep1, cat =', & istep1, n write (nu_diag,*) 'transport: conservation error, cat ',n l_stop = .false. call abort_ice & ('ice remap transport: conservation error') endif enddo ! n endif ! my_task = master_task endif ! l_conservation_check !------------------------------------------------------------------- ! Check tracer monotonicity. (Optional) !------------------------------------------------------------------- if (l_monotonicity_check .and. mod(istep,diagfreq) == 0) then do iblk = 1, nblocks this_block = get_block(blocks_ice(iblk),iblk) ilo = this_block%ilo ihi = this_block%ihi jlo = this_block%jlo jhi = this_block%jhi do n = 1, ncat call check_monotonicity & (nx_block, ny_block, & ilo, ihi, jlo, jhi, & iblk, & tmin(:,:,:,n,iblk), tmax(:,:,:,n,iblk), & aim (:,:, n,iblk), trm (:,:,:,n,iblk), & l_stop, & istop, jstop) if (l_stop) then write (nu_diag,*) 'istep1, my_task, iblk, cat =', & istep1, my_task, iblk, n write (nu_diag,*) 'i_glob, j_glob',this_block%i_glob(istop), & this_block%j_glob(jstop) call abort_ice('ice remap transport: monotonicity error') endif enddo ! n enddo ! iblk deallocate(tmin, tmax, STAT=alloc_error) if (alloc_error /= 0) call abort_ice ('deallocation error') endif ! l_monotonicity_check call ice_timer_stop(timer_advect) ! advection end subroutine transport_remap !======================================================================= !BOP ! ! !IROUTINE: transport_upwind - upwind transport ! ! !INTERFACE: ! subroutine transport_upwind (dt) 1,22 ! ! !DESCRIPTION: ! ! Computes the transport equations for one timestep using upwind. Sets ! several fields into a work array and passes it to upwind routine. ! ! !REVISION HISTORY: ! ! same as module ! ! !USES: ! use ice_boundary use ice_blocks use ice_domain use ice_state use ice_grid, only: HTE, HTN, tarea use ice_timers ! ! !INPUT/OUTPUT PARAMETERS: ! real (kind=dbl_kind), intent(in) :: & dt ! time step ! !EOP ! integer (kind=int_kind) :: & narr ! number of state variable arrays ! not including eicen, esnon integer (kind=int_kind) :: & i, j, iblk ,&! horizontal indices ilo,ihi,jlo,jhi ! beginning and end of physical domain real (kind=dbl_kind), dimension (nx_block,ny_block,nblocks) :: & uee, vnn ! cell edge velocities real (kind=dbl_kind), & dimension (:,:,:,:), allocatable :: & works ! work array type (block) :: & this_block ! block information for current block call ice_timer_start(timer_advect) ! advection narr = 1 + ncat*(3+ntrcr) ! max number of state variable arrays ! not including eicen, esnon allocate (works(nx_block,ny_block,narr,max_blocks)) !------------------------------------------------------------------- ! Get ghost cell values of state variables. ! (Assume velocities are already known for ghost cells, also.) !------------------------------------------------------------------- ! call bound_state (aicen, trcrn, & ! vicen, vsnon, & ! eicen, esnon) uee(:,:,:) = c0 vnn(:,:,:) = c0 !------------------------------------------------------------------- ! Average corner velocities to edges. !------------------------------------------------------------------- !$OMP PARALLEL DO PRIVATE(iblk,this_block,i,j,ilo,ihi,jlo,jhi) do iblk = 1, nblocks this_block = get_block(blocks_ice(iblk),iblk) ilo = this_block%ilo ihi = this_block%ihi jlo = this_block%jlo jhi = this_block%jhi do j = jlo, jhi do i = ilo, ihi uee(i,j,iblk) = p5*(uvel(i,j,iblk) + uvel(i,j-1,iblk)) vnn(i,j,iblk) = p5*(vvel(i,j,iblk) + vvel(i-1,j,iblk)) enddo enddo enddo !$OMP END PARALLEL DO call ice_timer_start(timer_bound) call ice_HaloUpdate (uee, halo_info, & field_loc_Eface, field_type_vector) call ice_HaloUpdate (vnn, halo_info, & field_loc_Nface, field_type_vector) call ice_timer_stop(timer_bound) !$OMP PARALLEL DO PRIVATE(iblk,this_block,ilo,ihi,jlo,jhi) do iblk = 1, nblocks this_block = get_block(blocks_ice(iblk),iblk) ilo = this_block%ilo ihi = this_block%ihi jlo = this_block%jlo jhi = this_block%jhi !----------------------------------------------------------------- ! fill work arrays with fields to be advected !----------------------------------------------------------------- call state_to_work (nx_block, ny_block, & ntrcr, & narr, trcr_depend, & aicen (:,:, :,iblk), trcrn (:,:,:,:,iblk), & vicen (:,:, :,iblk), vsnon (:,:, :,iblk), & aice0 (:,:, iblk), works (:,:, :,iblk)) !----------------------------------------------------------------- ! advect !----------------------------------------------------------------- call upwind_field (nx_block, ny_block, & ilo, ihi, jlo, jhi, & dt, & narr, works(:,:,:,iblk), & uee(:,:,iblk), vnn (:,:,iblk), & HTE(:,:,iblk), HTN (:,:,iblk), & tarea(:,:,iblk)) call upwind_field (nx_block, ny_block, & ilo, ihi, jlo, jhi, & dt, & ntilyr, eicen(:,:,:,iblk), & uee(:,:,iblk), vnn (:,:,iblk), & HTE(:,:,iblk), HTN (:,:,iblk), & tarea(:,:,iblk)) call upwind_field (nx_block, ny_block, & ilo, ihi, jlo, jhi, & dt, & ntslyr, esnon(:,:,:,iblk), & uee(:,:,iblk), vnn (:,:,iblk), & HTE(:,:,iblk), HTN (:,:,iblk), & tarea(:,:,iblk)) !----------------------------------------------------------------- ! convert work arrays back to state variables !----------------------------------------------------------------- call work_to_state (nx_block, ny_block, & ntrcr, & narr, trcr_depend, & aicen(:,:, :,iblk), trcrn (:,:,:,:,iblk), & vicen(:,:, :,iblk), vsnon (:,:, :,iblk), & aice0(:,:, iblk), works (:,:, :,iblk)) enddo ! iblk !$OMP END PARALLEL DO !------------------------------------------------------------------- ! Ghost cell updates for state variables. !------------------------------------------------------------------- call ice_timer_start(timer_bound) call bound_state (aicen, trcrn, & vicen, vsnon, & eicen, esnon) call ice_timer_stop(timer_bound) call ice_timer_stop(timer_advect) ! advection deallocate(works) end subroutine transport_upwind !======================================================================= ! The next few subroutines (through check_monotonicity) are called ! by transport_remap. !======================================================================= ! !BOP ! ! !IROUTINE: state_to_tracers -fill ice area and tracer arrays ! ! !INTERFACE: ! subroutine state_to_tracers (nx_block, ny_block, & 1,1 ntrcr, ntrace, & aice0, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & aim, trm) ! ! !DESCRIPTION: ! ! Fill ice area and tracer arrays. ! Assume that the advected tracers are hicen, hsnon, trcrn, ! qicen(1:nilyr), and qsnon(1:nslyr). ! This subroutine must be modified if a different set of tracers ! is to be transported. The rule for ordering tracers ! is that a dependent tracer (such as qice) must have a larger ! tracer index than the tracer it depends on (i.e., hice). ! ! !REVISION HISTORY: ! ! author William H. Lipscomb, LANL ! ! !USES: ! use ice_itd, only: ilyr1, slyr1 ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions ntrcr , & ! number of tracers in use ntrace ! number of tracers in use incl. hi, hs real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(in) :: & aice0 ! fractional open water area real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(in) :: & aicen ,&! fractional ice area vicen ,&! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(in) :: & trcrn ! ice area tracers real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(in) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(in) :: & esnon ! energy of melting for each snow layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,0:ncat), & intent(out):: & aim ! mean ice area in each grid cell real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrace,ncat), & intent(out) :: & trm ! mean tracer values in each grid cell real (kind=dbl_kind), dimension (nx_block,ny_block) :: & worka, & workb ! !EOP ! integer (kind=int_kind) :: & i, j, k, n ,&! standard indices it, kt ,&! tracer indices ij ! combined i/j index real (kind=dbl_kind) :: & w1 ! work variable integer (kind=int_kind), dimension(nx_block*ny_block,0:ncat) :: & indxi ,&! compressed i/j indices indxj integer (kind=int_kind), dimension(0:ncat) :: & icells ! number of cells with ice worka(:,:) = c0 workb(:,:) = c0 aim(:,:,0) = aice0(:,:) do n = 1, ncat trm(:,:,:,n) = c0 !------------------------------------------------------------------- ! Find grid cells where ice is present and fill area array. !------------------------------------------------------------------- icells(n) = 0 do j = 1, ny_block do i = 1, nx_block aim(i,j,n) = aicen(i,j,n) if (aim(i,j,n) > puny) then icells(n) = icells(n) + 1 ij = icells(n) indxi(ij,n) = i indxj(ij,n) = j endif ! aim > puny enddo enddo !------------------------------------------------------------------- ! Fill tracer array ! Note: If aice > 0, then hice > 0, but we can have hsno = 0. ! Alse note: We transport qice*nilyr rather than qice, so as to ! avoid extra operations here and in tracers_to_state. !------------------------------------------------------------------- do ij = 1, icells(n) i = indxi(ij,n) j = indxj(ij,n) w1 = c1 / aim(i,j,n) worka(i,j) = c1 / vicen(i,j,n) trm(i,j,1,n) = vicen(i,j,n) * w1 ! hice trm(i,j,2,n) = vsnon(i,j,n) * w1 ! hsno if (trm(i,j,2,n) > puny) then workb(i,j) = c1 / vsnon(i,j,n) else workb(i,j) = c0 endif enddo kt = 2 do it = 1, ntrcr do ij = 1, icells(n) i = indxi(ij,n) j = indxj(ij,n) trm(i,j,kt+it,n) = trcrn(i,j,it,n) ! ice area tracers enddo enddo kt = kt + ntrcr do k =1, nilyr do ij = 1, icells(n) i = indxi(ij,n) j = indxj(ij,n) trm(i,j,kt+k,n) = eicen(i,j,ilyr1(n)+k-1)*worka(i,j) ! qice enddo ! ij enddo ! ilyr kt = kt + nilyr do k = 1, nslyr do ij = 1, icells(n) i = indxi(ij,n) j = indxj(ij,n) if (trm(i,j,2,n) > puny) & ! hsno > puny trm(i,j,kt+k,n) = esnon(i,j,slyr1(n)+k-1)*workb(i,j) & ! qsno + rhos*Lfresh enddo ! ij enddo ! nslyr enddo ! ncat end subroutine state_to_tracers !======================================================================= !BOP ! ! !IROUTINE: tracers_to_state - convert tracer array to state variables ! ! !INTERFACE: ! subroutine tracers_to_state (nx_block, ny_block, & 1,1 ntrcr, ntrace, & aim, trm, & aice0, & aicen, trcrn, & vicen, vsnon, & eicen, esnon) ! ! !DESCRIPTION: ! ! Convert area and tracer arrays back to state variables. ! ! !REVISION HISTORY: ! ! author William H. Lipscomb, LANL ! ! !USES: ! use ice_itd, only: ilyr1, slyr1 ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions ntrcr , & ! number of tracers in use ntrace ! number of tracers in use incl. hi, hs real (kind=dbl_kind), dimension (nx_block,ny_block,0:ncat), & intent(in) :: & aim ! fractional ice area real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrace,ncat), & intent(in) :: & trm ! mean tracer values in each grid cell real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(inout) :: & aice0 ! fractional ice area real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(inout) :: & aicen ,&! fractional ice area vicen ,&! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(inout) :: & trcrn ! tracers real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(inout) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(inout) :: & esnon ! energy of melting for each snow layer (J/m^2) ! !EOP ! integer (kind=int_kind) :: & i, j, k, n ,&! standard indices it, kt ,&! tracer indices icells ,&! number of cells with ice ij integer (kind=int_kind), dimension (nx_block*ny_block) :: & indxi, indxj ! compressed indices aice0(:,:) = aim(:,:,0) do n = 1, ncat icells = 0 do j = 1, ny_block do i = 1, nx_block if (aim(i,j,n) > c0) then icells = icells + 1 indxi(icells) = i indxj(icells) = j endif enddo enddo !------------------------------------------------------------------- ! Compute state variables. !------------------------------------------------------------------- do ij = 1, icells i = indxi(ij) j = indxj(ij) aicen(i,j,n) = aim(i,j,n) vicen(i,j,n) = aim(i,j,n)*trm(i,j,1,n) ! aice*hice vsnon(i,j,n) = aim(i,j,n)*trm(i,j,2,n) ! aice*hsno enddo ! ij kt = 2 do it = 1, ntrcr do ij = 1, icells i = indxi(ij) j = indxj(ij) trcrn(i,j,it,n) = trm(i,j,kt+it,n) ! ice tracers enddo ! ij enddo ! ntrcr kt = kt + ntrcr do k = 1, nilyr do ij = 1, icells i = indxi(ij) j = indxj(ij) eicen(i,j,ilyr1(n)+k-1) = vicen(i,j,n)*trm(i,j,kt+k,n) enddo ! ij enddo ! nilyr kt = kt + nilyr do k = 1, nslyr do ij = 1, icells i = indxi(ij) j = indxj(ij) esnon(i,j,slyr1(n)+k-1) = (trm(i,j,kt+k,n) - rhos*Lfresh) & * vsnon(i,j,n) enddo ! ij enddo ! nslyr enddo ! ncat end subroutine tracers_to_state !======================================================================= ! !BOP ! ! !IROUTINE: global_conservation - check for changes in conserved quantities ! ! !INTERFACE: ! subroutine global_conservation (l_stop, & 2 asum_init, asum_final, & atsum_init, atsum_final) ! ! !DESCRIPTION: ! ! Check whether values of conserved quantities have changed. ! An error probably means that ghost cells are treated incorrectly. ! ! !REVISION HISTORY: ! ! author William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! real (kind=dbl_kind), intent(in) :: & asum_init ,&! initial global ice area asum_final ! final global ice area real (kind=dbl_kind), dimension(max_ntrace), intent(in), optional :: & atsum_init ,&! initial global ice area*tracer atsum_final ! final global ice area*tracer logical (kind=log_kind), intent(inout) :: & l_stop ! if true, abort on return ! !EOP ! integer (kind=int_kind) :: & nt ! tracer index real (kind=dbl_kind) :: & diff ! difference between initial and final values if (asum_init > puny) then diff = asum_final - asum_init if (abs(diff/asum_init) > puny) then l_stop = .true. write (nu_diag,*) write (nu_diag,*) 'Ice area conserv error' write (nu_diag,*) 'Initial global area =', asum_init write (nu_diag,*) 'Final global area =', asum_final write (nu_diag,*) 'Fractional error =', abs(diff)/asum_init write (nu_diag,*) 'asum_final-asum_init =', diff endif endif if (present(atsum_init)) then do nt = 1, ntrace if (abs(atsum_init(nt)) > puny) then diff = atsum_final(nt) - atsum_init(nt) if (abs(diff/atsum_init(nt)) > puny) then l_stop = .true. write (nu_diag,*) write (nu_diag,*) 'area*tracer conserv error' write (nu_diag,*) 'tracer index =', nt write (nu_diag,*) 'Initial global area*tracer =', & atsum_init(nt) write (nu_diag,*) 'Final global area*tracer =', & atsum_final(nt) write (nu_diag,*) 'Fractional error =', & abs(diff)/atsum_init(nt) write (nu_diag,*) 'atsum_final-atsum_init =', diff endif endif enddo endif ! present(atsum_init) end subroutine global_conservation !======================================================================= !BOP ! ! !IROUTINE: local_max_min - compute local max and min of a scalar field ! ! !INTERFACE: ! subroutine local_max_min (nx_block, ny_block, & 1 ilo, ihi, jlo, jhi, & trm, & tmin, tmax, & aimask, trmask) ! ! !DESCRIPTION: ! ! At each grid point, compute the local max and min of a scalar ! field phi: i.e., the max and min values in the nine-cell region ! consisting of the home cell and its eight neighbors. ! ! To extend to the neighbors of the neighbors (25 cells in all), ! follow this call with a call to quasilocal_max_min. ! ! !REVISION HISTORY: ! ! author William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block,&! block dimensions ilo,ihi,jlo,jhi ! beginning and end of physical domain real (kind=dbl_kind), intent(in), & dimension(nx_block,ny_block) :: & aimask ! ice area mask real (kind=dbl_kind), intent(in), & dimension (nx_block,ny_block,max_ntrace) :: & trm ,&! tracer fields trmask ! tracer mask real (kind=dbl_kind), intent(out), & dimension (nx_block,ny_block,max_ntrace) :: & tmin ,&! local min tracer tmax ! local max tracer ! !EOP ! integer (kind=int_kind) :: & i, j ,&! horizontal indices nt, nt1 ! tracer indices real (kind=dbl_kind), dimension(nx_block,ny_block) :: & phimask ! aimask or trmask, as appropriate real (kind=dbl_kind) :: & phi_nw, phi_n, phi_ne ,&! field values in 8 neighbor cells phi_w, phi_e ,& phi_sw, phi_s, phi_se do nt = 1, ntrace if (tracer_type(nt)==1) then ! does not depend on another tracer do j = 1, ny_block do i = 1, nx_block phimask(i,j) = aimask(i,j) enddo enddo else ! depends on another tracer nt1 = depend(nt) do j = 1, ny_block do i = 1, nx_block phimask(i,j) = trmask(i,j,nt1) enddo enddo endif !----------------------------------------------------------------------- ! Store values of trm in the 8 neighbor cells. ! If aimask = 1, use the true value; otherwise use the home cell value ! so that non-physical values of phi do not contribute to the gradient. !----------------------------------------------------------------------- do j = jlo, jhi do i = ilo, ihi phi_nw = phimask(i-1,j+1) * trm(i-1,j+1,nt) & + (c1-phimask(i-1,j+1))* trm(i, j, nt) phi_n = phimask(i, j+1) * trm(i, j+1,nt) & + (c1-phimask(i, j+1))* trm(i, j, nt) phi_ne = phimask(i+1,j+1) * trm(i+1,j+1,nt) & + (c1-phimask(i+1,j+1))* trm(i, j, nt) phi_w = phimask(i-1,j) * trm(i-1,j, nt) & + (c1-phimask(i-1,j)) * trm(i, j, nt) phi_e = phimask(i+1,j) * trm(i+1,j, nt) & + (c1-phimask(i+1,j)) * trm(i, j, nt) phi_sw = phimask(i-1,j-1) * trm(i-1,j-1,nt) & + (c1-phimask(i-1,j-1))* trm(i, j, nt) phi_s = phimask(i, j-1) * trm(i, j-1,nt) & + (c1-phimask(i, j-1))* trm(i, j, nt) phi_se = phimask(i+1,j-1) * trm(i+1,j-1,nt) & + (c1-phimask(i+1,j-1))* trm(i, j, nt) !----------------------------------------------------------------------- ! Compute the minimum and maximum among the nine local cells. !----------------------------------------------------------------------- tmax(i,j,nt) = max (phi_nw, phi_n, phi_ne, phi_w, & trm(i,j,nt), phi_e, phi_sw, phi_s, phi_se) tmin(i,j,nt) = min (phi_nw, phi_n, phi_ne, phi_w, & trm(i,j,nt), phi_e, phi_sw, phi_s, phi_se) enddo ! i enddo ! j enddo ! nt end subroutine local_max_min !======================================================================= !BOP ! ! !IROUTINE: quasilocal_max_min - look one grid cell farther away ! ! !INTERFACE: ! subroutine quasilocal_max_min (nx_block, ny_block, & 1 ilo, ihi, jlo, jhi, & tmin, tmax) ! ! !DESCRIPTION: ! ! Extend the local max and min by one grid cell in each direction. ! Incremental remapping is monotone for the "quasilocal" max and min, ! but in rare cases may violate monotonicity for the local max and min. ! ! !REVISION HISTORY: ! ! author William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block,&! block dimensions ilo,ihi,jlo,jhi ! beginning and end of physical domain real (kind=dbl_kind), intent(inout), & dimension (nx_block,ny_block,ntrace) :: & tmin ,&! local min tracer tmax ! local max tracer ! !EOP ! integer (kind=int_kind) :: & i, j ,&! horizontal indices nt ! tracer index do nt = 1, ntrace do j = jlo, jhi do i = ilo, ihi tmax(i,j,nt) = & max (tmax(i-1,j+1,nt), tmax(i,j+1,nt), tmax(i+1,j+1,nt), & tmax(i-1,j, nt), tmax(i,j, nt), tmax(i+1,j, nt), & tmax(i-1,j-1,nt), tmax(i,j-1,nt), tmax(i+1,j-1,nt)) tmin(i,j,nt) = & min (tmin(i-1,j+1,nt), tmin(i,j+1,nt), tmin(i+1,j+1,nt), & tmin(i-1,j, nt), tmin(i,j, nt), tmin(i+1,j, nt), & tmin(i-1,j-1,nt), tmin(i,j-1,nt), tmin(i+1,j-1,nt)) enddo ! i enddo ! j enddo end subroutine quasilocal_max_min !====================================================================== ! !BOP ! ! !IROUTINE: check_monotonicity - check bounds on new tracer values ! ! !INTERFACE: ! subroutine check_monotonicity (nx_block, ny_block, & 1 ilo, ihi, jlo, jhi, & iblk, & tmin, tmax, & aim, trm, & l_stop, & istop, jstop) ! ! !DESCRIPTION: ! ! At each grid point, make sure that the new tracer values ! fall between the local max and min values before transport. ! ! !REVISION HISTORY: ! ! author William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block,&! block dimensions ilo,ihi,jlo,jhi ,&! beginning and end of physical domain iblk ! block index (diagnostic only) real (kind=dbl_kind), intent(in), & dimension (nx_block,ny_block) :: & aim ! new ice area real (kind=dbl_kind), intent(in), & dimension (nx_block,ny_block,max_ntrace) :: & trm ! new tracers real (kind=dbl_kind), intent(in), & dimension (nx_block,ny_block,ntrace) :: & tmin ,&! local min tracer tmax ! local max tracer logical (kind=log_kind), intent(inout) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(inout) :: & istop, jstop ! indices of grid cell where model aborts ! !EOP ! integer (kind=int_kind) :: & i, j ,&! horizontal indices nt, nt1, nt2 ! tracer indices real (kind=dbl_kind) :: & w1, w2 ! work variables logical (kind=log_kind), dimension (nx_block, ny_block) :: & l_check ! if true, check monotonicity do nt = 1, ntrace !------------------------------------------------------------------- ! Load logical array to identify tracers that need checking. !------------------------------------------------------------------- if (tracer_type(nt)==1) then ! does not depend on another tracer do j = jlo, jhi do i = ilo, ihi if (aim(i,j) > puny) then l_check(i,j) = .true. else l_check(i,j) = .false. endif enddo enddo elseif (tracer_type(nt)==2) then ! depends on another tracer nt1 = depend(nt) do j = jlo, jhi do i = ilo, ihi if (abs(trm(i,j,nt1)) > puny .and. aim(i,j) > puny) then l_check(i,j) = .true. else l_check(i,j) = .false. endif enddo enddo elseif (tracer_type(nt)==3) then ! depends on two tracers nt1 = depend(nt) nt2 = depend(nt1) do j = jlo, jhi do i = ilo, ihi if (abs(trm(i,j,nt1)) > puny .and. & abs(trm(i,j,nt2)) > puny .and. aim(i,j) > puny) then l_check(i,j) = .true. else l_check(i,j) = .false. endif enddo enddo endif !------------------------------------------------------------------- ! Make sure new values lie between tmin and tmax !------------------------------------------------------------------- do j = jlo, jhi do i = ilo, ihi if (l_check(i,j)) then ! w1 and w2 allow for roundoff error when abs(trm) is big w1 = max(c1, abs(tmin(i,j,nt))) w2 = max(c1, abs(tmax(i,j,nt))) if (trm(i,j,nt) < tmin(i,j,nt)-w1*puny) then l_stop = .true. istop = i jstop = j write (nu_diag,*) ' ' write (nu_diag,*) 'new tracer < tmin' write (nu_diag,*) 'i, j, nt =', i, j, nt write (nu_diag,*) 'new tracer =', trm (i,j,nt) write (nu_diag,*) 'tmin =' , tmin(i,j,nt) write (nu_diag,*) 'ice area =' , aim(i,j) elseif (trm(i,j,nt) > tmax(i,j,nt)+w2*puny) then l_stop = .true. istop = i jstop = j write (nu_diag,*) ' ' write (nu_diag,*) 'new tracer > tmax' write (nu_diag,*) 'i, j, nt =', i, j, nt write (nu_diag,*) 'new tracer =', trm (i,j,nt) write (nu_diag,*) 'tmax =' , tmax(i,j,nt) write (nu_diag,*) 'ice area =' , aim(i,j) endif endif enddo ! i enddo ! j enddo ! nt end subroutine check_monotonicity !======================================================================= ! The remaining subroutines are called by transport_upwind. !======================================================================= !BOP ! ! !IROUTINE: state_to_work - fill work arrays with state variables ! ! !INTERFACE: ! subroutine state_to_work (nx_block, ny_block, & 1,1 ntrcr, & narr, trcr_depend, & aicen, trcrn, & vicen, vsnon, & aice0, works) ! ! !DESCRIPTION: ! ! Fill work array with state variables in preparation for upwind transport ! ! !REVISION HISTORY: ! ! same as module ! ! !USES: ! use ice_itd, only: ilyr1, slyr1 ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block ,&! block dimensions ntrcr , & ! number of tracers in use narr ! number of 2D state variable arrays in works array integer (kind=int_kind), dimension (max_ntrcr), intent(in) :: & trcr_depend ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(in) :: & aicen ,&! concentration of ice vicen ,&! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(in) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(in) :: & aice0 ! concentration of open water real (kind=dbl_kind), dimension(nx_block,ny_block,narr), & intent (out) :: & works ! work array ! !EOP ! integer (kind=int_kind) :: & i, j, k, n, it ,&! counting indices narrays ! counter for number of state variable arrays !----------------------------------------------------------------- ! This array is used for performance (balance memory/cache vs ! number of bound calls); a different number of arrays may perform ! better depending on the machine used, number of processors, etc. ! --tested on SGI R2000, using 4 pes for the ice model under MPI !----------------------------------------------------------------- do j = 1, ny_block do i = 1, nx_block works(i,j,1) = aice0(i,j) enddo enddo narrays = 1 do n=1, ncat do j = 1, ny_block do i = 1, nx_block works(i,j,narrays+1) = aicen(i,j,n) works(i,j,narrays+2) = vicen(i,j,n) works(i,j,narrays+3) = vsnon(i,j,n) enddo ! i enddo ! j narrays = narrays + 3 do it = 1, ntrcr if (trcr_depend(it) == 0) then do j = 1, ny_block do i = 1, nx_block works(i,j,narrays+it) = aicen(i,j,n)*trcrn(i,j,it,n) enddo enddo elseif (trcr_depend(it) == 1) then do j = 1, ny_block do i = 1, nx_block works(i,j,narrays+it) = vicen(i,j,n)*trcrn(i,j,it,n) enddo enddo elseif (trcr_depend(it) == 2) then do j = 1, ny_block do i = 1, nx_block works(i,j,narrays+it) = vsnon(i,j,n)*trcrn(i,j,it,n) enddo enddo endif enddo narrays = narrays + ntrcr enddo ! n if (narr /= narrays) write(nu_diag,*) & "Wrong number of arrays in transport bound call" end subroutine state_to_work !======================================================================= !BOP ! ! !IROUTINE: work_to_state - convert work arrays back to state variables ! ! !INTERFACE: ! subroutine work_to_state (nx_block, ny_block, & 1,2 ntrcr, & narr, trcr_depend, & aicen, trcrn, & vicen, vsnon, & aice0, works) ! ! !DESCRIPTION: ! ! Convert work array back to state variables ! ! !REVISION HISTORY: ! ! same as module ! ! !USES: ! use ice_itd, only: ilyr1, slyr1, compute_tracers ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent (in) :: & nx_block, ny_block, & ! block dimensions ntrcr , & ! number of tracers in use narr ! number of 2D state variable arrays in works array integer (kind=int_kind), dimension (max_ntrcr), intent(in) :: & trcr_depend ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon real (kind=dbl_kind), intent (in) :: & works (nx_block,ny_block,narr) real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(out) :: & aicen ,&! concentration of ice vicen ,&! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(out) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(out) :: & aice0 ! concentration of open water ! !EOP ! integer (kind=int_kind) :: & i, j, k, n , it,&! counting indices narrays ,&! counter for number of state variable arrays icells ! number of ocean/ice cells integer (kind=int_kind), dimension (nx_block*ny_block) :: & indxi, indxj real (kind=dbl_kind), dimension (nx_block*ny_block,narr) :: & work ! for call to compute_tracers icells = 0 do j = 1, ny_block do i = 1, nx_block icells = icells + 1 indxi(icells) = i indxj(icells) = j work (icells,:) = works(i,j,:) enddo enddo do j=1,ny_block do i=1,nx_block aice0(i,j) = works(i,j,1) enddo enddo narrays = 1 ! aice0 is first array do n=1,ncat do j=1,ny_block do i=1,nx_block aicen(i,j,n) = works(i,j,narrays+1) vicen(i,j,n) = works(i,j,narrays+2) vsnon(i,j,n) = works(i,j,narrays+3) enddo enddo narrays = narrays + 3 call compute_tracers (nx_block, ny_block, & icells, indxi, indxj, & ntrcr, trcr_depend, & work (:,narrays+1:narrays+ntrcr), & aicen(:,:,n), & vicen(:,:,n), vsnon(:,:,n), & trcrn(:,:,:,n)) narrays = narrays + ntrcr enddo ! ncat end subroutine work_to_state !======================================================================= !BOP ! ! !IROUTINE: upwind_field - advection according to upwind ! ! !INTERFACE: ! subroutine upwind_field (nx_block, ny_block, & 3,6 ilo, ihi, jlo, jhi, & dt, & narrays, phi, & uee, vnn, & HTE, HTN, & tarea) ! ! !DESCRIPTION: ! ! upwind transport algorithm ! ! !REVISION HISTORY: ! ! same as module ! ! !USES: ! ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent (in) :: & nx_block, ny_block ,&! block dimensions ilo,ihi,jlo,jhi ,&! beginning and end of physical domain narrays ! number of 2D arrays to be transported real (kind=dbl_kind), intent(in) :: & dt ! time step real (kind=dbl_kind), dimension(nx_block,ny_block,narrays), & intent(inout) :: & phi ! scalar field real (kind=dbl_kind), dimension(nx_block,ny_block), & intent(in):: & uee, vnn ! cell edge velocities real (kind=dbl_kind), dimension(nx_block,ny_block), intent(in) :: & HTE ,&! length of east cell edge HTN ,&! length of north cell edge tarea ! grid cell area ! !EOP ! integer (kind=int_kind) :: & i, j, k, n ! standard indices real (kind=dbl_kind) :: & upwind, y1, y2, a, h ! function real (kind=dbl_kind), dimension (nx_block,ny_block) :: & worka, & workb !------------------------------------------------------------------- ! Define upwind function !------------------------------------------------------------------- upwind(y1,y2,a,h) = p5*dt*h*((a+abs(a))*y1+(a-abs(a))*y2) !------------------------------------------------------------------- ! upwind transport !------------------------------------------------------------------- worka(:,:) = c0 workb(:,:) = c0 do n = 1, narrays do j = 1, jhi do i = 1, ihi worka(i,j)= & upwind(phi(i,j,n),phi(i+1,j,n),uee(i,j),HTE(i,j)) workb(i,j)= & upwind(phi(i,j,n),phi(i,j+1,n),vnn(i,j),HTN(i,j)) enddo enddo do j = jlo, jhi do i = ilo, ihi phi(i,j,n) = phi(i,j,n) - ( worka(i,j)-worka(i-1,j) & + workb(i,j)-workb(i,j-1) ) & / tarea(i,j) enddo enddo enddo ! narrays end subroutine upwind_field !======================================================================= end module ice_transport_driver !=======================================================================