! Conformal Cubic Atmospheric Model ! Copyright 2015 Commonwealth Scientific Industrial Research Organisation (CSIRO) ! This file is part of the Conformal Cubic Atmospheric Model (CCAM) ! ! CCAM is free software: you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation, either version 3 of the License, or ! (at your option) any later version. ! ! CCAM is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with CCAM. If not, see . !------------------------------------------------------------------------------ module jimcc_m use precis_m implicit none public :: jimcc, ctom ! ctom needs to be public for newdepts. private :: inrot, rgrid, inhedra, mtoc, mtocd, flip, stoc, cttom, & stoe, tay, tayd, taydd, lufm, invmm, vmtoc, vmtocd, vtay, vtaydd ! From cstoc (internal Purser) complex(kind=rx), save, private :: ci, cip4, cip3oss real(kind=rx), parameter, private, dimension(30) :: a = & (/ 1.47713062600964_rx, -0.38183510510174_rx, -0.05573058001191_rx, & -0.00895883606818_rx, -0.00791315785221_rx, -0.00486625437708_rx, & -0.00329251751279_rx, -0.00235481488325_rx, -0.00175870527475_rx, & -0.00135681133278_rx, -0.00107459847699_rx, -0.00086944475948_rx, & -0.00071607115121_rx, -0.00059867100093_rx, -0.00050699063239_rx, & -0.00043415191279_rx, -0.00037541003286_rx, -0.00032741060100_rx, & -0.00028773091482_rx, -0.00025458777519_rx, -0.00022664642371_rx, & -0.00020289261022_rx, -0.00018254510830_rx, -0.00016499474460_rx, & -0.00014976117167_rx, -0.00013646173947_rx, -0.00012478875822_rx, & -0.00011449267279_rx, -0.00010536946150_rx, -0.00009725109376_rx /) real(kind=rx), parameter, private, dimension(30) :: b = & (/ 0.67698819751739_rx, 0.11847293456554_rx, 0.05317178134668_rx, & 0.02965810434052_rx, 0.01912447304028_rx, 0.01342565621117_rx, & 0.00998873323180_rx, 0.00774868996406_rx, 0.00620346979888_rx, & 0.00509010874883_rx, 0.00425981184328_rx, 0.00362308956077_rx, & 0.00312341468940_rx, 0.00272360948942_rx, 0.00239838086555_rx, & 0.00213001905118_rx, 0.00190581316131_rx, 0.00171644156404_rx, & 0.00155493768255_rx, 0.00141600715207_rx, 0.00129556597754_rx, & 0.00119042140226_rx, 0.00109804711790_rx, 0.00101642216628_rx, & 0.00094391366522_rx, 0.00087919021224_rx, 0.00082115710311_rx, & 0.00076890728775_rx, 0.00072168382969_rx, 0.00067885087750_rx /) real(kind=rx), private, save :: ss, third, fourth ! Initialize some of the arrays, particularly those associated ! with the implicit representation of the group of symmetries of the cube. ! From chedra (internal Purser) integer, parameter, private, dimension(0:47) :: kda = & (/ 0,1,1,2,2,0, 0,1,1,5,5,0, 3,1,1,2,2,3, 3,1,1,5,5,3, & 0,4,4,2,2,0, 0,4,4,5,5,0, 3,4,4,2,2,3, 3,4,4,5,5,3 /) integer, parameter, private, dimension(0:47) :: kdb = & (/ 3,10,4,11,5,9, 6,7,1,11,5,0, 3,1,7,8,2,9, 6,4,10,8,2,0, & 0,10,4,2,8,6, 9,7,1,2,8,3, 0,1,7,5,11,6, 9,4,10,5,11,3 /) integer, parameter, private, dimension(0:47) :: kdc = & (/ 5,11,3,9,4,10, 5,11,6,0,1,7, 2,8,3,9,7,1, 2,8,6,0,10,4, & 8,2,0,6,4,10, 8,2,9,3,1,7, 11,5,0,6,7,1, 11,5,9,3,10,4 /) integer, parameter, private, dimension(0:47) :: kna = & (/ 12,25,26,9,10,17, 18,31,32,3,4,23, & 0,37,38,21,22,5, 6,43,44,15,16,11, & 36,1,2,33,34,41, 42,7,8,27,28,47, & 24,13,14,45,46,29, 30,19,20,39,40,35 /) integer, parameter, private, dimension(0:47) :: knb = & (/ 5,2,1,4,3,0, 11,8,7,10,9,6, & 17,14,13,16,15,12, 23,20,19,22,21,18, & 29,26,25,28,27,24, 35,32,31,34,33,30, & 41,38,37,40,39,36, 47,44,43,46,45,42 /) integer, parameter, private, dimension(0:47) :: knc = & (/ 1,0,3,2,5,4, 7,6,9,8,11,10, & 13,12,15,14,17,16, 19,18,21,20,23,22, & 25,24,27,26,29,28, 31,30,33,32,35,34, & 37,36,39,38,41,40, 43,42,45,44,47,46 /) integer, private, save, dimension(0:47) :: ipofig, kgofig integer, private, save, dimension(8,6) :: igofkg integer, save, private :: ig = 0 real(kind=rx), parameter, private, dimension(2,2,8) :: flip8 = & reshape ( (/ 1.0,0.0,0.0,1.0, -1.0,0.0,0.0,1.0, & 1.0,0.0,0.0,-1.0, -1.0,0.0,0.0,-1.0, & 0.0,1.0,1.0,0.0, 0.0,-1.0,1.0,0.0, & 0.0,1.0,-1.0,0.0, 0.0,-1.0,-1.0,0.0 /), & (/ 2, 2, 8 /) ) real(kind=rx), private, save, dimension(3,0:5) :: f real(kind=rx), private, save, dimension(3,0:11) :: e real(kind=rx), private, save, dimension(3,3) :: txe real(kind=rx), private, save, dimension(3,3,0:47) :: rotg contains subroutine jimcc ( np, xx4, yy4, em4, ax4, ay4, az4 ) ! like jim6.f but without stretch option ! hedra1 data is hardwired ! xx-->xx4, yy-->yy4, fm-->em4, dxa-->ax4, dxb-->ay4, dxc-->az4 ! dya, dyb, dyc commented out (not needed) integer, intent(in) :: np ! = iquad ! Actual dimensions(iquad,iqiad) = (np,np) real(kind=rx), intent(out), dimension(:,:) :: xx4, yy4, em4, ax4, ay4, az4 integer, parameter :: ipanel=2, ngr=1 real(kind=rx), dimension(np,np) :: xa, xb, xc integer :: i, j call inrot() call rgrid ( xa, xb, xc, ax4, ay4, az4, em4, np, ipanel, ngr ) ! Impose 8 fold symmetry (jlm) do i=1,(np+1)/2 xc(i,1) = max(-1.0_rx,xc(i,1)) ! for rounding errors end do do j=1,(np+1)/2 do i=j+1,(np+1/2) ! rest of bottom LH corner xa(j,i) = xa(i,j) xb(j,i) = xc(i,j) xc(j,i) = xb(i,j) end do do i=1,np/2 ! all of bottom RH corner xa(np+1-i,j) = xa(i,j) xb(np+1-i,j) = -xb(i,j) xc(np+1-i,j) = xc(i,j) end do do i=1,np ! all of top half xa(i,np+1-j) = xa(i,j) xb(i,np+1-j) = xb(i,j) xc(i,np+1-j) = -xc(i,j) end do end do ! Now convert these to just x,y values on the cube xx4 = xb/xa yy4 = xc/xa end subroutine jimcc !----------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE INROT ! ! Initialize the rotation matrix needed to set the orientation ! of the cube to user's specification, then call general initialization ! routine INHEDRA to make the transformation code ready for use. ! ! ROT is the user-defined orthogonal matrix determining the orientation of ! the mapping-cube with respect to the standard earth-centered ! coordinate basis defined in the comments below. ! IG1 is an array of 6 group indices. For designated map-panel IPANEL, the ! element IG1(IPANEL) is associated with the octant of this map ! in contact with the map-origin and that abuts the x-axis ! Note: it is up to the user to ensure that ROT is indeed orthogonal and ! that the 6 elements specified in IG1 do indeed belong to the 6 distinct ! faces of the cube in accordance with the numbering convention adopted ! for the triangulation into "group elements". !---------------------------------------------------------------------------- subroutine inrot() real(kind=rx), dimension(3,3) :: rot integer, parameter, dimension(6) :: ig1 = (/ 22, 13, 40, 43, 9, 45 /) real(kind=rx) :: r2, r3, r6 ! SPECIFY THE ORIENTATION OF THE TRIANGULATED MAPPING-CUBE IN TERMS OF 3 ! ORTHOGONAL UNIT-VECTORS, REFERRED TO HERE AS p, q, r, DEFINED AS FOLLOWS: ! VECTOR q POINTS TOWARDS THE MID-POINT OF EDGE-3 WHERE TRIANGULAR ELEMENTS ! 24, 29, 41 AND 36 MEET; VECTOR r POINTS TOWARDS VERTEX-0 WHERE ELEMENTS ! 0, 1, 2, 3, 4 AND 5 MEET; VECTOR p POINTS TOWARDS A POSITION ON THE ! COMMON BOUNDARY OF ELEMENTS 14 AND 15 (IN FACE-3) SUCH THAT IT IS ! ORTHOGONAL TO BOTH q AND r, OR EQUIVALENTLY, p IS DEFINED AS THE ! (RIGHT-HANDED) CROSS-PRODUCT, q*r. THE BASIS-VECTORS USED TO EXPRESS ! p, q, AND r ARE (1): THE UNIT VECTOR POINTING TO LAT,LONG=0,0; (2): THE ! VECTOR POINTING TO LAT,LONG=0,90E; (3) THE UNIT VECTOR POINTING TO THE ! NORTH POLE. r2 = sqrt(2.0_rx) r3 = sqrt(3.0_rx) r6 = r2*r3 ! DEFINE VECTOR p AND MAKE IT THE FIRST COLUMN OF MATRIX ROT: rot(1,1) = r6/6.0_rx rot(2,1) = -r6/6.0_rx rot(3,1) = -r6/3.0_rx ! DEFINE VECTOR q AND MAKE IT THE SECOND COLUMN OF MATRIX ROT: rot(1,2) = r2/2.0_rx rot(2,2) = r2/2.0_rx rot(3,2) = 0.0_rx ! DEFINE VECTOR r AND MAKE IT THE THIRD COLUMN OF MATRIX ROT: rot(1,3) = r3/3.0_rx rot(2,3) = -r3/3.0_rx rot(3,3) = r3/3.0_rx ! CUSTOMIZATION OF THE MAPPING TRANSFORMATION IS COMPLETED BY SPECIFYING, ! FOR EACH NUMBERED MAP-PANEL (FROM 1 TO 6) THE TRIANGULAR ELEMENT THAT ! CORRESPONDS TO THE 3 RESTRICTIONS IN THE LOCAL COORDINATES x,y: ! a: x < .5; ! b: 0. < y; ! c: y < x. ! FOR EACH MAP-PANEL, IP, THIS BASIC ELEMENT IS PRESCRIBED IN IG1(IP). ! THESE, TOGETHER WITH THE ORTHOGONAL MATRIX ROT MADE UP OF p,q,r, ARE ! PASSED TO THE GENERAL INITIALIZATION ROUTINE INHEDRA, AFTER WHICH, THE ! MAP-TRANSFORMATION ROUTINES ARE READY FOR USE. call inhedra(rot,ig1) end subroutine inrot !------------------------------------------------------------------------- ! r.j.purser, national meteorological center, washington d.c. 1994 ! subroutine rgrid ! ! set up array of standard earth-centered coordinates of a chosen panel ! of the rancic map. convention used for map coordinates here is that ! each origin is the pole (north or south as appropriate) and the (x,y) ! map coordinates in each panel form a right-handed pair, with x and y both ! between 0 and 1. to choose another panel-corner for origin, or to alter ! chiral convention, just rearrange the table igofkg. for more radical ! change in map-coordinate convention, rewrite this routine! ! ! <-- xe,ye,ze earth-centered coordinate of regular map-grid ! --> np number of points along each grid line (edges included) ! --> ipanel map-panel index [0,5] !---------------------------------------------------------------------------- subroutine rgrid ( xe, ye, ze, dxa, dxb, dxc, em4, np, ipanel, ngr ) ! ngr = 1 at unstaggered positions ! = 2 at i+.5,j+.5 positions ! = 3 at i+.5,j positions ! = 4 at i,j+.5 positions ! for staggered grids, actually only use 1,n part of the array ! set up earth-centered coordinates for points of chosen panel of the ! rancic map integer, intent(in) :: np, ipanel, ngr ! Actually dimension(np,np) real(kind=rx), intent(out), dimension(:,:) :: xe, ye, ze, & dxa, dxb, dxc, & em4 real(kind=rx), parameter :: stretch = 1.0_rx real(kind=rx), parameter :: stretchm = 1.0_rx - stretch ! Local variables real(kind=rx), dimension(3,np) :: ex real(kind=rx), dimension(3,3,np) :: xc real(kind=rx), dimension(2,np) :: dfdx integer :: n, j, jp, i, ip real(kind=rx) :: d, xadd, yadd, y, x, den real(kind=rx), dimension(np) :: xvec n = np - 1 d = 1.0_rx/real(n,kind=rx) xadd = 0.0 yadd = 0.0 if ( ngr==2 .or. ngr==3 ) then xadd = 0.5_rx*d endif if ( ngr==2 .or. ngr==4 ) then yadd = 0.5_rx*d endif do j = 0, n jp = j + 1 y = j*d + yadd ! jlm allows staggered v y = 0.5_rx + (y - 0.5_rx)*(stretch + stretchm*(2.0*y - 1.0)**2) do i = 0, n ip = i + 1 x = i*d + xadd ! jlm allows staggered u xvec(ip) = 0.5_rx + (x - 0.5_rx)*(stretch + stretchm*(2.0*x - 1.0)**2) end do call vmtoc (xvec, y, ipanel, ex, np) do ip=1,np xe(ip,jp) = ex(1,ip) ye(ip,jp) = ex(2,ip) ze(ip,jp) = ex(3,ip) ! print*, ' XYZ ', ip, jp, xe(ip,jp), ye(ip,jp), ze(ip,jp) end do call vmtocd (xvec, y, ipanel, xc, em4(:,jp), dfdx, np) ! return dxa etc as unit vectors do ip=1,np den = sqrt(xc(1,1,ip)**2+xc(2,1,ip)**2+xc(3,1,ip)**2) if (den < 1.0e-6_rx) then den = 1.0 endif dxa(ip,jp) = xc(1,1,ip)/den ! the three components of a vector along dx dxb(ip,jp) = xc(2,1,ip)/den dxc(ip,jp) = xc(3,1,ip)/den end do end do end subroutine rgrid ! ***************** ! * HED6A.FOR * ! * PURSER 1994 * ! ***************** ! ! SUBROUTINES NEEDED TO PERFORM RANCIC-CUBE TRANSFORMATIONS ! !---------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE INHEDRA ! Initialize variables needed to perform the Rancic-transformation and ! its inverse !---------------------------------------------------------------------------- subroutine inhedra(rot,ig1) ! real, intent(in), dimension(3,3) :: rot ! integer, intent(in), dimension(6) :: ig1 real(kind=rx), intent(in), dimension(:,:) :: rot integer, intent(in), dimension(:) :: ig1 integer, dimension(8) :: igk integer :: ip, kg, i, j, k, lg real(kind=rx) :: r2, r3, r6, r2o2, r3o2, r3o3, r6o3, r6o6, r3o6 ! SET UP CROSS-REFERENCE TABLES CONNECTING GROUP-ELEMENTS IG TO ! USER-DEFINED NUMBERING AND ORIENTATIONS OF PANELS: do ip = 1,6 igk(1) = ig1(ip) igk(2) = kna(igk(1)) igk(5) = knc(igk(1)) igk(6) = knc(igk(2)) igk(7) = kna(igk(5)) igk(8) = kna(igk(6)) igk(3) = knc(igk(7)) igk(4) = knc(igk(8)) do kg = 1,8 ig = igk(kg) igofkg(kg,ip) = ig ipofig(ig) = ip kgofig(ig) = kg end do end do r2 = sqrt(2.0_rx) r3 = sqrt(3.0_rx) r6 = sqrt(6.0_rx) r2o2 = r2/2.0_rx r3o2 = r3/2.0_rx r3o3 = r3/3.0_rx r6o3 = r6/3.0_rx r6o6 = r6/6.0_rx r3o6 = r3/6.0_rx ss = r2 third = 1.0_rx/3.0_rx fourth = 1.0_rx/4.0_rx ci = cmplx(0.0,1.0,kind=rx) cip4 = ci**fourth cip3oss = ci**third/ss f(1,0) = -r6o3 f(2,0) = 0.0 f(3,0) = r3o3 f(1,1) = r6o6 f(2,1) = -r2o2 f(3,1) = r3o3 f(1,2) = r6o6 f(2,2) = r2o2 f(3,2) = r3o3 e(1,0) = r3o3 e(2,0) = 0.0 e(3,0) = r6o3 e(1,1) = -r3o6 e(2,1) = 0.5_rx e(3,1) = r6o3 e(1,2) = -r3o6 e(2,2) = -0.5_rx e(3,2) = r6o3 e(1,3) = 0.0 e(2,3) = 1.0 e(3,3) = 0.0 e(1,4) = -r3o2 e(2,4) = -0.5_rx e(3,4) = 0.0 e(1,5) = r3o2 e(2,5) = -0.5_rx e(3,5) = 0.0 do j = 0,2 k = j+3 do i = 1,3 f(i,k) = -f(i,j) enddo enddo do j = 0,5 k = j+6 do i = 1,3 e(i,k) = -e(i,j) enddo enddo do i = 1,3 txe(1,i) = f(i,0) txe(2,i) = e(i,3) txe(3,i) = e(i,5) enddo ! call invmm(txe,txe,3,3,3) call invmm(txe) ! ROTATE THE 6 FACE-VECTORS, F, TO USER-DEFINED ORIENTATION: f = matmul ( rot, f ) ! ROTATE THE 12 EDGE-VECTORS, E, TO USER-DEFINED ORIENTATION: e = matmul ( rot, e ) ! BASED ON THE PRESCRIBED ORIENTATION (DEFINED BY "ROT"), ! CONSTRUCT THE ROTATION MATRIX ASSOCIATED WITH EACH GROUP ELEMENT LG: do lg = 0,47 do j = 1,3 do i = 1,3 rotg(i,j,lg) = f(i,kda(lg))*txe(j,1) + e(i,kdb(lg))*txe(j,2) + & e(i,kdc(lg))*txe(j,3) enddo enddo enddo end subroutine inhedra !---------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE MTOC ! Transform from map-coordinates to standard earth-centered cartesians ! ! --> XX,YY map-coordinates ! --> IPANEL map-panel index ! <-- XC standard earth-centered cartesian coordinates !---------------------------------------------------------------------------- subroutine mtoc(xx, yy, ipanel, xc) integer, intent(in) :: ipanel real(kind=rx), intent(in) :: xx real(kind=rx), intent(in) :: yy ! dimension(3) real(kind=rx), dimension(:), intent(out) :: xc integer :: kg real(kind=rx), dimension(3) :: xv real(kind=rx) :: x, y, t, xw, yw, h complex(kind=rx) :: w, z, arg !----------------------------------------------- x = xx y = yy kg = 1 if ( x > 0.5_rx ) then kg = kg + 1 x = 1.0 - x endif if (y > 0.5_rx) then kg = kg + 2 y = 1.0 - y endif if (y > x) then kg = kg + 4 t = x x = y y = t endif ! z=cmplx(x,y)**4 z = cmplx(x,y,kind=rx)*cmplx(x,y,kind=rx) z = z*z call tay (z, a, 30, w) arg = -ci*w ! mrd if (abs(arg) == 0.0) then ! mrd w = (0.0,0.0) ! mrd else ! mrd w = cip3oss*(-ci*w)**third ! mrd endif ! mrd xw = real(w) yw = aimag(w) h = 2.0/(1.0 + xw*xw + yw*yw) xv(1) = xw*h xv(2) = yw*h xv(3) = h - 1.0 ig = igofkg(kg,ipanel) xc = matmul ( rotg(:,:,ig), xv ) end subroutine mtoc subroutine vmtoc(xx, yy, ipanel, xc, n) integer, intent(in) :: ipanel integer, intent(in) :: n real(kind=rx), intent(in), dimension(:) :: xx real(kind=rx), intent(in) :: yy ! dimension(3,n) real(kind=rx), dimension(:,:), intent(out) :: xc integer :: i integer, dimension(n) :: kg real(kind=rx), dimension(3) :: xv real(kind=rx), dimension(n) :: x, y, t, xw, yw, h complex(kind=rx), dimension(n) :: w, z, arg !----------------------------------------------- x = xx y = yy kg = 1 where ( x > 0.5_rx ) kg = kg + 1 x = 1.0 - x endwhere where (y > 0.5_rx) kg = kg + 2 y = 1.0 - y endwhere where (y > x) kg = kg + 4 t = x x = y y = t endwhere ! z=cmplx(x,y)**4 z = cmplx(x,y,kind=rx)*cmplx(x,y,kind=rx) z = z*z call vtay (z, a, 30, w) arg = -ci*w ! mrd where ( abs(arg) == 0.0 ) ! mrd w = (0.0,0.0) ! mrd elsewhere ! mrd w = cip3oss*(-ci*w)**third ! mrd endwhere ! mrd xw = real(w) yw = aimag(w) h = 2.0/(1.0 + xw*xw + yw*yw) do i=1,n xv(1) = xw(i)*h(i) xv(2) = yw(i)*h(i) xv(3) = h(i) - 1.0 ig = igofkg(kg(i),ipanel) xc(:,i) = matmul ( rotg(:,:,ig), xv ) end do end subroutine vmtoc !--------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE MTOCD ! Transform from map-coordinates to standard earth-centered cartesians ! and simultaneously accumulate the derivative of the transformation in ! order to provide information on map-scaling factor and relative ! orientation ! ! --> XX,YY map-coordinates (from corner IG, each panel a unit square) ! --> IPANEL map-panel index ! <-- XC augmented jacobian matrix: first two columns represent the ! derivative with respect to X and Y of the earth-centered ! cartesian coordinates of the point corresponding to map-image ! X,Y. These cartesian coordinates themselves are inserted into ! column-3 of XDC. ! <-- em4 map-factor at this point ! <-- DFDX x- and y-derivatives of map-factor here !--------------------------------------------------------------------------- subroutine mtocd(xx, yy, ipanel, xc, em4, dfdx) integer, intent(in) :: ipanel real(kind=rx), intent(in) :: xx real(kind=rx), intent(in) :: yy real(kind=rx), intent(out) :: em4 ! Actually dimension(3,3) real(kind=rx), intent(out), dimension(:,:) :: xc ! dimension(2) real(kind=rx), intent(out), dimension(:) :: dfdx integer :: kg real(kind=rx), dimension(3,3) :: xdc real(kind=rx), dimension(3,2) :: xd real(kind=rx), dimension(2) :: v1 real(kind=rx) :: x, y, t, xw, yw, xwxw, xwyw, ywyw, h, hh, rd, qd, rdd, qdd, s, & dsdx, dsdy, dhdx, dhdy complex(kind=rx) :: w, z, zu, wu, cd, cdd, arg !----------------------------------------------- x = xx y = yy kg = 1 if ( x > 0.5_rx ) then kg = kg + 1 x = 1.0 - x endif if ( y > 0.5_rx ) then kg = kg + 2 y = 1.0 - y endif if ( y > x ) then kg = kg + 4 t = x x = y y = t endif zu = cmplx(x,y,kind=rx) z = zu**4 call taydd (z, a, 30, w, cd, cdd) arg = -ci*w ! mrd if ( abs(arg) == 0.0 ) then ! mrd wu = (0.0,0.0) ! mrd else ! mrd wu = cip3oss*(-ci*w)**third ! mrd endif ! mrd xw = real(wu,kind=rx) yw = aimag(wu) xwxw = xw*xw xwyw = xw*yw ywyw = yw*yw h = 2.0/(1.0 + xwxw + ywyw) hh = h*h xdc(1,3) = xw*h xdc(2,3) = yw*h xdc(3,3) = h - 1.0 xdc(1,1) = h - hh*xwxw xdc(2,1) = -hh*xwyw xdc(3,1) = -hh*xw xdc(1,2) = xdc(2,1) xdc(2,2) = h - hh*ywyw xdc(3,2) = -hh*yw if ( abs(z) == 0.0 ) then cd = 0.0 cdd = 0.0 em4 = 0.0 v1(1) = 0.0 v1(2) = 0.0 else cd = 4.0*wu*cd*z/(3.0*w*zu) cdd = 3.0*cd/zu - 2.0*cd*cd/wu + 16.0*wu*z**2*cdd/(3.0*w*zu**2) rd = real(cd,kind=rx) qd = aimag(cd) rdd = real(cdd,kind=rx) qdd = aimag(cdd) s = sqrt(rd*rd + qd*qd) dsdx = (rdd*rd + qdd*qd)/s dsdy = (rdd*qd - qdd*rd)/s dhdx = -hh*(xw*rd + yw*qd) dhdy = -hh*((-xw*qd) + yw*rd) em4 = h*s v1(1) = dhdx*s + h*dsdx v1(2) = dhdy*s + h*dsdy endif rd = real(cd,kind=rx) qd = aimag(cd) xd(:,1) = xdc(:,1)*rd + xdc(:,2)*qd xd(:,2) = (-xdc(:,1)*qd) + xdc(:,2)*rd ig = igofkg(kg,ipanel) ! call mulmm (v1, flip8(1,1,kg), dfdx, 1, 2, 2, 1, 2, 1) dfdx = matmul ( transpose(flip8(:,:,kg)), v1 ) xdc(:,1:2) = matmul ( xd, flip8(:,:,kg) ) xc = matmul ( rotg(:,:,ig), xdc ) end subroutine mtocd subroutine vmtocd(xx, yy, ipanel, xc, em4, dfdx, n) integer, intent(in) :: ipanel integer, intent(in) :: n real(kind=rx), intent(in), dimension(:) :: xx real(kind=rx), intent(in) :: yy real(kind=rx), intent(out), dimension(:) :: em4 ! Actually dimension(3,3,n) real(kind=rx), intent(out), dimension(:,:,:) :: xc ! dimension(2,n) real(kind=rx), intent(out), dimension(:,:) :: dfdx integer, dimension(n) :: kg integer :: i real(kind=rx), dimension(3,3,n) :: xdc real(kind=rx), dimension(3,2,n) :: xd real(kind=rx), dimension(2,n) :: v1 real(kind=rx), dimension(n) :: x, y, t, xw, yw, xwxw, xwyw, ywyw, h, hh, rd, qd, & rdd, qdd, s, dsdx, dsdy, dhdx, dhdy complex(kind=rx), dimension(n) :: w, z, zu, wu, cd, cdd, arg !----------------------------------------------- x = xx y = yy kg = 1 where ( x > 0.5_rx ) kg = kg + 1 x = 1.0 - x endwhere where ( y > 0.5_rx ) kg = kg + 2 y = 1.0 - y endwhere where ( y > x ) kg = kg + 4 t = x x = y y = t endwhere zu = cmplx(x,y,kind=rx) z = zu**4 call vtaydd (z, a, 30, w, cd, cdd) arg = -ci*w ! mrd where ( abs(arg) == 0.0 ) ! mrd wu = (0.0,0.0) ! mrd elsewhere ! mrd wu = cip3oss*(-ci*w)**third ! mrd endwhere ! mrd xw = real(wu,kind=rx) yw = aimag(wu) xwxw = xw*xw xwyw = xw*yw ywyw = yw*yw h = 2.0/(1.0 + xwxw + ywyw) hh = h*h xdc(1,3,:) = xw*h xdc(2,3,:) = yw*h xdc(3,3,:) = h - 1.0 xdc(1,1,:) = h - hh*xwxw xdc(2,1,:) = -hh*xwyw xdc(3,1,:) = -hh*xw xdc(1,2,:) = xdc(2,1,:) xdc(2,2,:) = h - hh*ywyw xdc(3,2,:) = -hh*yw where ( abs(z) == 0.0 ) cd = 0.0 cdd = 0.0 em4 = 0.0 v1(1,:) = 0.0 v1(2,:) = 0.0 elsewhere cd = 4.0*wu*cd*z/(3.0*w*zu) cdd = 3.0*cd/zu - 2.0*cd*cd/wu + 16.0*wu*z**2*cdd/(3.0*w*zu**2) rd = real(cd,kind=rx) qd = aimag(cd) rdd = real(cdd,kind=rx) qdd = aimag(cdd) s = sqrt(rd*rd + qd*qd) dsdx = (rdd*rd + qdd*qd)/s dsdy = (rdd*qd - qdd*rd)/s dhdx = -hh*(xw*rd + yw*qd) dhdy = -hh*((-xw*qd) + yw*rd) em4 = h*s v1(1,:) = dhdx*s + h*dsdx v1(2,:) = dhdy*s + h*dsdy endwhere rd = real(cd,kind=rx) qd = aimag(cd) do i=1,3 xd(i,1,:) = xdc(i,1,:)*rd + xdc(i,2,:)*qd xd(i,2,:) = (-xdc(i,1,:)*qd) + xdc(i,2,:)*rd end do do i=1,n ig = igofkg(kg(i),ipanel) ! call mulmm (v1, flip8(1,1,kg), dfdx, 1, 2, 2, 1, 2, 1) dfdx(:,i) = matmul ( transpose(flip8(:,:,kg(i))), v1(:,i) ) xdc(:,1:2,i) = matmul ( xd(:,:,i), flip8(:,:,kg(i)) ) xc(:,:,i) = matmul ( rotg(:,:,ig), xdc(:,:,i) ) end do end subroutine vmtocd !----------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE FLIP ! Use standard earth-centered cartesian coordinates of a point to determine ! the group-element IG of the orthogonal transformation needed to transform ! this point into the standard triangular wedge, and the new cartesian ! coordinates of the point once it has indergone this transformation. The ! transformed position puts the point close to the pole and with small non- ! negative components XC(1),XC(2), to ensure that it lies well inside the ! circle of convergence of the Taylor series and with appropriate azimuth ! to ensure intended solution is obtained when fractional power is taken. ! --> XE standard earth-centered cartesian coordinates [3] ! <-- XC transformed earth-centered cartesian coordinates [3] !----------------------------------------------------------------------------- subroutine flip(xe,xc) ! real, dimension(3) :: xe, xc real(kind=rx), intent(in), dimension(:) :: xe real(kind=rx), intent(out), dimension(:) :: xc real(kind=rx) :: dax, dbx, dcx integer :: i ! VALIDITY OF CONDITIONS A & B & C UNCERTAIN: dax = dot_product ( f(:,kda(ig)), xe) dbx = dot_product ( e(:,kdb(ig)), xe) if ( dax < 0.0 ) then dax = -dax ig = kna(ig) endif if ( dbx < 0.0 ) then dbx = -dbx ig = knb(ig) endif ! VALIDITY OF CONDITIONS A & B TRUE, C UNCERTAIN: do dcx = dot_product ( e(:,kdc(ig)), xe ) if ( dcx < 0.0 ) then dcx = -dcx ig = knc(ig) ! VALIDITY OF CONDITIONS A & B UNCERTAIN, C TRUE: dax = dot_product ( f(:,kda(ig)), xe) dbx = dot_product ( e(:,kdb(ig)), xe) if ( dax < 0.0 ) then dax = -dax ig = kna(ig) if ( dbx < 0.0 ) then dbx = -dbx ig = knb(ig) endif else if ( dbx >= 0.0 ) then exit endif dbx = -dbx ig = knb(ig) endif cycle endif exit end do ! VALIDITY OF CONDITIONS A & B & C TRUE: do i=1,3 xc(i) = txe(i,1)*dax+txe(i,2)*dbx+txe(i,3)*dcx enddo end subroutine flip !---------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE STOC ! Transform from latitude,longitude, to corner-origin coordinates of one ! of the maps. ! --> DLAT, DLON Latitude and longitude (degrees) ! <-- X,Y map coordinates scaled to the unit square ! <-- IPANEL map-panel index !---------------------------------------------------------------------------- subroutine stoc(dlat,dlon,x,y,ipanel) real(kind=rx), intent(in) :: dlat real(kind=rx), intent(in) :: dlon real(kind=rx), intent(out) :: x real(kind=rx), intent(out) :: y integer, intent(out) :: ipanel real(kind=rx), dimension(3) :: xe call stoe(dlat,dlon,xe) call ctom(xe,x,y,ipanel) end subroutine stoc !-------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE CTOM ! Transform from group-oriented cartesians to map-coordinates. ! Use CTTOM to transform first to corner-coordinates. Then use group ! element IG to determine map-panel and perform the conversion from ! corner-coordinates (0 XE earth-centered cartesians ! <-- X,Y map-coordinates ! <-- IPANEL map-panel index !-------------------------------------------------------------------------- subroutine ctom(xe, x, y, ipanel) ! dimension(3) real(kind=rx), dimension(:), intent(in) :: xe integer, intent(out) :: ipanel real(kind=rx), intent(out) :: x real(kind=rx), intent(out) :: y integer :: kg real(kind=rx), dimension(3) :: xc real(kind=rx) :: t !----------------------------------------------- call flip (xe, xc) ipanel = ipofig(ig) kg = kgofig(ig) call cttom (xc, x, y) if (kg > 4) then t = x x = y y = t kg = kg - 4 endif if (kg > 2) then y = 1.0 - y kg = kg - 2 endif if (kg > 1) then x = 1.0 - x endif end subroutine ctom !--------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE CTTOM ! Transform from earth-centered cartesians to map-coordinates ! Point must lie in standard wedge near the pole. Transform first to ! complex-position in the stereographic plane. Then apply fractional- ! power of Taylor-series. ! --> XE earth-centered cartesians ! <-- X,Y corner-origin map-coordinates oriented with respect to principal ! edge. !--------------------------------------------------------------------------- subroutine cttom(xe, x, y) ! dimension(3) real(kind=rx), intent(in), dimension(:) :: xe real(kind=rx), intent(out) :: x real(kind=rx), intent(out) :: y real(kind=rx) :: hi complex(kind=rx) :: w, z !----------------------------------------------- ! SS=SQRT(2.) AND REPRESENTS THE SCALE FACTOR NEEDED ! TO PLACE THE NEIGHBORING 3 VERTICES OF THE NOMINATED POLE OF THE ! 6-HEDRON ON THE UNIT CIRCLE OF THE RESCALED STEREOGRAPHIC MAP ! CIP4 IS THE COMPLEX 4th-ROOT OF i (i.e., 8th-ROOT OF -1) hi = ss/(1.0 + xe(3)) w = (cmplx(xe(1),xe(2),kind=rx)*hi)**3 call tay (w, b, 30, z) ! ROTATE AWAY FROM THE BRANCH-CUT THAT MARKS THE NEGATIVE-REAL AXIS: ! TAKE 4th-ROOT AND ROTATE BACK AGAIN z = cip4*(-ci*z)**fourth x = real(z,kind=rx) y = aimag(z) end subroutine cttom !--------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE STOE ! Transform latitude and longitude (degrees) to earth-centered cartesian ! coordinates. ! --> DLAT latitude ! --> DLON longitude ! <-- XE three cartesian components. !--------------------------------------------------------------------------- subroutine stoe(dlat, dlon, xe) use parm_m, only : dtor real(kind=rx), intent(in) :: dlat real(kind=rx), intent(in) :: dlon ! dimension(3) real(kind=rx), intent(out), dimension(:) :: xe real(kind=rx) :: rlat, rlon, sla, cla, slo, clo rlat = dtor*dlat rlon = dtor*dlon sla = sin(rlat) cla = cos(rlat) slo = sin(rlon) clo = cos(rlon) xe(1) = cla*clo xe(2) = cla*slo xe(3) = sla end subroutine stoe !-------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE TAY ! Evaluate the complex function W of Z whose real ! Taylor series coefficients are RA. ! ! --> Z function argument (complex) ! --> RA Taylor coefficients (real) ! --> N number of coefficients (starting with the linear term) ! <-- W Taylor-series approximation of the function (complex) !-------------------------------------------------------------------------- subroutine tay(z, ra, n, w) integer, intent(in) :: n complex(kind=rx), intent(in) :: z complex(kind=rx), intent(out) :: w real(kind=rx), intent(in), dimension(:) :: ra integer :: i w = 0.0 do i = n, 1, -1 w = (w + ra(i))*z end do end subroutine tay subroutine vtay(z, ra, n, w) integer, intent(in) :: n complex(kind=rx), intent(in), dimension(:) :: z complex(kind=rx), intent(out), dimension(:) :: w real(kind=rx), intent(in), dimension(:) :: ra integer :: i w = 0.0 do i = n, 1, -1 w = (w + ra(i))*z end do end subroutine vtay !---------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE TAYD ! Evaluate the complex function W of Z whose real ! Taylor series coefficients are RA, together with its derivative WD. ! ! --> Z function argument (complex) ! --> RA Taylor coefficients (real) ! --> N number of coefficients (starting with the linear term) ! <-- W Taylor-series approximation of the function (complex) ! <-- WD Taylor series approximation of the derivative of the function W !---------------------------------------------------------------------------- subroutine tayd(z, ra, n, w, wd) integer, intent(in) :: n complex(kind=rx), intent(in) :: z complex(kind=rx), intent(out) :: w complex(kind=rx), intent(out) :: wd real(kind=rx), intent(in), dimension(:) :: ra integer :: i w = 0.0 wd = 0.0 do i = n, 1, -1 w = (w + ra(i))*z wd = z*wd + i*ra(i) end do end subroutine tayd !--------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1994 ! SUBROUTINE TAYDD ! Evaluate the complex function W of Z whose real ! Taylor series coefficients are RA, together with its derivative WD. ! and second derivative WDD ! ! --> Z function argument (complex) ! --> RA Taylor coefficients (real) ! --> N number of coefficients (starting with the linear term) ! <-- W Taylor-series approximation of the function (complex) ! <-- WD Taylor series approximation of the derivative of the function W ! <-- WDD Taylor series approximation of the derivative of the function WD !--------------------------------------------------------------------------- subroutine taydd(z, ra, n, w, wd, wdd) integer, intent(in) :: n complex(kind=rx), intent(in) :: z complex(kind=rx), intent(out) :: w complex(kind=rx), intent(out) :: wd complex(kind=rx), intent(out) :: wdd real(kind=rx), intent(in), dimension(:) :: ra integer :: i w = 0.0 wd = 0.0 wdd = 0.0 do i = n, 1, -1 w = (w + ra(i))*z wd = z*wd + i*ra(i) end do do i = n, 2, -1 wdd = z*wdd + i*(i - 1)*ra(i) end do end subroutine taydd subroutine vtaydd(z, ra, n, w, wd, wdd) integer, intent(in) :: n complex(kind=rx), intent(in), dimension(:) :: z complex(kind=rx), intent(out), dimension(:) :: w complex(kind=rx), intent(out), dimension(:) :: wd complex(kind=rx), intent(out), dimension(:) :: wdd real(kind=rx), intent(in), dimension(:) :: ra integer :: i w = 0.0 wd = 0.0 wdd = 0.0 do i = n, 1, -1 w = (w + ra(i))*z wd = z*wd + i*ra(i) end do do i = n, 2, -1 wdd = z*wdd + i*(i - 1)*ra(i) end do end subroutine vtaydd !-------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1993 ! SUBROUTINE LUFM ! perform l-u decomposition of square matrix a in place with ! partial pivoting ! For DOUBLE PRECISION version see DLUFM ! ! --> a square matrix to be factorized ! <-- ipiv array encoding the pivoting sequence ! <-- d indicator for possible sign change of determinant ! --> m degree of (active part of) a ! --> na first fortran dimension of a ! !-------------------------------------------------------------------------- subroutine lufm(a, ipiv, d) real(kind=rx), intent(inout), dimension(:,:) :: a integer, intent(out), dimension(:) :: ipiv real(kind=rx), intent(out) :: d integer :: m integer :: j, jp, iquad, i, k, jm real(kind=rx) :: abig, aa, t, ajj, ajji, aij m = size(a,1) d = 1.0 ipiv(m) = m do j = 1, m - 1 jp = j + 1 abig = abs(a(j,j)) iquad = j do i = jp, m aa = abs(a(i,j)) if (aa <= abig) then cycle endif iquad = i abig = aa end do ! swap rows, recording changed sign of determinant ipiv(j) = iquad if (iquad /= j) then d = -d do k = 1, m t = a(j,k) a(j,k) = a(iquad,k) a(iquad,k) = t end do endif ajj = a(j,j) if (ajj == 0.0) then jm = j - 1 print *, "failure in lufact: matrix singular, rank=", jm stop endif ajji = 1.0/ajj do i = jp, m aij = ajji*a(i,j) a(i,j) = aij a(i,jp:m) = a(i,jp:m) - aij*a(j,jp:m) end do end do return end subroutine lufm !--------------------------------------------------------------------------- ! R.J.Purser, National Meteorological Center, Washington D.C. 1993 ! SUBROUTINE INVMM ! invert matrix in place using the l-u decomposition method ! For DOUBLE PRECISION version see DINVMM ! ! <--> a matrix ! --> m degree of (active part of) b and a ! --> nb first fortran dimension of b ! --> na first fortran dimension of a ! ! LIMITATION: ! ipiv is an index array, internal to this array, encoding the ! pivoting sequence used. It is given a fortran dimension of NN=500 ! in the parameter statement below. If the order of the linear system ! exceeds 500, increase this parameter accordingly ! !---------------------------------------------------------------------------- subroutine invmm(a) real(kind=rx), intent(inout), dimension(:,:) :: a integer, dimension(size(a,1)) :: ipiv integer :: m integer :: j, i, l real(kind=rx) :: d, s, t !----------------------------------------------- ! Check it's a square matrix if ( size(a, 1) /= size(a, 2) ) then print*, " Can't calculate inverse of non-square matrix " print*, " Shape is ", shape(a) stop end if m = size(a, 1) call lufm (a, ipiv, d) ! invert u in place: do i = 1, m a(i,i) = 1.0/a(i,i) end do do i = 1, m - 1 do j = i + 1, m s = 0.0 s = -sum(a(i,i:j-1)*a(i:j-1,j)) a(i,j) = a(j,j)*s end do end do ! invert l in place assuming implicitly diagonal elements of unity do j = 1, m - 1 do i = j + 1, m s = -a(i,j) s = s - sum(a(i,j+1:i-1)*a(j+1:i-1,j)) a(i,j) = s end do end do ! form the product of u**-1 and l**-1 in place do j = 1, m - 1 do i = 1, j s = a(i,j) s = s + sum(a(i,j+1:m)*a(j+1:m,j)) a(i,j) = s end do do i = j + 1, m s = 0.0 s = sum(a(i,i:m)*a(i:m,j)) a(i,j) = s end do end do ! permute columns according to ipiv do j = m - 1, 1, -1 l = ipiv(j) do i = 1, m t = a(i,j) a(i,j) = a(i,l) a(i,l) = t end do end do end subroutine invmm end module jimcc_m