moab
|
00001 /* 00002 * CslamUtils.cpp 00003 * 00004 * Created on: Oct 3, 2012 00005 * Author: iulian 00006 */ 00007 00008 #include "CslamUtils.hpp" 00009 #include <math.h> 00010 // this is from mbcoupler; maybe it should be moved somewhere in moab src 00011 // right now, add a dependency to mbcoupler 00012 // #include "ElemUtil.hpp" 00013 #include "moab/MergeMesh.hpp" 00014 #include "moab/ReadUtilIface.hpp" 00015 #include <iostream> 00016 // this is for sstream 00017 #include <sstream> 00018 00019 #include <queue> 00020 00021 namespace moab { 00022 // vec utilities that could be common between quads on a plane or sphere 00023 double dist2(double * a, double * b) 00024 { 00025 double abx = b[0] - a[0], aby = b[1] - a[1]; 00026 return sqrt(abx * abx + aby * aby); 00027 } 00028 double area2D(double *a, double *b, double *c) 00029 { 00030 // (b-a)x(c-a) / 2 00031 return ((b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])) / 2; 00032 } 00033 int borderPointsOfXinY2(double * X, int nX, double * Y, int nY, double * P, int side[MAXEDGES], double epsilon_area) 00034 { 00035 // 2 triangles, 3 corners, is the corner of X in Y? 00036 // Y must have a positive area 00037 /* 00038 */ 00039 int extraPoint = 0; 00040 for (int i = 0; i < nX; i++) 00041 { 00042 // compute twice the area of all nY triangles formed by a side of Y and a corner of X; if one is negative, stop 00043 // (negative means it is outside; X and Y are all oriented such that they are positive oriented; 00044 // if one area is negative, it means it is outside the convex region, for sure) 00045 double * A = X + 2 * i; 00046 00047 int inside = 1; 00048 for (int j = 0; j < nY; j++) 00049 { 00050 double * B = Y + 2 * j; 00051 00052 int j1 = (j + 1) % nY; 00053 double * C = Y + 2 * j1; // no copy of data 00054 00055 double area2 = (B[0] - A[0]) * (C[1] - A[1]) 00056 - (C[0] - A[0]) * (B[1] - A[1]); 00057 if (area2 < -epsilon_area) 00058 { 00059 inside = 0; 00060 break; 00061 } 00062 } 00063 if (inside) 00064 { 00065 side[i] = 1;// so vertex i of X is inside the convex region formed by Y 00066 // so side has nX dimension (first array) 00067 P[extraPoint * 2] = A[0]; 00068 P[extraPoint * 2 + 1] = A[1]; 00069 extraPoint++; 00070 } 00071 } 00072 return extraPoint; 00073 } 00074 00075 int swap2(double * p, double * q) 00076 { 00077 double tmp = *p; 00078 *p = *q; 00079 *q = tmp; 00080 return 0; 00081 } 00082 int SortAndRemoveDoubles2(double * P, int & nP, double epsilon_1) 00083 { 00084 if (nP < 2) 00085 return 0; // nothing to do 00086 // center of gravity for the points 00087 double c[2] = { 0., 0. }; 00088 int k = 0; 00089 for (k = 0; k < nP; k++) 00090 { 00091 c[0] += P[2 * k]; 00092 c[1] += P[2 * k + 1]; 00093 } 00094 c[0] /= nP; 00095 c[1] /= nP; 00096 // how many 00097 std::vector<double> angle(nP); // could be at most nP points 00098 for (k = 0; k < nP; k++) 00099 { 00100 double x = P[2 * k] - c[0], y = P[2 * k + 1] - c[1]; 00101 if (x != 0. || y != 0.) 00102 angle[k] = atan2(y, x); 00103 else 00104 { 00105 angle[k] = 0; 00106 // this means that the points are on a line, or all coincident // degenerate case 00107 } 00108 } 00109 // sort according to angle; also eliminate close points 00110 // this is a bubble sort, for np = 24 it could be pretty big 00111 // maybe a better sort is needed here (qsort?) 00112 int sorted = 1; 00113 do 00114 { 00115 sorted = 1; 00116 for (k = 0; k < nP - 1; k++) 00117 { 00118 if (angle[k] > angle[k + 1]) 00119 { 00120 sorted = 0; 00121 swap2(&angle[k], &angle[k+1]); 00122 swap2(P + (2 * k), P + (2 * k + 2)); 00123 swap2(P + (2 * k + 1), P + (2 * k + 3)); 00124 } 00125 } 00126 } while (!sorted); 00127 // eliminate doubles 00128 00129 int i = 0, j = 1; // the next one; j may advance faster than i 00130 // check the unit 00131 // double epsilon_1 = 1.e-5; // these are cm; 2 points are the same if the distance is less than 1.e-5 cm 00132 while (j < nP) 00133 { 00134 double d2 = dist2(&P[2 * i], &P[2 * j]); 00135 if (d2 > epsilon_1) 00136 { 00137 i++; 00138 P[2 * i] = P[2 * j]; 00139 P[2 * i + 1] = P[2 * j + 1]; 00140 } 00141 j++; 00142 } 00143 // test also the last point with the first one (index 0) 00144 00145 double d2 = dist2(P, &P[2 * i]); // check the first and last points (ordered from -pi to +pi) 00146 if (d2 > epsilon_1) 00147 { 00148 nP = i + 1; 00149 } 00150 else 00151 nP = i; // effectively delete the last point (that would have been the same with first) 00152 if (nP == 0) 00153 nP = 1; // we should be left with at least one point we already tested if nP is 0 originally 00154 return 0; 00155 } 00156 00157 // the marks will show what edges of blue intersect the red 00158 00159 int EdgeIntersections2(double * blue, int nsBlue, double * red, int nsRed, 00160 int markb[MAXEDGES], int markr[MAXEDGES], double * points, int & nPoints) 00161 { 00162 /* EDGEINTERSECTIONS computes edge intersections of two elements 00163 [P,n]=EdgeIntersections(X,Y) computes for the two given elements * red 00164 and blue ( stored column wise ) 00165 (point coordinates are stored column-wise, in counter clock 00166 order) the points P where their edges intersect. In addition, 00167 in n the indices of which neighbors of red are also intersecting 00168 with blue are given. 00169 */ 00170 00171 // points is an array with enough slots (24 * 2 doubles) 00172 nPoints = 0; 00173 for (int i=0; i<MAXEDGES; i++){ 00174 markb[i]=markr[i] = 0; 00175 } 00176 00177 for (int i = 0; i < nsBlue; i++) 00178 { 00179 for (int j = 0; j < nsRed; j++) 00180 { 00181 double b[2]; 00182 double a[2][2]; // 2*2 00183 int iPlus1 = (i + 1) % nsBlue; 00184 int jPlus1 = (j + 1) % nsRed; 00185 for (int k = 0; k < 2; k++) 00186 { 00187 b[k] = red[2 * j + k] - blue[2 * i + k]; 00188 // row k of a: a(k, 0), a(k, 1) 00189 a[k][0] = blue[2 * iPlus1 + k] - blue[2 * i + k]; 00190 a[k][1] = red[2 * j + k] - red[2 * jPlus1 + k]; 00191 00192 } 00193 double delta = a[0][0] * a[1][1] - a[0][1] * a[1][0]; 00194 if (delta != 0.) 00195 { 00196 // not parallel 00197 double alfa = (b[0] * a[1][1] - a[0][1] * b[1]) / delta; 00198 double beta = (-b[0] * a[1][0] + b[1] * a[0][0]) / delta; 00199 if (0 <= alfa && alfa <= 1. && 0 <= beta && beta <= 1.) 00200 { 00201 // the intersection is good 00202 for (int k = 0; k < 2; k++) 00203 { 00204 points[2 * nPoints + k] = blue[2 * i + k] 00205 + alfa * (blue[2 * iPlus1 + k] - blue[2 * i + k]); 00206 } 00207 markb[i] = 1; // so neighbor number i of blue will be considered too. 00208 markr[j] = 1; // this will be used in advancing red around blue quad 00209 nPoints++; 00210 } 00211 } 00212 // the case delta == 0. will be considered by the interior points logic 00213 00214 } 00215 } 00216 return 0; 00217 } 00218 00219 00220 // vec utils related to gnomonic projection on a sphere 00221 00222 // vec utils 00223 00224 /* 00225 * 00226 * position on a sphere of radius R 00227 * if plane specified, use it; if not, return the plane, and the point in the plane 00228 * there are 6 planes, numbered 1 to 6 00229 * plane 1: x=R, plane 2: y=R, 3: x=-R, 4: y=-R, 5: z=-R, 6: z=R 00230 * 00231 * projection on the plane will preserve the orientation, such that a triangle, quad pointing 00232 * outside the sphere will have a positive orientation in the projection plane 00233 */ 00234 void decide_gnomonic_plane(const CartVect & pos, int & plane) 00235 { 00236 // decide plane, based on max x, y, z 00237 if (fabs(pos[0]) < fabs(pos[1])) 00238 { 00239 if (fabs(pos[2]) < fabs(pos[1])) 00240 { 00241 // pos[1] is biggest 00242 if (pos[1] > 0) 00243 plane = 2; 00244 else 00245 plane = 4; 00246 } 00247 else 00248 { 00249 // pos[2] is biggest 00250 if (pos[2] < 0) 00251 plane = 5; 00252 else 00253 plane = 6; 00254 } 00255 } 00256 else 00257 { 00258 if (fabs(pos[2]) < fabs(pos[0])) 00259 { 00260 // pos[0] is the greatest 00261 if (pos[0] > 0) 00262 plane = 1; 00263 else 00264 plane = 3; 00265 } 00266 else 00267 { 00268 // pos[2] is biggest 00269 if (pos[2] < 0) 00270 plane = 5; 00271 else 00272 plane = 6; 00273 } 00274 } 00275 return; 00276 } 00277 // point on a sphere is projected on one of six planes, decided earlier 00278 int gnomonic_projection(const CartVect & pos, double R, int plane, double & c1, double & c2) 00279 { 00280 double alfa = 1.; // the new point will be on line alfa*pos 00281 00282 switch (plane) 00283 { 00284 case 1: 00285 { 00286 // the plane with x = R; x>y, x>z 00287 // c1->y, c2->z 00288 alfa = R / pos[0]; 00289 c1 = alfa * pos[1]; 00290 c2 = alfa * pos[2]; 00291 break; 00292 } 00293 case 2: 00294 { 00295 // y = R -> zx 00296 alfa = R / pos[1]; 00297 c1 = alfa * pos[2]; 00298 c2 = alfa * pos[0]; 00299 break; 00300 } 00301 case 3: 00302 { 00303 // x=-R, -> yz 00304 alfa = -R / pos[0]; 00305 c1 = -alfa * pos[1]; // the sign is to preserve orientation 00306 c2 = alfa * pos[2]; 00307 break; 00308 } 00309 case 4: 00310 { 00311 // y = -R 00312 alfa = -R / pos[1]; 00313 c1 = -alfa * pos[2]; // the sign is to preserve orientation 00314 c2 = alfa * pos[0]; 00315 break; 00316 } 00317 case 5: 00318 { 00319 // z = -R 00320 alfa = -R / pos[2]; 00321 c1 = -alfa * pos[0]; // the sign is to preserve orientation 00322 c2 = alfa * pos[1]; 00323 break; 00324 } 00325 case 6: 00326 { 00327 alfa = R / pos[2]; 00328 c1 = alfa * pos[0]; 00329 c2 = alfa * pos[1]; 00330 break; 00331 } 00332 default: 00333 return 1; // error 00334 } 00335 00336 return 0; // no error 00337 } 00338 // given the position on plane (one out of 6), find out the position on sphere 00339 int reverse_gnomonic_projection(const double & c1, const double & c2, double R, int plane, 00340 CartVect & pos) 00341 { 00342 00343 // the new point will be on line beta*pos 00344 double len = sqrt(c1 * c1 + c2 * c2 + R * R); 00345 double beta = R / len; // it is less than 1, in general 00346 00347 switch (plane) 00348 { 00349 case 1: 00350 { 00351 // the plane with x = R; x>y, x>z 00352 // c1->y, c2->z 00353 pos[0] = beta * R; 00354 pos[1] = c1 * beta; 00355 pos[2] = c2 * beta; 00356 break; 00357 } 00358 case 2: 00359 { 00360 // y = R -> zx 00361 pos[1] = R * beta; 00362 pos[2] = c1 * beta; 00363 pos[0] = c2 * beta; 00364 break; 00365 } 00366 case 3: 00367 { 00368 // x=-R, -> yz 00369 pos[0] = -R * beta; 00370 pos[1] = -c1 * beta; // the sign is to preserve orientation 00371 pos[2] = c2 * beta; 00372 break; 00373 } 00374 case 4: 00375 { 00376 // y = -R 00377 pos[1] = -R * beta; 00378 pos[2] = -c1 * beta; // the sign is to preserve orientation 00379 pos[0] = c2 * beta; 00380 break; 00381 } 00382 case 5: 00383 { 00384 // z = -R 00385 pos[2] = -R * beta; 00386 pos[0] = -c1 * beta; // the sign is to preserve orientation 00387 pos[1] = c2 * beta; 00388 break; 00389 } 00390 case 6: 00391 { 00392 pos[2] = R * beta; 00393 pos[0] = c1 * beta; 00394 pos[1] = c2 * beta; 00395 break; 00396 } 00397 default: 00398 return 1; // error 00399 } 00400 00401 return 0; // no error 00402 } 00403 00404 /* 00405 * 00406 use physical_constants, only : dd_pi 00407 type(cartesian3D_t), intent(in) :: cart 00408 type(spherical_polar_t) :: sphere 00409 00410 sphere%r=distance(cart) 00411 sphere%lat=ASIN(cart%z/sphere%r) 00412 sphere%lon=0 00413 00414 ! ========================================================== 00415 ! enforce three facts: 00416 ! 00417 ! 1) lon at poles is defined to be zero 00418 ! 00419 ! 2) Grid points must be separated by about .01 Meter (on earth) 00420 ! from pole to be considered "not the pole". 00421 ! 00422 ! 3) range of lon is { 0<= lon < 2*pi } 00423 ! 00424 ! ========================================================== 00425 00426 if (distance(cart) >= DIST_THRESHOLD) then 00427 sphere%lon=ATAN2(cart%y,cart%x) 00428 if (sphere%lon<0) then 00429 sphere%lon=sphere%lon+2*DD_PI 00430 end if 00431 end if 00432 00433 end function cart_to_spherical 00434 */ 00435 SphereCoords cart_to_spherical(CartVect & cart3d) 00436 { 00437 SphereCoords res; 00438 res.R = cart3d.length(); 00439 if (res.R < 0) 00440 { 00441 res.lon=res.lat = 0.; 00442 return res; 00443 } 00444 res.lat = asin(cart3d[2]/res.R); 00445 res.lon=atan2(cart3d[1], cart3d[0]); 00446 if (res.lon<0) 00447 res.lon+=2*M_PI;// M_PI is defined in math.h? it seems to be true, although 00448 // there are some defines it depends on :( 00449 // #if defined __USE_BSD || defined __USE_XOPEN ??? 00450 00451 return res; 00452 } 00453 /* 00454 * ! =================================================================== 00455 ! spherical_to_cart: 00456 ! converts spherical polar {lon,lat} to 3D cartesian {x,y,z} 00457 ! on unit sphere 00458 ! =================================================================== 00459 00460 function spherical_to_cart(sphere) result (cart) 00461 00462 type(spherical_polar_t), intent(in) :: sphere 00463 type(cartesian3D_t) :: cart 00464 00465 cart%x=sphere%r*COS(sphere%lat)*COS(sphere%lon) 00466 cart%y=sphere%r*COS(sphere%lat)*SIN(sphere%lon) 00467 cart%z=sphere%r*SIN(sphere%lat) 00468 00469 end function spherical_to_cart 00470 */ 00471 CartVect spherical_to_cart (SphereCoords & sc) 00472 { 00473 CartVect res; 00474 res[0] = sc.R * cos(sc.lat)*cos(sc.lon) ; // x coordinate 00475 res[1] = sc.R * cos(sc.lat)*sin(sc.lon); // y 00476 res[2] = sc.R * sin(sc.lat); // z 00477 return res; 00478 } 00479 // remove dependency to coupler now 00480 #if 0 00481 ErrorCode SpectralVisuMesh(Interface * mb, Range & input, int NP, EntityHandle & outputSet, double tolerance) 00482 { 00483 ErrorCode rval = MB_SUCCESS; 00484 00485 // this will compute the gl points on parametric element 00486 moab::Element::SpectralQuad specQuad(NP); 00487 Range fineElem; 00488 00489 std::vector<int> gids(input.size()*(NP-1)*(NP-1));// total number of "linear" elements 00490 // get all edges? or not? Form all gl points + quads, then merge, then output 00491 int startGid=0; 00492 for (Range::iterator it=input.begin(); it!=input.end(); it++) 00493 { 00494 const moab::EntityHandle * conn4 = NULL; 00495 int num_nodes = 0; 00496 rval = mb->get_connectivity(*it, conn4, num_nodes); 00497 if (moab::MB_SUCCESS != rval) 00498 { 00499 std::cout << "can't get connectivity for quad \n"; 00500 return rval; 00501 } 00502 assert(num_nodes==4); 00503 00504 std::vector<CartVect> verts(4); 00505 rval = mb->get_coords(conn4, num_nodes, &(verts[0][0])); 00506 if (moab::MB_SUCCESS != rval) 00507 { 00508 std::cout << "can't get coords for quad \n"; 00509 return rval; 00510 } 00511 00512 specQuad.set_vertices(verts); 00513 specQuad.compute_gl_positions(); 00514 double * xyz[3]; 00515 int size; 00516 specQuad.get_gl_points(xyz[0], xyz[1], xyz[2], size); 00517 assert(NP*NP==size); 00518 // these points are in lexicographic order; 00519 std::vector<EntityHandle> nodes(size); 00520 for (int i=0; i<size; i++) 00521 { 00522 double coords[3]={ xyz[0][i], xyz[1][i], xyz[2][i] }; 00523 rval = mb->create_vertex(coords, nodes[i] ); 00524 if (rval!=moab::MB_SUCCESS) 00525 return rval; 00526 } 00527 // create (NP-1)^2 quads, one by one (sic) 00528 for (int i=0; i<NP-1; i++) 00529 { 00530 for (int j=0; j<NP-1; j++) 00531 { 00532 EntityHandle connec[4]={ nodes[ i*NP+j], nodes[i*NP+j+1], 00533 nodes[(i+1)*NP+j+1], nodes[(i+1)*NP+j] }; 00534 EntityHandle element_handle; 00535 rval = mb->create_element(MBQUAD, connec, 4, element_handle); 00536 if (rval!=moab::MB_SUCCESS) 00537 return rval; 00538 fineElem.insert(element_handle); 00539 gids[startGid]=startGid+1; 00540 startGid++; 00541 } 00542 } 00543 00544 } 00545 00546 mb->add_entities(outputSet, fineElem); 00547 // merge all nodes 00548 MergeMesh mgtool(mb); 00549 rval = mgtool.merge_entities(fineElem, tolerance); 00550 if (MB_SUCCESS!=rval) 00551 return rval; 00552 Tag gidTag; 00553 rval = mb->tag_get_handle("GLOBAL_ID", 1, MB_TYPE_INTEGER, 00554 gidTag, MB_TAG_DENSE|MB_TAG_CREAT); 00555 if (MB_SUCCESS!=rval) 00556 return rval; 00557 rval = mb->tag_set_data(gidTag, fineElem, &gids[0]); 00558 00559 return rval; 00560 } 00561 // remove for the time being dependency on coupler 00562 #endif 00563 ErrorCode ProjectOnSphere(Interface * mb, EntityHandle set, double R) 00564 { 00565 Range ents; 00566 ErrorCode rval = mb->get_entities_by_handle(set, ents); 00567 if (rval!=moab::MB_SUCCESS) 00568 return rval; 00569 00570 Range nodes; 00571 rval = mb->get_connectivity( ents, nodes); 00572 if (rval!=moab::MB_SUCCESS) 00573 return rval; 00574 00575 // one by one, get the node and project it on the sphere, with a radius given 00576 // the center of the sphere is at 0,0,0 00577 for (Range::iterator nit=nodes.begin(); nit!=nodes.end(); nit++) 00578 { 00579 EntityHandle nd=*nit; 00580 CartVect pos; 00581 rval = mb->get_coords(&nd, 1, (double*)&(pos[0])); 00582 if (rval!=moab::MB_SUCCESS) 00583 return rval; 00584 double len=pos.length(); 00585 if (len==0.) 00586 return MB_FAILURE; 00587 pos = R/len*pos; 00588 rval = mb->set_coords(&nd, 1, (double*)&(pos[0])); 00589 if (rval!=moab::MB_SUCCESS) 00590 return rval; 00591 } 00592 return MB_SUCCESS; 00593 } 00594 // 00595 bool point_in_interior_of_convex_polygon (double * points, int np, double pt[2]) 00596 { 00597 bool inside = true; 00598 // assume points are counterclockwise 00599 for (int i = 0; i < np; i++) 00600 { 00601 // compute the area of triangles formed by a side of polygon and the pt; if one is negative, stop 00602 double * A = points + 2 * i; 00603 00604 int i1=(i+1)%np; 00605 double * B = points + 2 * i1; // no copy of data 00606 00607 double area2 = (B[0] - A[0]) * (pt[1] - A[1]) 00608 - (pt[0] - A[0]) * (B[1] - A[1]); 00609 if (area2 < 0.) 00610 { 00611 inside = false; 00612 break; 00613 } 00614 } 00615 return inside; 00616 } 00617 // assume they are one the same sphere 00618 double spherical_angle(double * A, double * B, double * C, double Radius) 00619 { 00620 // the angle by definition is between the planes OAB and OBC 00621 CartVect a(A); 00622 CartVect b(B); 00623 CartVect c(C); 00624 double err1=a.length_squared()-Radius*Radius; 00625 if (fabs(err1)>0.0001) 00626 { 00627 std::cout << " error in input " << a << " radius: " << Radius << " error:" << err1<< "\n"; 00628 } 00629 CartVect normalOAB = a * b; 00630 CartVect normalOCB = c * b; 00631 return angle(normalOAB, normalOCB); 00632 } 00633 // could be bigger than M_PI; 00634 // angle at B could be bigger than M_PI, if the orientation is such that ABC points toward the interior 00635 double oriented_spherical_angle(double * A, double * B, double * C) 00636 { 00637 // assume the same radius, sphere at origin 00638 CartVect a(A), b(B), c(C); 00639 CartVect normalOAB = a * b; 00640 CartVect normalOCB = c * b; 00641 CartVect orient = (c-b)*(a-b); 00642 double ang = angle(normalOAB, normalOCB); // this is between 0 and M_PI 00643 if (ang!=ang) 00644 { 00645 // signal of a nan 00646 std::cout << a << " " << b << " " << c <<"\n"; 00647 std::cout << ang << "\n"; 00648 } 00649 if (orient%b < 0) 00650 return (2*M_PI-ang);// the other angle, supplement 00651 00652 return ang; 00653 00654 } 00655 double area_spherical_triangle(double *A, double *B, double *C, double Radius) 00656 { 00657 double correction = spherical_angle(A, B, C, Radius)+spherical_angle(B, C, A, Radius)+ 00658 spherical_angle(C, A, B, Radius)-M_PI; 00659 double area = Radius*Radius*correction; 00660 // now, is it negative or positive? is it pointing toward the center or outward? 00661 CartVect a(A), b(B), c(C); 00662 CartVect abc = (b-a)*(c-a); 00663 if (abc%a > 0) // dot product positive, means ABC points out 00664 return area; 00665 else 00666 return -area; 00667 00668 } 00669 00670 double area_spherical_polygon (double * A, int N, double Radius) 00671 { 00672 // this should work for non-convex polygons too 00673 // assume that the A, A+3, ..., A+3*(N-1) are the coordinates 00674 // 00675 if (N<=2) 00676 return 0.; 00677 double sum_angles = 0.; 00678 for (int i=0; i<N; i++) 00679 { 00680 int i1 = (i+1)%N; 00681 int i2 = (i+2)%N; 00682 sum_angles += oriented_spherical_angle( A+3*i, A+3*i1, A+3*i2); 00683 } 00684 double correction = sum_angles-(N-2)*M_PI; 00685 return Radius*Radius *correction; 00686 00687 } 00688 /* 00689 * l'Huiller's formula for spherical triangle 00690 * http://williams.best.vwh.net/avform.htm 00691 * a, b, c are arc measures in radians, too 00692 * A, B, C are angles on the sphere, for which we already have formula 00693 * c 00694 * A -------B 00695 * \ | 00696 * \ | 00697 * \b |a 00698 * \ | 00699 * \ | 00700 * \ | 00701 * \C| 00702 * \| 00703 * 00704 * (The angle at B is not necessarily a right angle) 00705 * 00706 * sin(a) sin(b) sin(c) 00707 * ----- = ------ = ------ 00708 * sin(A) sin(B) sin(C) 00709 * 00710 * In terms of the sides (this is excess, as before, but numerically stable) 00711 * 00712 * E = 4*atan(sqrt(tan(s/2)*tan((s-a)/2)*tan((s-b)/2)*tan((s-c)/2))) 00713 */ 00714 00715 double area_spherical_triangle_lHuiller(double * ptA, double * ptB, double * ptC, double Radius) 00716 { 00717 // now, a is angle BOC, O is origin 00718 CartVect vA(ptA), vB(ptB), vC(ptC); 00719 double a = angle(vB, vC); 00720 double b = angle(vC, vA); 00721 double c = angle(vA, vB); 00722 int sign =1; 00723 if ((vA*vB)%vC < 0) 00724 sign = -1; 00725 double s = (a+b+c)/2; 00726 double tmp = tan(s/2)*tan((s-a)/2)*tan((s-b)/2)*tan((s-c)/2); 00727 if (tmp<0.) 00728 tmp = 0.; 00729 double E = 4*atan(sqrt(tmp)); 00730 if (E!=E) 00731 std::cout << " NaN at spherical triangle area \n"; 00732 return sign * E * Radius*Radius; 00733 } 00734 00735 00736 double area_spherical_polygon_lHuiller (double * A, int N, double Radius) 00737 { 00738 // this should work for non-convex polygons too 00739 // assume that the A, A+3, ..., A+3*(N-1) are the coordinates 00740 // 00741 // assume that the orientation is positive; 00742 // if negative orientation, the area will be negative 00743 if (N<=2) 00744 return 0.; 00745 double area = 0.; 00746 for (int i=1; i<N-1; i++) 00747 { 00748 int i1 = i+1; 00749 area += area_spherical_triangle_lHuiller( A, A+3*i, A+3*i1, Radius); 00750 } 00751 return area; 00752 } 00753 00754 double area_on_sphere(Interface * mb, EntityHandle set, double R) 00755 { 00756 // get all entities of dimension 2 00757 // then get the connectivity, etc 00758 Range inputRange; 00759 ErrorCode rval = mb->get_entities_by_dimension(set, 2, inputRange); 00760 if (MB_SUCCESS != rval) 00761 return -1; 00762 00763 // compare total area with 4*M_PI * R^2 00764 double total_area = 0.; 00765 for (Range::iterator eit=inputRange.begin(); eit!= inputRange.end(); eit++ ) 00766 { 00767 EntityHandle eh=*eit; 00768 // get the nodes, then the coordinates 00769 const EntityHandle * verts; 00770 int num_nodes; 00771 rval = mb->get_connectivity(eh, verts, num_nodes); 00772 if (MB_SUCCESS != rval) 00773 return -1; 00774 std::vector<double> coords(3*num_nodes); 00775 // get coordinates 00776 rval = mb->get_coords(verts, num_nodes, &coords[0]); 00777 if (MB_SUCCESS != rval) 00778 return -1; 00779 total_area+=area_spherical_polygon (&coords[0], num_nodes, R); 00780 } 00781 return total_area; 00782 } 00783 double area_on_sphere_lHuiller(Interface * mb, EntityHandle set, double R) 00784 { 00785 // get all entities of dimension 2 00786 // then get the connectivity, etc 00787 Range inputRange; 00788 ErrorCode rval = mb->get_entities_by_dimension(set, 2, inputRange); 00789 if (MB_SUCCESS != rval) 00790 return -1; 00791 00792 double total_area = 0.; 00793 for (Range::iterator eit = inputRange.begin(); eit != inputRange.end(); eit++) 00794 { 00795 EntityHandle eh = *eit; 00796 // get the nodes, then the coordinates 00797 const EntityHandle * verts; 00798 int num_nodes; 00799 rval = mb->get_connectivity(eh, verts, num_nodes); 00800 if (MB_SUCCESS != rval) 00801 return -1; 00802 std::vector<double> coords(3 * num_nodes); 00803 // get coordinates 00804 rval = mb->get_coords(verts, num_nodes, &coords[0]); 00805 if (MB_SUCCESS != rval) 00806 return -1; 00807 total_area += area_spherical_polygon_lHuiller(&coords[0], num_nodes, R); 00808 } 00809 return total_area; 00810 } 00811 00812 double area_spherical_element(Interface * mb, EntityHandle elem, double R) 00813 { 00814 const EntityHandle * verts; 00815 int num_nodes; 00816 ErrorCode rval = mb->get_connectivity(elem, verts, num_nodes); 00817 if (MB_SUCCESS != rval) 00818 return -1; 00819 std::vector<double> coords(3 * num_nodes); 00820 // get coordinates 00821 rval = mb->get_coords(verts, num_nodes, &coords[0]); 00822 if (MB_SUCCESS != rval) 00823 return -1; 00824 return area_spherical_polygon_lHuiller(&coords[0], num_nodes, R); 00825 00826 } 00827 /* 00828 * 00829 */ 00830 double distance_on_great_circle(CartVect & p1, CartVect & p2) 00831 { 00832 SphereCoords sph1 = cart_to_spherical(p1); 00833 SphereCoords sph2 = cart_to_spherical(p2); 00834 // radius should be the same 00835 return sph1.R * acos(sin (sph1.lon)* sin(sph2.lon) + cos(sph1.lat)*cos(sph2.lat)*cos(sph2.lon-sph2.lon) ); 00836 } 00837 /* 00838 * based on paper A class of deformational flow test cases for linear transport problems on the sphere 00839 * longitude lambda [0.. 2*pi) and latitude theta (-pi/2, pi/2) 00840 * lambda: -> lon (0, 2*pi) 00841 * theta : -> lat (-pi/2, po/2) 00842 * Radius of the sphere is 1 (if not, everything gets multiplied by 1) 00843 * 00844 * cosine bell: center lambda0, theta0: 00845 */ 00846 void departure_point_case1(CartVect & arrival_point, double t, double delta_t, CartVect & departure_point) 00847 { 00848 // always assume radius is 1 here? 00849 SphereCoords sph = cart_to_spherical(arrival_point); 00850 double T=5; // duration of integration (5 units) 00851 double k = 2.4; //flow parameter 00852 /* radius needs to be within some range */ 00853 double sl2 = sin(sph.lon/2); 00854 double pit = M_PI * t / T; 00855 double omega = M_PI/T; 00856 double costetha = cos(sph.lat); 00857 //double u = k * sl2*sl2 * sin(2*sph.lat) * cos(pit); 00858 double v = k * sin(sph.lon) * costetha * cos(pit); 00859 //double psi = k * sl2 * sl2 *costetha * costetha * cos(pit); 00860 double u_tilda = 2*k*sl2*sl2*sin(sph.lat)*cos(pit); 00861 00862 // formula 35, page 8 00863 // this will approximate dep point using a Taylor series with up to second derivative 00864 // this will be O(delta_t^3) exact. 00865 double lon_dep = sph.lon - delta_t * u_tilda -delta_t*delta_t * k * sl2 * 00866 ( sl2 * sin (sph.lat) * sin(pit) * omega 00867 - u_tilda * sin(sph.lat) * cos(pit) * cos (sph.lon/2) 00868 - v * sl2 * costetha * cos(pit) ); 00869 // formula 36, page 8 again 00870 double lat_dep = sph.lat - delta_t*v - delta_t * delta_t/4* k * 00871 ( sin(sph.lon)* cos(sph.lat) * sin(pit) * omega 00872 - u_tilda * cos(sph.lon) * cos(sph.lat) * cos(pit) 00873 + v * sin(sph.lon) * sin(sph.lat) * cos(pit) ); 00874 SphereCoords sph_dep; 00875 sph_dep.R = 1.; // radius 00876 sph_dep.lat = lat_dep; 00877 sph_dep.lon = lon_dep; 00878 00879 departure_point = spherical_to_cart(sph_dep); 00880 return; 00881 } 00882 00883 void velocity_case1(CartVect & arrival_point, double t, CartVect & velo) 00884 { 00885 // always assume radius is 1 here? 00886 SphereCoords sph = cart_to_spherical(arrival_point); 00887 double T=5; // duration of integration (5 units) 00888 double k = 2.4; //flow parameter 00889 /* radius needs to be within some range */ 00890 double sl2 = sin(sph.lon/2); 00891 double pit = M_PI * t / T; 00892 //double omega = M_PI/T; 00893 double coslat = cos(sph.lat); 00894 double sinlat = sin(sph.lat); 00895 double sinlon = sin(sph.lon); 00896 double coslon = cos(sph.lon); 00897 double u = k * sl2*sl2 * sin(2*sph.lat) * cos(pit); 00898 double v = k/2 * sinlon * coslat * cos(pit); 00899 velo[0] = - u * sinlon - v * sinlat * coslon; 00900 velo[1] = u * coslon - v * sinlat * sinlon; 00901 velo[2] = v * coslat; 00902 00903 } 00904 // break the nonconvex quads into triangles; remove the quad from the set? yes. 00905 // maybe radius is not needed; 00906 // 00907 ErrorCode enforce_convexity(Interface * mb, EntityHandle lset, int my_rank) 00908 { 00909 // look at each polygon; compute all angles; if one is reflex, break that angle with 00910 // the next triangle; put the 2 new polys in the set; 00911 // still look at the next poly 00912 // replace it with 2 triangles, and remove from set; 00913 // it should work for all polygons / tested first for case 1, with dt 0.5 (too much deformation) 00914 // get all entities of dimension 2 00915 // then get the connectivity, etc 00916 00917 Range inputRange; 00918 ErrorCode rval = mb->get_entities_by_dimension(lset, 2, inputRange); 00919 if (MB_SUCCESS != rval) 00920 return rval; 00921 00922 Tag corrTag=0; 00923 EntityHandle dumH=0; 00924 rval = mb->tag_get_handle(CORRTAGNAME, 00925 1, MB_TYPE_HANDLE, corrTag, 00926 MB_TAG_DENSE, &dumH); 00927 if(rval==MB_TAG_NOT_FOUND) 00928 corrTag = 0; 00929 00930 Tag gidTag; 00931 rval = mb->tag_get_handle("GLOBAL_ID", 1, MB_TYPE_INTEGER, 00932 gidTag, MB_TAG_DENSE); 00933 00934 if(rval!=MB_SUCCESS) 00935 return rval; 00936 00937 std::vector<double> coords; 00938 coords.resize(3*MAXEDGES); // at most 10 vertices per polygon 00939 // we should create a queue with new polygons that need processing for reflex angles 00940 // (obtuse) 00941 std::queue<EntityHandle> newPolys; 00942 int brokenPolys=0; 00943 Range::iterator eit = inputRange.begin(); 00944 while (eit != inputRange.end() || !newPolys.empty()) 00945 { 00946 EntityHandle eh; 00947 if (eit != inputRange.end()) 00948 { 00949 eh = *eit; 00950 eit++; 00951 } 00952 else 00953 { 00954 eh = newPolys.front(); 00955 newPolys.pop(); 00956 } 00957 // get the nodes, then the coordinates 00958 const EntityHandle * verts; 00959 int num_nodes; 00960 rval = mb->get_connectivity(eh, verts, num_nodes); 00961 if (MB_SUCCESS != rval) 00962 return rval; 00963 int nsides = num_nodes; 00964 // account for possible padded polygons 00965 while (verts[nsides-2]==verts[nsides-1] && nsides>3) 00966 nsides--; 00967 EntityHandle corrHandle=0; 00968 if (corrTag) 00969 { 00970 rval = mb->tag_get_data(corrTag, &eh, 1, &corrHandle); 00971 if (MB_SUCCESS != rval) 00972 return rval; 00973 } 00974 int gid=0; 00975 rval = mb->tag_get_data(gidTag, &eh, 1, &gid); 00976 if (MB_SUCCESS != rval) 00977 return rval; 00978 coords.resize(3 * nsides); 00979 if (nsides < 4) 00980 continue; // if already triangles, don't bother 00981 // get coordinates 00982 rval = mb->get_coords(verts, nsides, &coords[0]); 00983 if (MB_SUCCESS != rval) 00984 return rval; 00985 // compute each angle 00986 bool alreadyBroken = false; 00987 00988 for (int i=0; i<nsides; i++) 00989 { 00990 double * A = &coords[3*i]; 00991 double * B = &coords[3*((i+1)%nsides)]; 00992 double * C = &coords[3*((i+2)%nsides)]; 00993 double angle = oriented_spherical_angle(A, B, C); 00994 if (angle-M_PI > 0.) // even almost reflex is bad; break it! 00995 { 00996 if (alreadyBroken) 00997 { 00998 mb->list_entities(&eh, 1); 00999 mb->list_entities(verts, nsides); 01000 double * D = &coords[3*((i+3)%nsides)]; 01001 std::cout<< "ABC: " << angle << " \n"; 01002 std::cout<< "BCD: " << oriented_spherical_angle( B, C, D) << " \n"; 01003 std::cout<< "CDA: " << oriented_spherical_angle( C, D, A) << " \n"; 01004 std::cout<< "DAB: " << oriented_spherical_angle( D, A, B)<< " \n"; 01005 std::cout << " this cell has at least 2 angles > 180, it has serious issues\n"; 01006 01007 return MB_FAILURE; 01008 } 01009 // the bad angle is at i+1; 01010 // create 1 triangle and one polygon; add the polygon to the input range, so 01011 // it will be processed too 01012 // also, add both to the set :) and remove the original polygon from the set 01013 // break the next triangle, even though not optimal 01014 // so create the triangle i+1, i+2, i+3; remove i+2 from original list 01015 // even though not optimal in general, it is good enough. 01016 EntityHandle conn3[3]={ verts[ (i+1)%nsides], 01017 verts[ (i+2)%nsides], 01018 verts[ (i+3)%nsides] }; 01019 // create a polygon with num_nodes-1 vertices, and connectivity 01020 // verts[i+1], verts[i+3], (all except i+2) 01021 std::vector<EntityHandle> conn(nsides-1); 01022 for (int j=1; j<nsides; j++) 01023 { 01024 conn[j-1]=verts[(i+j+2)%nsides]; 01025 } 01026 EntityHandle newElement; 01027 rval = mb->create_element(MBTRI, conn3, 3, newElement); 01028 if (MB_SUCCESS != rval) 01029 return rval; 01030 01031 rval = mb->add_entities(lset, &newElement, 1); 01032 if (MB_SUCCESS != rval) 01033 return rval; 01034 if (corrTag) 01035 { 01036 rval = mb->tag_set_data(corrTag, &newElement, 1, &corrHandle); 01037 if (MB_SUCCESS != rval) 01038 return rval; 01039 } 01040 rval = mb->tag_set_data(gidTag, &newElement, 1, &gid); 01041 if (MB_SUCCESS != rval) 01042 return rval; 01043 if (nsides == 4) 01044 { 01045 // create another triangle 01046 rval = mb->create_element(MBTRI, &conn[0], 3, newElement); 01047 if (MB_SUCCESS != rval) 01048 return rval; 01049 } 01050 else 01051 { 01052 // create another polygon, and add it to the inputRange 01053 rval = mb->create_element(MBPOLYGON, &conn[0], nsides-1, newElement); 01054 if (MB_SUCCESS != rval) 01055 return rval; 01056 newPolys.push(newElement); // because it has less number of edges, the 01057 // reverse should work to find it. 01058 } 01059 rval = mb->add_entities(lset, &newElement, 1); 01060 if (MB_SUCCESS != rval) 01061 return rval; 01062 if (corrTag) 01063 { 01064 rval = mb->tag_set_data(corrTag, &newElement, 1, &corrHandle); 01065 if (MB_SUCCESS != rval) 01066 return rval; 01067 } 01068 rval = mb->tag_set_data(gidTag, &newElement, 1, &gid); 01069 if (MB_SUCCESS != rval) 01070 return rval; 01071 mb->remove_entities(lset, &eh, 1); 01072 brokenPolys++; 01073 /*std::cout<<"remove: " ; 01074 mb->list_entities(&eh, 1); 01075 01076 std::stringstream fff; 01077 fff << "file0" << brokenQuads<< ".vtk"; 01078 mb->write_file(fff.str().c_str(), 0, 0, &lset, 1);*/ 01079 alreadyBroken=true; // get out of the loop, element is broken 01080 } 01081 } 01082 } 01083 std::cout << "on local process " << my_rank << " " << brokenPolys << " concave polygons were decomposed in convex ones \n"; 01084 return MB_SUCCESS; 01085 } 01086 ErrorCode create_span_quads(Interface * mb, EntityHandle euler_set, int rank) 01087 { 01088 // first get all edges adjacent to polygons 01089 Tag dpTag = 0; 01090 std::string tag_name("DP"); 01091 ErrorCode rval = mb->tag_get_handle(tag_name.c_str(), 3, MB_TYPE_DOUBLE, dpTag, MB_TAG_DENSE); 01092 // if the tag does not exist, get out early 01093 if (rval!=MB_SUCCESS) 01094 return rval; 01095 Range polygons; 01096 rval = mb->get_entities_by_dimension(euler_set, 2, polygons); 01097 if (MB_SUCCESS != rval) 01098 return rval; 01099 Range iniEdges; 01100 rval = mb->get_adjacencies(polygons, 01101 1, 01102 false, 01103 iniEdges, 01104 Interface::UNION); 01105 if (MB_SUCCESS != rval) 01106 return rval; 01107 // now create some if missing 01108 Range allEdges; 01109 rval = mb->get_adjacencies(polygons, 01110 1, 01111 true, 01112 allEdges, 01113 Interface::UNION); 01114 // create the vertices at the DP points, and the quads after that 01115 Range verts; 01116 rval = mb->get_connectivity(polygons, verts); 01117 if (MB_SUCCESS != rval) 01118 return rval; 01119 int num_verts = (int) verts.size(); 01120 // now see the departure points; to what boxes should we send them? 01121 std::vector<double> dep_points(3*num_verts); 01122 rval = mb->tag_get_data(dpTag, verts, (void*)&dep_points[0]); 01123 if (MB_SUCCESS != rval) 01124 return rval; 01125 01126 // create vertices corresponding to dp locations 01127 ReadUtilIface *read_iface; 01128 rval = mb->query_interface(read_iface); 01129 if (MB_SUCCESS != rval) 01130 return rval; 01131 std::vector<double *> coords; 01132 EntityHandle start_vert, start_elem, *connect; 01133 // create verts, num is 2(nquads+1) because they're in a 1d row; will initialize coords in loop over quads later 01134 rval = read_iface->get_node_coords (3, num_verts, 0, start_vert, coords); 01135 if (MB_SUCCESS != rval) 01136 return rval; 01137 // fill it up 01138 for (int i=0; i<num_verts; i++) 01139 { 01140 // block from interleaved 01141 coords[0][i]=dep_points[3*i]; 01142 coords[1][i]=dep_points[3*i+1]; 01143 coords[2][i]=dep_points[3*i+2]; 01144 } 01145 // create quads; one quad for each edge 01146 rval = read_iface->get_element_connect(allEdges.size(), 4, MBQUAD, 0, start_elem, connect); 01147 if (MB_SUCCESS != rval) 01148 return rval; 01149 01150 const EntityHandle * edge_conn = NULL; 01151 int quad_index=0; 01152 EntityHandle firstVertHandle = verts[0];// assume vertices are contiguous... 01153 for (Range::iterator eit=allEdges.begin(); eit!=allEdges.end(); eit++, quad_index++) 01154 { 01155 EntityHandle edge=*eit; 01156 int num_nodes; 01157 rval = mb->get_connectivity(edge, edge_conn, num_nodes); 01158 if (MB_SUCCESS != rval) 01159 return rval; 01160 connect[quad_index*4] = edge_conn[0]; 01161 connect[quad_index*4+1] = edge_conn[1]; 01162 01163 // maybe some indexing in range? 01164 connect[quad_index*4+2] = start_vert + edge_conn[1]- firstVertHandle; 01165 connect[quad_index*4+3] = start_vert + edge_conn[0]- firstVertHandle; 01166 } 01167 01168 01169 Range quads(start_elem, start_elem+allEdges.size()-1); 01170 EntityHandle outSet; 01171 rval = mb->create_meshset(MESHSET_SET, outSet); 01172 if (MB_SUCCESS != rval) 01173 return rval; 01174 mb->add_entities(outSet, quads); 01175 01176 Tag colTag; 01177 rval = mb->tag_get_handle("COLOR_ID", 1, MB_TYPE_INTEGER, 01178 colTag, MB_TAG_DENSE|MB_TAG_CREAT); 01179 if (MB_SUCCESS != rval) 01180 return rval; 01181 int j=1; 01182 for (Range::iterator itq=quads.begin(); itq!=quads.end(); itq++, j++) 01183 { 01184 EntityHandle q=*itq; 01185 rval = mb->tag_set_data(colTag, &q, 1, &j); 01186 if (MB_SUCCESS != rval) 01187 return rval; 01188 } 01189 std::stringstream outf; 01190 outf << "SpanQuads" << rank << ".h5m"; 01191 rval = mb->write_file(outf.str().c_str(), 0, 0, &outSet, 1); 01192 if (MB_SUCCESS != rval) 01193 return rval; 01194 EntityHandle outSet2; 01195 rval = mb->create_meshset(MESHSET_SET, outSet2); 01196 if (MB_SUCCESS != rval) 01197 return rval; 01198 01199 Range quadEdges; 01200 rval = mb->get_adjacencies(quads, 01201 1, 01202 true, 01203 quadEdges, 01204 Interface::UNION); 01205 mb->add_entities(outSet2, quadEdges); 01206 01207 std::stringstream outf2; 01208 outf2 << "SpanEdges" << rank << ".h5m"; 01209 rval = mb->write_file(outf2.str().c_str(), 0, 0, &outSet2, 1); 01210 if (MB_SUCCESS != rval) 01211 return rval; 01212 01213 // maybe some clean up 01214 mb->delete_entities(&outSet, 1); 01215 mb->delete_entities(&outSet2, 1); 01216 mb->delete_entities(quads); 01217 Range new_edges=subtract(allEdges, iniEdges); 01218 mb->delete_entities(new_edges); 01219 new_edges=subtract(quadEdges, iniEdges); 01220 mb->delete_entities(new_edges); 01221 Range new_verts(start_vert, start_vert+num_verts); 01222 mb->delete_entities(new_verts); 01223 01224 return MB_SUCCESS; 01225 01226 } 01227 01228 // distance along a great circle on a sphere of radius 1 01229 // page 4 01230 double distance_on_sphere(double la1, double te1, double la2, double te2) 01231 { 01232 return acos(sin(te1)*sin(te2)+cos(te1)*cos(te2)*cos(la1-la2)); 01233 } 01234 // page 4 Nair Lauritzen paper 01235 // param will be: (la1, te1), (la2, te2), b, c; hmax=1, r=1/2 01236 double quasi_smooth_field(double lam, double tet, double * params) 01237 { 01238 double la1 = params[0]; 01239 double te1 = params[1]; 01240 double la2 = params[2]; 01241 double te2 = params[3]; 01242 double b = params[4]; 01243 double c = params[5]; 01244 double hmax = params[6]; // 1; 01245 double r = params[7] ; // 0.5; 01246 double r1 = distance_on_sphere(lam, tet, la1, te1); 01247 double r2 = distance_on_sphere(lam, tet, la2, te2); 01248 double value = b; 01249 if (r1<r) 01250 { 01251 value += c*hmax/2*(1+cos(M_PI*r1/r)); 01252 } 01253 if (r2<r) 01254 { 01255 value += c*hmax/2*(1+cos(M_PI*r2/r)); 01256 } 01257 return value; 01258 } 01259 // page 4 01260 // params are now x1, y1, ..., y2, z2 (6 params) 01261 // plus h max and b0 (total of 8 params); radius is 1 01262 double smooth_field(double lam, double tet, double * params) 01263 { 01264 SphereCoords sc; 01265 sc.R = 1.; 01266 sc.lat= tet; 01267 sc.lon = lam; 01268 double hmax = params[6]; 01269 double b0 = params[7]; 01270 CartVect xyz = spherical_to_cart(sc); 01271 CartVect c1(params); 01272 CartVect c2(params+3); 01273 double expo1 = -b0 * (xyz-c1).length_squared(); 01274 double expo2 = -b0 * (xyz-c2).length_squared(); 01275 return hmax*( exp(expo1) + exp(expo2)); 01276 } 01277 // page 5 01278 double slotted_cylinder_field(double lam, double tet, double * params) 01279 { 01280 double la1 = params[0]; 01281 double te1 = params[1]; 01282 double la2 = params[2]; 01283 double te2 = params[3]; 01284 double b = params[4]; 01285 double c = params[5]; 01286 //double hmax = params[6]; // 1; 01287 double r = params[6] ; // 0.5; 01288 double r1 = distance_on_sphere(lam, tet, la1, te1); 01289 double r2 = distance_on_sphere(lam, tet, la2, te2); 01290 double value = b; 01291 double d1=fabs(lam-la1); 01292 double d2 = fabs(lam-la2); 01293 double rp6 = r/6; 01294 double rt5p12 = r*5/12; 01295 01296 if (r1<=r && d1>=rp6) 01297 value=c; 01298 if (r2<=r && d2>=rp6) 01299 value =c; 01300 if (r1<=r && d1<rp6 && tet-te1<-rt5p12) 01301 value = c; 01302 if (r2<=r && d2<rp6 && tet-te2 > rt5p12) 01303 value =c; 01304 01305 return value; 01306 } 01307 01308 } //namespace moab