#include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #include "mpp.h" #include "constant.h" #include "mosaic_util.h" #include "tool_util.h" #include "create_hgrid.h" #define D2R (M_PI/180.) #define R2D (180./M_PI) #define EPSLN10 (1.e-10) #define EPSLN4 (1.e-4) #define EPSLN5 (1.e-5) #define EPSLN7 (1.e-7) #define EPSLN8 (1.0e-8) /* private subroutines */ void gnomonic_ed (int ni, double* lamda, double* theta); void gnomonic_angl(int ni, double* lamda, double* theta); void gnomonic_dist(int ni, double* lamda, double* theta); void cartesian_to_spherical(double x, double y, double z, double *lon, double *lat, double *r); void spherical_to_cartesian(double lon, double lat, double r, double *x, double *y, double *z); void symm_ed(int ni, double *lamda, double *theta); void mirror_grid(int ni, int ntiles, double *x, double *y ); void mirror_latlon(double lon1, double lat1, double lon2, double lat2, double lon0, double lat0, double *lon, double *lat); void rot_3d(int axis, double x1in, double y1in, double z1in, double angle, double *x2out, double *y2out, double *z2out, int degrees, int convert); double excess_of_quad2(const double *vec1, const double *vec2, const double *vec3, const double *vec4 ); double angle_between_vectors2(const double *vec1, const double *vec2); void plane_normal2(const double *P1, const double *P2, double *plane); void calc_rotation_angle2(int nxp, double *x, double *y, double *angle_dx, double *angle_dy); void cell_center(int ni, int nj, const double *lonc, const double *latc, double *lont, double *latt); void cell_east(int ni, int nj, const double *lonc, const double *latc, double *lone, double *late); void cell_north(int ni, int nj, const double *lonc, const double *latc, double *lonn, double *latn); void calc_cell_area(int nx, int ny, const double *x, const double *y, double *area); void direct_transform(double stretch_factor, int i1, int i2, int j1, int j2, double lon_p, double lat_p, int n, double *lon, double *lat); void setup_aligned_nest(int parent_ni, int parent_nj, const double *parent_xc, const double *parent_yc, int halo, int refine_ratio, int istart, int iend, int jstart, int jend, double *xc, double *yc); void spherical_linear_interpolation(double beta, const double *p1, const double *p2, double *pb); /******************************************************************************* void create_gnomonic_cubic_grid( int *npoints, int *nratio, char *method, char *orientation, double *x, double *y, double *dx, double *dy, double *area, double *angle_dx, double *angle_dy ) create nomomic cubic grid. All six tiles grid will be generated. *******************************************************************************/ void create_gnomonic_cubic_grid( char* grid_type, int *nlon, int *nlat, double *x, double *y, double *dx, double *dy, double *area, double *angle_dx, double *angle_dy, double shift_fac, int do_schmidt, double stretch_factor, double target_lon, double target_lat, int nest_grid, int parent_tile, int refine_ratio, int istart_nest, int iend_nest, int jstart_nest, int jend_nest, int halo) { const int ntiles = 6; int ntiles2, global_nest=0; int nx, ny, nxp, nyp, ni, nj, nip, njp; int ni_nest, nj_nest, nx_nest, ny_nest; int istart, iend, jstart, jend; int ni2, nj2, ni2p, nj2p, n1, n2; int *nxl=NULL, *nyl=NULL, *nil=NULL, *njl=NULL; int i, j, n, npts; double p1[2], p2[2]; double *lon=NULL, *lat=NULL; double *xc=NULL, *yc=NULL, *xtmp=NULL, *ytmp=NULL; double *xc2=NULL, *yc2=NULL; int stretched_grid=0; /* make sure the first 6 tiles have the same grid size and the size in x and y-direction are the same */ for(n=0; n<ntiles; n++) { if(nlon[n] != nlat[n] ) mpp_error("create_gnomonic_cubic_grid: the grid size in x and y-direction " "should be the same for the 6 tiles of cubic sphere grid"); if( nlon[n]%2 ) mpp_error("create_gnomonic_cubic_grid: supergrid size in x-direction should be divided by 2"); if( nlat[n]%2 ) mpp_error("create_gnomonic_cubic_grid: supergrid size in y-direction should be divided by 2"); } for(n=1; n<ntiles; n++) { if(nlon[n] != nlon[0]) mpp_error("create_gnomonic_cubic_grid: all six tiles should have same size"); } nx = nlon[0]; ny = nx; nxp = nx+1; nyp = ny+1; ni = nx/2; nj = ni; nip = ni+1; njp = nip; ni_nest = 0; nj_nest = 0; ntiles2=ntiles; global_nest=0; if(nest_grid && parent_tile== 0) global_nest = 1; else if(nest_grid) { ntiles2 = ntiles+1; if( (istart_nest+1)%2 ) mpp_error("create_gnomonic_cubic_grid: istart_nest+1 is not divisbile by 2"); if( iend_nest%2 ) mpp_error("create_gnomonic_cubic_grid: iend_nest is not divisbile by 2"); if( (jstart_nest+1)%2 ) mpp_error("create_gnomonic_cubic_grid: jstart_nest+1 is not divisbile by 2"); if( jend_nest%2 ) mpp_error("create_gnomonic_cubic_grid: jend_nest is not divisbile by 2"); istart = (istart_nest+1)/2; iend = iend_nest/2; jstart = (jstart_nest+1)/2; jend = jend_nest/2; ni_nest = (iend-istart+1)*refine_ratio; nj_nest = (jend-jstart+1)*refine_ratio; } nx_nest = ni_nest*2; ny_nest = nj_nest*2; /* nxl/nyl supergrid size, nil, njl model grid size */ nxl = (int *)malloc(ntiles2*sizeof(int)); nyl = (int *)malloc(ntiles2*sizeof(int)); nil = (int *)malloc(ntiles2*sizeof(int)); njl = (int *)malloc(ntiles2*sizeof(int)); for(n=0; n<ntiles; n++) { nxl[n] = nx; nyl[n] = ny; nil[n] = ni; njl[n] = nj; } if(ntiles2 > ntiles) { nxl[ntiles] = nx_nest; nyl[ntiles] = ny_nest; nil[ntiles] = ni_nest; njl[ntiles] = nj_nest; } /* for global nest grid, set ni to the coarse grid size */ if(global_nest) { ni /= refine_ratio; nj /= refine_ratio; } nip=ni+1; njp=nj+1; if ( do_schmidt && fabs(stretch_factor-1.) > EPSLN5 ) stretched_grid = 1; lon = (double *)malloc(nip*nip*sizeof(double)); lat = (double *)malloc(nip*nip*sizeof(double)); if(strcmp(grid_type, "gnomonic_ed")==0 ) gnomonic_ed( ni, lon, lat); else if(strcmp(grid_type,"gnomonic_dist")==0) gnomonic_dist(ni, lon, lat); else if(strcmp(grid_type,"gnomonic_angl")==0) gnomonic_angl(ni, lon, lat); else mpp_error("create_gnomonic_cubic_grid: grid type should be 'gnomonic_ed', " "'gnomonic_dist' or 'gnomonic_angl'"); symm_ed(ni, lon, lat); npts = ntiles*nip*nip; if(ntiles2>ntiles) npts += (ni_nest+1)*(nj_nest+1); xc = (double *)malloc(npts*sizeof(double)); yc = (double *)malloc(npts*sizeof(double)); for(j=0; j<nip; j++) { for(i=0; i<nip; i++) { xc[j*nip+i] = lon[j*nip+i] - M_PI; yc[j*nip+i] = lat[j*nip+i]; } } /* mirror_grid assumes that the tile=1 is centered on equator and greenwich meridian Lon[-pi,pi] */ mirror_grid(ni, ntiles, xc, yc); for(n=0; n<ntiles*nip*nip; n++) { /* This will result in the corner close to east coast of china */ if( do_schmidt == 0 && shift_fac > EPSLN4) xc[n] -= M_PI/18.; if(xc[n] < 0.) xc[n] += 2.*M_PI; if(fabs(xc[n]) < EPSLN10) xc[n] = 0; if(fabs(yc[n]) < EPSLN10) yc[n] = 0; } /* ensure consistency on the boundary between tiles */ for(j=0; j<nip; j++) { xc[ nip*nip+j*nip] = xc[j*nip+ni]; /* 1E -> 2W */ yc[ nip*nip+j*nip] = yc[j*nip+ni]; /* 1E -> 2W */ xc[2*nip*nip+j*nip] = xc[ni*nip+ni-j]; /* 1N -> 3W */ yc[2*nip*nip+j*nip] = yc[ni*nip+ni-j]; /* 1N -> 3W */ } for(i=0; i<nip; i++) { xc[4*nip*nip+ni*nip+i] = xc[(ni-i)*nip]; /* 1W -> 5N */ yc[4*nip*nip+ni*nip+i] = yc[(ni-i)*nip]; /* 1W -> 2N */ xc[5*nip*nip+ni*nip+i] = xc[i]; /* 1S -> 6N */ yc[5*nip*nip+ni*nip+i] = yc[i]; /* 1S -> 6N */ xc[2*nip*nip+i] = xc[nip*nip+ni*nip+i]; /* 2N -> 3S */ yc[2*nip*nip+i] = yc[nip*nip+ni*nip+i]; /* 2N -> 3S */ xc[3*nip*nip+i] = xc[nip*nip+(ni-i)*nip+ni]; /* 2E -> 4S */ yc[3*nip*nip+i] = yc[nip*nip+(ni-i)*nip+ni]; /* 2E -> 4S */ } for(j=0; j<nip; j++) { xc[5*nip*nip+j*nip+ni] = xc[nip*nip+ni-j]; /* 2S -> 6E */ yc[5*nip*nip+j*nip+ni] = yc[nip*nip+ni-j]; /* 2S -> 6E */ xc[3*nip*nip+j*nip] = xc[2*nip*nip+j*nip+ni]; /* 3E -> 4W */ yc[3*nip*nip+j*nip] = yc[2*nip*nip+j*nip+ni]; /* 3E -> 4W */ xc[4*nip*nip+j*nip] = xc[2*nip*nip+ni*nip+ni-j]; /* 3N -> 5W */ yc[4*nip*nip+j*nip] = yc[2*nip*nip+ni*nip+ni-j]; /* 3N -> 5W */ } for(i=0; i<nip; i++) { xc[4*nip*nip+i] = xc[3*nip*nip+ni*nip+i]; /* 4N -> 5S */ yc[4*nip*nip+i] = yc[3*nip*nip+ni*nip+i]; /* 4N -> 5S */ xc[5*nip*nip+i] = xc[3*nip*nip+(ni-i)*nip+ni]; /* 4E -> 6S */ yc[5*nip*nip+i] = yc[3*nip*nip+(ni-i)*nip+ni]; /* 4E -> 6S */ } for(j=0; j<nip; j++) { xc[5*nip*nip+j*nip] = xc[4*nip*nip+j*nip+ni]; /* 5E -> 6W */ yc[5*nip*nip+j*nip] = yc[4*nip*nip+j*nip+ni]; /* 5E -> 6W */ } /* Schmidt transformation */ if ( do_schmidt ) { for(n=0; n<ntiles; n++) { direct_transform(stretch_factor, 0, ni, 0, ni, target_lon*D2R, target_lat*D2R, n, xc+n*nip*nip, yc+n*nip*nip); } } /* get nest grid */ if(global_nest) { npts = ntiles*nip*nip; xc2 = (double *)malloc(npts*sizeof(double)); yc2 = (double *)malloc(npts*sizeof(double)); for(n=0; n<npts; n++) { xc2[n] = xc[n]; yc2[n] = yc[n]; } free(xc); free(yc); ni2 = ni; ni2p = nip; ni = nx/2; nip = ni + 1; npts = ntiles*nip*nip; xc = (double *)malloc(npts*sizeof(double)); yc = (double *)malloc(npts*sizeof(double)); for(n=0; n<ntiles; n++) { printf("calling setup_aligned_nest, n=%d\n",n); setup_aligned_nest(ni2, ni2, xc2+ni2p*ni2p*n, yc2+ni2p*ni2p*n, 0, refine_ratio, 1, ni2, 1, ni2, xc+n*nip*nip, yc+n*nip*nip ); } } else if( nest_grid ) { setup_aligned_nest(ni, ni, xc+nip*nip*(parent_tile-1), yc+nip*nip*(parent_tile-1), halo, refine_ratio, istart, iend, jstart, jend, xc+ntiles*nip*nip, yc+ntiles*nip*nip ); } /* calculate grid box center location */ ni2 = 0; nj2 = 0; for(n=0; n<ntiles2; n++) { if(nil[n]>ni2) ni2 = nil[n]; if(njl[n]>nj2) nj2 = njl[n]; } ni2p = ni2+1; nj2p = nj2+1; xtmp = (double *)malloc(ni2p*nj2p*sizeof(double)); ytmp = (double *)malloc(ni2p*nj2p*sizeof(double)); for(n=0; n<ntiles2; n++) { /* copy C-cell to supergrid */ for(j=0; j<=njl[n]; j++) for(i=0; i<=nil[n]; i++) { n1 = n*nxp*nxp+j*2*(2*nil[n]+1)+i*2; n2 = n*nip*nip+j*(nil[n]+1)+i; x[n1]=xc[n2]; y[n1]=yc[n2]; } /* cell center and copy to super grid */ cell_center(nil[n], njl[n], xc+n*nip*nip, yc+n*nip*nip, xtmp, ytmp); for(j=0; j<njl[n]; j++) for(i=0; i<nil[n]; i++) { n1 = n*nxp*nxp+(j*2+1)*(2*nil[n]+1)+i*2+1; n2 = j*nil[n]+i; x[n1]=xtmp[n2]; y[n1]=ytmp[n2]; } /* cell east and copy to super grid */ cell_east(nil[n], njl[n], xc+n*nip*nip, yc+n*nip*nip, xtmp, ytmp); for(j=0; j<njl[n]; j++) for(i=0; i<=nil[n]; i++) { n1 = n*nxp*nxp+(j*2+1)*(2*nil[n]+1)+i*2; n2 = j*(nil[n]+1)+i; x[n1]=xtmp[n2]; y[n1]=ytmp[n2]; } /* cell north and copy to super grid */ cell_north(nil[n], njl[n], xc+n*nip*nip, yc+n*nip*nip, xtmp, ytmp); for(j=0; j<=njl[n]; j++) for(i=0; i<nil[n]; i++) { n1 = n*nxp*nxp+(j*2)*(2*nil[n]+1)+i*2+1; n2 = j*nil[n]+i; x[n1]=xtmp[n2]; y[n1]=ytmp[n2]; } } free(xtmp); free(ytmp); /* calculate grid cell length */ for(n=0; n<ntiles2; n++) { for(j=0; j<=nyl[n]; j++) { for(i=0; i<nxl[n]; i++) { p1[0] = x[n*nxp*nxp+j*(nxl[n]+1)+i]; p1[1] = y[n*nxp*nxp+j*(nxl[n]+1)+i]; p2[0] = x[n*nxp*nxp+j*(nxl[n]+1)+i+1]; p2[1] = y[n*nxp*nxp+j*(nxl[n]+1)+i+1]; dx[n*nx*nxp+j*nxl[n]+i] = great_circle_distance(p1, p2); } } } for(n=0; n<ntiles2; n++) { if( stretched_grid || n==ntiles ) { for(j=0; j<nyl[n]; j++) { for(i=0; i<=nxl[n]; i++) { p1[0] = x[n*nxp*nxp+j*(nxl[n]+1)+i]; p1[1] = y[n*nxp*nxp+j*(nxl[n]+1)+i]; p2[0] = x[n*nxp*nxp+(j+1)*(nxl[n]+1)+i]; p2[1] = y[n*nxp*nxp+(j+1)*(nxl[n]+1)+i]; dy[n*nx*nxp+j*(nxl[n]+1)+i] = great_circle_distance(p1, p2); } } } else { for(n=0; n<ntiles; n++) { for(j=0; j<nyp; j++) { for(i=0; i<nx; i++) dy[n*nx*nxp+i*nxp+j] = dx[n*nx*nxp+j*nx+i]; } } } } /* ensure consistency on the boundaries between tiles */ for(j=0; j<nx; j++) { dy[j*nxp] = dx[4*nx*nxp+nx*nx+nx-j-1]; /* 5N -> 1W */ dy[j*nxp+nx] = dy[nxp*nx+j*nxp]; /* 2W -> 1E */ dy[nxp*nx+j*nxp+nx] = dx[3*nx*nxp+(nx-j-1)]; /* 4S -> 2E */ dy[2*nxp*nx+j*nxp] = dx[nx*nx+nx-j-1]; /* 1N -> 3W */ dy[2*nxp*nx+j*nxp+nx] = dy[3*nxp*nx+j*nxp]; /* 4W -> 3E */ dy[3*nxp*nx+j*nxp+nx] = dx[5*nx*nxp+(nx-j-1)]; /* 4S -> 2E */ dy[4*nxp*nx+j*nxp] = dx[2*nx*nxp+nx*nx+nx-j-1]; /* 3N -> 5W */ dy[4*nxp*nx+j*nxp+nx] = dy[5*nxp*nx+j*nxp]; /* 6W -> 5E */ dy[5*nxp*nx+j*nxp+nx] = dx[nx*nxp+(nx-j-1)]; /* 2S -> 6E */ } if(do_schmidt) { /* calculate area for each tile */ for(n=0; n<ntiles; n++) { calc_cell_area(nx, ny, x+n*nxp*nyp, y+n*nxp*nyp, area+n*nx*ny); } } else { calc_cell_area(nx, ny, x, y, area); for(j=0; j<nx; j++) { for(i=0; i<nx; i++) { double ar; /* all the face have the same area */ ar = area[j*nx+i]; area[nx*nx+j*nx+i] = ar; area[2*nx*nx+j*nx+i] = ar; area[3*nx*nx+j*nx+i] = ar; area[4*nx*nx+j*nx+i] = ar; area[5*nx*nx+j*nx+i] = ar; } } } /* calculate nested grid area */ if(ntiles2>ntiles) calc_cell_area(nx_nest, ny_nest, x+ntiles*nxp*nyp, y+ntiles*nxp*nyp, area+ntiles*nx*ny); /*calculate rotation angle, just some workaround, will modify this in the future. */ calc_rotation_angle2(nxp, x, y, angle_dx, angle_dy ); /* since angle is used in the model, set angle to 0 for nested region */ if(ntiles2>ntiles) { for(i=0; i<=(nx_nest+1)*(ny_nest+1); i++) { angle_dx[ntiles*nxp*nxp+i]=0; angle_dy[ntiles*nxp*nxp+i]=0; } } /* convert grid location from radians to degree */ npts = ntiles*nxp*nyp; if(nx_nest>0) npts += (nx_nest+1)*(ny_nest+1); for(i=0; i<npts; i++) { x[i] = x[i]*R2D; y[i] = y[i]*R2D; } free(xc); free(yc); }; /* void create_gnomonic_cubic_grid */ void calc_cell_area(int nx, int ny, const double *x, const double *y, double *area) { int i,j, nxp; double p_ll[2], p_ul[2], p_lr[2], p_ur[2]; nxp = nx+1; for(j=0; j<ny; j++) { for(i=0; i<nx; i++) { p_ll[0] = x[j*nxp+i]; p_ll[1] = y[j*nxp+i]; p_ul[0] = x[(j+1)*nxp+i]; p_ul[1] = y[(j+1)*nxp+i]; p_lr[0] = x[j*nxp+i+1]; p_lr[1] = y[j*nxp+i+1]; p_ur[0] = x[(j+1)*nxp+i+1]; p_ur[1] = y[(j+1)*nxp+i+1]; /* all the face have the same area */ area[j*nx+i] = spherical_excess_area(p_ll, p_ul, p_lr, p_ur, RADIUS); } } } /*------------------------------------------------------------------------- void direct_transform(double c, int i1, int i2, int j1, int j2, double lon_p, double lat_p, int n, double *lon, double *lat) This is a direct transformation of the standard (symmetrical) cubic grid to a locally enhanced high-res grid on the sphere; it is an application of the Schmidt transformation at the south pole followed by a pole_shift_to_target (rotation) operation arguments: c : Stretching factor lon_p, lat_p : center location of the target face, radian n : grid face number i1,i2,j1,j2 : starting and ending index in i- and j-direction lon : longitude. 0 <= lon <= 2*pi lat : latitude. -pi/2 <= lat <= pi/2 ------------------------------------------------------------------------*/ void direct_transform(double stretch_factor, int i1, int i2, int j1, int j2, double lon_p, double lat_p, int n, double *lon, double *lat) { #ifdef NO_QUAD_PRECISION double lat_t, sin_p, cos_p, sin_lat, cos_lat, sin_o, p2, two_pi; double c2p1, c2m1; #else long double lat_t, sin_p, cos_p, sin_lat, cos_lat, sin_o, p2, two_pi; long double c2p1, c2m1; #endif int i, j, l, nxp; nxp = i2-i1+1; p2 = 0.5*M_PI; two_pi = 2.*M_PI; if(n==0) printf("create_gnomonic_cubic_grid: Schmidt transformation: stretching factor=%g, center=(%g,%g)\n", stretch_factor, lon_p, lat_p); c2p1 = 1. + stretch_factor*stretch_factor; c2m1 = 1. - stretch_factor*stretch_factor; sin_p = sin(lat_p); cos_p = cos(lat_p); for(j=j1; j<=j2; j++) for(i=i1; i<=i2; i++) { l = j*nxp+i; if ( fabs(c2m1) > EPSLN7 ) { sin_lat = sin(lat[l]); lat_t = asin( (c2m1+c2p1*sin_lat)/(c2p1+c2m1*sin_lat) ); } else { lat_t = lat[l]; } sin_lat = sin(lat_t); cos_lat = cos(lat_t); sin_o = -(sin_p*sin_lat + cos_p*cos_lat*cos(lon[l])); if ( (1.-fabs(sin_o)) < EPSLN7 ) { /* poles */ lon[l] = 0.; lat[l] = (sin_o < 0) ? -p2:p2; } else { lat[l] = asin( sin_o ); lon[l] = lon_p + atan2(-cos_lat*sin(lon[l]), -sin_lat*cos_p+cos_lat*sin_p*cos(lon[l])); if ( lon[l] < 0. ) lon[l] +=two_pi; else if( lon[l] >= two_pi ) lon[l] -=two_pi; } } }; /* direct_transform */ /*----------------------------------------------------- void gnomonic_ed Equal distance along the 4 edges of the cubed sphere ----------------------------------------------------- Properties: * defined by intersections of great circles * max(dx,dy; global) / min(dx,dy; global) = sqrt(2) = 1.4142 * Max(aspect ratio) = 1.06089 * the N-S coordinate curves are const longitude on the 4 faces with equator For C2000: (dx_min, dx_max) = (3.921, 5.545) in km unit ! Ranges: ! lamda = [0.75*pi, 1.25*pi] ! theta = [-alpha, alpha] --------------------------------------------------------*/ void gnomonic_ed(int ni, double* lamda, double* theta) { int i, j, n, nip; double dely; double *pp, *x, *y, *z; double rsq3, alpha; nip = ni + 1; rsq3 = 1./sqrt(3.); alpha = asin( rsq3 ); dely = 2.*alpha/ni; /* Define East-West edges: */ for(j=0; j<nip; j++) { lamda[j*nip] = 0.75*M_PI; /* West edge */ lamda[j*nip+ni] = 1.25*M_PI; /* East edge */ theta[j*nip] = -alpha + dely*j; /* West edge */ theta[j*nip+ni] = theta[j*nip]; /* East edge */ } /* Get North-South edges by symmetry: */ for(i=1; i<ni; i++) { mirror_latlon(lamda[0], theta[0], lamda[ni*nip+ni], theta[ni*nip+ni], lamda[i*nip], theta[i*nip], &lamda[i], &theta[i] ); lamda[ni*nip+i] = lamda[i]; theta[ni*nip+i] = -theta[i]; } x = (double *)malloc(nip*nip*sizeof(double)); y = (double *)malloc(nip*nip*sizeof(double)); z = (double *)malloc(nip*nip*sizeof(double)); /* Set 4 corners: */ latlon2xyz(1, &lamda[0], &theta[0], &x[0], &y[0], &z[0]); latlon2xyz(1, &lamda[ni], &theta[ni], &x[ni], &y[ni], &z[ni]); latlon2xyz(1, &lamda[ni*nip], &theta[ni*nip], &x[ni*nip], &y[ni*nip], &z[ni*nip]); latlon2xyz(1, &lamda[ni*nip+ni], &theta[ni*nip+ni], &x[ni*nip+ni], &y[ni*nip+ni], &z[ni*nip+ni]); /* Map edges on the sphere back to cube: Intersections at x=-rsq3 */ for(j=1; j<ni; j++) { n = j*nip; latlon2xyz(1, &lamda[n], &theta[n], &x[n], &y[n], &z[n]); y[n] = -y[n]*rsq3/x[n]; z[n] = -z[n]*rsq3/x[n]; } for(i=1; i<ni; i++) { latlon2xyz(1, &lamda[i], &theta[i], &x[i], &y[i], &z[i]); y[i] = -y[i]*rsq3/x[i]; z[i] = -z[i]*rsq3/x[i]; } for(j=0; j<nip; j++) for(i=0; i<nip; i++) x[j*nip+i] = -rsq3; for(j=1;j<nip; j++) { for(i=1; i<nip; i++) { y[j*nip+i] = y[i]; z[j*nip+i] = z[j*nip]; } } xyz2latlon(nip*nip, x, y, z, lamda, theta); }; /* gnomonic_ed */ /*---------------------------------------------------------- void gnomonic_angl() This is the commonly known equi-angular grid **************************************************************/ void gnomonic_angl(int ni, double* lamda, double* theta) { }; /* gnomonic_angl */ /*---------------------------------------------------------- void gnomonic_dist() This is the commonly known equi-distance grid **************************************************************/ void gnomonic_dist(int ni, double* lamda, double* theta) { }; /* gnomonic_dist */ /*------------------------------------------------------------------ void mirror_latlon Given the "mirror" as defined by (lon1, lat1), (lon2, lat2), and center of the sphere, compute the mirror image of (lon0, lat0) as (lon, lat) ---------------------------------------------------------------*/ void mirror_latlon(double lon1, double lat1, double lon2, double lat2, double lon0, double lat0, double *lon, double *lat) { double p0[3], p1[3], p2[3], pp[3], nb[3]; double pdot; int k; latlon2xyz(1, &lon0, &lat0, &p0[0], &p0[1], &p0[2]); latlon2xyz(1, &lon1, &lat1, &p1[0], &p1[1], &p1[2]); latlon2xyz(1, &lon2, &lat2, &p2[0], &p2[1], &p2[2]); vect_cross(p1, p2, nb); pdot = sqrt(nb[0]*nb[0]+nb[1]*nb[1]+nb[2]*nb[2]); for(k=0; k<3; k++) nb[k] = nb[k]/pdot; pdot = p0[0]*nb[0] + p0[1]*nb[1] + p0[2]*nb[2]; for(k=0; k<3; k++) pp[k] = p0[k] - 2*pdot*nb[k]; xyz2latlon(1, &pp[0], &pp[1], &pp[2], lon, lat); }; /* mirror_latlon */ /*------------------------------------------------------------------------- void symm_ed(int ni, double *lamda, double *theta) ! Make grid symmetrical to i=ni/2+1 and j=nj/2+1 ------------------------------------------------------------------------*/ void symm_ed(int ni, double *lamda, double *theta) { int nip, i, j, ip, jp; double avg; nip = ni+1; for(j=1; j<nip; j++) for(i=1; i<ni; i++) lamda[j*nip+i] = lamda[i]; for(j=0; j<nip; j++) { for(i=0; i<ni/2; i++) { ip = ni - i; avg = 0.5*(lamda[j*nip+i]-lamda[j*nip+ip]); lamda[j*nip+i] = avg + M_PI; lamda[j*nip+ip] = M_PI - avg; avg = 0.5*(theta[j*nip+i]+theta[j*nip+ip]); theta[j*nip+i] = avg; theta[j*nip+ip] = avg; } } /* Make grid symmetrical to j=im/2+1 */ for(j = 0; j<ni/2; j++) { jp = ni - j; for(i=1; i<ni; i++) { avg = 0.5*(lamda[j*nip+i]+lamda[jp*nip+i]); lamda[j*nip+i] = avg; lamda[jp*nip+i] = avg; avg = 0.5*(theta[j*nip+i]-theta[jp*nip+i]); theta[j*nip+i] = avg; theta[jp*nip+i] = -avg; } } }/* symm_ed */ /*------------------------------------------------------------------------------ void mirror_grid( ) Mirror Across the 0-longitude ----------------------------------------------------------------------------*/ void mirror_grid(int ni, int ntiles, double *x, double *y ) { int nip, i, j, ip, jp, nt; double x1, y1, z1, x2, y2, z2, ang; nip = ni+1; for(j=0; j<ceil(nip/2.); j++) { jp = ni - j; for(i=0; i<ceil(nip/2.); i++) { ip = ni - i; x1 = 0.25 * (fabs(x[j*nip+i]) + fabs(x[j*nip+ip]) + fabs(x[jp*nip+i]) + fabs(x[jp*nip+ip]) ); x[j*nip+i] = x1 * (x[j*nip+i] >=0 ? 1:-1); x[j*nip+ip] = x1 * (x[j*nip+ip] >=0 ? 1:-1); x[jp*nip+i] = x1 * (x[jp*nip+i] >=0 ? 1:-1); x[jp*nip+ip] = x1 * (x[jp*nip+ip] >=0 ? 1:-1); y1 = 0.25 * (fabs(y[j*nip+i]) + fabs(y[j*nip+ip]) + fabs(y[jp*nip+i]) + fabs(y[jp*nip+ip]) ); y[j*nip+i] = y1 * (y[j*nip+i] >=0 ? 1:-1); y[j*nip+ip] = y1 * (y[j*nip+ip] >=0 ? 1:-1); y[jp*nip+i] = y1 * (y[jp*nip+i] >=0 ? 1:-1); y[jp*nip+ip] = y1 * (y[jp*nip+ip] >=0 ? 1:-1); /* force dateline/greenwich-meridion consitency */ if( nip%2 ) { if( i == (nip-1)/2 ) { x[j*nip+i] = 0.0; x[jp*nip+i] = 0.0; } } } } /* define the the other five tiles. */ for(nt=1; nt<ntiles; nt++) { for(j=0; j<nip; j++) { for(i=0; i<nip; i++) { x1 = x[j*nip+i]; y1 = y[j*nip+i]; z1 = RADIUS; switch (nt) { case 1: /* tile 2 */ ang = -90.; rot_3d( 3, x1, y1, z1, ang, &x2, &y2, &z2, 1, 1); /* rotate about the z-axis */ break; case 2: /* tile 3 */ ang = -90.; rot_3d( 3, x1, y1, z1, ang, &x2, &y2, &z2, 1, 1); /* rotate about the z-axis */ ang = 90.; rot_3d( 1, x2, y2, z2, ang, &x1, &y1, &z1, 1, 1); /* rotate about the z-axis */ x2=x1; y2=y1; z2=z1; /* force North Pole and dateline/greenwich-meridion consitency */ if(nip%2) { if( (i==(nip-1)/2) && (i==j) ) { x2 = 0; y2 = M_PI*0.5; } if( (j==(nip-1)/2) && (i<(nip-1)/2) ) x2 = 0; if( (j==(nip-1)/2) && (i>(nip-1)/2) ) x2 = M_PI; } break; case 3: /* tile 4 */ ang = -180.; rot_3d( 3, x1, y1, z1, ang, &x2, &y2, &z2, 1, 1); /* rotate about the z-axis */ ang = 90.; rot_3d( 1, x2, y2, z2, ang, &x1, &y1, &z1, 1, 1); /* rotate about the z-axis */ x2=x1; y2=y1; z2=z1; /* force dateline/greenwich-meridion consitency */ if( nip%2 ) { if( j == (nip-1)/2 ) x2 = M_PI; } break; case 4: /* tile 5 */ ang = 90.; rot_3d( 3, x1, y1, z1, ang, &x2, &y2, &z2, 1, 1); /* rotate about the z-axis */ ang = 90.; rot_3d( 2, x2, y2, z2, ang, &x1, &y1, &z1, 1, 1); /* rotate about the z-axis */ x2=x1; y2=y1; z2=z1; break; case 5: /* tile 6 */ ang = 90.; rot_3d( 2, x1, y1, z1, ang, &x2, &y2, &z2, 1, 1); /* rotate about the z-axis */ ang = 0.; rot_3d( 3, x2, y2, z2, ang, &x1, &y1, &z1, 1, 1); /* rotate about the z-axis */ x2=x1; y2=y1; z2=z1; /* force South Pole and dateline/greenwich-meridion consitency */ if(nip%2) { if( (i==(nip-1)/2) && (i==j) ) { x2 = 0; y2 = -M_PI*0.5; } if( (i==(nip-1)/2) && (j>(nip-1)/2) ) x2 = 0; if( (i==(nip-1)/2) && (j<(nip-1)/2) ) x2 = M_PI; } break; } x[nt*nip*nip+j*nip+i] = x2; y[nt*nip*nip+j*nip+i] = y2; } } } }; /* mirror_grid */ /*------------------------------------------------------------------------------- void rot_3d() rotate points on a sphere in xyz coords (convert angle from degrees to radians if necessary) -----------------------------------------------------------------------------*/ void rot_3d(int axis, double x1in, double y1in, double z1in, double angle, double *x2out, double *y2out, double *z2out, int degrees, int convert) { double x1, y1, z1, x2, y2, z2, c, s; if(convert) spherical_to_cartesian(x1in, y1in, z1in, &x1, &y1, &z1); else { x1=x1in; y1=y1in; z1=z1in; } if(degrees) angle = angle*D2R; c = cos(angle); s = sin(angle); switch (axis) { case 1: x2 = x1; y2 = c*y1 + s*z1; z2 = -s*y1 + c*z1; break; case 2: x2 = c*x1 - s*z1; y2 = y1; z2 = s*x1 + c*z1; break; case 3: x2 = c*x1 + s*y1; y2 = -s*x1 + c*y1; z2 = z1; break; default: mpp_error("Invalid axis: must be 1 for X, 2 for Y, 3 for Z."); } if(convert) cartesian_to_spherical(x2, y2, z2, x2out, y2out, z2out); else { *x2out=x2;; *y2out=y2; *z2out=z2; } } /* rot_3d */ /*------------------------------------------------------------- void cartesian_to_spherical(x, y, z, lon, lat, r) may merge with xyz2latlon in the future ------------------------------------------------------------*/ void cartesian_to_spherical(double x, double y, double z, double *lon, double *lat, double *r) { *r = sqrt(x*x + y*y + z*z); if ( (fabs(x) + fabs(y)) < EPSLN10 ) /* poles */ *lon = 0.; else *lon = atan2(y,x); /* range: [-pi,pi] */ *lat = acos(z/(*r)) - M_PI/2.; };/* cartesian_to_spherical */ /*------------------------------------------------------------------------------- void spherical_to_cartesian convert from spheircal coordinates to xyz coords may merge with latlon2xyz in the future -----------------------------------------------------------------------------*/ void spherical_to_cartesian(double lon, double lat, double r, double *x, double *y, double *z) { *x = r * cos(lon) * cos(lat); *y = r * sin(lon) * cos(lat); *z = -r * sin(lat); } /* spherical_to_cartesian */ /***************************************************************** double* excess_of_quad(int ni, int nj, double *vec1, double *vec2, double *vec3, double *vec4 ) *******************************************************************/ double excess_of_quad2(const double *vec1, const double *vec2, const double *vec3, const double *vec4 ) { double plane1[3], plane2[3], plane3[3], plane4[3]; double angle12, angle23, angle34, angle41, excess; double ang12, ang23, ang34, ang41; plane_normal2(vec1, vec2, plane1); plane_normal2(vec2, vec3, plane2); plane_normal2(vec3, vec4, plane3); plane_normal2(vec4, vec1, plane4); angle12 = angle_between_vectors2(plane2,plane1); angle23 = angle_between_vectors2(plane3,plane2); angle34 = angle_between_vectors2(plane4,plane3); angle41 = angle_between_vectors2(plane1,plane4); ang12 = M_PI-angle12; ang23 = M_PI-angle23; ang34 = M_PI-angle34; ang41 = M_PI-angle41; excess = ang12+ang23+ang34+ang41-2*M_PI; /* excess = 2*M_PI - angle12 - angle23 - angle34 - angle41; */ return excess; }; /* excess_of_quad */ /******************************************************************************* double angle_between_vectors(const double *vec1, const double *vec2) *******************************************************************************/ double angle_between_vectors2(const double *vec1, const double *vec2) { int n; double vector_prod, nrm1, nrm2; double angle; vector_prod=vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]; nrm1=pow(vec1[0],2)+pow(vec1[1],2)+pow(vec1[2],2); nrm2=pow(vec2[0],2)+pow(vec2[1],2)+pow(vec2[2],2); if(nrm1*nrm2>0) angle = acos( vector_prod/sqrt(nrm1*nrm2) ); else angle = 0; return angle; }; /* angle_between_vectors */ /*********************************************************************** double* plane_normal(int ni, int nj, double *P1, double *P2) ***********************************************************************/ void plane_normal2(const double *P1, const double *P2, double *plane) { double mag; plane[0] = P1[1] * P2[2] - P1[2] * P2[1]; plane[1] = P1[2] * P2[0] - P1[0] * P2[2]; plane[2] = P1[0] * P2[1] - P1[1] * P2[0]; mag=sqrt(pow(plane[0],2) + pow(plane[1],2) + pow(plane[2],2)); if(mag>0) { plane[0]=plane[0]/mag; plane[1]=plane[1]/mag; plane[2]=plane[2]/mag; } }; /* plane_normal */ /****************************************************************** void calc_rotation_angle() ******************************************************************/ void calc_rotation_angle2(int nxp, double *x, double *y, double *angle_dx, double *angle_dy) { int ip1, im1, jp1, jm1, tp1, tm1, i, j, n, ntiles, nx; double lon_scale; nx = nxp-1; ntiles = 6; for(n=0; n<ntiles; n++) { for(j=0; j<nxp; j++) { for(i=0; i<nxp; i++) { lon_scale = cos(y[n*nxp*nxp+j*nxp+i]*D2R); tp1 = n; tm1 = n; ip1 = i+1; im1 = i-1; jp1 = j; jm1 = j; if(ip1 >= nxp) { /* find the neighbor tile. */ if(n % 2 == 0) { /* tile 1, 3, 5 */ tp1 = n+1; ip1 = 0; } else { /* tile 2, 4, 6 */ tp1 = n+2; if(tp1 >= ntiles) tp1 -= ntiles; ip1 = nx-j-1; jp1 = 0; } } if(im1 < 0) { /* find the neighbor tile. */ if(n % 2 == 0) { /* tile 1, 3, 5 */ tm1 = n-2; if(tm1 < 0) tm1 += ntiles; jm1 = nx; im1 = nx-j; } else { /* tile 2, 4, 6 */ tm1 = n-1; im1 = nx; } } angle_dx[n*nxp*nxp+j*nxp+i] = atan2(y[tp1*nxp*nxp+jp1*nxp+ip1]-y[tm1*nxp*nxp+jm1*nxp+im1], (x[tp1*nxp*nxp+jp1*nxp+ip1]-x[tm1*nxp*nxp+jm1*nxp+im1])*lon_scale )*R2D; tp1 = n; tm1 = n; ip1 = i; im1 = i; jp1 = j+1; jm1 = j-1; if(jp1 >=nxp) { /* find the neighbor tile. */ if(n % 2 == 0) { /* tile 1, 3, 5 */ tp1 = n+2; if(tp1 >= ntiles) tp1 -= ntiles; jp1 = nx-i; ip1 = 0; } else { /* tile 2, 4, 6 */ tp1 = n+1; if(tp1 >= ntiles) tp1 -= ntiles; jp1 = 0; } } if(jm1 < 0) { /* find the neighbor tile. */ if(n % 2 == 0) { /* tile 1, 3, 5 */ tm1 = n-1; if(tm1 < 0) tm1 += ntiles; jm1 = nx; } else { /* tile 2, 4, 6 */ tm1 = n-2; if(tm1 < 0) tm1 += ntiles; im1 = nx; jm1 = nx-i; } } angle_dy[n*nxp*nxp+j*nxp+i] = atan2(y[tp1*nxp*nxp+jp1*nxp+ip1]-y[tm1*nxp*nxp+jm1*nxp+im1], (x[tp1*nxp*nxp+jp1*nxp+ip1]-x[tm1*nxp*nxp+jm1*nxp+im1])*lon_scale )*R2D; } } } }; /* calc_rotation_angle2 */ /* This routine calculate center location based on the vertices location */ void cell_center(int ni, int nj, const double *lonc, const double *latc, double *lont, double *latt) { int nip, njp, i, j, p, p1, p2, p3, p4; double *xc, *yc, *zc, *xt, *yt, *zt; double dd; nip = ni+1; njp = nj+1; xc = (double *)malloc(nip*njp*sizeof(double)); yc = (double *)malloc(nip*njp*sizeof(double)); zc = (double *)malloc(nip*njp*sizeof(double)); xt = (double *)malloc(ni *nj *sizeof(double)); yt = (double *)malloc(ni *nj *sizeof(double)); zt = (double *)malloc(ni *nj *sizeof(double)); latlon2xyz(nip*njp, lonc, latc, xc, yc, zc); for(j=0; j<nj; j++) for(i=0; i<ni; i++) { p = j*ni+i; p1 = j*nip+i; p2 = j*nip+i+1; p3 = (j+1)*nip+i+1; p4 = (j+1)*nip+i; xt[p] = xc[p1] + xc[p2] + xc[p3] + xc[p4]; yt[p] = yc[p1] + yc[p2] + yc[p3] + yc[p4]; zt[p] = zc[p1] + zc[p2] + zc[p3] + zc[p4]; dd = sqrt(pow(xt[p],2) + pow(yt[p],2) + pow(zt[p],2) ); xt[p] /= dd; yt[p] /= dd; zt[p] /= dd; } xyz2latlon(ni*nj, xt, yt, zt, lont, latt); free(zt); free(yt); free(xt); free(zc); free(yc); free(xc); }; /* cell_center */ /* This routine calculate east location based on the vertices location */ void cell_east(int ni, int nj, const double *lonc, const double *latc, double *lone, double *late) { int nip, njp, i, j, p, p1, p2; double *xc, *yc, *zc, *xe, *ye, *ze; double dd; nip = ni+1; njp = nj+1; xc = (double *)malloc(nip*njp*sizeof(double)); yc = (double *)malloc(nip*njp*sizeof(double)); zc = (double *)malloc(nip*njp*sizeof(double)); xe = (double *)malloc(nip*nj *sizeof(double)); ye = (double *)malloc(nip*nj *sizeof(double)); ze = (double *)malloc(nip*nj *sizeof(double)); latlon2xyz(nip*njp, lonc, latc, xc, yc, zc); for(j=0; j<nj; j++) for(i=0; i<nip; i++) { p = j*nip+i; p1 = j*nip+i; p2 = (j+1)*nip+i; xe[p] = xc[p1] + xc[p2]; ye[p] = yc[p1] + yc[p2]; ze[p] = zc[p1] + zc[p2]; dd = sqrt(pow(xe[p],2) + pow(ye[p],2) + pow(ze[p],2) ); xe[p] /= dd; ye[p] /= dd; ze[p] /= dd; } xyz2latlon(nip*nj, xe, ye, ze, lone, late); free(ze); free(ye); free(xe); free(zc); free(yc); free(xc); }; /* cell_east */ /* This routine calculate center location based on the vertices location */ void cell_north(int ni, int nj, const double *lonc, const double *latc, double *lonn, double *latn) { int nip, njp, i, j, p, p1, p2; double *xc, *yc, *zc, *xn, *yn, *zn; double dd; nip = ni+1; njp = nj+1; xc = (double *)malloc(nip*njp*sizeof(double)); yc = (double *)malloc(nip*njp*sizeof(double)); zc = (double *)malloc(nip*njp*sizeof(double)); xn = (double *)malloc(ni *njp*sizeof(double)); yn = (double *)malloc(ni *njp*sizeof(double)); zn = (double *)malloc(ni *njp*sizeof(double)); latlon2xyz(nip*njp, lonc, latc, xc, yc, zc); for(j=0; j<njp; j++) for(i=0; i<ni; i++) { p = j*ni+i; p1 = j*nip+i; p2 = j*nip+i+1; xn[p] = xc[p1] + xc[p2]; yn[p] = yc[p1] + yc[p2]; zn[p] = zc[p1] + zc[p2]; dd = sqrt(pow(xn[p],2) + pow(yn[p],2) + pow(zn[p],2) ); xn[p] /= dd; yn[p] /= dd; zn[p] /= dd; } xyz2latlon(ni*njp, xn, yn, zn, lonn, latn); free(zn); free(yn); free(xn); free(zc); free(yc); free(xc); }; /* cell_north */ /*------------------------------------------------------------------------------------------- void spherical_linear_interpolation This formula interpolates along the great circle connecting points p1 and p2. This formula is taken from http://en.wikipedia.org/wiki/Slerp and is attributed to Glenn Davis based on a concept by Ken Shoemake. -------------------------------------------------------------------------------------------*/ void spherical_linear_interpolation(double beta, const double *p1, const double *p2, double *pb) { double pm[2]; double e1[3], e2[3], eb[3]; double dd, alpha, omega; if ( fabs(p1[0] - p2[0]) < EPSLN8 && fabs(p1[1] - p2[1]) < EPSLN8 ) { printf("WARNING from create_gnomonic_cubic_grid: spherical_linear_interpolation was passed two colocated points.\n"); pb[0] = p1[0]; pb[1] = p1[1]; return ; } latlon2xyz(1, p1, p1+1, e1, e1+1, e1+2); latlon2xyz(1, p2, p2+1, e2, e2+1, e2+2); dd = sqrt( e1[0]*e1[0] + e1[1]*e1[1] + e1[2]*e1[2]); e1[0] /= dd; e1[1] /= dd; e1[2] /= dd; dd = sqrt( e2[0]*e2[0] + e2[1]*e2[1] + e2[2]*e2[2]); e2[0] /= dd; e2[1] /= dd; e2[2] /= dd; alpha = 1. - beta; omega = acos( e1[0]*e2[0] + e1[1]*e2[1] + e1[2]*e2[2] ); if ( fabs(omega) < EPSLN5 ) { printf("spherical_linear_interpolation: omega=%g, p1 = %g,%g, p2 = %g,%g\n", omega, p1[0], p1[1], p2[0], p2[1]); mpp_error("spherical_linear_interpolation: interpolation not well defined between antipodal points"); } eb[0] = sin( beta*omega )*e2[0] + sin(alpha*omega)*e1[0]; eb[1] = sin( beta*omega )*e2[1] + sin(alpha*omega)*e1[1]; eb[2] = sin( beta*omega )*e2[2] + sin(alpha*omega)*e1[2]; eb[0] /= sin(omega); eb[1] /= sin(omega); eb[2] /= sin(omega); xyz2latlon(1, eb, eb+1, eb+2, pb, pb+1); } /* void setup_aligned_nest /* ni_parent : parent grid size in x-direction. nj_parent : parent grid size in y-direction. */ void setup_aligned_nest(int parent_ni, int parent_nj, const double *parent_xc, const double *parent_yc, int halo, int refine_ratio, int istart, int iend, int jstart, int jend, double *xc, double *yc) { double q1[2], q2[2], t1[2], t2[2], p1[0], p2[0]; double two_pi; int ni, nj, npi, npj; int parent_npi, i, j, ic, jc, imod, jmod; two_pi = 2.*M_PI; /* Check that the grid does not lie outside its parent */ if( (jstart - halo) < 1 || (istart - halo) < 1 || (jend + halo) > parent_nj || (iend + halo) > parent_ni ) mpp_error("create_gnomonic_cubic_grid(setup_aligned_nest): nested grid lies outside its parent"); ni = (iend-istart+1)*refine_ratio; nj = (jend-jstart+1)*refine_ratio; npi = ni+1; npj = nj+1; parent_npi = parent_ni+1; for(j=0; j<npj; j++) { jc = jstart - 1 + j/refine_ratio; jmod = j%refine_ratio; for(i=0; i<npi; i++) { ic = istart - 1 + i/refine_ratio; imod = i%refine_ratio; if(jmod == 0) { q1[0] = parent_xc[jc*parent_npi+ic]; q1[1] = parent_yc[jc*parent_npi+ic]; q2[0] = parent_xc[jc*parent_npi+ic+1]; q2[1] = parent_yc[jc*parent_npi+ic+1]; } else { t1[0] = parent_xc[jc*parent_npi+ic]; t1[1] = parent_yc[jc*parent_npi+ic]; t2[0] = parent_xc[(jc+1)*parent_npi+ic]; t2[1] = parent_yc[(jc+1)*parent_npi+ic]; spherical_linear_interpolation( (double)jmod/refine_ratio, t1, t2, q1); t1[0] = parent_xc[jc*parent_npi+ic+1]; t1[1] = parent_yc[jc*parent_npi+ic+1]; t2[0] = parent_xc[(jc+1)*parent_npi+ic+1]; t2[1] = parent_yc[(jc+1)*parent_npi+ic+1]; spherical_linear_interpolation( (double)jmod/refine_ratio, t1, t2, q2); } if (imod == 0) { xc[j*npi+i] = q1[0]; yc[j*npi+i] = q1[1]; } else { spherical_linear_interpolation( (double)imod/refine_ratio, q1, q2, t1 ); xc[j*npi+i] = t1[0]; yc[j*npi+i] = t1[1]; } if( xc[j*npi+i] > two_pi ) xc[j*npi+i] -= two_pi; if( xc[j*npi+i] < 0. ) xc[j*npi+i] += two_pi; } } }