prismtrs_pole_intersection.F90

Go to the documentation of this file.
00001 !------------------------------------------------------------------------
00002 ! Copyright 2006-2010, CERFACS, Toulouse, France.
00003 ! All rights reserved. Use is subject to OASIS4 license terms.
00004 !-----------------------------------------------------------------------
00005 !BOP
00006 !
00007 ! !ROUTINE: PRISMTrs_pole_intersection
00008 !
00009 ! !INTERFACE
00010       subroutine prismtrs_pole_intersection(location, &
00011                       intrsct_lat,intrsct_lon,lcoinc,lthresh, &
00012                       beglat, beglon, endlat, endlon, begseg, lrevers, &
00013                       num_srch_cells, id_nbcorners, &
00014                       srch_corner_lat, srch_corner_lon, srch_add)
00015 
00016 !-----------------------------------------------------------------------
00017 !
00018 !     this routine 
00019 !
00020 !-----------------------------------------------------------------------
00021 
00022         !
00023       USE PRISMDrv, dummy_INTERFACE => PRISMTrs_pole_intersection
00024       IMPLICIT NONE
00025 !
00026 ! !PARAMETERS:
00027 !
00028       INTEGER, INTENT(in) :: num_srch_cells, id_nbcorners
00029       DOUBLE PRECISION, DIMENSION(id_nbcorners, num_srch_cells), INTENT(in) ::
00030            srch_corner_lat, srch_corner_lon
00031       INTEGER, DIMENSION(num_srch_cells), INTENT(in) :: srch_add
00032         !
00033       DOUBLE PRECISION, intent(in) :: 
00034            beglat, beglon,  ! beginning lat/lon endpoints for segment
00035            endlat, endlon   ! ending    lat/lon endpoints for segment
00036 
00037       DOUBLE PRECISION, dimension(2), intent(inout) :: 
00038            begseg ! begin lat/lon of full segment
00039 
00040       logical, intent(in) :: 
00041               lrevers   ! flag true if segment integrated in reverse
00042 !
00043 ! !RETURN VALUE
00044 !
00045       integer, intent(inout) :: 
00046               location  ! address in destination array containing this
00047                         ! segment -- also may contain last location on
00048                         ! entry
00049 
00050       logical, intent(out) :: 
00051              lcoinc    ! flag segment coincident with grid line
00052 
00053       logical, intent(inout) :: 
00054              lthresh   ! flag segment crossing threshold boundary
00055 
00056       DOUBLE PRECISION, intent(out) :: 
00057           intrsct_lat, intrsct_lon ! lat/lon coords of next intersect.
00058 !
00059 ! !DESCRIPTION
00060 !
00061 ! SUBROUTINE "PRISMTrs_pole_intersection" is identical to the intersection 
00062 ! routine except that a coordinate transformation (using a Lambert azimuthal
00063 ! equivalent projection) is performed to treat polar cells more accurately.
00064 ! This routine comes from the SCRIP 1.4 library available from Los Alamos 
00065 ! National Laboratory.
00066 !
00067 ! !REVISED HISTORY
00068 !   Date      Programmer   Description
00069 ! ----------  ----------   -----------
00070 ! 21/03/2007  S. Valcke    Duplicated from the SCRIP library
00071 !
00072 ! EOP
00073 !----------------------------------------------------------------------
00074 ! $Id: prismtrs_pole_intersection.F90 2082 2009-10-21 13:31:19Z hanke $
00075 ! $Author: hanke $
00076 !----------------------------------------------------------------------
00077 !
00078 !----------------------------------------------------------------------
00079 ! Local declarations
00080 !-----------------------------------------------------------------------
00081   CHARACTER(LEN=len_cvs_string), SAVE  :: mycvs = 
00082      '$Id: prismtrs_pole_intersection.F90 2082 2009-10-21 13:31:19Z hanke $'
00083       integer  :: n, next_n, cell, srch_corners
00084 
00085       logical  :: loutside ! flags points outside grid
00086 
00087       DOUBLE PRECISION :: pi4, rns,  ! north/south conversion
00088           x1, x2,       ! local x variables for segment
00089           y1, y2,       ! local y variables for segment
00090           begx, begy,   ! beginning x,y variables for segment
00091           endx, endy,   ! beginning x,y variables for segment
00092           begsegx, begsegy,   ! beginning x,y variables for segment
00093           grdx1, grdx2,  ! local x variables for grid cell
00094           grdy1, grdy2,  ! local y variables for grid cell
00095           vec1_y, vec1_x,  ! vectors and cross products used
00096           vec2_y, vec2_x,  ! during grid search
00097           cross_product, eps,  ! eps=small offset away from intersect
00098           s1, s2, determ,     ! variables used for linear solve to
00099           mat1, mat2, mat3, mat4, rhs1, rhs2  ! find intersection
00100 
00101       DOUBLE PRECISION, dimension(:,:), allocatable :: 
00102            srch_corner_x,  ! x of each corner of srch cells
00103            srch_corner_y   ! y of each corner of srch cells
00104 
00105       !***
00106       !*** save last intersection to avoid roundoff during coord
00107       !*** transformation
00108       !***
00109 
00110       logical, save :: luse_last = .false.
00111 
00112       DOUBLE PRECISION, save :: 
00113            intrsct_x, intrsct_y  ! x,y for intersection
00114 
00115       !***
00116       !*** variables necessary if segment manages to hit pole
00117       !***
00118 
00119       integer, save :: 
00120            avoid_pole_count = 0  ! count attempts to avoid pole
00121 
00122       DOUBLE PRECISION, save :: 
00123            avoid_pole_offset = tiny  ! endpoint offset to avoid pole
00124 
00125 #ifdef VERBOSE
00126   PRINT *, '| | | | | | | Enter PRISMTrs_pole_intersection'
00127   call psmile_flushstd
00128 #endif
00129 !-----------------------------------------------------------------------
00130 !
00131 !     initialize defaults, flags, etc.
00132 !
00133 !-----------------------------------------------------------------------
00134 
00135       if (.not. lthresh) location = 0
00136       lcoinc = .false.
00137       intrsct_lat = endlat
00138       intrsct_lon = endlon
00139 
00140       loutside = .false.
00141       s1 = zero
00142 
00143 !-----------------------------------------------------------------------
00144 !
00145 !     convert coordinates
00146 !
00147 !-----------------------------------------------------------------------
00148 
00149       allocate(srch_corner_x(size(srch_corner_lat,DIM=1), &
00150                              size(srch_corner_lat,DIM=2)), &
00151                srch_corner_y(size(srch_corner_lat,DIM=1), &
00152                             size(srch_corner_lat,DIM=2)))
00153 
00154       if (beglat > zero) then
00155         pi4 = quart*dble_pi
00156         rns = one
00157       else
00158         pi4 = -quart*dble_pi
00159         rns = -one
00160       endif
00161 
00162       if (luse_last) then
00163         x1 = intrsct_x
00164         y1 = intrsct_y
00165       else
00166         x1 = rns*two*sin(pi4 - half*beglat)*cos(beglon)
00167         y1 =     two*sin(pi4 - half*beglat)*sin(beglon)
00168         luse_last = .true.
00169       endif
00170       x2 = rns*two*sin(pi4 - half*endlat)*cos(endlon)
00171       y2 =     two*sin(pi4 - half*endlat)*sin(endlon)
00172       srch_corner_x = rns*two*sin(pi4 - half*srch_corner_lat)* &
00173                               cos(srch_corner_lon)
00174       srch_corner_y =     two*sin(pi4 - half*srch_corner_lat)* &
00175                               sin(srch_corner_lon)
00176 
00177       begx = x1
00178       begy = y1
00179       endx = x2
00180       endy = y2
00181       begsegx = rns*two*sin(pi4 - half*begseg(1))*cos(begseg(2))
00182       begsegy =     two*sin(pi4 - half*begseg(1))*sin(begseg(2))
00183       intrsct_x = endx
00184       intrsct_y = endy
00185 
00186 !-----------------------------------------------------------------------
00187 !
00188 !     search for location of this segment in ocean grid using cross
00189 !     product method to determine whether a point is enclosed by a cell
00190 !
00191 !-----------------------------------------------------------------------
00192 
00193       ! call timer_start(12)
00194       srch_corners = size(srch_corner_lat,DIM=1)
00195       srch_loop: do
00196 
00197         !***
00198         !*** if last segment crossed threshold, use that location
00199         !***
00200 
00201         if (lthresh) then
00202           do cell=1,num_srch_cells
00203             if (srch_add(cell) == location) then
00204               eps = tiny
00205               exit srch_loop
00206             endif
00207           end do
00208         endif
00209 
00210         !***
00211         !*** otherwise normal search algorithm
00212         !***
00213 
00214         cell_loop: do cell=1,num_srch_cells
00215           corner_loop: do n=1,srch_corners
00216             next_n = MOD(n,srch_corners) + 1
00217 
00218             !***
00219             !*** here we take the cross product of the vector making 
00220             !*** up each cell side with the vector formed by the vertex
00221             !*** and search point.  if all the cross products are 
00222             !*** positive, the point is contained in the cell.
00223             !***
00224 
00225             vec1_x = srch_corner_x(next_n,cell) -  &
00226                     srch_corner_x(n     ,cell)
00227             vec1_y = srch_corner_y(next_n,cell) -  &
00228                     srch_corner_y(n     ,cell)
00229             vec2_x = x1 - srch_corner_x(n,cell)
00230             vec2_y = y1 - srch_corner_y(n,cell)
00231 
00232             !***
00233             !*** if endpoint coincident with vertex, offset
00234             !*** the endpoint
00235             !***
00236 
00237             if (vec2_x == 0 .and. vec2_y == 0) then
00238               x1 = x1 + 1.d-10*(x2-x1)
00239               y1 = y1 + 1.d-10*(y2-y1)
00240               vec2_x = x1 - srch_corner_x(n,cell)
00241               vec2_y = y1 - srch_corner_y(n,cell)
00242             endif
00243 
00244             cross_product = vec1_x*vec2_y - vec2_x*vec1_y
00245 
00246             !***
00247             !*** if the cross product for a side is zero, the point 
00248             !***   lies exactly on the side or the length of a side
00249             !***   is zero.  if the length is zero set det > 0.
00250             !***   otherwise, perform another cross 
00251             !***   product between the side and the segment itself. 
00252             !*** if this cross product is also zero, the line is 
00253             !***   coincident with the cell boundary - perform the 
00254             !***   dot product and only choose the cell if the dot 
00255             !***   product is positive (parallel vs anti-parallel).
00256             !***
00257 
00258             if (cross_product == zero) then
00259               if (vec1_x /= zero .or. vec1_y /= 0) then
00260                 vec2_x = x2 - x1
00261                 vec2_y = y2 - y1
00262                 cross_product = vec1_x*vec2_y - vec2_x*vec1_y
00263               else
00264                 cross_product = one
00265               endif
00266 
00267               if (cross_product == zero) then
00268                 lcoinc = .true.
00269                 cross_product = vec1_x*vec2_x + vec1_y*vec2_y
00270                 if (lrevers) cross_product = -cross_product
00271               endif
00272             endif
00273 
00274             !***
00275             !*** if cross product is less than zero, this cell
00276             !*** doesn't work
00277             !***
00278 
00279             if (cross_product < zero) exit corner_loop
00280 
00281           end do corner_loop
00282 
00283           !***
00284           !*** if cross products all positive, we found the location
00285           !***
00286 
00287           if (n > srch_corners) then
00288             location = srch_add(cell)
00289 
00290             !***
00291             !*** if the beginning of this segment was outside the
00292             !*** grid, invert the segment so the intersection found
00293             !*** will be the first intersection with the grid
00294             !***
00295 
00296             if (loutside) then
00297               x2 = begx
00298               y2 = begy
00299               location = 0
00300               eps  = -tiny
00301             else
00302               eps  = tiny
00303             endif
00304 
00305             exit srch_loop
00306           endif
00307 
00308           !***
00309           !*** otherwise move on to next cell
00310           !***
00311 
00312         end do cell_loop
00313 
00314         !***
00315         !*** if no cell found, the point lies outside the grid.
00316         !***   take some baby steps along the segment to see if any
00317         !***   part of the segment lies inside the grid.  
00318         !***
00319 
00320         loutside = .true.
00321         s1 = s1 + baby
00322         x1 = begx + s1*(x2 - begx)
00323         y1 = begy + s1*(y2 - begy)
00324 
00325         !***
00326         !*** reached the end of the segment and still outside the grid
00327         !*** return no intersection
00328         !***
00329 
00330         if (s1 >= one) then
00331           deallocate(srch_corner_x, srch_corner_y)
00332           luse_last = .false.
00333           return
00334         endif
00335 
00336       end do srch_loop
00337       ! call timer_stop(12)
00338 
00339 !-----------------------------------------------------------------------
00340 !
00341 !     now that a cell is found, search for the next intersection.
00342 !     loop over sides of the cell to find intersection with side
00343 !     must check all sides for coincidences or intersections
00344 !
00345 !-----------------------------------------------------------------------
00346 
00347       ! call timer_start(13)
00348       intrsct_loop: do n=1,srch_corners
00349         next_n = mod(n,srch_corners) + 1
00350 
00351         grdy1 = srch_corner_y(n     ,cell)
00352         grdy2 = srch_corner_y(next_n,cell)
00353         grdx1 = srch_corner_x(n     ,cell)
00354         grdx2 = srch_corner_x(next_n,cell)
00355 
00356         !***
00357         !*** set up linear system to solve for intersection
00358         !***
00359 
00360         mat1 = x2 - x1
00361         mat2 = grdx1 - grdx2
00362         mat3 = y2 - y1
00363         mat4 = grdy1 - grdy2
00364         rhs1 = grdx1 - x1
00365         rhs2 = grdy1 - y1
00366 
00367         determ = mat1*mat4 - mat2*mat3
00368 
00369         !***
00370         !*** if the determinant is zero, the segments are either 
00371         !***   parallel or coincident or one segment has zero length.  
00372         !***   coincidences were detected above so do nothing.
00373         !*** if the determinant is non-zero, solve for the linear 
00374         !***   parameters s for the intersection point on each line 
00375         !***   segment.
00376         !*** if 0<s1,s2<1 then the segment intersects with this side.
00377         !***   return the point of intersection (adding a small
00378         !***   number so the intersection is off the grid line).
00379         !***
00380 
00381         if (abs(determ) > 1.e-30) then
00382 
00383           s1 = (rhs1*mat4 - mat2*rhs2)/determ
00384           s2 = (mat1*rhs2 - rhs1*mat3)/determ
00385  
00386           if (s2 >= zero .and. s2 <= one .and.   &
00387               s1 >  zero .and. s1 <= one) then
00388 
00389             !***
00390             !*** recompute intersection using entire segment
00391             !*** for consistency between sweeps
00392             !***
00393 
00394             if (.not. loutside) then
00395               mat1 = x2 - begsegx
00396               mat3 = y2 - begsegy
00397               rhs1 = grdx1 - begsegx
00398               rhs2 = grdy1 - begsegy
00399             else 
00400               mat1 = x2 - endx
00401               mat3 = y2 - endy
00402               rhs1 = grdx1 - endx
00403               rhs2 = grdy1 - endy
00404             endif
00405 
00406             determ = mat1*mat4 - mat2*mat3
00407 
00408             !***
00409             !*** sometimes due to roundoff, the previous 
00410             !*** determinant is non-zero, but the lines
00411             !*** are actually coincident.  if this is the
00412             !*** case, skip the rest.
00413             !***
00414 
00415             if (determ /= zero) then
00416               s1 = (rhs1*mat4 - mat2*rhs2)/determ
00417               s2 = (mat1*rhs2 - rhs1*mat3)/determ
00418 
00419               if (.not. loutside) then
00420                 intrsct_x = begsegx + s1*mat1
00421                 intrsct_y = begsegy + s1*mat3
00422               else 
00423                 intrsct_x = endx + s1*mat1
00424                 intrsct_y = endy + s1*mat3
00425               endif
00426 
00427               !***
00428               !*** convert back to lat/lon coordinates
00429               !***
00430 
00431               intrsct_lon = rns*atan2(intrsct_y,intrsct_x)
00432               if (intrsct_lon < zero)  &
00433                 intrsct_lon = intrsct_lon + dble_pi2
00434 
00435               if (abs(intrsct_x) > 1.d-10) then
00436                 intrsct_lat = (pi4 -  &
00437                   asin(rns*half*intrsct_x/cos(intrsct_lon)))*two
00438               else if (abs(intrsct_y) > 1.d-10) then
00439                 intrsct_lat = (pi4 -  &
00440                   asin(half*intrsct_y/sin(intrsct_lon)))*two
00441               else
00442                 intrsct_lat = two*pi4
00443               endif
00444 
00445               !***
00446               !*** add offset in transformed space for next pass.
00447               !***
00448 
00449               if (s1 - eps/determ < one) then
00450                 intrsct_x = intrsct_x - mat1*(eps/determ)
00451                 intrsct_y = intrsct_y - mat3*(eps/determ)
00452               else
00453                 if (.not. loutside) then
00454                   intrsct_x = endx
00455                   intrsct_y = endy
00456                   intrsct_lat = endlat
00457                   intrsct_lon = endlon
00458                 else 
00459                   intrsct_x = begsegx
00460                   intrsct_y = begsegy
00461                   intrsct_lat = begseg(1)
00462                   intrsct_lon = begseg(2)
00463                 endif
00464               endif
00465 
00466               exit intrsct_loop
00467             endif
00468           endif
00469         endif
00470 
00471         !***
00472         !*** no intersection this side, move on to next side
00473         !***
00474 
00475       end do intrsct_loop
00476       ! call timer_stop(13)
00477 
00478       deallocate(srch_corner_x, srch_corner_y)
00479 
00480 !-----------------------------------------------------------------------
00481 !
00482 !     if segment manages to cross over pole, shift the beginning 
00483 !     endpoint in order to avoid hitting pole directly
00484 !     (it is ok for endpoint to be pole point)
00485 !
00486 !-----------------------------------------------------------------------
00487 
00488       if (abs(intrsct_x) < 1.e-10 .and. abs(intrsct_y) < 1.e-10 .and. &
00489          (endx /= zero .and. endy /=0)) then
00490         if (avoid_pole_count > 2) then
00491            avoid_pole_count = 0
00492            avoid_pole_offset = 10.*avoid_pole_offset
00493         endif
00494 
00495         cross_product = begsegx*(endy-begsegy) - begsegy*(endx-begsegx)
00496         intrsct_lat = begseg(1)
00497         if (cross_product*intrsct_lat > zero) then
00498           intrsct_lon = beglon    + avoid_pole_offset
00499           begseg(2)   = begseg(2) + avoid_pole_offset
00500         else
00501           intrsct_lon = beglon    - avoid_pole_offset
00502           begseg(2)   = begseg(2) - avoid_pole_offset
00503         endif
00504 
00505         avoid_pole_count = avoid_pole_count + 1
00506         luse_last = .false.
00507       else
00508         avoid_pole_count = 0
00509         avoid_pole_offset = tiny
00510       endif
00511 
00512 !-----------------------------------------------------------------------
00513 !
00514 !     if the segment crosses a pole threshold, reset the intersection
00515 !     to be the threshold latitude and do not reuse x,y intersect
00516 !     on next entry.  only check if did not cross threshold last
00517 !     time - sometimes the coordinate transformation can place a
00518 !     segment on the other side of the threshold again
00519 !
00520 !-----------------------------------------------------------------------
00521 
00522       if (lthresh) then
00523         if (intrsct_lat > north_thresh .or. intrsct_lat < south_thresh) &
00524          lthresh = .false.
00525       else if (beglat > zero .and. intrsct_lat < north_thresh) then
00526         mat4 = endlat - begseg(1)
00527         mat3 = endlon - begseg(2)
00528         if (mat3 >  dble_pi) mat3 = mat3 - dble_pi2
00529         if (mat3 < -dble_pi) mat3 = mat3 + dble_pi2
00530         intrsct_lat = north_thresh - tiny
00531         s1 = (north_thresh - begseg(1))/mat4
00532         intrsct_lon = begseg(2) + s1*mat3
00533         luse_last = .false.
00534         lthresh = .true.
00535       else if (beglat < zero .and. intrsct_lat > south_thresh) then
00536         mat4 = endlat - begseg(1)
00537         mat3 = endlon - begseg(2)
00538         if (mat3 >  dble_pi) mat3 = mat3 - dble_pi2
00539         if (mat3 < -dble_pi) mat3 = mat3 + dble_pi2
00540         intrsct_lat = south_thresh + tiny
00541         s1 = (south_thresh - begseg(1))/mat4
00542         intrsct_lon = begseg(2) + s1*mat3
00543         luse_last = .false.
00544         lthresh = .true.
00545       endif
00546 
00547       !***
00548       !*** if reached end of segment, do not use x,y intersect 
00549       !*** on next entry
00550       !***
00551 
00552       if (intrsct_lat == endlat .and. intrsct_lon == endlon) then
00553         luse_last = .false.
00554       endif
00555 #ifdef VERBOSE
00556   PRINT *, '| | | | | | | Quit PRISMTrs_pole_intersection'
00557   call psmile_flushstd
00558 #endif
00559 !-----------------------------------------------------------------------
00560 
00561       end subroutine PRISMTrs_pole_intersection
00562 

Generated on 18 Mar 2011 for Oasis4 by  doxygen 1.6.1