@@ -26,9 +26,11 @@ def _buf_split(b):
2626
2727
2828class ArrayIndexer (np .ndarray ):
29- """ a class that wraps the data region of a single array (d)
30- and allows us to easily do array operations like d[i+1,j]
31- using the ip() method. """
29+ """a class that wraps the data region of a single cell-centered data
30+ array (d) and allows us to easily do array operations like
31+ d[i+1,j] using the ip() method.
32+
33+ """
3234
3335 def __new__ (self , d , grid = None ):
3436 obj = np .asarray (d ).view (self )
@@ -331,3 +333,244 @@ def pretty_print(self, n=0, fmt=None, show_ghost=True):
331333 +---> x
332334 """
333335 print (leg )
336+
337+
338+ class ArrayIndexerFC (ArrayIndexer ):
339+ """a class that wraps the data region of a single face-centered data
340+ array (d) and allows us to easily do array operations like
341+ d[i+1,j] using the ip() method.
342+
343+ """
344+
345+ def __new__ (self , d , idir , grid = None ):
346+ obj = np .asarray (d ).view (self )
347+ obj .g = grid
348+ obj .idir = idir
349+ obj .c = len (d .shape )
350+
351+ return obj
352+
353+ def __array_finalize__ (self , obj ):
354+ if obj is None :
355+ return
356+ self .g = getattr (obj , "g" , None )
357+ self .idir = getattr (obj , "idir" , None )
358+ self .c = getattr (obj , "c" , None )
359+
360+ def __array_wrap__ (self , out_arr , context = None ):
361+ return np .ndarray .__array_wrap__ (self , out_arr , context )
362+
363+ def ip_jp (self , ishift , jshift , buf = 0 , n = 0 , s = 1 ):
364+ """return a view of the data shifted by ishift in the x direction and
365+ jshift in the y direction. By default the view is the same
366+ size as the valid region, but the buf can specify how many
367+ ghost cells on each side to include. The component is n and s
368+ is the stride
369+
370+ """
371+ bxlo , bxhi , bylo , byhi = _buf_split (buf )
372+ c = len (self .shape )
373+
374+ if self .idir == 1 :
375+ # face-centered in x
376+ if c == 2 :
377+ return np .asarray (self [self .g .ilo - bxlo + ishift :self .g .ihi + 2 + bxhi + ishift :s ,
378+ self .g .jlo - bylo + jshift :self .g .jhi + 1 + byhi + jshift :s ])
379+ else :
380+ return np .asarray (self [self .g .ilo - bxlo + ishift :self .g .ihi + 2 + bxhi + ishift :s ,
381+ self .g .jlo - bylo + jshift :self .g .jhi + 1 + byhi + jshift :s , n ])
382+ elif self .idir == 2 :
383+ # face-centered in y
384+ if c == 2 :
385+ return np .asarray (self [self .g .ilo - bxlo + ishift :self .g .ihi + 1 + bxhi + ishift :s ,
386+ self .g .jlo - bylo + jshift :self .g .jhi + 2 + byhi + jshift :s ])
387+ else :
388+ return np .asarray (self [self .g .ilo - bxlo + ishift :self .g .ihi + 1 + bxhi + ishift :s ,
389+ self .g .jlo - bylo + jshift :self .g .jhi + 2 + byhi + jshift :s , n ])
390+
391+ def lap (self , n = 0 , buf = 0 ):
392+ raise NotImplementedError ("lap not implemented for ArrayIndexerFC" )
393+
394+ def norm (self , n = 0 ):
395+ """
396+ find the norm of the quantity (index n) defined on the same grid,
397+ in the domain's valid region
398+
399+ """
400+ c = len (self .shape )
401+ if self .idir == 1 :
402+ if c == 2 :
403+ return np .sqrt (self .g .dx * self .g .dy *
404+ np .sum ((self [self .g .ilo :self .g .ihi + 2 , self .g .jlo :self .g .jhi + 1 ]** 2 ).flat ))
405+
406+ else :
407+ _tmp = self [:, :, n ]
408+ return np .sqrt (self .g .dx * self .g .dy *
409+ np .sum ((_tmp [self .g .ilo :self .g .ihi + 2 , self .g .jlo :self .g .jhi + 1 ]** 2 ).flat ))
410+ elif self .idir == 2 :
411+ if c == 2 :
412+ return np .sqrt (self .g .dx * self .g .dy *
413+ np .sum ((self [self .g .ilo :self .g .ihi + 1 , self .g .jlo :self .g .jhi + 2 ]** 2 ).flat ))
414+
415+ else :
416+ _tmp = self [:, :, n ]
417+ return np .sqrt (self .g .dx * self .g .dy *
418+ np .sum ((_tmp [self .g .ilo :self .g .ihi + 1 , self .g .jlo :self .g .jhi + 2 ]** 2 ).flat ))
419+
420+ def copy (self ):
421+ """make a copy of the array, defined on the same grid"""
422+ return ArrayIndexer (np .asarray (self ).copy (), self .idir , grid = self .g )
423+
424+ def is_symmetric (self , nodal = False , tol = 1.e-14 , asymmetric = False ):
425+ """return True is the data is left-right symmetric (to the tolerance
426+ tol) For node-centered data, set nodal=True
427+
428+ """
429+ raise NotImplementedError ()
430+
431+ def is_asymmetric (self , nodal = False , tol = 1.e-14 ):
432+ """return True is the data is left-right asymmetric (to the tolerance
433+ tol)---e.g, the sign flips. For node-centered data, set nodal=True
434+
435+ """
436+ raise NotImplementedError ()
437+
438+ def fill_ghost (self , n = 0 , bc = None ):
439+ """Fill the boundary conditions. This operates on a single component,
440+ n. We do periodic, reflect-even, reflect-odd, and outflow
441+
442+ We need a BC object to tell us what BC type on each boundary.
443+ """
444+
445+ # there is only a single grid, so every boundary is on
446+ # a physical boundary (except if we are periodic)
447+
448+ # Note: we piggy-back on outflow and reflect-odd for
449+ # Neumann and Dirichlet homogeneous BCs respectively, but
450+ # this only works for a single ghost cell
451+
452+ # -x boundary
453+ if bc .xlb in ["outflow" , "neumann" , "reflect-even" , "reflect-odd" , "dirichlet" ]:
454+ raise NotImplementedError ("boundary condition not implemented for -x" )
455+ elif bc .xlb == "periodic" :
456+ if self .idir == 1 :
457+ # face-centered in x
458+ for i in range (self .g .ilo ):
459+ self [i , :, n ] = self [self .g .ihi - self .g .ng + i + 1 , :, n ]
460+ elif self .idir == 2 :
461+ # face-centered in y
462+ for i in range (self .g .ilo ):
463+ self [i , :, n ] = self [self .g .ihi - self .g .ng + i + 1 , :, n ]
464+
465+ # +x boundary
466+ if bc .xrb in ["outflow" , "neumann" , "reflect-even" , "reflect-odd" , "dirichlet" ]:
467+ raise NotImplementedError ("boundary condition not implemented for +x" )
468+ elif bc .xrb == "periodic" :
469+ if self .idir == 1 :
470+ # face-centered in x
471+ for i in range (self .g .ihi + 2 , 2 * self .g .ng + self .g .nx + 1 ):
472+ self [i , :, n ] = self [i - self .g .ihi - 1 + self .g .ng , :, n ]
473+ elif self .idir == 2 :
474+ # face-centered in y
475+ for i in range (self .g .ihi + 1 , 2 * self .g .ng + self .g .nx ):
476+ self [i , :, n ] = self [i - self .g .ihi - 1 + self .g .ng , :, n ]
477+
478+ # -y boundary
479+ if bc .ylb in ["outflow" , "neumann" , "reflect-even" , "reflect-odd" , "dirichlet" ]:
480+ raise NotImplementedError ("boundary condition not implemented for -y" )
481+ elif bc .ylb == "periodic" :
482+ if self .idir == 1 :
483+ # face-centered in x
484+ for j in range (self .g .jlo ):
485+ self [:, j , n ] = self [:, self .g .jhi - self .g .ng + j + 1 , n ]
486+ elif self .idir == 2 :
487+ # face-centered in y
488+ for j in range (self .g .jlo ):
489+ self [:, j , n ] = self [:, self .g .jhi - self .g .ng + j + 1 , n ]
490+
491+ # +y boundary
492+ if bc .yrb in ["outflow" , "neumann" , "reflect-even" , "reflect-odd" , "dirichlet" ]:
493+ raise NotImplementedError ("boundary condition not implemented for +y" )
494+ elif bc .yrb == "periodic" :
495+ if self .idir == 1 :
496+ # face-centered in x
497+ for j in range (self .g .jhi + 1 , 2 * self .g .ng + self .g .ny ):
498+ self [:, j , n ] = self [:, j - self .g .jhi - 1 + self .g .ng , n ]
499+ elif self .idir == 2 :
500+ for j in range (self .g .jhi + 2 , 2 * self .g .ng + self .g .ny + 1 ):
501+ self [:, j , n ] = self [:, j - self .g .jhi - 1 + self .g .ng , n ]
502+
503+ def pretty_print (self , n = 0 , fmt = None , show_ghost = True ):
504+ """
505+ Print out a small dataset to the screen with the ghost cells
506+ a different color, to make things stand out
507+ """
508+
509+ if fmt is None :
510+ if self .dtype == np .int :
511+ fmt = "%4d"
512+ elif self .dtype == np .float64 :
513+ fmt = "%10.5g"
514+ else :
515+ raise ValueError ("ERROR: dtype not supported" )
516+
517+ # print j descending, so it looks like a grid (y increasing
518+ # with height)
519+ if show_ghost :
520+ if self .idir == 1 :
521+ ilo = 0
522+ ihi = self .g .qx
523+ jlo = 0
524+ jhi = self .g .qy - 1
525+ elif self .idir == 2 :
526+ ilo = 0
527+ ihi = self .g .qx - 1
528+ jlo = 0
529+ jhi = self .g .qy
530+
531+ else :
532+ if self .idir == 1 :
533+ ilo = self .g .ilo
534+ ihi = self .g .ihi + 1
535+ jlo = self .g .jlo
536+ jhi = self .g .jhi
537+ elif self .idir == 2 :
538+ ilo = self .g .ilo
539+ ihi = self .g .ihi
540+ jlo = self .g .jlo
541+ jhi = self .g .jhi + 1
542+
543+ for j in reversed (range (jlo , jhi + 1 )):
544+ for i in range (ilo , ihi + 1 ):
545+
546+ if self .idir == 1 :
547+ if (j < self .g .jlo or j > self .g .jhi or
548+ i < self .g .ilo or i > self .g .ihi + 1 ):
549+ gc = 1
550+ else :
551+ gc = 0
552+ elif self .idir == 2 :
553+ if (j < self .g .jlo or j > self .g .jhi + 1 or
554+ i < self .g .ilo or i > self .g .ihi ):
555+ gc = 1
556+ else :
557+ gc = 0
558+
559+ if self .c == 2 :
560+ val = self [i , j ]
561+ else :
562+ val = self [i , j , n ]
563+
564+ if gc :
565+ print ("\033 [31m" + fmt % (val ) + "\033 [0m" , end = "" )
566+ else :
567+ print (fmt % (val ), end = "" )
568+
569+ print (" " )
570+
571+ leg = """
572+ ^ y
573+ |
574+ +---> x
575+ """
576+ print (leg )
0 commit comments