/*
 * MLMESH.C - 2D logical-rectangular mesh generation routines
 *
 * Source Version: 2.0
 * Software Release #92-0043
 *
 */

#include "cpyright.h"
 
#include "pml.h"

#define FIND_ELEMENT(off, i0)                                                \
    i = PM_element(n_map, j, 1) + off;                                       \
    for (m = 1; m <= n; m++)                                                 \
        {if (PM_element(n_map, m, 1) == i)                                   \
            PM_element(n_map, j, i0) = m;}

#define LOAD_LAPLACIAN(ar, dt, off, n, a)                                    \
    j0 = j + off;                                                            \
    if (nodet[j0] == 0.0)                                                    \
       {m = PM_element(n_map, i, n);                                         \
        if (m >= 0)                                                          \
           {PM_element(lapl, i, m) = a;};}                                   \
    else                                                                     \
       {for (k = 0; k < na; k++)                                             \
            {PM_element(ar[k], i, 1) -= a*dt[k][j0];};}                      \

#define vecset4(v,v1,v2,v3,v4)                                               \
   v2 = v;                                                                   \
   v3 = v2 - 1;                                                              \
   v4 = v3 - kbnd;                                                           \
   v1 = v4 + 1

#define NODE_OF(k, l) (((l) - 1)*kbnd + (k) - 1)

/*--------------------------------------------------------------------------*/

/*                           SERVICE ROUTINES                               */

/*--------------------------------------------------------------------------*/

/* _PM_FILL_MAP - fill out the map array with nearest neighbor information */

static void _PM_fill_map(n_map, kmax, lmax, nodet)
   PM_matrix *n_map;
   int kmax, lmax;
   REAL *nodet;
   {int m, j, i, k, l, n;
    int kbnd, lbnd;

    kbnd = kmax + 1;
    lbnd = lmax + 1;

/* put the nodes into the n_map array */
    n = 0;
    for (k = 1; k <= kmax; k++)
        for (l = 1; l <= lmax; l++)
            {i = NODE_OF(k, l);
             if (nodet[i] == -1.0)
                {PM_element(n_map, ++n, 1) = i;
                 nodet[i] = 0.0;};};

/* put the neighbors into the n_map array */
    for (j = 1; j <= n; j++)
        {FIND_ELEMENT(1, 2);
         FIND_ELEMENT(kbnd, 3);
         FIND_ELEMENT(-1, 4);
         FIND_ELEMENT(-kbnd, 5);
         FIND_ELEMENT(kbnd+1, 6);
         FIND_ELEMENT(kbnd-1, 7);
         FIND_ELEMENT(-kbnd-1, 8);
         FIND_ELEMENT(-kbnd+1, 9);};

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_FIN_SOL - map the solutions in tx and ty back into the mesh */

static void _PM_fin_sol(n_map, m, reg_map, nodet)
   PM_matrix *n_map;
   int m, *reg_map;
   REAL *nodet;
   {int i, j, n;

    n = n_map->nrow;

    for (j = 1; j <= n; j++)
        {i          = PM_element(n_map, j, 1);
         reg_map[i] = m;
         nodet[i]   = 1.0;};

    return;}

/*--------------------------------------------------------------------------*/

#if 0

/*--------------------------------------------------------------------------*/

/* _PM_MESH_CONVERGED - return TRUE iff the coordinates have converged */

static int _PM_mesh_converged(xn, yn, xo, yo, n_map, n)
   PM_matrix *xn, *yn, *xo, *yo;
   int n;
   {int i;
    REAL *pxo, *pyo, *pxn, *pyn;
    double s, dx, dy, rx1, ry1, rx2, ry2, ax, ay;

    pxo = xo->array;
    pyo = yo->array;
    pxn = xn->array;
    pyn = yn->array;

    s = 0.0;
    for (i = 0; i < n; i++)
        {rx1 = *pxo++;
         rx2 = *pxn++;
         ry1 = *pyo++;
         ry2 = *pyn++;
         dx  = rx2 - rx1;
         dy  = ry2 - ry1;
         ax  = 0.5*(rx2 + rx1);
         ay  = 0.5*(ry2 + ry1);
         s  += (dx*dx + dy*dy)/(ax*ax + ay*ay + SMALL);};
    s = sqrt(s/((REAL) n));

    return((s < 0.0001) ? TRUE : FALSE);}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_CARTESIAN_REGION - return TRUE iff the region described by the part
 *                      - has straight line side all the way around
 */

static int _PM_cartesian_region(ipart)
   PM_part *ipart;
   {int ret;
    PM_conic_curve *crv;
    PM_side *ib;

    ret = TRUE;
    for (ib = ipart->leg; TRUE; )
        {crv = ib->crve;
         ret &= ((crv->xx == 0.0) && (crv->yy == 0.0) && (crv->xy == 0.0));
         if ((ib = ib->next) == ipart->leg)
            break;};

    return(ret);}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_CURVATURE - compute the curvature at J of the coordinate contour
 *               - implied by the stride, S
 */

static double _PM_curvature(j, s)
   int j, s;
   {double dx1, dy1, dx2, dy2, dx3, dy3, dxf, dyf;
    double d0, t0, r0;

    dx1 = rx[j] - rx[j-s];
    dy1 = ry[j] - ry[j-s];
    dx2 = rx[j+s] - rx[j];
    dy2 = ry[j+s] - ry[j];
    dx3 = 0.5*(rx[j+s] - rx[j-s]);
    dy3 = 0.5*(ry[j+s] - ry[j-s]);
    d0  = dx1*dy2 - dy1*dx2;
    if (d0 == 0.0)
       r0 = SMALL;
    else
       {t0  = (dx3*dx2 + dy3*dy2)/d0;
        dxf = 0.5*dx1 + t0*dy1;
        dyf = 0.5*dy1 - t0*dx1;
        r0  = 1.0/(sqrt(dxf*dxf + dyf*dyf) + SMALL);};

    r0 = min(r0, param[9]);
    r0 = max(r0, param[8]);

    return(r0);}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_COPY_SOL - copy the coordinate solutions */

static void _PM_copy_sol(xo, xn, yo, yn, n_map)
   PM_matrix *xo, *xn, *yo, *yn, *n_map;
   {int i, j, n;
    REAL *pxo, *pyo, *pxn, *pyn;

    n   = xo->nrow;
    pxo = xo->array;
    pyo = yo->array;
    pxn = xn->array;
    pyn = yn->array;
    for (i = 0; i < n; i++)
        {*pxo++ = *pxn++;
         *pyo++ = *pyn++;};

    for (j = 1; j <= n; j++)
        {i     = PM_element(n_map, j, 1);
         rx[i] = PM_element(xn, j, 1);
         ry[i] = PM_element(yn, j, 1);};

    return;}

/*--------------------------------------------------------------------------*/

#endif

/*                            LAPLACE SOLVER                                */

/*--------------------------------------------------------------------------*/

/* _PM_FILL_LAPL_OP - set up the laplacian operator and the boundary arrays
 *                  - for the 2D laplacian solver
 *                  - this operator uses only the tangential
 *                  - spacing ratios
 *                  - if KRA or LRA are NULL or CRF is TRUE, use constant
 *                  - ratios KRC and LRC
 */

static void _PM_fill_lapl_op(lapl, kmax, lmax,
			     na, ar, dt, kra, lra, nodet,
			     n_map, ts, krc, lrc, crf)
   PM_matrix *lapl;
   int kmax, lmax, na;
   PM_matrix **ar;
   REAL **dt, *kra, *lra, *nodet;
   PM_matrix *n_map;
   double ts, krc, lrc;
   int crf;
   {int n, m, j, j0, i, k;
    int kbnd, lbnd;
    double s1, s2, s3, s4, sr, sl, sb, st;
    double pnt, cnt;

    kbnd = kmax + 1;
    lbnd = lmax + 1;

    n = n_map->nrow;

    PM_set_value(lapl->array, n*n, 0.0);

    pnt = min(ts, 1.0);
    pnt = max(pnt, 0.0);
    cnt = 2.0 - pnt;

    for (i = 1; i <= n; i++)
        {j  = PM_element(n_map, i, 1);

         if (crf || (kra == NULL) || (lra == NULL))
            {sr = 1.0/(1.0 + krc);
             sl = krc*sr;
             st = 1.0/(1.0 + lrc);
             sb = lrc*st;}
         else
            {sr = 1.0/(1.0 + kra[j]);
             sl = kra[j]*sr;
             st = 1.0/(1.0 + lra[j]);
             sb = lra[j]*st;};

         s1 = -pnt*sr*sb;
         s2 = -pnt*sr*st;
         s3 = -pnt*sl*st;
         s4 = -pnt*sl*sb;

         PM_element(lapl, i, i) = -cnt;
         for (k = 0; k < na; k++)
             {PM_element(ar[k], i, 1) = 0;};

         LOAD_LAPLACIAN(ar, dt, 1,       2, sr);
         LOAD_LAPLACIAN(ar, dt, kbnd,    3, st);
         LOAD_LAPLACIAN(ar, dt, -1,      4, sl);
         LOAD_LAPLACIAN(ar, dt, -kbnd,   5, sb);
         LOAD_LAPLACIAN(ar, dt, kbnd+1,  6, s2);
         LOAD_LAPLACIAN(ar, dt, kbnd-1,  7, s3);
         LOAD_LAPLACIAN(ar, dt, -kbnd-1, 8, s4);
         LOAD_LAPLACIAN(ar, dt, -kbnd+1, 9, s1);};

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_LAPLACE_SOL - compute the coordinates using the correct laplacian
 *                 - this operator uses only the tangential
 *                 - spacing ratios
 */

static void _PM_laplace_sol(lapl, kmax, lmax,
			    na, ar, dt, kra, lra, nodet, n_map, ips,
			    ts, krc, lrc, crf)
   PM_matrix *lapl;
   int kmax, lmax, na;
   PM_matrix **ar;
   REAL **dt, *kra, *lra, *nodet;
   PM_matrix *n_map;
   int *ips;
   double ts, krc, lrc;
   int crf;
   {int n, j, i, k;

/* fill in the laplacian, bx, by and n_map arrays */
    _PM_fill_lapl_op(lapl, kmax, lmax,
		     na, ar, dt, kra, lra, nodet, n_map,
		     ts, krc, lrc, crf);

/* do the lu decomposition */
    PM_decompose(lapl, ips, FALSE);

/* do the sol part */
    for (k = 0; k < na; k++)
        {PM_sol(lapl, ar[k], ips, FALSE);};

    n = n_map->nrow;
    for (j = 1; j <= n; j++)
        {i = PM_element(n_map, j, 1);
         for (k = 0; k < na; k++)
             dt[k][i] = PM_element(ar[k], j, 1);};

    return;}

/*--------------------------------------------------------------------------*/

/*                           LAPLACE SOLVER A                               */

/*--------------------------------------------------------------------------*/

/* _PM_COMPUTE_A_BND - compute the a quantities on the given side */

static void _PM_compute_a_bnd(as, xs, ae, xe, v,
			      kmax, lmax, kmn, kmx, lmn, lmx)
   double as, xs, ae, xe;
   REAL *v;
   int kmax, lmax, kmn, kmx, lmn, lmx;
   {int i, j, n, nt, sdk, sdl;
    int kbnd, lbnd;
    double ps, pe, dk, dl;

    kbnd = kmax + 1;
    lbnd = lmax + 1;

    if (as < 0.0)
       {ps  = -1.0;
	as *= -1.0;}
    else
       ps = 1.0;

    if (ae < 0.0)
       {pe  = -1.0;
	ae *= -1.0;}
    else
       pe = 1.0;

    dk = kmx - kmn;
    dl = lmx - lmn;
    n  = sqrt(dk*dk + dl*dl);
    nt = n/2;

    sdk = (dk > 0);
    sdl = (dl > 0);

    if (as != 0.0)
       {for (j = 0; j < nt; j++)
            {i    = NODE_OF(kmn + sdk*(j + 1), lmn + sdl*(j + 1));
             v[i] = pow((1.0 + as*exp(-j*xs)), ps);};};

    if (ae != 0.0)
       {for (j = 0; j < nt; j++)
            {i    = NODE_OF(kmx - sdk*j, lmx - sdl*j);
             v[i] = pow((1.0 + ae*exp(-j*xe)), pe);};};

    if (2*nt != n)
       {i    = NODE_OF(kmn + sdk*(nt + 1), lmn + sdl*(nt + 1));
        v[i] = 1.0;};

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_COMPUTE_A - compute the a quantities */

static void _PM_compute_a(apk, apl, kra, lra, kmax, lmax,
			  kmn, kmx, lmn, lmx,
			  ask, xsk, aek, xek, asl, xsl, ael, xel,
			  constr)
   REAL *apk, *apl, *kra, *lra;
   int kmax, lmax, kmn, kmx, lmn, lmx;
   double ask, xsk, aek, xek;
   double asl, xsl, ael, xel;
   int constr;
   {int j, j0, k, l;
    int kbnd, lbnd, nz;
    double u, dk, dl, dkl;
    REAL *x1, *x2, *x3, *x4, *s, *t;

    kbnd = kmax + 1;
    lbnd = lmax + 1;

    nz = kbnd*lbnd;

/* extrapolate lra to the undetermined boundary */
    if (lmx - lmn < 3)
       {for (k = kmn; k <= kmx; k++)
            {j      = NODE_OF(k, lmx);
             lra[j] = lra[j-kbnd];
             j      = NODE_OF(k, lmn);
             lra[j] = lra[j+kbnd];};}
    else
       {for (k = kmn; k <= kmx; k++)
            {j      = NODE_OF(k, lmx);
             lra[j] = 2.0*lra[j-kbnd] - lra[j-2*kbnd];
             j      = NODE_OF(k, lmn);
             lra[j] = 2.0*lra[j+kbnd] - lra[j+2*kbnd];};};

/* extrapolate kra to the undetermined boundary */
    if (kmx - kmn < 3)
       {for (l = lmn; l <= lmx; l++)
            {j      = NODE_OF(kmx, l);
             kra[j] = kra[j-1];
             j      = NODE_OF(kmn, l);
             kra[j] = kra[j+1];};}
    else
       {for (l = lmn; l <= lmx; l++)
            {j      = NODE_OF(kmx, l);
             kra[j] = 2.0*kra[j-1] - kra[j-2];
             j      = NODE_OF(kmn, l);
             kra[j] = 2.0*kra[j+1] - kra[j+2];};};

    _PM_compute_a_bnd(ask, xsk, aek, xek, apk,
		      kmax, lmax, kmn, kmx, lmx, lmx);

    _PM_compute_a_bnd(asl, xsl, ael, xel, apl,
		      kmax, lmax, kmx, kmx, lmn, lmx);

    t = FMAKE_N(REAL, nz, "COMPUTE_A:t");
    s = FMAKE_N(REAL, nz, "COMPUTE_A:s");

/* compute apl */
    PM_set_value(s, nz, 0.0);
    PM_set_value(t, nz, 0.0);

    vecset4(lra, x1, x2, x3, x4);
    for (l = lmn+1; l <= lmx; l++)
        {j0 = NODE_OF(kmx, l);

         if (constr)

/* sweep to the right */
            {for (k = kmn+1; k <= kmx; k++)
                 {j   = NODE_OF(k, l);
                  u   = 4.0/(x1[j] + x2[j] + x3[j] + x4[j]);
                  dk  = 0.5*(x2[j] - x3[j] - x4[j] + x1[j]);
                  dl  = 0.5*(x2[j] + x3[j] - x4[j] - x1[j]);
                  dkl = 0.25*(x2[j] - x3[j] + x4[j] - x1[j]);

                  s[j] = s[j-1] + 2.0*dl + dk*u*u - dkl*u;};

/* sweep to the left */
             for (k = kmx-1; k >= kmn; k--)
                 {j   = NODE_OF(k, l);
                  u   = 4.0/(x1[j] + x2[j] + x3[j] + x4[j]);
                  dk  = 0.5*(x2[j] - x3[j] - x4[j] + x1[j]);
                  dl  = 0.5*(x2[j] + x3[j] - x4[j] - x1[j]);
                  dkl = 0.25*(x2[j] - x3[j] + x4[j] - x1[j]);

                  t[j] = t[j+1] - 2.0*dl - dk*u*u + dkl*u;};};

         for (k = kmn+1; k <= kmx; k++)
             {j      = NODE_OF(k, l);
              apl[j] = 0.5*(s[j] + t[j]) + apl[j0];};};

/* compute apk */
    PM_set_value(s, nz, 0.0);
    PM_set_value(t, nz, 0.0);

    vecset4(kra, x1, x2, x3, x4);
    for (k = kmn+1; k <= kmx; k++)
        {j0 = NODE_OF(k, lmx);

         if (constr)

/* sweep up */
            {for (l = lmn+1; l <= lmx; l++)
                 {j   = NODE_OF(k, l);
                  u   = 4.0/(x1[j] + x2[j] + x3[j] + x4[j]);
                  dk  = 0.5*(x2[j] - x3[j] - x4[j] + x1[j]);
                  dl  = 0.5*(x2[j] + x3[j] - x4[j] - x1[j]);
                  dkl = 0.25*(x2[j] - x3[j] + x4[j] - x1[j]);

                  s[j] = s[j-kbnd] + 2.0*dk + dl*u*u - dkl*u;};

/* sweep down */
             for (l = lmx-1; l > lmn; l--)
                 {j   = NODE_OF(k, l);
                  u   = 4.0/(x1[j] + x2[j] + x3[j] + x4[j]);
                  dk  = 0.5*(x2[j] - x3[j] - x4[j] + x1[j]);
                  dl  = 0.5*(x2[j] + x3[j] - x4[j] - x1[j]);
                  dkl = 0.25*(x2[j] - x3[j] + x4[j] - x1[j]);

                  t[j] = t[j+kbnd] - 2.0*dk - dl*u*u + dkl*u;};};

         for (l = lmn+1; l <= lmx; l++)
             {j   = NODE_OF(k, l);
              apk[j] = 0.5*(s[j] + t[j]) + apk[j0];};};

    SFREE_N(s, nz);
    SFREE_N(t, nz);

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_FILL_LAPL_OPA - set up the laplacian operator and the boundary arrays
 *                   - for the 2D laplacian solver
 *                   - this operator uses both the normal and tangential
 *                   - spacing ratios
 *                   - if KRA, LRA, APK, or APL are NULL or CRF is TRUE,
 *                   - use constant ratios KRC and LRC
 */

static void _PM_fill_lapl_opa(lapl, kmax, lmax,
			      na, ar, dt, kra, lra, apk, apl, nodet,
			      n_map, ts, krc, lrc, crf, theta)
   PM_matrix *lapl;
   int kmax, lmax, na;
   PM_matrix **ar;
   REAL **dt, *kra, *lra, *apk, *apl, *nodet;
   PM_matrix *n_map;
   double ts, krc, lrc;
   int crf;
   double theta;
   {int n, m, j, j0, i, k, kbnd, lbnd;
    double alpha, beta;
    double s1, s2, s3, s4, sr, sl, sb, st;
    double akp, akm, alp, alm;
    double ckp, ck, ckm, clp, cl, clm;
    double bkp, bk, bkm, blp, bl, blm;
    double pnt, cnt;

    theta *= 0.5*PI;
    alpha = cos(theta);
    beta  = sin(theta);
    pnt   = alpha + beta;
    alpha /= pnt;
    beta  /= pnt;

    kbnd = kmax + 1;
    lbnd = lmax + 1;

    n = n_map->nrow;

    PM_set_value(lapl->array, n*n, 0.0);

    pnt = min(ts, 1.0);
    pnt = max(pnt, 0.0);
    cnt = 2.0 - pnt;

    for (i = 1; i <= n; i++)
        {j = PM_element(n_map, i, 1);

         if ((kra == NULL) || (lra == NULL) ||
             (apk == NULL) || (apl == NULL) ||
             crf)
            {sr = 1.0/(1.0 + krc);
             sl = krc*sr;
             st = 1.0/(1.0 + lrc);
             sb = lrc*st;

             s1 = -pnt*sr*sb;
             s2 = -pnt*sr*st;
             s3 = -pnt*sl*st;
             s4 = -pnt*sl*sb;}

         else
            {akp = apk[j+1];
             akm = 1.0/apk[j];
             alp = apl[j+kbnd];
             alm = 1.0/apl[j];

             ckp = 1.0/(1.0 + kra[j+kbnd]);
             ck  = 1.0/(1.0 + kra[j]);
             ckm = 1.0/(1.0 + kra[j-kbnd]);
             clp = 1.0/(1.0 + lra[j+1]);
             cl  = 1.0/(1.0 + lra[j]);
             clm = 1.0/(1.0 + lra[j-1]);

             bkp = ckp*kra[j+kbnd];
             bk  = ck*kra[j];
             bkm = ckm*kra[j-kbnd];
             blp = clp*lra[j+1];
             bl  = cl*lra[j];
             blm = clm*lra[j-1];

             sr = (alpha + beta*akp)*ck;
             sl = (alpha + beta*akm)*bk;
             st = (alpha*alp + beta)*cl;
             sb = (alpha*alm + beta)*bl;

             s1 = -pnt*(alpha*alm*bl*ckm + beta*akp*ck*blp);
             s2 = -pnt*(alpha*alp*cl*ckp + beta*akp*ck*clp);
             s3 = -pnt*(alpha*alp*cl*bkp + beta*akm*bk*clm);
             s4 = -pnt*(alpha*alm*bl*bkm + beta*akm*bk*blm);};

         PM_element(lapl, i, i) = -cnt;
         for (k = 0; k < na; k++)
             {PM_element(ar[k], i, 1) = 0;};

         LOAD_LAPLACIAN(ar, dt, 1,       2, sr);
         LOAD_LAPLACIAN(ar, dt, kbnd,    3, st);
         LOAD_LAPLACIAN(ar, dt, -1,      4, sl);
         LOAD_LAPLACIAN(ar, dt, -kbnd,   5, sb);
         LOAD_LAPLACIAN(ar, dt, kbnd+1,  6, s2);
         LOAD_LAPLACIAN(ar, dt, kbnd-1,  7, s3);
         LOAD_LAPLACIAN(ar, dt, -kbnd-1, 8, s4);
         LOAD_LAPLACIAN(ar, dt, -kbnd+1, 9, s1);};

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* _PM_LAPLACE_SOLA - compute the coordinates using the correct laplacian
 *                  - this operator uses both the normal and tangential
 *                  - spacing ratios
 */

static void _PM_laplace_sola(lapl, kmax, lmax,
			     na, ar, dt, kra, lra, apk, apl, nodet, n_map, ips,
			     ts, krc, lrc, crf, theta)
   PM_matrix *lapl;
   int kmax, lmax, na;
   PM_matrix **ar;
   REAL **dt, *kra, *lra, *apk, *apl, *nodet;
   PM_matrix *n_map;
   int *ips;
   double ts, krc, lrc;
   int crf;
   double theta;
   {int n, j, i, k;

/* fill in the laplacian, bx, by and n_map arrays */
    _PM_fill_lapl_opa(lapl, kmax, lmax,
		      na, ar, dt, kra, lra, apk, apl, nodet, n_map,
		      ts, krc, lrc, crf, theta);

/* do the lu decomposition */
    PM_decompose(lapl, ips, FALSE);

/* do the sol part on bx and by, the boundary information for rx and ry */
    for (k = 0; k < na; k++)
        {PM_sol(lapl, ar[k], ips, FALSE);};

    n = n_map->nrow;
    for (j = 1; j <= n; j++)
        {i = PM_element(n_map, j, 1);
         for (k = 0; k < na; k++)
             dt[k][i] = PM_element(ar[k], j, 1);};

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/* PM_MESH_PART - find the rest of the nodes in the part
 *              - and compute the mesh in the part
 *              - Arguments:
 *              -   RX       space for x coordinate values (i/o)
 *              -   RY       space for y coordinate values (i/o)
 *              -   NODET    node type array (i)
 *              -   REG_MAP  material region map array (i)
 *              -   N        number of nodes in this part of the mesh
 *              -   (K,L)    a logical point in this part of the mesh
 *              -   KMN      patch minimum k value
 *              -   KMX      patch maximum k value
 *              -   LMN      patch minimum l value
 *              -   LMX      patch maximum l value
 *              -   KMAX     global mesh maximum k value
 *              -   LMAX     global mesh maximum l value
 *              -   M        material region number (i)
 *              -   KR       patch wide k-ratio (i)
 *              -   LR       patch wide l-ratio (i)
 *              -   KRA      k-ratio array (i)
 *              -   LRA      l-ratio array (i)
 *              -   APK      k-product array method 2 only (i)
 *              -   APL      l-product array method 2 only (i)
 *              -   ASK      k-product magnitude start method 2 only (i)
 *              -   XSK      k-product exponent start method 2 only (i)
 *              -   AEK      k-product magnitude end method 2 only (i)
 *              -   XEK      k-product exponent end method 2 only (i)
 *              -   ASL      l-product magnitude start method 2 only (i)
 *              -   XSL      l-product exponent start method 2 only (i)
 *              -   AEL      l-product magnitude end method 2 only (i)
 *              -   XEL      l-product exponent end method 2 only (i)
 *              -   METHOD   generation method: 1) ratios only;
 *              -                               2) products and rations
 *              -   CONSTR   impose mesh generation constraint
 *              -   DSPAT    spatial differencing: 0.0 -> pure 5 point
 *              -                                  1.0 -> pure 9 point
 *              -   DRAT     ratio differencing: 0.0 -> pure 5 point
 *              -                                1.0 -> pure 9 point
 *              -   ORIENT   orientation: 0.0 -> pure K orientation
 *              -                         1.0 -> pure L orientation
 */

void PM_mesh_part(rx, ry, nodet, reg_map,
		  n, k, l, kmn, kmx, lmn, lmx, kmax, lmax,
		  m, kr, lr, kra, lra, apk, apl,
		  ask, xsk, aek, xek,
		  asl, xsl, ael, xel,
		  method, constr, dspat, drat, orient)
   REAL *rx, *ry, *nodet;
   int *reg_map;
   int n, k, l, kmn, kmx, lmn, lmx, kmax, lmax, m;
   double kr, lr;
   REAL *kra, *lra, *apk, *apl;
   double ask, xsk, aek, xek, asl, xsl, ael, xel;
   int method, constr;
   double dspat, drat, orient;
   {int j, na;
    int *ips;
    REAL **dt;
    PM_matrix **ar, *n_map, *lapl;

    switch (method)

/* this method uses only the tangential spacing ratios */
       {case 1 :

	     ips   = FMAKE_N(int, n, "MESH_PART:ips");
	     n_map = PM_create(n, 9);
	     lapl  = PM_create(n, n);

	     _PM_fill_map(n_map, kmax, lmax, nodet);

	     na = 2;
	     ar = FMAKE_N(PM_matrix *, na, "MESH_PART:ar");
	     dt = FMAKE_N(REAL *, na, "MESH_PART:dt");
	     for (j = 0; j < na; j++)
	         ar[j] = PM_create(n, 1);

	     dt[0] = kra;
	     dt[1] = lra;
	     _PM_laplace_sol(lapl, kmax, lmax,
			     na, ar, dt, NULL, NULL, nodet,
			     n_map, ips, drat,
			     kr, lr, TRUE);

	     dt[0] = rx;
	     dt[1] = ry;
	     _PM_laplace_sol(lapl, kmax, lmax,
			     na, ar, dt, kra, lra, nodet,
			     n_map, ips, dspat,
			     1.0, 1.0, FALSE);

/* map xn and yn into rx and ry with n_map */
	     _PM_fin_sol(n_map, m, reg_map, nodet);

/* release the intermediate storage */
	     PM_destroy(lapl);
	     PM_destroy(n_map);
	     for (j = 0; j < na; j++)
	         PM_destroy(ar[j]);
	     SFREE(ar);
	     SFREE(ips);

	     break;

/* this method uses both the normal and tangential spacing ratios */
        case 2 :

	     ips   = FMAKE_N(int, n, "MESH_PART:ips");
	     n_map = PM_create(n, 9);
	     lapl  = PM_create(n, n);

	     _PM_fill_map(n_map, kmax, lmax, nodet);

	     na = 4;
	     ar = FMAKE_N(PM_matrix *, na, "MESH_PART:ar");
	     dt = FMAKE_N(REAL *, na, "MESH_PART:dt");
	     for (j = 0; j < na; j++)
	         ar[j] = PM_create(n, 1);

	     dt[0] = kra;
	     dt[1] = lra;
	     dt[2] = apk;
	     dt[3] = apl;
	     _PM_laplace_sola(lapl, kmax, lmax,
			      na, ar, dt, NULL, NULL,
			      NULL, NULL, nodet,
			      n_map, ips, drat,
			      kr, lr, TRUE, orient);

	     _PM_compute_a(apk, apl, kra, lra, kmax, lmax,
			   kmn, kmx, lmn, lmx,
			   ask, xsk, aek, xek, asl, xsl, ael, xel,
			   constr);

	     na    = 2;
	     dt[0] = rx;
	     dt[1] = ry;
	     _PM_laplace_sola(lapl, kmax, lmax,
			      na, ar, dt, kra, lra,
			      apk, apl, nodet,
			      n_map, ips, dspat,
			      1.0, 1.0, FALSE, orient);

/* map xn and yn into rx and ry with n_map */
	     _PM_fin_sol(n_map, m, reg_map, nodet);

/* release the intermediate storage */
	     PM_destroy(lapl);
	     PM_destroy(n_map);
	     for (j = 0; j < na; j++)
	         PM_destroy(ar[j]);
	     SFREE(ar);
	     SFREE(ips);

	     break;

        default :
	     break;};

    return;}

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
