Actual source code: rgpolygon.c
slepc-main 2024-12-17
1: /*
2: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3: SLEPc - Scalable Library for Eigenvalue Problem Computations
4: Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain
6: This file is part of SLEPc.
7: SLEPc is distributed under a 2-clause BSD license (see LICENSE).
8: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9: */
10: /*
11: Polygonal region defined by a set of vertices
12: */
14: #include <slepc/private/rgimpl.h>
15: #include <petscdraw.h>
17: #define VERTMAX 30
19: typedef struct {
20: PetscInt n; /* number of vertices */
21: PetscScalar *vr,*vi; /* array of vertices (vi not used in complex scalars) */
22: } RG_POLYGON;
24: static PetscErrorCode RGComputeBoundingBox_Polygon(RG,PetscReal*,PetscReal*,PetscReal*,PetscReal*);
26: #if !defined(PETSC_USE_COMPLEX)
27: static PetscBool CheckSymmetry(PetscInt n,PetscScalar *vr,PetscScalar *vi)
28: {
29: PetscInt i,j,k;
30: /* find change of sign in imaginary part */
31: j = vi[0]!=0.0? 0: 1;
32: for (k=j+1;k<n;k++) {
33: if (vi[k]!=0.0) {
34: if (vi[k]*vi[j]<0.0) break;
35: j++;
36: }
37: }
38: if (k==n) return (j==1)? PETSC_TRUE: PETSC_FALSE;
39: /* check pairing vertices */
40: for (i=0;i<n/2;i++) {
41: if (vr[k]!=vr[j] || vi[k]!=-vi[j]) return PETSC_FALSE;
42: k = (k+1)%n;
43: j = (j-1+n)%n;
44: }
45: return PETSC_TRUE;
46: }
47: #endif
49: static PetscErrorCode RGPolygonSetVertices_Polygon(RG rg,PetscInt n,PetscScalar *vr,PetscScalar *vi)
50: {
51: PetscInt i;
52: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
54: PetscFunctionBegin;
55: PetscCheck(n>2,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"At least 3 vertices required, you provided %" PetscInt_FMT,n);
56: PetscCheck(n<=VERTMAX,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"Too many points, maximum allowed is %d",VERTMAX);
57: #if !defined(PETSC_USE_COMPLEX)
58: PetscCheck(CheckSymmetry(n,vr,vi),PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_WRONG,"In real scalars the region must be symmetric wrt real axis");
59: #endif
60: if (ctx->n) {
61: PetscCall(PetscFree(ctx->vr));
62: #if !defined(PETSC_USE_COMPLEX)
63: PetscCall(PetscFree(ctx->vi));
64: #endif
65: }
66: ctx->n = n;
67: PetscCall(PetscMalloc1(n,&ctx->vr));
68: #if !defined(PETSC_USE_COMPLEX)
69: PetscCall(PetscMalloc1(n,&ctx->vi));
70: #endif
71: for (i=0;i<n;i++) {
72: ctx->vr[i] = vr[i];
73: #if !defined(PETSC_USE_COMPLEX)
74: ctx->vi[i] = vi[i];
75: #endif
76: }
77: PetscFunctionReturn(PETSC_SUCCESS);
78: }
80: /*@
81: RGPolygonSetVertices - Sets the vertices that define the polygon region.
83: Logically Collective
85: Input Parameters:
86: + rg - the region context
87: . n - number of vertices
88: . vr - array of vertices
89: - vi - array of vertices (imaginary part)
91: Options Database Keys:
92: + -rg_polygon_vertices - Sets the vertices
93: - -rg_polygon_verticesi - Sets the vertices (imaginary part)
95: Notes:
96: In the case of complex scalars, only argument vr is used, containing
97: the complex vertices; the list of vertices can be provided in the
98: command line with a comma-separated list of complex values
99: [+/-][realnumber][+/-]realnumberi with no spaces.
101: When PETSc is built with real scalars, the real and imaginary parts of
102: the vertices must be provided in two separate arrays (or two lists in
103: the command line). In this case, the region must be symmetric with
104: respect to the real axis.
106: Level: advanced
108: .seealso: RGPolygonGetVertices()
109: @*/
110: PetscErrorCode RGPolygonSetVertices(RG rg,PetscInt n,PetscScalar vr[],PetscScalar vi[])
111: {
112: PetscFunctionBegin;
115: PetscAssertPointer(vr,3);
116: #if !defined(PETSC_USE_COMPLEX)
117: PetscAssertPointer(vi,4);
118: #endif
119: PetscTryMethod(rg,"RGPolygonSetVertices_C",(RG,PetscInt,PetscScalar*,PetscScalar*),(rg,n,vr,vi));
120: PetscFunctionReturn(PETSC_SUCCESS);
121: }
123: static PetscErrorCode RGPolygonGetVertices_Polygon(RG rg,PetscInt *n,PetscScalar **vr,PetscScalar **vi)
124: {
125: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
126: PetscInt i;
128: PetscFunctionBegin;
129: if (n) *n = ctx->n;
130: if (vr) {
131: if (!ctx->n) *vr = NULL;
132: else {
133: PetscCall(PetscMalloc1(ctx->n,vr));
134: for (i=0;i<ctx->n;i++) (*vr)[i] = ctx->vr[i];
135: }
136: }
137: #if !defined(PETSC_USE_COMPLEX)
138: if (vi) {
139: if (!ctx->n) *vi = NULL;
140: else {
141: PetscCall(PetscMalloc1(ctx->n,vi));
142: for (i=0;i<ctx->n;i++) (*vi)[i] = ctx->vi[i];
143: }
144: }
145: #endif
146: PetscFunctionReturn(PETSC_SUCCESS);
147: }
149: /*@C
150: RGPolygonGetVertices - Gets the vertices that define the polygon region.
152: Not Collective
154: Input Parameter:
155: . rg - the region context
157: Output Parameters:
158: + n - number of vertices
159: . vr - array of vertices
160: - vi - array of vertices (imaginary part)
162: Notes:
163: The values passed by user with RGPolygonSetVertices() are returned (or null
164: pointers otherwise).
165: The returned arrays should be freed by the user when no longer needed.
167: Level: advanced
169: .seealso: RGPolygonSetVertices()
170: @*/
171: PetscErrorCode RGPolygonGetVertices(RG rg,PetscInt *n,PetscScalar **vr,PetscScalar **vi)
172: {
173: PetscFunctionBegin;
175: PetscUseMethod(rg,"RGPolygonGetVertices_C",(RG,PetscInt*,PetscScalar**,PetscScalar**),(rg,n,vr,vi));
176: PetscFunctionReturn(PETSC_SUCCESS);
177: }
179: static PetscErrorCode RGView_Polygon(RG rg,PetscViewer viewer)
180: {
181: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
182: PetscBool isdraw,isascii;
183: int winw,winh;
184: PetscDraw draw;
185: PetscDrawAxis axis;
186: PetscReal a,b,c,d,ab,cd,lx,ly,w,x0,y0,x1,y1,scale=1.2;
187: PetscInt i;
188: char str[50];
190: PetscFunctionBegin;
191: PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERDRAW,&isdraw));
192: PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
193: if (isascii) {
194: PetscCall(PetscViewerASCIIPrintf(viewer," vertices: "));
195: PetscCall(PetscViewerASCIIUseTabs(viewer,PETSC_FALSE));
196: for (i=0;i<ctx->n;i++) {
197: #if defined(PETSC_USE_COMPLEX)
198: PetscCall(SlepcSNPrintfScalar(str,sizeof(str),ctx->vr[i],PETSC_FALSE));
199: #else
200: if (ctx->vi[i]!=0.0) PetscCall(PetscSNPrintf(str,sizeof(str),"%g%+gi",(double)ctx->vr[i],(double)ctx->vi[i]));
201: else PetscCall(PetscSNPrintf(str,sizeof(str),"%g",(double)ctx->vr[i]));
202: #endif
203: PetscCall(PetscViewerASCIIPrintf(viewer,"%s%s",str,(i<ctx->n-1)?", ":""));
204: }
205: PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
206: PetscCall(PetscViewerASCIIUseTabs(viewer,PETSC_TRUE));
207: } else if (isdraw) {
208: PetscCall(PetscViewerDrawGetDraw(viewer,0,&draw));
209: PetscCall(PetscDrawCheckResizedWindow(draw));
210: PetscCall(PetscDrawGetWindowSize(draw,&winw,&winh));
211: winw = PetscMax(winw,1); winh = PetscMax(winh,1);
212: PetscCall(PetscDrawClear(draw));
213: PetscCall(PetscDrawSetTitle(draw,"Polygonal region"));
214: PetscCall(PetscDrawAxisCreate(draw,&axis));
215: PetscCall(RGComputeBoundingBox_Polygon(rg,&a,&b,&c,&d));
216: a *= rg->sfactor;
217: b *= rg->sfactor;
218: c *= rg->sfactor;
219: d *= rg->sfactor;
220: lx = b-a;
221: ly = d-c;
222: ab = (a+b)/2;
223: cd = (c+d)/2;
224: w = scale*PetscMax(lx/winw,ly/winh)/2;
225: PetscCall(PetscDrawAxisSetLimits(axis,ab-w*winw,ab+w*winw,cd-w*winh,cd+w*winh));
226: PetscCall(PetscDrawAxisDraw(axis));
227: PetscCall(PetscDrawAxisDestroy(&axis));
228: for (i=0;i<ctx->n;i++) {
229: #if defined(PETSC_USE_COMPLEX)
230: x0 = PetscRealPart(ctx->vr[i]); y0 = PetscImaginaryPart(ctx->vr[i]);
231: if (i<ctx->n-1) {
232: x1 = PetscRealPart(ctx->vr[i+1]); y1 = PetscImaginaryPart(ctx->vr[i+1]);
233: } else {
234: x1 = PetscRealPart(ctx->vr[0]); y1 = PetscImaginaryPart(ctx->vr[0]);
235: }
236: #else
237: x0 = ctx->vr[i]; y0 = ctx->vi[i];
238: if (i<ctx->n-1) {
239: x1 = ctx->vr[i+1]; y1 = ctx->vi[i+1];
240: } else {
241: x1 = ctx->vr[0]; y1 = ctx->vi[0];
242: }
243: #endif
244: PetscCall(PetscDrawLine(draw,x0*rg->sfactor,y0*rg->sfactor,x1*rg->sfactor,y1*rg->sfactor,PETSC_DRAW_MAGENTA));
245: }
246: PetscCall(PetscDrawFlush(draw));
247: PetscCall(PetscDrawSave(draw));
248: PetscCall(PetscDrawPause(draw));
249: }
250: PetscFunctionReturn(PETSC_SUCCESS);
251: }
253: static PetscErrorCode RGIsTrivial_Polygon(RG rg,PetscBool *trivial)
254: {
255: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
257: PetscFunctionBegin;
258: *trivial = PetscNot(ctx->n);
259: PetscFunctionReturn(PETSC_SUCCESS);
260: }
262: static PetscErrorCode RGComputeContour_Polygon(RG rg,PetscInt n,PetscScalar *ucr,PetscScalar *uci)
263: {
264: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
265: PetscReal length,h,d,rem=0.0;
266: PetscInt k=1,idx=ctx->n-1,i;
267: PetscBool ini=PETSC_FALSE;
268: PetscScalar incr,*cr=ucr,*ci=uci;
269: #if !defined(PETSC_USE_COMPLEX)
270: PetscScalar inci;
271: #endif
273: PetscFunctionBegin;
274: PetscCheck(ctx->n,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_WRONGSTATE,"No vertices have been set yet");
275: length = SlepcAbsEigenvalue(ctx->vr[0]-ctx->vr[ctx->n-1],ctx->vi[0]-ctx->vi[ctx->n-1]);
276: for (i=0;i<ctx->n-1;i++) length += SlepcAbsEigenvalue(ctx->vr[i]-ctx->vr[i+1],ctx->vi[i]-ctx->vi[i+1]);
277: h = length/n;
278: if (!ucr) PetscCall(PetscMalloc1(n,&cr));
279: if (!uci) PetscCall(PetscMalloc1(n,&ci));
280: cr[0] = ctx->vr[0];
281: #if !defined(PETSC_USE_COMPLEX)
282: ci[0] = ctx->vi[0];
283: #endif
284: incr = ctx->vr[ctx->n-1]-ctx->vr[0];
285: #if !defined(PETSC_USE_COMPLEX)
286: inci = ctx->vi[ctx->n-1]-ctx->vi[0];
287: #endif
288: d = SlepcAbsEigenvalue(incr,inci);
289: incr /= d;
290: #if !defined(PETSC_USE_COMPLEX)
291: inci /= d;
292: #endif
293: while (k<n) {
294: if (ini) {
295: incr = ctx->vr[idx]-ctx->vr[idx+1];
296: #if !defined(PETSC_USE_COMPLEX)
297: inci = ctx->vi[idx]-ctx->vi[idx+1];
298: #endif
299: d = SlepcAbsEigenvalue(incr,inci);
300: incr /= d;
301: #if !defined(PETSC_USE_COMPLEX)
302: inci /= d;
303: #endif
304: if (rem+d>h) {
305: cr[k] = ctx->vr[idx+1]+incr*(h-rem);
306: #if !defined(PETSC_USE_COMPLEX)
307: ci[k] = ctx->vi[idx+1]+inci*(h-rem);
308: #endif
309: k++;
310: ini = PETSC_FALSE;
311: } else {rem += d; idx--;}
312: } else {
313: #if !defined(PETSC_USE_COMPLEX)
314: rem = SlepcAbsEigenvalue(ctx->vr[idx]-cr[k-1],ctx->vi[idx]-ci[k-1]);
315: #else
316: rem = PetscAbsScalar(ctx->vr[idx]-cr[k-1]);
317: #endif
318: if (rem>h) {
319: cr[k] = cr[k-1]+incr*h;
320: #if !defined(PETSC_USE_COMPLEX)
321: ci[k] = ci[k-1]+inci*h;
322: #endif
323: k++;
324: } else {ini = PETSC_TRUE; idx--;}
325: }
326: }
327: if (!ucr) PetscCall(PetscFree(cr));
328: if (!uci) PetscCall(PetscFree(ci));
329: PetscFunctionReturn(PETSC_SUCCESS);
330: }
332: static PetscErrorCode RGComputeBoundingBox_Polygon(RG rg,PetscReal *a,PetscReal *b,PetscReal *c,PetscReal *d)
333: {
334: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
335: PetscInt i;
337: PetscFunctionBegin;
338: if (a) *a = PETSC_MAX_REAL;
339: if (b) *b = -PETSC_MAX_REAL;
340: if (c) *c = PETSC_MAX_REAL;
341: if (d) *d = -PETSC_MAX_REAL;
342: for (i=0;i<ctx->n;i++) {
343: #if defined(PETSC_USE_COMPLEX)
344: if (a) *a = PetscMin(*a,PetscRealPart(ctx->vr[i]));
345: if (b) *b = PetscMax(*b,PetscRealPart(ctx->vr[i]));
346: if (c) *c = PetscMin(*c,PetscImaginaryPart(ctx->vr[i]));
347: if (d) *d = PetscMax(*d,PetscImaginaryPart(ctx->vr[i]));
348: #else
349: if (a) *a = PetscMin(*a,ctx->vr[i]);
350: if (b) *b = PetscMax(*b,ctx->vr[i]);
351: if (c) *c = PetscMin(*c,ctx->vi[i]);
352: if (d) *d = PetscMax(*d,ctx->vi[i]);
353: #endif
354: }
355: PetscFunctionReturn(PETSC_SUCCESS);
356: }
358: static PetscErrorCode RGCheckInside_Polygon(RG rg,PetscReal px,PetscReal py,PetscInt *inout)
359: {
360: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
361: PetscReal val,x[VERTMAX],y[VERTMAX];
362: PetscBool mx,my,nx,ny;
363: PetscInt i,j;
365: PetscFunctionBegin;
366: for (i=0;i<ctx->n;i++) {
367: #if defined(PETSC_USE_COMPLEX)
368: x[i] = PetscRealPart(ctx->vr[i])-px;
369: y[i] = PetscImaginaryPart(ctx->vr[i])-py;
370: #else
371: x[i] = ctx->vr[i]-px;
372: y[i] = ctx->vi[i]-py;
373: #endif
374: }
375: *inout = -1;
376: for (i=0;i<ctx->n;i++) {
377: j = (i+1)%ctx->n;
378: mx = PetscNot(x[i]<0.0);
379: nx = PetscNot(x[j]<0.0);
380: my = PetscNot(y[i]<0.0);
381: ny = PetscNot(y[j]<0.0);
382: if (!((my||ny) && (mx||nx)) || (mx&&nx)) continue;
383: if (((my && ny && (mx||nx)) && (!(mx&&nx)))) {
384: *inout = -*inout;
385: continue;
386: }
387: val = (y[i]*x[j]-x[i]*y[j])/(x[j]-x[i]);
388: if (PetscAbs(val)<10*PETSC_MACHINE_EPSILON) {
389: *inout = 0;
390: PetscFunctionReturn(PETSC_SUCCESS);
391: } else if (val>0.0) *inout = -*inout;
392: }
393: PetscFunctionReturn(PETSC_SUCCESS);
394: }
396: static PetscErrorCode RGSetFromOptions_Polygon(RG rg,PetscOptionItems *PetscOptionsObject)
397: {
398: PetscScalar array[VERTMAX];
399: PetscInt i,k;
400: PetscBool flg,flgi=PETSC_FALSE;
401: #if !defined(PETSC_USE_COMPLEX)
402: PetscScalar arrayi[VERTMAX];
403: PetscInt ki;
404: #else
405: PetscScalar *arrayi=NULL;
406: #endif
408: PetscFunctionBegin;
409: PetscOptionsHeadBegin(PetscOptionsObject,"RG Polygon Options");
411: k = VERTMAX;
412: for (i=0;i<k;i++) array[i] = 0;
413: PetscCall(PetscOptionsScalarArray("-rg_polygon_vertices","Vertices of polygon","RGPolygonSetVertices",array,&k,&flg));
414: #if !defined(PETSC_USE_COMPLEX)
415: ki = VERTMAX;
416: for (i=0;i<ki;i++) arrayi[i] = 0;
417: PetscCall(PetscOptionsScalarArray("-rg_polygon_verticesi","Vertices of polygon (imaginary part)","RGPolygonSetVertices",arrayi,&ki,&flgi));
418: PetscCheck(ki==k,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_SIZ,"The number of real %" PetscInt_FMT " and imaginary %" PetscInt_FMT " parts do not match",k,ki);
419: #endif
420: if (flg || flgi) PetscCall(RGPolygonSetVertices(rg,k,array,arrayi));
422: PetscOptionsHeadEnd();
423: PetscFunctionReturn(PETSC_SUCCESS);
424: }
426: static PetscErrorCode RGDestroy_Polygon(RG rg)
427: {
428: RG_POLYGON *ctx = (RG_POLYGON*)rg->data;
430: PetscFunctionBegin;
431: if (ctx->n) {
432: PetscCall(PetscFree(ctx->vr));
433: #if !defined(PETSC_USE_COMPLEX)
434: PetscCall(PetscFree(ctx->vi));
435: #endif
436: }
437: PetscCall(PetscFree(rg->data));
438: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGPolygonSetVertices_C",NULL));
439: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGPolygonGetVertices_C",NULL));
440: PetscFunctionReturn(PETSC_SUCCESS);
441: }
443: SLEPC_EXTERN PetscErrorCode RGCreate_Polygon(RG rg)
444: {
445: RG_POLYGON *polygon;
447: PetscFunctionBegin;
448: PetscCall(PetscNew(&polygon));
449: rg->data = (void*)polygon;
451: rg->ops->istrivial = RGIsTrivial_Polygon;
452: rg->ops->computecontour = RGComputeContour_Polygon;
453: rg->ops->computebbox = RGComputeBoundingBox_Polygon;
454: rg->ops->checkinside = RGCheckInside_Polygon;
455: rg->ops->setfromoptions = RGSetFromOptions_Polygon;
456: rg->ops->view = RGView_Polygon;
457: rg->ops->destroy = RGDestroy_Polygon;
458: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGPolygonSetVertices_C",RGPolygonSetVertices_Polygon));
459: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGPolygonGetVertices_C",RGPolygonGetVertices_Polygon));
460: PetscFunctionReturn(PETSC_SUCCESS);
461: }