// // Author: Joshua Cohen // Copyright 2016 // // This code is based on older Fortran code, therefore the algorithms (especially array-accessors) // are based on one-indexed arrays. Since some of the Fortran algorithms were adapted from // languages that have zero-indexed arrays, it is easier and safer to simply modify the actual // array-access calls as necessary (i.e. subtract 1 at the last possible moment) #include #include #include #include #include "AkimaLib.h" #include "Constants.h" using std::max; using std::min; using std::vector; #if __cplusplus >= 201103L using std::isnan; #endif bool AkimaLib::aki_almostEqual(double x, double y) { bool ret = (abs(x - y) <= AKI_EPS) ? true : false; // Compressed version is a little cleaner return ret; } void AkimaLib::printAkiNaN(int nx, int ny, vector > &ZZ, int ix, int iy, double slpx, double slpy, double slpxy) { int ii,jj; if (isnan(slpx) || isnan(slpy) || isnan(slpxy)) { printf("Slopes: %g %g %g\n", slpx, slpy, slpxy); printf("Location: %d %d\n", ix, iy); printf("Data:\n"); for (int i=(iy-2); i<=(iy+2); i++) { ii = min(max(i,3),(ny-2)); for (int j=(ix-2); j<=(ix+2); j++) { jj = min(max(j,3),(nx-2)); printf("%g ",ZZ[jj-1][ii-1]); } printf("\n"); } } } void AkimaLib::getParDer(int nx, int ny, vector > &ZZ, int ix, int iy, vector > &slpx, vector > &slpy, vector > &slpxy) { double m1,m2,m3,m4,wx2,wx3,wy2,wy3,d22,e22,d23,e23,d42,e32,d43,e33; int xx,yy; wx2 = wx3 = wy2 = wy3 = 0.0; // Avoid 'unused' warnings for (int ii=1; ii<=2; ii++) { yy = min(max((iy+ii),3),(ny-2)) - 1; for (int jj=1; jj<=2; jj++) { xx = min(max((ix+jj),3),(nx-2)) - 1; m1 = ZZ[(xx-1)][yy] - ZZ[(xx-2)][yy]; m2 = ZZ[xx][yy] - ZZ[(xx-1)][yy]; m3 = ZZ[(xx+1)][yy] - ZZ[xx][yy]; m4 = ZZ[(xx+2)][yy] - ZZ[(xx+1)][yy]; if (aki_almostEqual(m1,m2) && aki_almostEqual(m3,m4)) slpx[jj-1][ii-1] = 0.5 * (m2 + m3); else { wx2 = abs(m4 - m3); wx3 = abs(m2 - m1); slpx[jj-1][ii-1] = ((wx2 * m2) + (wx3 * m3)) / (wx2 + wx3); } m1 = ZZ[xx][(yy-1)] - ZZ[xx][(yy-2)]; m2 = ZZ[xx][yy] - ZZ[xx][(yy-1)]; m3 = ZZ[xx][(yy+1)] - ZZ[xx][yy]; m4 = ZZ[xx][(yy+2)] - ZZ[xx][(yy+1)]; if (aki_almostEqual(m1,m2) && aki_almostEqual(m3,m4)) slpx[jj-1][ii-1] = 0.5 * (m2 + m3); else { wy2 = abs(m4 - m3); wy3 = abs(m2 - m1); slpx[jj-1][ii-1] = ((wy2 * m2) + (wy3 * m3)) / (wy2 + wy3); } d22 = ZZ[(xx-1)][yy] - ZZ[(xx-1)][(yy-1)]; d23 = ZZ[(xx-1)][(yy+1)] - ZZ[(xx-1)][yy]; d42 = ZZ[(xx+1)][yy] - ZZ[(xx+1)][(yy-1)]; d43 = ZZ[(xx+1)][(yy+1)] - ZZ[(xx+1)][yy]; e22 = m2 - d22; e23 = m3 - d23; e32 = d42 - m2; e33 = d43 - m3; double dummyzero = 0.0; if (aki_almostEqual(wx2,dummyzero) && aki_almostEqual(wx3,dummyzero)) wx2 = wx3 = 1.0; if (aki_almostEqual(wy2,dummyzero) && aki_almostEqual(wy3,dummyzero)) wy2 = wy3 = 1.0; slpxy[jj-1][ii-1] = ((wx2 * ((wy2 * e22) + (wy3 * e23))) + (wx3 * ((wy2 * e32) + (wy3 * e33)))) / ((wx2 + wx3) * (wy2 + wy3)); } } } void AkimaLib::polyfitAkima(int nx, int ny, vector > &ZZ, int ix, int iy, vector &poly) { vector > sx(2,vector(2)), sy(2,vector(2)), sxy(2,vector(2)); vector d(9); getParDer(nx,ny,ZZ,ix,iy,sx,sy,sxy); // Welp this'll be bad if they're all already zero-indexed... // See isceobj/Util/src/Akima_reg.F for original expanded version (this is somewhat compressed) d[0] = (ZZ[ix-1][iy-1] - ZZ[ix][iy-1]) + (ZZ[ix][iy] - ZZ[ix-1][iy]); d[1] = (sx[0][0] + sx[1][0]) - (sx[1][1] + sx[0][1]); d[2] = (sy[0][0] - sy[1][0]) - (sy[1][1] - sy[0][1]); d[3] = (sxy[0][0] + sxy[1][0]) + (sxy[1][1] + sxy[0][1]); d[4] = ((2 * sx[0][0]) + sx[1][0]) - (sx[1][1] + (2 * sx[0][1])); d[5] = (2 * (sy[0][0] - sy[1][0])) - (sy[1][1] - sy[0][1]); d[6] = (2 * (sxy[0][0] + sxy[1][0])) + (sxy[1][1] + sxy[0][1]); d[7] = ((2 * sxy[0][0]) + sxy[1][0]) + (sxy[1][1] + (2 * sxy[0][1])); d[8] = (2 * ((2 * sxy[0][0]) + sxy[1][0])) + (sxy[1][1] + (2 * sxy[0][1])); poly[0] = (2 * ((2 * d[0]) + d[1])) + ((2 * d[2]) + d[3]); poly[1] = -((3 * ((2 * d[0]) + d[1])) + ((2 * d[5]) + d[6])); poly[2] = (2 * (sy[0][0] - sy[1][0])) + (sxy[0][0] + sxy[1][0]); poly[3] = (2 * (ZZ[ix-1][iy-1] - ZZ[ix][iy-1])) + (sx[0][0] + sx[1][0]); poly[4] = -((2 * ((3 * d[0]) + d[4])) + ((3 * d[2]) + d[7])); poly[5] = (3 * ((3 * d[0]) + d[4])) + ((3 * d[5]) + d[8]); poly[6] = -((3 * (sy[0][0] - sy[1][0])) + ((2 * sxy[0][0]) + sxy[1][0])); poly[7] = -((3 * (ZZ[ix-1][iy-1] - ZZ[ix][iy-1])) + ((2 * sx[0][0]) + sx[1][0])); poly[8] = (2 * (sx[0][0] - sx[0][1])) + (sxy[0][0] + sxy[0][1]); poly[9] = -((3 * (sx[0][0] - sx[0][1])) + ((2 * sxy[0][0]) + sxy[0][1])); poly[10] = sxy[0][0]; poly[11] = sx[0][0]; poly[12] = (2 * (ZZ[ix-1][iy-1] - ZZ[ix-1][iy])) + (sy[0][0] + sy[0][1]); poly[13] = -((3 * (ZZ[ix-1][iy-1] - ZZ[ix-1][iy])) + ((2 * sy[0][0]) + sy[0][1])); poly[14] = sy[0][0]; poly[15] = ZZ[ix-1][iy-1]; } double AkimaLib::polyvalAkima(int ix, int iy, double xx, double yy, vector &V) { double p1, p2, p3, p4, ret; p1 = (((((V[0] * (yy - iy)) + V[1]) * (yy - iy)) + V[2]) * (yy - iy)) + V[3]; p2 = (((((V[4] * (yy - iy)) + V[5]) * (yy - iy)) + V[6]) * (yy - iy)) + V[7]; p3 = (((((V[8] * (yy - iy)) + V[9]) * (yy - iy)) + V[10]) * (yy - iy)) + V[11]; p4 = (((((V[12] * (yy - iy)) + V[13]) * (yy - iy)) + V[14]) * (yy - iy)) + V[15]; ret = (((((p1 * (xx - ix)) + p2) * (xx - ix)) + p3) * (xx - ix)) + p4; return ret; } double AkimaLib::akima_intp(int nx, int ny, vector > &z, double x, double y) { vector poly(AKI_NSYS); double ret; int xx,yy; xx = int(x); yy = int(y); polyfitAkima(nx,ny,z,xx,yy,poly); ret = polyvalAkima(xx,yy,x,y,poly); return ret; }