
    
    
! +++++++++++  begin srend snippet +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! This goes in FLASH4.3/source/Driver/DriverMain/Split/Driver_evolveFlash.F90
! This goes between the 2 lines: the last "call Driver_driftUnk(__FILE__,__LINE__,driftUnk_flags)"
! and "enddo" in the main evolution loop.
! >>>> uncomment of of these two calls: UG or paramesh
#ifdef SRENDERING
!    call srend_wrapper_paramesh()
    call srend_wrapper()
#endif
! +++++++++++ end srend snippet ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


    

  
  
  
  
  
! ++++++++++ begin srend snippet +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! This goes in FLASH4.3/source/Driver/DriverMain/Split/Driver_evolveFlash.F90
! This goes between the last 2 lines: after "return" and before "end subroutine Driver_evolveFlash"
#ifdef SRENDERING

  contains
  
! uncomment this to pass the color table keys; recompile srend with this definition set too
#define SREND_COTAB_PASSNAMELIST

! --- paramesh call: MUST have at least 1 LEAF block for every MPI rank!
subroutine srend_wrapper_paramesh()
      USE srend
      USE physicalData
#ifdef _OPENMP
      USE OMP_LIB
#endif
      USE Grid_interface, ONLY : Grid_fillGuardCells,   &
                                 Grid_getMaxRefinement, &
                                 Grid_getBlkRefineLevel,&
                                 Grid_getBlkCornerID ! add for srend_wrapper
      IMPLICIT none


      character*1,allocatable,dimension(:,:,:) :: bytes  ! pass to srend_render
      real, allocatable,save,dimension(:,:,:,:) :: dat ! hold pres_var or dens_var
      integer :: ix, iy, iz ! loop indices
      real :: x,y           ! scaling variables
      
      real :: smax, smin ! scanning data
      real :: dxdy,dxdz,dydx,dydz,dzdx,dzdy,vort2,vort(1:3),uxyz(1:3),dxyz(1:3) ! vorticity calc vars

      integer :: srend_cornerID(1:3), srend_stride(1:3),srend_cornerIDhigh(1:3) ! for Grid call
      integer :: inxyz(1:3) ! calc geometry of block for offsets
      integer :: iblk, srend_iblock !indeces for blocks 
      integer :: srend_lrefine, srend_lrefine_max ! AMR geometry for offsets

#ifdef SREND_COTAB_PASSNAMELIST
      integer*4 :: nalpha, nrgb
      character*256 :: alpha_knot(1:22), rgb_knot(1:22)
! end var declaractions ----------------------------------------------------------------

! set color table
      nalpha = 22
      alpha_knot(1) = "0   0.0" !1: ramped up R0-->255B
      alpha_knot(2) = "128 0.001"
      alpha_knot(3) = "255 1.0"
        alpha_knot(4)  = "0   1.0"!8: zero center R0<-128->255B to ends
        alpha_knot(5)  = "128 0.0"
        alpha_knot(6)  = "255 1.0"
      alpha_knot(8)  = "0 0.0" !8: rainbow
      alpha_knot(9)  = "64 0.3"
      alpha_knot(10) = "128 0.5"
      alpha_knot(11) = "192 0.5"
      alpha_knot(12) = "232 0.5"
      alpha_knot(13) = "255 0.5"
        alpha_knot(14) = "0  0.0" !14: ramp up, gentler
        alpha_knot(15) = "128  0.1"
        alpha_knot(16) = "255  0.5"
      alpha_knot(17) = "0   0.0"
      alpha_knot(18) = "128 0.001"
      alpha_knot(19) = "200 0.01"
      alpha_knot(20) = "225 0.05"
      alpha_knot(21) = "240 0.1"
      alpha_knot(22) = "255 1.0"
      
      nrgb = 22
      rgb_knot(1) = "0   1.0 0.0 0.0"
      rgb_knot(2) = "255 0.5 0.5 1.0"
        rgb_knot(4)  = "0   1.0 0.0 0.0"
        rgb_knot(5)  = "128 0.0 0.0 0.0"
        rgb_knot(6)  = "255 0.0 0.0 1.0"
      rgb_knot(8)  = "0   1.0 0.0 0.0"
      rgb_knot(9)  = "64  1.0 1.0 0.0"
      rgb_knot(10) = "128 0.0 1.0 0.0"
      rgb_knot(11) = "192 0.0 1.0 1.0"
      rgb_knot(12) = "232 1.0 0.0 1.0"
      rgb_knot(13) = "255 0.5 0.5 1.0"
        rgb_knot(14) = "0   1.0 0.0 0.0"
        rgb_knot(15) = "128 1.0 1.0 0.0"
        rgb_knot(16) = "255 0.0 0.0 1.0"
      rgb_knot(17) = "0   1.0 0.0 0.0" !red
      rgb_knot(18) = "128 0.0 1.0 1.0"!aqua
      rgb_knot(19) = "200 1.0 1.0 0.0"!yellow
      rgb_knot(20) = "225 0.0 1.0 0.0"!green
      rgb_knot(21) = "225 0.0 0.0 1.0"!blue
      rgb_knot(22) = "255 1.0 1.0 1.0"!white
#endif
  
! position for Srend call ----
if( mod(dr_nstep,15) .eq. 1) then

! flash UNK cell center vars available, from flash.h
!#define DENS_VAR 1
!#define EINT_VAR 2
!#define ENER_VAR 3
!#define PRES_VAR 4
!#define TEMP_VAR 5
!#define VELX_VAR 6
!#define VELY_VAR 7
!#define VELZ_VAR 8

      if(.NOT. allocated(dat)) allocate( dat(1:NUNK_VARS,1-NGUARD:NXB+NGUARD,1-NGUARD:NYB+NGUARD,1-NGUARD:NZB+NGUARD) )
      if(.NOT. allocated(bytes)) allocate( bytes(0:NXB+1,0:NYB+1,0:NXB+1) )

!      print *,'NUNK_VARS=',NUNK_VARS
      
do iblk = 1, blockCount
      srend_iblock = blocklist(iblk)            ! block ID
      call Grid_fillGuardCells(CENTER,ALLDIR)   ! obtain boundaries,default NGUARD=4
      dat(:,:,:,:) =  unk(:,:,:,:,srend_iblock) ! this specific copy is the ONLY kind of access to unk() that worked ?!?!?
      

#ifdef NOTHING
      smin = 1.0e30; smax = 0.0 ! vorticity, use cotab 17
      do iz = 0,NZB+1
      do iy = 0,NYB+1
      do ix = 0,NXB+1
        uxyz(1) = dat(VELX_VAR,ix,iy,iz)
        uxyz(2) = dat(VELY_VAR,ix,iy,iz)
        uxyz(3) = dat(VELZ_VAR,ix,iy,iz)
        dxyz(1) = .5*( dat(VELX_VAR,ix+1,iy,iz) - dat(VELX_VAR,ix-1,iy,iz) )
        dxyz(2) = .5*( dat(VELY_VAR,ix,iy+1,iz) - dat(VELY_VAR,ix,iy-1,iz) )
        dxyz(3) = .5*( dat(VELZ_VAR,ix,iy,iz+1) - dat(VELZ_VAR,ix,iy,iz-1) )
        vort(1) = dxyz(2)*uxyz(3) - dxyz(3)*uxyz(2)
        vort(2) = dxyz(3)*uxyz(1) - dxyz(1)*uxyz(3)
        vort(3) = dxyz(1)*uxyz(2) - dxyz(2)*uxyz(1)
        x = 50.0 * log( 1.+  sqrt(vort(1)**2 + vort(2)**2 + vort(3)**2) )
          if(x<smin) smin = x
          if(x>smax) smax = x
        y = 0.0 + 254.0 * x/sqrt(x + 1.0)
        bytes(ix,iy,iz) = achar( floor( y ) )
      end do
      end do
      end do
#endif
      
!#ifdef NOTHING
!      smax = 0.0; smin = 1.0e30 ! velocity magnitude
      do iz = 0,NZB+1
      do iy = 0,NYB+1
      do ix = 0,NXB+1
        x = 10.0 * sqrt(dat(VELX_VAR,ix,iy,iz)**2 + dat(VELY_VAR,ix,iy,iz)**2 + dat(VELZ_VAR,ix,iy,iz)**2)
!          if(x<smin) smin = x
!          if(x>smax)smax = x
        y = 0.0 + 255.0 * x/sqrt(x + 1.0)
        bytes(ix,iy,iz) = achar( floor( y ) )
      end do
      end do
      end do 
!#endif

#ifdef NOTHING
      smax = dat(VELX_VAR,0,0,0); smin = smax ! wind vector direction
      do iz = 0,NZB+1
      do iy = 0,NYB+1
      do ix = 0,NXB+1
        x = dat(VELX_VAR,ix,iy,iz)
          if(x<smin) smin = x
          if(x>smax)smax = x
        y = 128.0 + 127.0 * x/sqrt(x*x + 1.0)
        bytes(ix,iy,iz) = achar( floor( y ) )
      end do
      end do
      end do 
#endif

!      print *,'<<rank=',dr_globalMe,'smax=',smax,'smin=',smin,'>>' ! data scanning

! find AMR level and calc geometry for position offsets
      call Grid_getMaxRefinement(srend_lrefine_max)
      call Grid_getBlkRefineLevel(srend_iblock, srend_lrefine)
      call Grid_getBlkCornerID(srend_iblock,srend_cornerID,srend_stride,srend_cornerIDhigh) ! UG always (1,...)
      do ix=1,3 ! experiment
        inxyz(ix) = (srend_cornerID(ix) - 1)/2**(srend_lrefine_max - srend_lrefine)
      end do

      call srend_render_load(      &
      1,2,                         & ! nV, nV_out
      NXB*2**(srend_lrefine-1),    & ! Vdim (NXB=NYB=NZB=8)
      (/0.8,1.5,1.5/),             & ! Eye position
      (/-0.3,  -0.8, -1.0/),       & ! View vector
      (/  0.0,  1.0,  0.0/),       & ! Up vector
      60.0, 60.0,                  & ! Alpha-horz, Beta-vert
      0.0,                         & ! stereo: EyeRight
      -10.0,10.0,                  & ! x clip plans 
      -10.0,10.0,                  & ! y clip plans
      -10.0,10.0,                  & ! z clip plans
      180.0,                       & ! far polar clip
      (/.5,.5,.5/), 1.0,           & ! center(x,y,z) and radius of sphere to clip about
      0.1, 10.0, 1,                & ! near clip0, far clip1, nsh
      1,                           & ! 0 spherical, 1 perspective
      0,                           & ! 0=norm, 1=npole, 2=equator,3=spole
      0.25,                        & ! sampling in cell units,(-) out2in
      bytes,                       & ! data array
      NXB,NYB,NZB,                 & ! XN,YN,ZN of data
      1,                           & ! Bd
      inxyz(1),inxyz(2),inxyz(3),  & ! x,y,z offsets in this Vdim grid units spanning volume
      400, 400,                    & ! rendering Width, Height in pixels
      1,                           & ! nR, number of variable arrays and color table offsets
      (/14/),                      & ! cotab_offset(1:nR),
#ifdef SREND_COTAB_PASSNAMELIST
      nalpha,     &
      alpha_knot, &
      nrgb,       &
      rgb_knot,   &
#endif
      'vel/######.ppm,',           & ! output file name masks, requires Imagemagick convert
      2,2,                         & ! tiles right, tiles down: usually 1,1
      (/0,1,2,3/))                   ! target MPI rank   
end do ! over blocks
      
      call srend_render_flush(1)
      
      call srend_tile_finish(1,2)  

end if ! vis, every 10 or so dr_nstep loops

end subroutine srend_wrapper_paramesh
  
  
  
  
  
  
! -------- UG call ---------------------------------------------------
subroutine srend_wrapper()
      USE srend
      USE physicalData
#ifdef _OPENMP
      USE OMP_LIB
#endif
!      USE MPI
      USE Grid_interface, ONLY : Grid_fillGuardCells, &
                                 Grid_getBlkCornerID ! add for srend_wrapper
      IMPLICIT none
      
      character*1,allocatable,dimension(:,:,:) :: bytes
      real, allocatable,save,dimension(:,:,:,:) :: dat ! hold pres_var or dens_var
      integer :: ix, iy, iz
      real :: x,y    
      
      real :: smax, smin ! scaling data
      real :: dxdy,dxdz,dydx,dydz,dzdx,dzdy,vort2,vort(1:3),uxyz(1:3),dxyz(1:3) ! vorticity calc vars

      integer :: srend_cornerID(1:3), srend_stride(1:3),srend_cornerIDhigh(1:3)
      integer :: inxyz(1:3)
      
! ------------------------------------------------------------------------------------------------
#ifdef NOTHING
! test create grads .ctl files: these are enabled for a Grads dump
      character*80 :: ctl_line
      real*4 :: gdat(0:NXB+1,0:NYB+1,0:NXB+1) ! must be 32-bit real*4 for grads in ctl.py
      integer,save :: ndump = 0 
      integer :: iv
      
      integer    :: n_var = 16      ! n_var will be reset according to flash.h and user desires
      character*20 :: var_str(1:16)   ! 16 is just an upper bound here
      integer    :: var_index(1:16)
#endif
! ------------------------------------------------------------------------------------------------
      
#ifdef SREND_COTAB_PASSNAMELIST
      integer :: nalpha, nrgb
      character*256 :: alpha_knot(1:22), rgb_knot(1:22) ! some upper bound, 16 here

! end var declaractions ----------------------------------------------

! set color table: this requires SREND_COTAB_PASSNAMELIST to be defined
! always pass the array index for the "0" alpha and rgb entry desired
! There must be a "0" and a following "255" entry for alpha and rgb
! with, possibly, a continuous span of entries between.
      nalpha = 22
      alpha_knot(1) = "0   0.0" !1: ramped up R0-->255B
      alpha_knot(2) = "128 0.001"
      alpha_knot(3) = "255 1.0"
        alpha_knot(4)  = "0   1.0"!8: zero center R0<-128->255B to ends
        alpha_knot(5)  = "128 0.0"
        alpha_knot(6)  = "255 1.0"
      alpha_knot(8)  = "0 0.0" !8: rainbow
      alpha_knot(9)  = "64 0.3"
      alpha_knot(10) = "128 0.5"
      alpha_knot(11) = "192 0.5"
      alpha_knot(12) = "232 0.5"
      alpha_knot(13) = "255 0.5"
        alpha_knot(14) = "0  0.0" !14: ramp up, gentler
        alpha_knot(15) = "128  0.1"
        alpha_knot(16) = "255  0.5"
      alpha_knot(17) = "0   0.0"
      alpha_knot(18) = "128 0.001"
      alpha_knot(19) = "200 0.01"
      alpha_knot(20) = "225 0.05"
      alpha_knot(21) = "240 0.1"
      alpha_knot(22) = "255 1.0"
      
      nrgb = 22
      rgb_knot(1) = "0   1.0 0.0 0.0"
      rgb_knot(2) = "255 0.5 0.5 1.0"
        rgb_knot(4)  = "0   1.0 0.0 0.0"
        rgb_knot(5)  = "128 0.0 0.0 0.0"
        rgb_knot(6)  = "255 0.0 0.0 1.0"
      rgb_knot(8)  = "0   1.0 0.0 0.0"
      rgb_knot(9)  = "64  1.0 1.0 0.0"
      rgb_knot(10) = "128 0.0 1.0 0.0"
      rgb_knot(11) = "192 0.0 1.0 1.0"
      rgb_knot(12) = "232 1.0 0.0 1.0"
      rgb_knot(13) = "255 0.5 0.5 1.0"
        rgb_knot(14) = "0   1.0 0.0 0.0"
        rgb_knot(15) = "128 1.0 1.0 0.0"
        rgb_knot(16) = "255 0.0 0.0 1.0"
      rgb_knot(17) = "0   1.0 0.0 0.0" !red
      rgb_knot(18) = "128 0.0 1.0 1.0"!aqua
      rgb_knot(19) = "200 1.0 1.0 0.0"!yellow
      rgb_knot(20) = "225 0.0 1.0 0.0"!green
      rgb_knot(21) = "225 0.0 0.0 1.0"!blue
      rgb_knot(22) = "255 1.0 1.0 1.0"!white
#endif



if( mod(dr_nstep,15) .eq. 1) then



      if(.NOT. allocated(dat)) allocate( dat(1:NUNK_VARS,1-NGUARD:NXB+NGUARD,1-NGUARD:NYB+NGUARD,1-NGUARD:NZB+NGUARD) )
      if(.NOT. allocated(bytes) ) allocate( bytes(0:NXB+1,0:NYB+1,0:NXB+1) )
      call Grid_fillGuardCells(CENTER,ALLDIR)
      dat(:,:,:,:) = unk(:,:,:,:,1) ! this specific copy is the ONLY kind of access to unk() that worked ?!?!?

!      print *,'NUNK_VARS=',NUNK_VARS

      
! ---------------------------------------------------------------------------------
! ------------------ .ctl + .dat Grads dump start ---------------------------------
! comment out the following "#ifdef NOTHING"  
! and trailing "#endif" to enable grads dumping
#ifdef NOTHING

! flash UNK cell center vars, or what it is: check flash.h to see after configure
!#define DENS_VAR 1
!#define EINT_VAR 2
!#define ENER_VAR 3
!#define GAMC_VAR 4
!#define GAME_VAR 5
!#define PRES_VAR 6
!#define TEMP_VAR 7
!#define VELX_VAR 8
!#define VELY_VAR 9
!#define VELZ_VAR 10

! set these from the cell center vars desired, see flash.h after configure
      n_var = 8
      var_str(1:8) = (/'dens','eint','ener','pres','temp','velx','vely','velz'/)
      var_index(1:8) = (/DENS_VAR,EINT_VAR,ENER_VAR,PRES_VAR,TEMP_VAR,VELX_VAR,VELY_VAR,VELZ_VAR/)

!create grads ctl.
      call system('rm -f unk.ctl') ! replace every dump
      open(unit=999,status='unknown',file='unk.ctl',access='stream')
      write(ctl_line,"('xdef ',I5)") NXB+2; write(999) trim(ctl_line),achar(10)
      write(ctl_line,"('ydef ',I5)") NYB+2; write(999) trim(ctl_line),achar(10)
      write(ctl_line,"('zdef ',I5)") NZB+2; write(999) trim(ctl_line),achar(10)
      
      ndump = ndump+1
      write(ctl_line,"('tdef ',I5)") ndump; write(999) trim(ctl_line),achar(10)
      write(ctl_line,"('vars ',I5)") n_var; write(999) trim(ctl_line),achar(10)
      
      do iv=1,n_var
        write(ctl_line,"(A,' ',I5)") trim(var_str(iv)), NZB+2
        write(999) trim(ctl_line), achar(10)
      end do
      close(999)
      
! create grads .dat
      if(ndump==1) call system('rm -f unk.dat')
      open(unit=998,status='unknown',file='unk.dat',access='stream',position='append')
      
      do iv=1,n_var
        do iz = 0,NZB+1
        do iy = 0,NYB+1
        do ix = 0,NXB+1
          gdat(ix,iy,iz) = dat(var_index(iv),ix,iy,iz)
        end do
        end do
        end do
        write(998) gdat
      end do
      close(998)
      
#endif
! comment out the above "#endif" to enable Grads dump, 
! also the leading "#ifdef NOTHING" enclosing
! ------------------- end .ctl + .dat (Grads) dump ---------------------------------
! ----------------------------------------------------------------------------------



!#ifdef NOTHING
! try with cotab_offset 14 and Sedov explosion
!      smax = 0.0; smin = 1.0e30 ! velocity magnitude;
      do iz = 0,NZB+1
      do iy = 0,NYB+1
      do ix = 0,NXB+1
        x = 1.0 * sqrt(dat(VELX_VAR,ix,iy,iz)**2 + dat(VELY_VAR,ix,iy,iz)**2 + dat(VELZ_VAR,ix,iy,iz)**2)
!          if(x<smin) smin = x
!          if(x>smax)smax = x
        y = 0.0 + 255.0 * x/sqrt(x + 1.0)
        bytes(ix,iy,iz) = achar( floor( y ) )
      end do
      end do
      end do 
!#endif
      
#ifdef NOTHING
      smin = 1.0e30; smax = 0.0 ! vorticity
      do iz = 0,NZB+1
      do iy = 0,NYB+1
      do ix = 0,NXB+1
        uxyz(1) = dat(VELX_VAR,ix,iy,iz)
        uxyz(2) = dat(VELY_VAR,ix,iy,iz)
        uxyz(3) = dat(VELZ_VAR,ix,iy,iz)
        dxyz(1) = .5*( dat(VELX_VAR,ix+1,iy,iz) - dat(VELX_VAR,ix-1,iy,iz) )
        dxyz(2) = .5*( dat(VELY_VAR,ix,iy+1,iz) - dat(VELY_VAR,ix,iy-1,iz) )
        dxyz(3) = .5*( dat(VELZ_VAR,ix,iy,iz+1) - dat(VELZ_VAR,ix,iy,iz-1) )
        vort(1) = dxyz(2)*uxyz(3) - dxyz(3)*uxyz(2)
        vort(2) = dxyz(3)*uxyz(1) - dxyz(1)*uxyz(3)
        vort(3) = dxyz(1)*uxyz(2) - dxyz(2)*uxyz(1)
        x = 50.0 * log( 1.0 +  sqrt(vort(1)**2 + vort(2)**2 + vort(3)**2) )
          if(x<smin) smin = x
          if(x>smax) smax = x
        y = 0.0 + 254.0 * x/sqrt(x + 1.0)
        bytes(ix,iy,iz) = achar( floor( y ) )
      end do
      end do
      end do
#endif

#ifdef NOTHING
      smax = dat(VELX_VAR,0,0,0); smin = smax ! wind vector direction
      do iz = 0,NZB+1
      do iy = 0,NYB+1
      do ix = 0,NXB+1
        x = 3.0 * dat(VELX_VAR,ix,iy,iz)
          if(x<smin) smin = x
          if(x>smax)smax = x
        y = 128.0 + 127.0 * x/sqrt(x*x + 1.0)
        bytes(ix,iy,iz) = achar( floor( y ) )
      end do
      end do
      end do 
#endif

!      print *,'<<rank=',dr_globalMe,'smax=',smax,'smin=',smin,'>>' ! data scanning
      
      call Grid_getBlkCornerID(1,srend_cornerID,srend_stride,srend_cornerIDhigh) ! UG always (1,...)
      do ix=1,3 ! experiment
        inxyz(ix) = srend_cornerID(ix)-1
      end do
      
      call srend_render(           &
      1,2,                         & ! nV, nV_out
      64,                          & ! Vdim
      (/  0.8,   1.5,  1.5/),      & ! Eye position
      (/ -0.3,  -0.8, -1.0/),      & ! View vector
      (/  0.0,  1.0,   0.0/),      & ! Up vector
      60.0, 60.0,                  & ! Alpha-horz, Beta-vert
      0.0,                         & ! stereo: EyeRight
      -10.0,10.0,                  & ! x clip plans 
      -10.0,10.0,                  & ! y clip plans
      -10.0,10.0,                  & ! z clip plans
      180.0,                       & ! far polar clip
      (/.5,.5,.5/), 1.0,           & ! center(x,y,z) and radius of sphere to clip about
      0.1, 10.0, 1,                & ! near clip0, far clip1, nsh
      1,                           & ! 0 spherical, 1 perspective
      0,                           & ! 0=norm, 1=npole, 2=equator,3=spole
      0.25,                        & ! sampling in cell units,(-) out2in
      bytes,                       & ! data array
      NXB,NYB,NZB,                 & ! XN,YN,ZN of data
      1,                           & ! Bd
      inxyz(1),inxyz(2),inxyz(3),  & ! offsets x,y,z
      400, 400,                    & ! rendering Width, Height in pixels
      1,                           & ! nR
      (/14/),                      & ! cotab_offset(1:nR),
#ifdef SREND_COTAB_PASSNAMELIST
      nalpha,     &
      alpha_knot, &
      nrgb,       &
      rgb_knot,   &
#endif
      'vel/######.ppm,',           & ! output file name masks, requires Imagemagick convert
      1,1,                         & ! tiles right, tiles down: usually 1,1
      (/0/))                         ! target MPI rank   

      print *,'<<after rendering rank=',dr_globalMe,'ranks=',dr_globalNumProcs,'>>' ! data scanning
      if(dr_globalMe .eq. 0) then
         call srend_finish(          &
          2,                         & ! nV, the view index
          dr_globalNumProcs)           ! number of sources
      end if  

end if ! vis, every 10 or so dr_nstep loops
! --------- end srend call --  
end subroutine srend_wrapper



#endif
! +++ end srend snippet ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


