From c4b580d36854c153a0da1055b4147df3295d2b50 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Mon, 4 Apr 2022 15:16:06 -0600 Subject: [PATCH 001/365] Started working on the new, global remix. --- CMakeLists.txt | 11 + src/base/math.F90 | 14 ++ src/base/shellinterp.F90 | 434 +++++++++++++++++++++------------------ 3 files changed, 262 insertions(+), 197 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ab89ed0..587bca5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,17 @@ target_link_libraries(remix2remix.x remixlib baselib) target_link_libraries(remix2rcm.x remixlib baselib) add_dependencies(remix remix.x remix2remix.x remix2rcm.x baselib) +#------------- +#Kaiju: G-ReMIX +message("Adding G-ReMIX module ...") +#Add source +#add_subdirectory(src/base) +#add_custom_target(base ALL) +message("\tAdding executable gremix.x") +add_executable(gremix.x src/drivers/gremix.F90) +target_link_libraries(gremix.x baselib) +add_dependencies(gremix.x baselib) + #------------- #Kaiju: RCM message("Adding RCM module ...") diff --git a/src/base/math.F90 b/src/base/math.F90 index b1f0e2f5..03315436 100644 --- a/src/base/math.F90 +++ b/src/base/math.F90 @@ -589,4 +589,18 @@ module math endif end function SmoothOperator33 + + function isAscending(A) result(isAsc) + real(rp), dimension(:), intent(in) :: A + logical :: isAsc + integer :: N + + N = size(A) + + if ( all(A(2:N)-A(1:N-1)>0) ) then + isAsc = .True. + else + isAsc = .False. + end if + end function isAscending end module math diff --git a/src/base/shellinterp.F90 b/src/base/shellinterp.F90 index a85324b0..25228cce 100644 --- a/src/base/shellinterp.F90 +++ b/src/base/shellinterp.F90 @@ -1,82 +1,122 @@ -!Various data structures and routines to do interpolation from (and to?) a spherical shell grid -!TODO: Add routine to take list of scattered points and interpolate to ShellGrid_T +! Various data structures and routines to do interpolation from (and to?) a spherical shell grid +! TODO: Add routine to take list of scattered points and interpolate to ShellGrid_T module shellinterp use kdefs + use math implicit none integer, parameter, private :: NumTSC = 9 - !Data type for holding 2D spherical shell grid + ! Data type for holding 2D spherical shell grid type ShellGrid_T - integer :: NLat,NLon !Number of lat/lon cells - !xxI = interfaces (Nx+1) - !xxC = centers (Nx) - !Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] - real(rp), dimension(:), allocatable :: LatI,LatC,LonI,LonC !Radians + integer :: Np,Nt !nNumber of lon/lat cells (phi/theta for those of us who are not geophysicists) + ! xxc = centers (Nx) + ! xx = corners (Nx+1) + ! th (theta) runs from north pole toward south + ! Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] + ! note, th/ph are colatitude/longitude; also, defining lat/latc for geophysicists + real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc !Radians logical :: doSP = .false., doNP = .false. !Whether grid contains south/north pole - real(rp) :: minLat,maxLat + real(rp) :: minTheta, maxTheta, minPhi, maxPhi + + ! Local indices, active region + integer :: is=1,ie,js=1,je + ! Local indices, including ghosts + integer :: isg,ieg,jsg,jeg + + ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. + integer :: Ngn, Ngs, Nge, Ngw end type ShellGrid_T contains - !Create a shell grid data structure - !Takes iLats (latitudinal edges, size NLat+1) and NLon cells (assuming uniform spacing) - subroutine GenShellGrid(shGr,iLats,NLat,NLon) + ! Create a shell grid data structure + ! Takes Theta and Phi 1D arrays (uniform or not) + subroutine GenShellGrid(shGr,Theta,Phi) type(ShellGrid_T), intent(inout) :: shGr - real(rp), intent(in) :: iLats(NLat+1) - integer , intent(in) :: NLat,NLon + real(rp), dimension(:), intent(in) :: Theta, Phi - real(rp) :: dphi - integer :: n + integer :: Np, Nt - !Nuke arrays if already allocated - if (allocated(shGr%LatI)) deallocate(shGr%LatI) - if (allocated(shGr%LatC)) deallocate(shGr%LatC) + ! do some checks first + if (.not.(isAscending(Theta))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array must be ascending. Quitting..." + stop + end if - if (allocated(shGr%LonI)) deallocate(shGr%LonI) - if (allocated(shGr%LonC)) deallocate(shGr%LonC) + if (.not.(isAscending(Phi))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array must be ascending. Quitting..." + stop + end if - !Create new arrays - shGr%NLat = NLat - shGr%NLon = NLon + if (any(Theta<0.).or.(any(Theta>PI/2))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array should be in the range [0,PI/2]. Quitting..." + stop + end if - allocate(shGr%LatI(shGr%NLat+1)) - allocate(shGr%LonI(shGr%NLon+1)) + if (any(Phi<0.).or.(any(Phi>2*PI))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." + stop + end if - allocate(shGr%LatC(shGr%NLat)) - allocate(shGr%LonC(shGr%NLon)) - - !Set edges - shGr%LatI(:) = iLats - dphi = 2*PI/shGr%NLon - do n=1,shGr%NLon+1 - shGr%LonI(n) = (n-1)*dphi - enddo - - !Set centers - shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 - shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 - - !Decide if this has north/south pole - shGr%doNP = .false. - shGr%doSP = .false. - if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then - shGr%doNP = .true. - endif - - if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then - shGr%doSP = .true. - endif - - if (shGr%doSP .or. shGr%doNP) then - !Die for now - write(*,*) "This routine does not yet support handling north/south pole" - stop - endif + write(*,*) 'OHUET' - shGr%minLat = minval(shGr%LatI) - shGr%maxLat = maxval(shGr%LatI) + + ! shGr%Np = size(Phi) + ! shGr%Nt = size(Theta) + + ! !Nuke arrays if already allocated + ! if (allocated(shGr%LatI)) deallocate(shGr%LatI) + ! if (allocated(shGr%LatC)) deallocate(shGr%LatC) + + ! if (allocated(shGr%LonI)) deallocate(shGr%LonI) + ! if (allocated(shGr%LonC)) deallocate(shGr%LonC) + + ! !Create new arrays + ! shGr%NLat = NLat + ! shGr%NLon = NLon + + ! allocate(shGr%LatI(shGr%NLat+1)) + ! allocate(shGr%LonI(shGr%NLon+1)) + + ! allocate(shGr%LatC(shGr%NLat)) + ! allocate(shGr%LonC(shGr%NLon)) + + ! !Set edges + ! shGr%LatI(:) = iLats + ! dphi = 2*PI/shGr%NLon + ! do n=1,shGr%NLon+1 + ! shGr%LonI(n) = (n-1)*dphi + ! enddo + + ! !Set centers + ! shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 + ! shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 + + ! !Decide if this has north/south pole + ! shGr%doNP = .false. + ! shGr%doSP = .false. + ! if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then + ! shGr%doNP = .true. + ! endif + + ! if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then + ! shGr%doSP = .true. + ! endif + + ! if (shGr%doSP .or. shGr%doNP) then + ! !Die for now + ! write(*,*) "This routine does not yet support handling north/south pole" + ! stop + ! endif + + ! shGr%minLat = minval(shGr%LatI) + ! shGr%maxLat = maxval(shGr%LatI) end subroutine GenShellGrid @@ -84,183 +124,183 @@ module shellinterp !Result is Qp !Optional : isGood (NLat,NLon), a mask for good/bad data !Optional : isGoodP, whether Qp is a good value - subroutine InterpShell(shGr,Q,lat,lonin,Qp,isGoodP,isGood) - type(ShellGrid_T), intent(in) :: shGr - real(rp), intent(in) :: Q(shGr%NLat,shGr%NLon) - real(rp), intent(out) :: Qp - real(rp), intent(in) :: lat,lonin - logical , intent(out), optional :: isGoodP - logical , intent(in) , optional :: isGood(shGr%NLat,shGr%NLon) + ! subroutine InterpShell(shGr,Q,lat,lonin,Qp,isGoodP,isGood) + ! type(ShellGrid_T), intent(in) :: shGr + ! real(rp), intent(in) :: Q(shGr%NLat,shGr%NLon) + ! real(rp), intent(out) :: Qp + ! real(rp), intent(in) :: lat,lonin + ! logical , intent(out), optional :: isGoodP + ! logical , intent(in) , optional :: isGood(shGr%NLat,shGr%NLon) - integer :: i0,j0,ij0(2),di,dj - integer :: ip,jp,n - real(rp) :: dlat,dphi,eta,zeta,lon - real(rp), dimension(NumTSC) :: Ws,Qs - logical , dimension(NumTSC) :: isGs - real(rp), dimension(-1:+1) :: wE,wZ + ! integer :: i0,j0,ij0(2),di,dj + ! integer :: ip,jp,n + ! real(rp) :: dlat,dphi,eta,zeta,lon + ! real(rp), dimension(NumTSC) :: Ws,Qs + ! logical , dimension(NumTSC) :: isGs + ! real(rp), dimension(-1:+1) :: wE,wZ - Qp = 0.0 - if (present(isGoodP)) then - isGoodP = .false. - endif + ! Qp = 0.0 + ! if (present(isGoodP)) then + ! isGoodP = .false. + ! endif - !Do some short circuiting - if ( (lat>shGr%maxLat) .or. (latshGr%maxLat) .or. (latshGr%NLon) jp = 1 + ! n = 1 + ! do dj=-1,+1 + ! do di=-1,+1 + ! ip = i0+di + ! jp = j0+dj + ! !Wrap around boundary + ! if (jp<1) jp = shGr%NLon + ! if (jp>shGr%NLon) jp = 1 - !Do zero-grad for lat - if (ip<1) ip = 1 - if (ip>shGr%NLat) ip = shGr%NLat + ! !Do zero-grad for lat + ! if (ip<1) ip = 1 + ! if (ip>shGr%NLat) ip = shGr%NLat - Qs(n) = Q(ip,jp) - Ws(n) = wE(di)*wZ(dj) + ! Qs(n) = Q(ip,jp) + ! Ws(n) = wE(di)*wZ(dj) - if (present(isGood)) then - isGs(n) = isGood(ip,jp) - else - isGs(n) = .true. - endif - if (.not. isGs(n)) Ws(n) = 0.0 + ! if (present(isGood)) then + ! isGs(n) = isGood(ip,jp) + ! else + ! isGs(n) = .true. + ! endif + ! if (.not. isGs(n)) Ws(n) = 0.0 - n = n + 1 - enddo - enddo !dj + ! n = n + 1 + ! enddo + ! enddo !dj - !Renormalize - Ws = Ws/sum(Ws) + ! !Renormalize + ! Ws = Ws/sum(Ws) - !Get final value - Qp = dot_product(Qs,Ws) + ! !Get final value + ! Qp = dot_product(Qs,Ws) - !Have some internal functions - contains + ! !Have some internal functions + ! contains - !Clamps mapping in [-0.5,0.5] - subroutine ClampMapVar(ez) - REAL(rp), intent(inout) :: ez - if (ez<-0.5) ez = -0.5 - if (ez>+0.5) ez = +0.5 - end subroutine ClampMapVar + ! !Clamps mapping in [-0.5,0.5] + ! subroutine ClampMapVar(ez) + ! REAL(rp), intent(inout) :: ez + ! if (ez<-0.5) ez = -0.5 + ! if (ez>+0.5) ez = +0.5 + ! end subroutine ClampMapVar - !1D triangular shaped cloud weights - !1D weights for triangular shaped cloud interpolation - !Assuming on -1,1 reference element, dx=1 - !Check for degenerate cases ( |eta| > 0.5 ) - subroutine TSCweight1D(eta,wE) - real(rp), intent(in) :: eta - real(rp), intent(out) :: wE(-1:1) + ! !1D triangular shaped cloud weights + ! !1D weights for triangular shaped cloud interpolation + ! !Assuming on -1,1 reference element, dx=1 + ! !Check for degenerate cases ( |eta| > 0.5 ) + ! subroutine TSCweight1D(eta,wE) + ! real(rp), intent(in) :: eta + ! real(rp), intent(out) :: wE(-1:1) - wE(-1) = 0.5*(0.5-eta)**2.0 - wE( 1) = 0.5*(0.5+eta)**2.0 - wE( 0) = 0.75 - eta**2.0 + ! wE(-1) = 0.5*(0.5-eta)**2.0 + ! wE( 1) = 0.5*(0.5+eta)**2.0 + ! wE( 0) = 0.75 - eta**2.0 - end subroutine TSCweight1D + ! end subroutine TSCweight1D - end subroutine InterpShell + ! end subroutine InterpShell - !For a shGr type find the ij cell that the point lat/lon is in - !NOTE: Returns 0,0 if the point isn't in the grid - subroutine GetShellIJ(shGr,lat,lonin,ij0) - type(ShellGrid_T), intent(in) :: shGr - real(rp), intent(in) :: lat,lonin - integer, intent(out) :: ij0(2) + ! !For a shGr type find the ij cell that the point lat/lon is in + ! !NOTE: Returns 0,0 if the point isn't in the grid + ! subroutine GetShellIJ(shGr,lat,lonin,ij0) + ! type(ShellGrid_T), intent(in) :: shGr + ! real(rp), intent(in) :: lat,lonin + ! integer, intent(out) :: ij0(2) - real(rp) :: lon,dp,dJ - integer :: iX,jX + ! real(rp) :: lon,dp,dJ + ! integer :: iX,jX - if (lonin<0) then - lon = lonin+2*PI - else - lon = lonin - endif + ! if (lonin<0) then + ! lon = lonin+2*PI + ! else + ! lon = lonin + ! endif - ij0 = 0 + ! ij0 = 0 - !Do some short circuiting - if ( (lat>shGr%maxLat) .or. (latshGr%maxLat) .or. (lat Date: Wed, 13 Apr 2022 10:13:14 -0600 Subject: [PATCH 002/365] Separating shell interpolation and grids into separate source files. Moving both under shellutils/. This breaks CMakeLists.txt, need to fix. --- CMakeLists.txt | 6 +- src/base/shellutils/shellGrid.F90 | 137 ++++++++++++++++++ src/base/shellutils/shellGrid.F90~ | 111 ++++++++++++++ .../shellInterp.F90} | 116 +-------------- 4 files changed, 252 insertions(+), 118 deletions(-) create mode 100644 src/base/shellutils/shellGrid.F90 create mode 100644 src/base/shellutils/shellGrid.F90~ rename src/base/{shellinterp.F90 => shellutils/shellInterp.F90} (59%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 587bca5d..a6562c30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,12 +180,10 @@ add_dependencies(remix remix.x remix2remix.x remix2rcm.x baselib) #Kaiju: G-ReMIX message("Adding G-ReMIX module ...") #Add source -#add_subdirectory(src/base) -#add_custom_target(base ALL) message("\tAdding executable gremix.x") add_executable(gremix.x src/drivers/gremix.F90) -target_link_libraries(gremix.x baselib) -add_dependencies(gremix.x baselib) +target_link_libraries(gremix.x shellutilslib baselib) +add_dependencies(gremix.x shellutilslib baselib) #------------- #Kaiju: RCM diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 new file mode 100644 index 00000000..65e2ef0e --- /dev/null +++ b/src/base/shellutils/shellGrid.F90 @@ -0,0 +1,137 @@ +! Various data structures and routines to define grids on spherical shells +module shellGrid + use kdefs + use math + + ! Data type for holding 2D spherical shell grid + type ShellGrid_T + integer :: Np,Nt ! Number of lon/lat cells (phi/theta) + ! xxc = centers (Nx) + ! xx = corners (Nx+1) + ! th (theta) runs from north pole toward south + ! Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] + ! note, th/ph are colatitude/longitude; also, defining lat/latc for latitude + real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc ! Radians + logical :: doSP = .false., doNP = .false. ! Whether grid contains south/north pole + real(rp) :: minTheta, maxTheta, minPhi, maxPhi + + ! Local indices, active region + integer :: is,ie,js,je + ! Local indices, including ghosts + integer :: isg,ieg,jsg,jeg + + ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. + ! default to 0 + integer :: Ngn, Ngs, Nge, Ngw + + logical :: isPeriodic ! Whether the low/high phi boundary is periodic + end type ShellGrid_T + + contains + + ! Create a shell grid data structure + ! Takes Theta and Phi 1D arrays (uniform or not) + ! Default the number of ghosts to 0 + subroutine GenShellGrid(shGr,Theta,Phi,Ngn=0,Ngs=0,Nge=0,Ngw=0,isPeriodic=.True.) + type(ShellGrid_T), intent(inout) :: shGr + real(rp), dimension(:), intent(in) :: Theta, Phi + integer, intent (in) :: Ngn, Ngs, Nge, Ngw ! how many ghosts on each side + + integer :: Np, Nt + + ! do some checks first + if (.not.(isAscending(Theta))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array must be ascending. Quitting..." + stop + end if + + if (.not.(isAscending(Phi))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array must be ascending. Quitting..." + stop + end if + + if (any(Theta<0.).or.(any(Theta>PI/2))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array should be in the range [0,PI/2]. Quitting..." + stop + end if + + if (any(Phi<0.).or.(any(Phi>2*PI))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." + stop + end if + + if ( (isPeriodic).and.(Nge<>Ngw) ) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Periodic grid must have the same number of ghosts on low/high phi boundaries. Quitting..." + stop + end if + + ! define various array indices + shGr%Np = size(Phi)-1 + shGr%Nt = size(Theta)-1 + + shGr%is = 1; shGr%ie = shGr%Nt + shGr%js = 1; shGr%je = shGr%Np + + shGr%Ngn = Ngn; shGr%Ngs = Ngs + shGr%Nge = Nge; shGr%Ngw = Ngw + + shGr%isg = shGr%is - shGr%Ngn + shGr%ieg = shGr%ie + shGr%Ngs + shGr%jsg = shGr%js - shGr%Ngw + shGr%jeg = shGr%je + shGr%Nge + + ! Nuke arrays if already allocated + if (allocated(shGr%th)) deallocate(shGr%th) + if (allocated(shGr%ph)) deallocate(shGr%ph) + if (allocated(shGr%thc)) deallocate(shGr%thc) + if (allocated(shGr%phc)) deallocate(shGr%phc) + if (allocated(shGr%lat)) deallocate(shGr%lat) + if (allocated(shGr%latc)) deallocate(shGr%latc) + + ! Create new arrays + allocate(shGr%th (shGr%Nt+Ngn+Ngs+1)) + allocate(shGr%ph (shGr%Np+Ngw+Nge+1)) + allocate(shGr%thc (shGr%Nt+Ngn+Ngs)) + allocate(shGr%phc (shGr%Np+Ngw+Nge)) + allocate(shGr%lat (shGr%Nt+Ngn+Ngs+1)) + allocate(shGr%latc(shGr%Nt+Ngn+Ngs)) + + + ! !Set edges + ! shGr%LatI(:) = iLats + ! dphi = 2*PI/shGr%NLon + ! do n=1,shGr%NLon+1 + ! shGr%LonI(n) = (n-1)*dphi + ! enddo + + ! !Set centers + ! shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 + ! shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 + + ! !Decide if this has north/south pole + ! shGr%doNP = .false. + ! shGr%doSP = .false. + ! if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then + ! shGr%doNP = .true. + ! endif + + ! if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then + ! shGr%doSP = .true. + ! endif + + ! if (shGr%doSP .or. shGr%doNP) then + ! !Die for now + ! write(*,*) "This routine does not yet support handling north/south pole" + ! stop + ! endif + + ! shGr%minLat = minval(shGr%LatI) + ! shGr%maxLat = maxval(shGr%LatI) + + end subroutine GenShellGrid +end module shellGrid diff --git a/src/base/shellutils/shellGrid.F90~ b/src/base/shellutils/shellGrid.F90~ new file mode 100644 index 00000000..37814b48 --- /dev/null +++ b/src/base/shellutils/shellGrid.F90~ @@ -0,0 +1,111 @@ +module shellGrid + ! Data type for holding 2D spherical shell grid + type ShellGrid_T + integer :: Np,Nt ! Number of lon/lat cells (phi/theta) + ! xxc = centers (Nx) + ! xx = corners (Nx+1) + ! th (theta) runs from north pole toward south + ! Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] + ! note, th/ph are colatitude/longitude; also, defining lat/latc for latitude + real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc !Radians + logical :: doSP = .false., doNP = .false. !Whether grid contains south/north pole + real(rp) :: minTheta, maxTheta, minPhi, maxPhi + + ! Local indices, active region + integer :: is=1,ie,js=1,je + ! Local indices, including ghosts + integer :: isg,ieg,jsg,jeg + + ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. + ! default to 0 + integer :: Ngn=0, Ngs=0, Nge=0, Ngw=0 + end type ShellGrid_T + + contains + + ! Create a shell grid data structure + ! Takes Theta and Phi 1D arrays (uniform or not) + subroutine GenShellGrid(shGr,Theta,Phi) + type(ShellGrid_T), intent(inout) :: shGr + real(rp), dimension(:), intent(in) :: Theta, Phi + + integer :: Np, Nt + + ! do some checks first + if (.not.(isAscending(Theta))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array must be ascending. Quitting..." + stop + end if + + if (.not.(isAscending(Phi))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array must be ascending. Quitting..." + stop + end if + + if (any(Theta<0.).or.(any(Theta>PI/2))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array should be in the range [0,PI/2]. Quitting..." + stop + end if + + if (any(Phi<0.).or.(any(Phi>2*PI))) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." + stop + end if + + ! define various array indices + is = 1 + + ! Nuke arrays if already allocated + if (allocated(shGr%th)) deallocate(shGr%th) + if (allocated(shGr%ph)) deallocate(shGr%ph) + if (allocated(shGr%thc)) deallocate(shGr%thc) + if (allocated(shGr%phc)) deallocate(shGr%phc) + if (allocated(shGr%lat)) deallocate(shGr%lat) + if (allocated(shGr%latc)) deallocate(shGr%latc) + + ! Create new arrays + shGr%Np = size(Phi)-1 + shGr%Nt = size(Theta)-1 + + allocate(shGr%th(shGr%Nt+1 + Ngn + Ngs)) + allocate(shGr%ph(shGr%Np+1)) + + ! allocate(shGr%LatC(shGr%NLat)) + ! allocate(shGr%LonC(shGr%NLon)) + + ! !Set edges + ! shGr%LatI(:) = iLats + ! dphi = 2*PI/shGr%NLon + ! do n=1,shGr%NLon+1 + ! shGr%LonI(n) = (n-1)*dphi + ! enddo + + ! !Set centers + ! shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 + ! shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 + + ! !Decide if this has north/south pole + ! shGr%doNP = .false. + ! shGr%doSP = .false. + ! if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then + ! shGr%doNP = .true. + ! endif + + ! if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then + ! shGr%doSP = .true. + ! endif + + ! if (shGr%doSP .or. shGr%doNP) then + ! !Die for now + ! write(*,*) "This routine does not yet support handling north/south pole" + ! stop + ! endif + + ! shGr%minLat = minval(shGr%LatI) + ! shGr%maxLat = maxval(shGr%LatI) + + end subroutine GenShellGrid diff --git a/src/base/shellinterp.F90 b/src/base/shellutils/shellInterp.F90 similarity index 59% rename from src/base/shellinterp.F90 rename to src/base/shellutils/shellInterp.F90 index 25228cce..5e79be69 100644 --- a/src/base/shellinterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -1,6 +1,6 @@ ! Various data structures and routines to do interpolation from (and to?) a spherical shell grid ! TODO: Add routine to take list of scattered points and interpolate to ShellGrid_T -module shellinterp +module shellInterp use kdefs use math @@ -8,118 +8,6 @@ module shellinterp integer, parameter, private :: NumTSC = 9 - ! Data type for holding 2D spherical shell grid - type ShellGrid_T - integer :: Np,Nt !nNumber of lon/lat cells (phi/theta for those of us who are not geophysicists) - ! xxc = centers (Nx) - ! xx = corners (Nx+1) - ! th (theta) runs from north pole toward south - ! Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] - ! note, th/ph are colatitude/longitude; also, defining lat/latc for geophysicists - real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc !Radians - logical :: doSP = .false., doNP = .false. !Whether grid contains south/north pole - real(rp) :: minTheta, maxTheta, minPhi, maxPhi - - ! Local indices, active region - integer :: is=1,ie,js=1,je - ! Local indices, including ghosts - integer :: isg,ieg,jsg,jeg - - ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. - integer :: Ngn, Ngs, Nge, Ngw - end type ShellGrid_T - - contains - - ! Create a shell grid data structure - ! Takes Theta and Phi 1D arrays (uniform or not) - subroutine GenShellGrid(shGr,Theta,Phi) - type(ShellGrid_T), intent(inout) :: shGr - real(rp), dimension(:), intent(in) :: Theta, Phi - - integer :: Np, Nt - - ! do some checks first - if (.not.(isAscending(Theta))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array must be ascending. Quitting..." - stop - end if - - if (.not.(isAscending(Phi))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Phi array must be ascending. Quitting..." - stop - end if - - if (any(Theta<0.).or.(any(Theta>PI/2))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array should be in the range [0,PI/2]. Quitting..." - stop - end if - - if (any(Phi<0.).or.(any(Phi>2*PI))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." - stop - end if - - write(*,*) 'OHUET' - - - ! shGr%Np = size(Phi) - ! shGr%Nt = size(Theta) - - ! !Nuke arrays if already allocated - ! if (allocated(shGr%LatI)) deallocate(shGr%LatI) - ! if (allocated(shGr%LatC)) deallocate(shGr%LatC) - - ! if (allocated(shGr%LonI)) deallocate(shGr%LonI) - ! if (allocated(shGr%LonC)) deallocate(shGr%LonC) - - ! !Create new arrays - ! shGr%NLat = NLat - ! shGr%NLon = NLon - - ! allocate(shGr%LatI(shGr%NLat+1)) - ! allocate(shGr%LonI(shGr%NLon+1)) - - ! allocate(shGr%LatC(shGr%NLat)) - ! allocate(shGr%LonC(shGr%NLon)) - - ! !Set edges - ! shGr%LatI(:) = iLats - ! dphi = 2*PI/shGr%NLon - ! do n=1,shGr%NLon+1 - ! shGr%LonI(n) = (n-1)*dphi - ! enddo - - ! !Set centers - ! shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 - ! shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 - - ! !Decide if this has north/south pole - ! shGr%doNP = .false. - ! shGr%doSP = .false. - ! if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then - ! shGr%doNP = .true. - ! endif - - ! if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then - ! shGr%doSP = .true. - ! endif - - ! if (shGr%doSP .or. shGr%doNP) then - ! !Die for now - ! write(*,*) "This routine does not yet support handling north/south pole" - ! stop - ! endif - - ! shGr%minLat = minval(shGr%LatI) - ! shGr%maxLat = maxval(shGr%LatI) - - end subroutine GenShellGrid - !Interpolate on grid shGr a cell-centered variable (Q) at point lat,lon !Result is Qp !Optional : isGood (NLat,NLon), a mask for good/bad data @@ -303,4 +191,4 @@ module shellinterp ! end subroutine GetShellIJ -end module shellinterp +end module shellInterp From 20db5db73cac6cc069d4d058976a473805a59ba0 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Wed, 13 Apr 2022 10:14:35 -0600 Subject: [PATCH 003/365] Reverting changes to CMakeLists from previous commit. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6562c30..e20e81f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,8 +182,8 @@ message("Adding G-ReMIX module ...") #Add source message("\tAdding executable gremix.x") add_executable(gremix.x src/drivers/gremix.F90) -target_link_libraries(gremix.x shellutilslib baselib) -add_dependencies(gremix.x shellutilslib baselib) +target_link_libraries(gremix.x baselib) +add_dependencies(gremix.x baselib) #------------- #Kaiju: RCM From 21ebbf1e8ee3dedf4f25b68b4e4b8d3ab57830b9 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Wed, 13 Apr 2022 12:12:34 -0600 Subject: [PATCH 004/365] Adding gremix driver. --- src/drivers/gremix.F90 | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/drivers/gremix.F90 diff --git a/src/drivers/gremix.F90 b/src/drivers/gremix.F90 new file mode 100644 index 00000000..a2882692 --- /dev/null +++ b/src/drivers/gremix.F90 @@ -0,0 +1,26 @@ +!Driver for testing gremix + +program gremixx + use shellGrid + + implicit none + + real(rp) :: LowLatBoundary, HighLatBoundary + real(rp), dimension(100) :: t,p + integer :: i, Nt=100 + type(ShellGrid_T) :: shGr + + LowLatBoundary = 90.*deg2rad + HighLatBoundary = 0. + + do i=1,Nt + t(i) = (LowLatBoundary-HighLatBoundary)/(Nt-1)*(i-1) + HighLatBoundary + end do + + do i=1,Nt + p(i) = 2*pi/Nt*(i-1)+pi + end do + + call GenShellGrid(shGr,t,p) + +end program gremixx From 88c3ebcd3c7115f9cae45cac4f059dc7151be545 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Wed, 13 Apr 2022 13:16:40 -0600 Subject: [PATCH 005/365] Cleaning up. --- src/base/CMakeLists.txt | 2 +- src/base/shellutils/shellGrid.F90 | 35 ++++++++++++++++++------------- src/drivers/gremix.F90 | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index e009874e..92b3fb39 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -1,2 +1,2 @@ -file(GLOB base_srcs *.F90 defs/*.F90 types/*.F90) +file(GLOB base_srcs *.F90 defs/*.F90 types/*.F90 shellutils/*.F90) add_library(baselib ${base_srcs}) diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 index 65e2ef0e..f6621fcd 100644 --- a/src/base/shellutils/shellGrid.F90 +++ b/src/base/shellutils/shellGrid.F90 @@ -16,15 +16,15 @@ module shellGrid real(rp) :: minTheta, maxTheta, minPhi, maxPhi ! Local indices, active region - integer :: is,ie,js,je + integer :: is=1,ie,js=1,je ! Local indices, including ghosts integer :: isg,ieg,jsg,jeg ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. ! default to 0 - integer :: Ngn, Ngs, Nge, Ngw + integer :: Ngn=0, Ngs=0, Nge=0, Ngw=0 - logical :: isPeriodic ! Whether the low/high phi boundary is periodic + logical :: isPeriodic=.True. ! Whether the low/high phi boundary is periodic end type ShellGrid_T contains @@ -32,13 +32,21 @@ module shellGrid ! Create a shell grid data structure ! Takes Theta and Phi 1D arrays (uniform or not) ! Default the number of ghosts to 0 - subroutine GenShellGrid(shGr,Theta,Phi,Ngn=0,Ngs=0,Nge=0,Ngw=0,isPeriodic=.True.) + subroutine GenShellGrid(shGr,Theta,Phi,Ngn,Ngs,Nge,Ngw,isPeriodic) type(ShellGrid_T), intent(inout) :: shGr real(rp), dimension(:), intent(in) :: Theta, Phi - integer, intent (in) :: Ngn, Ngs, Nge, Ngw ! how many ghosts on each side + integer, optional, intent(in) :: Ngn, Ngs, Nge, Ngw ! how many ghosts on each side + logical, optional, intent(in) :: isPeriodic integer :: Np, Nt + ! Parse optional parameters + if (present(isPeriodic)) shGr%isPeriodic = isPeriodic ! otherwise, always periodic as set in ShellGrid type + if (present(Ngn)) shGr%Ngn = Ngn ! otherwise, always 0 as set in ShellGrid type + if (present(Ngs)) shGr%Ngs = Ngs ! otherwise, always 0 as set in ShellGrid type + if (present(Ngw)) shGr%Ngw = Ngw ! otherwise, always 0 as set in ShellGrid type + if (present(Nge)) shGr%Nge = Nge ! otherwise, always 0 as set in ShellGrid type + ! do some checks first if (.not.(isAscending(Theta))) then write(*,*) "Inside shell grid generator (GenShellGrid)." @@ -64,7 +72,7 @@ module shellGrid stop end if - if ( (isPeriodic).and.(Nge<>Ngw) ) then + if ( (shGr%isPeriodic).and.(shGr%Nge/=shGr%Ngw) ) then write(*,*) "Inside shell grid generator (GenShellGrid)." write(*,*) "Periodic grid must have the same number of ghosts on low/high phi boundaries. Quitting..." stop @@ -77,9 +85,6 @@ module shellGrid shGr%is = 1; shGr%ie = shGr%Nt shGr%js = 1; shGr%je = shGr%Np - shGr%Ngn = Ngn; shGr%Ngs = Ngs - shGr%Nge = Nge; shGr%Ngw = Ngw - shGr%isg = shGr%is - shGr%Ngn shGr%ieg = shGr%ie + shGr%Ngs shGr%jsg = shGr%js - shGr%Ngw @@ -94,12 +99,12 @@ module shellGrid if (allocated(shGr%latc)) deallocate(shGr%latc) ! Create new arrays - allocate(shGr%th (shGr%Nt+Ngn+Ngs+1)) - allocate(shGr%ph (shGr%Np+Ngw+Nge+1)) - allocate(shGr%thc (shGr%Nt+Ngn+Ngs)) - allocate(shGr%phc (shGr%Np+Ngw+Nge)) - allocate(shGr%lat (shGr%Nt+Ngn+Ngs+1)) - allocate(shGr%latc(shGr%Nt+Ngn+Ngs)) + allocate(shGr%th (shGr%Nt+shGr%Ngn+shGr%Ngs+1)) + allocate(shGr%ph (shGr%Np+shGr%Ngw+shGr%Nge+1)) + allocate(shGr%thc (shGr%Nt+shGr%Ngn+shGr%Ngs)) + allocate(shGr%phc (shGr%Np+shGr%Ngw+shGr%Nge)) + allocate(shGr%lat (shGr%Nt+shGr%Ngn+shGr%Ngs+1)) + allocate(shGr%latc(shGr%Nt+shGr%Ngn+shGr%Ngs)) ! !Set edges diff --git a/src/drivers/gremix.F90 b/src/drivers/gremix.F90 index a2882692..06ad8db7 100644 --- a/src/drivers/gremix.F90 +++ b/src/drivers/gremix.F90 @@ -18,7 +18,7 @@ program gremixx end do do i=1,Nt - p(i) = 2*pi/Nt*(i-1)+pi + p(i) = 2*pi/Nt*(i-1) end do call GenShellGrid(shGr,t,p) From f756e481fa1ac3eb1ec4bfcb9a8d3d8edbd0b687 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Fri, 22 Apr 2022 18:30:38 -0600 Subject: [PATCH 006/365] Almost finished shell grid generator. --- src/base/shellutils/shellGrid.F90 | 156 +++++++++++++++++++++-------- src/base/shellutils/shellGrid.F90~ | 111 -------------------- src/drivers/gremix.F90 | 4 +- 3 files changed, 116 insertions(+), 155 deletions(-) delete mode 100644 src/base/shellutils/shellGrid.F90~ diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 index f6621fcd..e5b72b45 100644 --- a/src/base/shellutils/shellGrid.F90 +++ b/src/base/shellutils/shellGrid.F90 @@ -13,7 +13,6 @@ module shellGrid ! note, th/ph are colatitude/longitude; also, defining lat/latc for latitude real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc ! Radians logical :: doSP = .false., doNP = .false. ! Whether grid contains south/north pole - real(rp) :: minTheta, maxTheta, minPhi, maxPhi ! Local indices, active region integer :: is=1,ie,js=1,je @@ -22,9 +21,9 @@ module shellGrid ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. ! default to 0 - integer :: Ngn=0, Ngs=0, Nge=0, Ngw=0 + integer :: Ngn=0, Ngs=0, Nge=1, Ngw=1 - logical :: isPeriodic=.True. ! Whether the low/high phi boundary is periodic + logical :: isPeriodic ! Whether the low/high phi boundary is periodic end type ShellGrid_T contains @@ -32,20 +31,20 @@ module shellGrid ! Create a shell grid data structure ! Takes Theta and Phi 1D arrays (uniform or not) ! Default the number of ghosts to 0 - subroutine GenShellGrid(shGr,Theta,Phi,Ngn,Ngs,Nge,Ngw,isPeriodic) + subroutine GenShellGrid(shGr,Theta,Phi,Ngn,Ngs,Nge,Ngw) type(ShellGrid_T), intent(inout) :: shGr real(rp), dimension(:), intent(in) :: Theta, Phi integer, optional, intent(in) :: Ngn, Ngs, Nge, Ngw ! how many ghosts on each side - logical, optional, intent(in) :: isPeriodic + integer :: i,j integer :: Np, Nt + real(rp) :: delta ! Parse optional parameters - if (present(isPeriodic)) shGr%isPeriodic = isPeriodic ! otherwise, always periodic as set in ShellGrid type if (present(Ngn)) shGr%Ngn = Ngn ! otherwise, always 0 as set in ShellGrid type if (present(Ngs)) shGr%Ngs = Ngs ! otherwise, always 0 as set in ShellGrid type - if (present(Ngw)) shGr%Ngw = Ngw ! otherwise, always 0 as set in ShellGrid type - if (present(Nge)) shGr%Nge = Nge ! otherwise, always 0 as set in ShellGrid type + if (present(Ngw)) shGr%Ngw = Ngw ! otherwise, always 1 as set in ShellGrid type + if (present(Nge)) shGr%Nge = Nge ! otherwise, always 1 as set in ShellGrid type ! do some checks first if (.not.(isAscending(Theta))) then @@ -60,9 +59,9 @@ module shellGrid stop end if - if (any(Theta<0.).or.(any(Theta>PI/2))) then + if (any(Theta<0.).or.(any(Theta>PI))) then write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array should be in the range [0,PI/2]. Quitting..." + write(*,*) "Theta array should be in the range [0,PI]. Quitting..." stop end if @@ -72,12 +71,48 @@ module shellGrid stop end if + ! decide if the grid is periodic + if ( ( minval(Phi) <= TINY ).and.( maxval(Phi) >= 2*PI - TINY) ) then + shGr%isPeriodic = .True. + else + shGr%isPeriodic = .False. + end if + if ( (shGr%isPeriodic).and.(shGr%Nge/=shGr%Ngw) ) then write(*,*) "Inside shell grid generator (GenShellGrid)." write(*,*) "Periodic grid must have the same number of ghosts on low/high phi boundaries. Quitting..." stop end if + if ( (.not.(shGr%isPeriodic)).and.(shGr%Nge/=shGr%Ngw) ) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Grids with Nge/=Ngw have not been implemented. Quitting..." + stop + end if + + ! Decide if this has north/south pole + shGr%doNP = .false. + shGr%doSP = .false. + if ( maxval(Theta) >= (PI - TINY) ) then + shGr%doSP = .true. + endif + + if ( minval(Theta) <= (TINY) ) then + shGr%doNP = .true. + endif + + if ( (shGr%doNP).and.(shGr%Ngn/=0) ) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Grid containing the north pole can't have Ngn/=0. Quitting..." + stop + end if + + if ( (shGr%doSP).and.(shGr%Ngs/=0) ) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Grid containing the south pole can't have Ngs/=0. Quitting..." + stop + end if + ! define various array indices shGr%Np = size(Phi)-1 shGr%Nt = size(Theta)-1 @@ -90,6 +125,9 @@ module shellGrid shGr%jsg = shGr%js - shGr%Ngw shGr%jeg = shGr%je + shGr%Nge + associate(is=>shGr%is, ie=>shGr%ie, js=>shGr%js, je=>shGr%je, & + isg=>shGr%isg, ieg=>shGr%ieg, jsg=>shGr%jsg, jeg=>shGr%jeg) + ! Nuke arrays if already allocated if (allocated(shGr%th)) deallocate(shGr%th) if (allocated(shGr%ph)) deallocate(shGr%ph) @@ -99,44 +137,78 @@ module shellGrid if (allocated(shGr%latc)) deallocate(shGr%latc) ! Create new arrays - allocate(shGr%th (shGr%Nt+shGr%Ngn+shGr%Ngs+1)) - allocate(shGr%ph (shGr%Np+shGr%Ngw+shGr%Nge+1)) - allocate(shGr%thc (shGr%Nt+shGr%Ngn+shGr%Ngs)) - allocate(shGr%phc (shGr%Np+shGr%Ngw+shGr%Nge)) - allocate(shGr%lat (shGr%Nt+shGr%Ngn+shGr%Ngs+1)) - allocate(shGr%latc(shGr%Nt+shGr%Ngn+shGr%Ngs)) + allocate(shGr%th (isg:ieg+1)) + allocate(shGr%thc (isg:ieg )) + allocate(shGr%lat (isg:ieg+1)) + allocate(shGr%latc(isg:ieg )) + allocate(shGr%ph (jsg:jeg+1)) + allocate(shGr%phc (jsg:jeg )) + ! Set grid coordinates + shGr%th(is:ie+1) = Theta ! note the arrays are conformable because of the index definitions above + shGr%ph(js:je+1) = Phi - ! !Set edges - ! shGr%LatI(:) = iLats - ! dphi = 2*PI/shGr%NLon - ! do n=1,shGr%NLon+1 - ! shGr%LonI(n) = (n-1)*dphi - ! enddo + ! Define ghost coordinates - ! !Set centers - ! shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 - ! shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 + ! Do north, unless it's the pole (no ghosts for poles) + if ( (.not.shGr%doNP).and.(shGr%Ngn/=0) ) then + ! linearly extrapolate + do i=1,shGr%Ngn + shGr%th(is-i) = 2*shGr%th(is) - shGr%th(is+i) + end do - ! !Decide if this has north/south pole - ! shGr%doNP = .false. - ! shGr%doSP = .false. - ! if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then - ! shGr%doNP = .true. - ! endif + ! check if we got into negative thetas + ! then just fill the space with same size cells down to theta=0 + if ( shGr%th(is-shGr%Ngn) < 0 ) then + delta = shGr%th(is)/shGr%Ngn - ! if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then - ! shGr%doSP = .true. - ! endif + do i=1,shGr%Ngn + shGr%th(is-i) = shGr%th(is) - delta*i + end do + end if + end if - ! if (shGr%doSP .or. shGr%doNP) then - ! !Die for now - ! write(*,*) "This routine does not yet support handling north/south pole" - ! stop - ! endif - - ! shGr%minLat = minval(shGr%LatI) - ! shGr%maxLat = maxval(shGr%LatI) + ! Do south, unless it's the pole (no ghosts for poles) + if ( (.not.shGr%doSP).and.(shGr%Ngs/=0) ) then + ! linearly extrapolate + do i=1,shGr%Ngs + shGr%th(ie+1+i) = 2*shGr%th(ie+1) - shGr%th(ie+1-i) + end do + ! check if we got into thetas > pi + ! then just fill the space with same size cells up to theta=pi + if ( shGr%th(ie+1+shGr%Ngs) > PI ) then + delta = (PI - shGr%th(ie+1))/shGr%Ngs + do i=1,shGr%Ngs + shGr%th(ie+1+i) = shGr%th(ie+1) + delta*i + end do + end if + end if + + ! if non-periodic grid is needed, can implement ghosts on E/W ends similarly to N/S above + ! but we assume the grid is always periodic + if ( (.not.shGr%isPeriodic) ) then + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Non-periodic grids are not implemented. Quitting..." + stop + endif + + ! Note, we already made sure that for a periodic grid Nge=Ngw, phi(js)=0 and phi(je+1)=pi + do j=1,shGr%Nge + shGr%ph(js-j) = shGr%ph(je+1-j) - 2*PI + shGr%ph(je+1+j) = shGr%ph(js+j) + 2*PI + end do + + !Set centers + shGr%thc = ( shGr%th(isg+1:ieg+1) + shGr%th(isg:ieg) )/2. + shGr%phc = ( shGr%ph(jsg+1:jeg+1) + shGr%ph(jsg:jeg) )/2. + + ! Define latitudes just in case + shGr%lat = PI/2 - shGr%th + shGr%latc = PI/2 - shGr%thc + + end associate + + ! TODO: define isPhiUniform end subroutine GenShellGrid end module shellGrid diff --git a/src/base/shellutils/shellGrid.F90~ b/src/base/shellutils/shellGrid.F90~ deleted file mode 100644 index 37814b48..00000000 --- a/src/base/shellutils/shellGrid.F90~ +++ /dev/null @@ -1,111 +0,0 @@ -module shellGrid - ! Data type for holding 2D spherical shell grid - type ShellGrid_T - integer :: Np,Nt ! Number of lon/lat cells (phi/theta) - ! xxc = centers (Nx) - ! xx = corners (Nx+1) - ! th (theta) runs from north pole toward south - ! Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] - ! note, th/ph are colatitude/longitude; also, defining lat/latc for latitude - real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc !Radians - logical :: doSP = .false., doNP = .false. !Whether grid contains south/north pole - real(rp) :: minTheta, maxTheta, minPhi, maxPhi - - ! Local indices, active region - integer :: is=1,ie,js=1,je - ! Local indices, including ghosts - integer :: isg,ieg,jsg,jeg - - ! Ghosts for north, south, east, and west boundaries. East=>larger phi. West => smaller phi. - ! default to 0 - integer :: Ngn=0, Ngs=0, Nge=0, Ngw=0 - end type ShellGrid_T - - contains - - ! Create a shell grid data structure - ! Takes Theta and Phi 1D arrays (uniform or not) - subroutine GenShellGrid(shGr,Theta,Phi) - type(ShellGrid_T), intent(inout) :: shGr - real(rp), dimension(:), intent(in) :: Theta, Phi - - integer :: Np, Nt - - ! do some checks first - if (.not.(isAscending(Theta))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array must be ascending. Quitting..." - stop - end if - - if (.not.(isAscending(Phi))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Phi array must be ascending. Quitting..." - stop - end if - - if (any(Theta<0.).or.(any(Theta>PI/2))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array should be in the range [0,PI/2]. Quitting..." - stop - end if - - if (any(Phi<0.).or.(any(Phi>2*PI))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." - stop - end if - - ! define various array indices - is = 1 - - ! Nuke arrays if already allocated - if (allocated(shGr%th)) deallocate(shGr%th) - if (allocated(shGr%ph)) deallocate(shGr%ph) - if (allocated(shGr%thc)) deallocate(shGr%thc) - if (allocated(shGr%phc)) deallocate(shGr%phc) - if (allocated(shGr%lat)) deallocate(shGr%lat) - if (allocated(shGr%latc)) deallocate(shGr%latc) - - ! Create new arrays - shGr%Np = size(Phi)-1 - shGr%Nt = size(Theta)-1 - - allocate(shGr%th(shGr%Nt+1 + Ngn + Ngs)) - allocate(shGr%ph(shGr%Np+1)) - - ! allocate(shGr%LatC(shGr%NLat)) - ! allocate(shGr%LonC(shGr%NLon)) - - ! !Set edges - ! shGr%LatI(:) = iLats - ! dphi = 2*PI/shGr%NLon - ! do n=1,shGr%NLon+1 - ! shGr%LonI(n) = (n-1)*dphi - ! enddo - - ! !Set centers - ! shGr%LatC = ( shGr%LatI(2:shGr%NLat+1) + shGr%LatI(1:shGr%NLat) )/2.0 - ! shGr%LonC = ( shGr%LonI(2:shGr%NLon+1) + shGr%LonI(1:shGr%NLon) )/2.0 - - ! !Decide if this has north/south pole - ! shGr%doNP = .false. - ! shGr%doSP = .false. - ! if ( maxval(shGr%LatI) >= (PI/2.0 - TINY) ) then - ! shGr%doNP = .true. - ! endif - - ! if ( minval(shGr%LatI) <= (-PI/2.0 + TINY) ) then - ! shGr%doSP = .true. - ! endif - - ! if (shGr%doSP .or. shGr%doNP) then - ! !Die for now - ! write(*,*) "This routine does not yet support handling north/south pole" - ! stop - ! endif - - ! shGr%minLat = minval(shGr%LatI) - ! shGr%maxLat = maxval(shGr%LatI) - - end subroutine GenShellGrid diff --git a/src/drivers/gremix.F90 b/src/drivers/gremix.F90 index 06ad8db7..80a9d7d9 100644 --- a/src/drivers/gremix.F90 +++ b/src/drivers/gremix.F90 @@ -10,7 +10,7 @@ program gremixx integer :: i, Nt=100 type(ShellGrid_T) :: shGr - LowLatBoundary = 90.*deg2rad + LowLatBoundary = PI !180.*deg2rad HighLatBoundary = 0. do i=1,Nt @@ -18,7 +18,7 @@ program gremixx end do do i=1,Nt - p(i) = 2*pi/Nt*(i-1) + p(i) = 2*pi/(Nt-1)*(i-1) end do call GenShellGrid(shGr,t,p) From 71ffb15b47dc019fee8bce443921a5b3dd0a9df2 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Mon, 25 Apr 2022 15:15:04 -0600 Subject: [PATCH 007/365] A working version of Kareem's TCS interpolator. No significant changes in the code, just some renaming of variables and minor additions. --- src/base/shellutils/shellGrid.F90 | 30 ++- src/base/shellutils/shellInterp.F90 | 294 ++++++++++++++-------------- src/drivers/gremix.F90 | 29 ++- 3 files changed, 196 insertions(+), 157 deletions(-) diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 index e5b72b45..812958d8 100644 --- a/src/base/shellutils/shellGrid.F90 +++ b/src/base/shellutils/shellGrid.F90 @@ -14,6 +14,8 @@ module shellGrid real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc ! Radians logical :: doSP = .false., doNP = .false. ! Whether grid contains south/north pole + real(rp) :: minTheta, maxTheta, minPhi, maxPhi + ! Local indices, active region integer :: is=1,ie,js=1,je ! Local indices, including ghosts @@ -24,13 +26,14 @@ module shellGrid integer :: Ngn=0, Ngs=0, Nge=1, Ngw=1 logical :: isPeriodic ! Whether the low/high phi boundary is periodic + logical :: isPhiUniform ! Define this to speed up search for interpolation end type ShellGrid_T contains ! Create a shell grid data structure ! Takes Theta and Phi 1D arrays (uniform or not) - ! Default the number of ghosts to 0 + ! Decides if isPeriodic and isPhiUniform based on the Phi array passed in subroutine GenShellGrid(shGr,Theta,Phi,Ngn,Ngs,Nge,Ngw) type(ShellGrid_T), intent(inout) :: shGr real(rp), dimension(:), intent(in) :: Theta, Phi @@ -39,6 +42,7 @@ module shellGrid integer :: i,j integer :: Np, Nt real(rp) :: delta + real(rp), dimension(:), allocatable :: dphi ! Parse optional parameters if (present(Ngn)) shGr%Ngn = Ngn ! otherwise, always 0 as set in ShellGrid type @@ -125,6 +129,18 @@ module shellGrid shGr%jsg = shGr%js - shGr%Ngw shGr%jeg = shGr%je + shGr%Nge + ! decide if the grid is uniform in Phi + ! helps to speed up interpolation search + if (allocated(dphi)) deallocate(dphi) + allocate(dphi(shGr%Np)) ! helper + dphi = Phi(2:shGr%Np+1) - Phi(1:shGr%Np) + if ( all( dphi - dphi(1) <= TINY ) ) then + shGr%isPhiUniform = .True. + else + shGr%isPhiUniform = .False. + end if + deallocate(dphi) + associate(is=>shGr%is, ie=>shGr%ie, js=>shGr%js, je=>shGr%je, & isg=>shGr%isg, ieg=>shGr%ieg, jsg=>shGr%jsg, jeg=>shGr%jeg) @@ -133,14 +149,10 @@ module shellGrid if (allocated(shGr%ph)) deallocate(shGr%ph) if (allocated(shGr%thc)) deallocate(shGr%thc) if (allocated(shGr%phc)) deallocate(shGr%phc) - if (allocated(shGr%lat)) deallocate(shGr%lat) - if (allocated(shGr%latc)) deallocate(shGr%latc) ! Create new arrays allocate(shGr%th (isg:ieg+1)) allocate(shGr%thc (isg:ieg )) - allocate(shGr%lat (isg:ieg+1)) - allocate(shGr%latc(isg:ieg )) allocate(shGr%ph (jsg:jeg+1)) allocate(shGr%phc (jsg:jeg )) @@ -203,12 +215,12 @@ module shellGrid shGr%thc = ( shGr%th(isg+1:ieg+1) + shGr%th(isg:ieg) )/2. shGr%phc = ( shGr%ph(jsg+1:jeg+1) + shGr%ph(jsg:jeg) )/2. - ! Define latitudes just in case - shGr%lat = PI/2 - shGr%th - shGr%latc = PI/2 - shGr%thc + shGr%minTheta = minval(shGr%th) + shGr%maxTheta = maxval(shGr%th) + shGr%minPhi = minval(shGr%ph) + shGr%maxPhi = maxval(shGr%ph) end associate - ! TODO: define isPhiUniform end subroutine GenShellGrid end module shellGrid diff --git a/src/base/shellutils/shellInterp.F90 b/src/base/shellutils/shellInterp.F90 index 5e79be69..39c57d0d 100644 --- a/src/base/shellutils/shellInterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -3,192 +3,200 @@ module shellInterp use kdefs use math + use shellgrid implicit none integer, parameter, private :: NumTSC = 9 - !Interpolate on grid shGr a cell-centered variable (Q) at point lat,lon - !Result is Qp - !Optional : isGood (NLat,NLon), a mask for good/bad data - !Optional : isGoodP, whether Qp is a good value - ! subroutine InterpShell(shGr,Q,lat,lonin,Qp,isGoodP,isGood) - ! type(ShellGrid_T), intent(in) :: shGr - ! real(rp), intent(in) :: Q(shGr%NLat,shGr%NLon) - ! real(rp), intent(out) :: Qp - ! real(rp), intent(in) :: lat,lonin - ! logical , intent(out), optional :: isGoodP - ! logical , intent(in) , optional :: isGood(shGr%NLat,shGr%NLon) + contains - ! integer :: i0,j0,ij0(2),di,dj - ! integer :: ip,jp,n - ! real(rp) :: dlat,dphi,eta,zeta,lon - ! real(rp), dimension(NumTSC) :: Ws,Qs - ! logical , dimension(NumTSC) :: isGs - ! real(rp), dimension(-1:+1) :: wE,wZ + ! Interpolate on grid shGr a cell-centered variable (Q) at point t(heta),p(hi) + ! Result is Qp + ! Optional : isGood (Nt,Np), a mask for good/bad data + ! Optional : isGoodP, whether Qp is a good value + subroutine InterpShell(shGr,Q,t,pin,Qinterp,isGoodP,isGood) + type(ShellGrid_T), intent(in) :: shGr + real(rp), intent(in) :: Q(shGr%Nt,shGr%Np) + real(rp), intent(out) :: Qinterp + real(rp), intent(in) :: t,pin + logical , intent(out), optional :: isGoodP + logical , intent(in) , optional :: isGood(shGr%Nt,shGr%Np) - ! Qp = 0.0 - ! if (present(isGoodP)) then - ! isGoodP = .false. - ! endif + integer :: i0,j0,ij0(2),di,dj + integer :: ip,jp,n + real(rp) :: p,dt,dp,eta,zeta + real(rp), dimension(NumTSC) :: Ws,Qs + logical , dimension(NumTSC) :: isGs + real(rp), dimension(-1:+1) :: wE,wZ - ! !Do some short circuiting - ! if ( (lat>shGr%maxLat) .or. (latshGr%NLon) jp = 1 + ! Calculate weights + call TSCweight1D(eta ,wE) + call TSCweight1D(zeta,wZ) - ! !Do zero-grad for lat - ! if (ip<1) ip = 1 - ! if (ip>shGr%NLat) ip = shGr%NLat + ! Now loop over surrounding cells and get weights/values + n = 1 + do dj=-1,+1 + do di=-1,+1 + ip = i0+di + jp = j0+dj + ! Wrap around boundary + if (jp<1) jp = shGr%Np + if (jp>shGr%Np) jp = 1 - ! Qs(n) = Q(ip,jp) - ! Ws(n) = wE(di)*wZ(dj) + ! Do zero-grad for theta + if (ip<1) ip = 1 + if (ip>shGr%Nt) ip = shGr%Nt + + Qs(n) = Q(ip,jp) + Ws(n) = wE(di)*wZ(dj) - ! if (present(isGood)) then - ! isGs(n) = isGood(ip,jp) - ! else - ! isGs(n) = .true. - ! endif - ! if (.not. isGs(n)) Ws(n) = 0.0 + if (present(isGood)) then + isGs(n) = isGood(ip,jp) + else + isGs(n) = .true. + endif + if (.not. isGs(n)) Ws(n) = 0.0 - ! n = n + 1 - ! enddo - ! enddo !dj + n = n + 1 + enddo + enddo !dj - ! !Renormalize - ! Ws = Ws/sum(Ws) + ! Renormalize + Ws = Ws/sum(Ws) - ! !Get final value - ! Qp = dot_product(Qs,Ws) + ! Get final value + Qinterp = dot_product(Qs,Ws) - ! !Have some internal functions - ! contains + ! Have some internal functions + contains - ! !Clamps mapping in [-0.5,0.5] - ! subroutine ClampMapVar(ez) - ! REAL(rp), intent(inout) :: ez - ! if (ez<-0.5) ez = -0.5 - ! if (ez>+0.5) ez = +0.5 - ! end subroutine ClampMapVar + ! Clamps mapping in [-0.5,0.5] + subroutine ClampMapVar(ez) + REAL(rp), intent(inout) :: ez + if (ez<-0.5) ez = -0.5 + if (ez>+0.5) ez = +0.5 + end subroutine ClampMapVar - ! !1D triangular shaped cloud weights - ! !1D weights for triangular shaped cloud interpolation - ! !Assuming on -1,1 reference element, dx=1 - ! !Check for degenerate cases ( |eta| > 0.5 ) - ! subroutine TSCweight1D(eta,wE) - ! real(rp), intent(in) :: eta - ! real(rp), intent(out) :: wE(-1:1) + ! 1D triangular shaped cloud weights + ! 1D weights for triangular shaped cloud interpolation + ! Assuming on -1,1 reference element, dx=1 + ! Check for degenerate cases ( |eta| > 0.5 ) + subroutine TSCweight1D(eta,wE) + real(rp), intent(in) :: eta + real(rp), intent(out) :: wE(-1:1) - ! wE(-1) = 0.5*(0.5-eta)**2.0 - ! wE( 1) = 0.5*(0.5+eta)**2.0 - ! wE( 0) = 0.75 - eta**2.0 + wE(-1) = 0.5*(0.5-eta)**2.0 + wE( 1) = 0.5*(0.5+eta)**2.0 + wE( 0) = 0.75 - eta**2.0 - ! end subroutine TSCweight1D + end subroutine TSCweight1D - ! end subroutine InterpShell + end subroutine InterpShell - ! !For a shGr type find the ij cell that the point lat/lon is in - ! !NOTE: Returns 0,0 if the point isn't in the grid - ! subroutine GetShellIJ(shGr,lat,lonin,ij0) - ! type(ShellGrid_T), intent(in) :: shGr - ! real(rp), intent(in) :: lat,lonin - ! integer, intent(out) :: ij0(2) + ! For a shGr type find the ij cell that the point t(theta)/p(hi) is in + ! NOTE: Returns 0,0 if the point isn't in the grid + subroutine GetShellIJ(shGr,t,pin,ij0) + type(ShellGrid_T), intent(in) :: shGr + real(rp), intent(in) :: t,pin + integer, intent(out) :: ij0(2) - ! real(rp) :: lon,dp,dJ - ! integer :: iX,jX + real(rp) :: p,dp,dJ + integer :: iX,jX - ! if (lonin<0) then - ! lon = lonin+2*PI - ! else - ! lon = lonin - ! endif + ! Do some short circuiting + ! TODO: treat points outside of the grid + if ( (t>shGr%maxTheta) .or. (tshGr%maxLat) .or. (latshGr%maxPhi) .or. (p Date: Mon, 25 Apr 2022 15:31:28 -0600 Subject: [PATCH 008/365] Fixed interp variable name. Corrected traps for poles to make consistent with the transition from latitude to theta (colatitude) introduced in the previous commit. --- src/base/shellutils/shellInterp.F90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/base/shellutils/shellInterp.F90 b/src/base/shellutils/shellInterp.F90 index 39c57d0d..cbe0de00 100644 --- a/src/base/shellutils/shellInterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -12,9 +12,9 @@ module shellInterp contains ! Interpolate on grid shGr a cell-centered variable (Q) at point t(heta),p(hi) - ! Result is Qp + ! Result is Qinterp ! Optional : isGood (Nt,Np), a mask for good/bad data - ! Optional : isGoodP, whether Qp is a good value + ! Optional : isGoodP, whether Qinterp is a good value subroutine InterpShell(shGr,Q,t,pin,Qinterp,isGoodP,isGood) type(ShellGrid_T), intent(in) :: shGr real(rp), intent(in) :: Q(shGr%Nt,shGr%Np) @@ -56,13 +56,13 @@ module shellInterp end if ! Trap for near-pole cases - if (shGr%doSP .and. (i0==1)) then + if (shGr%doSP .and. (i0==shGr%Nt)) then ! Handle south pole and return write(*,*) "Not implemented!" stop endif - if (shGr%doNP .and. (i0==shGr%Nt)) then + if (shGr%doNP .and. (i0==1)) then ! Handle north pole and return write(*,*) "Not implemented!" stop From 7568376ab3894a2bad7e935475efa4df6c83a698 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Fri, 17 Jun 2022 09:42:15 -0600 Subject: [PATCH 009/365] Fixing indentation. --- src/base/shellutils/shellInterp.F90 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/base/shellutils/shellInterp.F90 b/src/base/shellutils/shellInterp.F90 index cbe0de00..a784ee8b 100644 --- a/src/base/shellutils/shellInterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -52,7 +52,7 @@ module shellInterp ! Have central cell and know that it's good if (present(isGoodP)) then - isGoodP = .true. + isGoodP = .true. end if ! Trap for near-pole cases @@ -124,9 +124,9 @@ module shellInterp ! Clamps mapping in [-0.5,0.5] subroutine ClampMapVar(ez) - REAL(rp), intent(inout) :: ez - if (ez<-0.5) ez = -0.5 - if (ez>+0.5) ez = +0.5 + REAL(rp), intent(inout) :: ez + if (ez<-0.5) ez = -0.5 + if (ez>+0.5) ez = +0.5 end subroutine ClampMapVar ! 1D triangular shaped cloud weights @@ -181,12 +181,12 @@ module shellInterp ! Now get lon part, assume uniform phi spacing if (shGr%isPhiUniform) then - ! note this is faster, thus preferred - dp = shGr%phc(2)-shGr%phc(1) - dJ = p/dp - jX = floor(dJ) + 1 + ! note this is faster, thus preferred + dp = shGr%phc(2)-shGr%phc(1) + dJ = p/dp + jX = floor(dJ) + 1 else - jX = minloc( abs(shGr%phc-p),dim=1 ) ! Find closest lat cell center + jX = minloc( abs(shGr%phc-p),dim=1 ) ! Find closest lat cell center end if ! Impose bounds just in case From cf45d55ee10e230a04537551514aa70be8672d0b Mon Sep 17 00:00:00 2001 From: vmerkin Date: Mon, 20 Mar 2023 15:35:58 -0600 Subject: [PATCH 010/365] Introducing a shellGridFunction type to hold functions defined on the grid. These are stored as part of the grid object. Modifying the InterShell call to access the function to interpolate by index rather than passing the array. A number other small changes. --- src/base/shellutils/shellGrid.F90 | 56 ++++++++++++++++++++++------- src/base/shellutils/shellInterp.F90 | 9 ++--- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 index 812958d8..4e5ace75 100644 --- a/src/base/shellutils/shellGrid.F90 +++ b/src/base/shellutils/shellGrid.F90 @@ -5,6 +5,9 @@ module shellGrid ! Data type for holding 2D spherical shell grid type ShellGrid_T + integer :: nShellVars = 1 ! number of shell grid functions + type(shellGridFunction_T), dimension(:), allocatable :: shellVars + integer :: Np,Nt ! Number of lon/lat cells (phi/theta) ! xxc = centers (Nx) ! xx = corners (Nx+1) @@ -29,26 +32,34 @@ module shellGrid logical :: isPhiUniform ! Define this to speed up search for interpolation end type ShellGrid_T + type ShellGridFunction_T + real(rp), dimension(:,:), allocatable :: cellData +! corner data currently unused. If ever uncommented, need to allocate/init in initShellGridFunctions below +! real(rp), dimension(:,:), allocatable :: cornerData + logical, dimension(4) :: bcsApplied ! flag indicating whether BCs were applied (ghosts filled) for [n,s,e,w] boundaries + end type ShellGridFunction_T + contains ! Create a shell grid data structure ! Takes Theta and Phi 1D arrays (uniform or not) ! Decides if isPeriodic and isPhiUniform based on the Phi array passed in - subroutine GenShellGrid(shGr,Theta,Phi,Ngn,Ngs,Nge,Ngw) + subroutine GenShellGrid(shGr,Theta,Phi,nGhosts) type(ShellGrid_T), intent(inout) :: shGr real(rp), dimension(:), intent(in) :: Theta, Phi - integer, optional, intent(in) :: Ngn, Ngs, Nge, Ngw ! how many ghosts on each side + integer, optional, dimension(4), intent(in) :: nGhosts ! how many ghosts on each side (n,s,e,w) integer :: i,j - integer :: Np, Nt real(rp) :: delta real(rp), dimension(:), allocatable :: dphi ! Parse optional parameters - if (present(Ngn)) shGr%Ngn = Ngn ! otherwise, always 0 as set in ShellGrid type - if (present(Ngs)) shGr%Ngs = Ngs ! otherwise, always 0 as set in ShellGrid type - if (present(Ngw)) shGr%Ngw = Ngw ! otherwise, always 1 as set in ShellGrid type - if (present(Nge)) shGr%Nge = Nge ! otherwise, always 1 as set in ShellGrid type + if (present(nGhosts)) then + shGr%Ngn = nGhosts(1) ! otherwise, always 0 as set in ShellGrid type + shGr%Ngs = nGhosts(2) ! otherwise, always 0 as set in ShellGrid type + shGr%Nge = nGhosts(3) ! otherwise, always 1 as set in ShellGrid type + shGr%Ngw = nGhosts(4) ! otherwise, always 1 as set in ShellGrid type + end if ! do some checks first if (.not.(isAscending(Theta))) then @@ -211,16 +222,37 @@ module shellGrid shGr%ph(je+1+j) = shGr%ph(js+j) + 2*PI end do - !Set centers + ! Set centers shGr%thc = ( shGr%th(isg+1:ieg+1) + shGr%th(isg:ieg) )/2. shGr%phc = ( shGr%ph(jsg+1:jeg+1) + shGr%ph(jsg:jeg) )/2. - shGr%minTheta = minval(shGr%th) - shGr%maxTheta = maxval(shGr%th) - shGr%minPhi = minval(shGr%ph) - shGr%maxPhi = maxval(shGr%ph) + ! Note, the bounds below only include the active cells + shGr%minTheta = minval(shGr%th(is:ie+1)) + shGr%maxTheta = maxval(shGr%th(is:ie+1)) + shGr%minPhi = minval(shGr%ph(js:je+1)) + shGr%maxPhi = maxval(shGr%ph(js:je+1)) end associate + ! finally, initialize the shell grid functions + ! nuke everything + if (allocated(shGr%shellVars)) deallocate(shGr%shellVars) + allocate(shGr%shellVars(shGr%nShellVars)) + do i=1,shGr%nShellVars + call initShellGridFunctions(shGr%shellVars(i)) + end do + + contains + subroutine initShellGridFunctions(shellVar) + type(shellGridFunction_T), intent(out) :: shellVar + + if (.not.allocated(shellVar%cellData)) allocate(shellVar%cellData(shGr%Nt,shGr%Np)) + shellVar%cellData = 0. ! initialize to 0 + + ! unset all BC's + shellVar%bcsApplied = .false. + + end subroutine initShellGridFunctions + end subroutine GenShellGrid end module shellGrid diff --git a/src/base/shellutils/shellInterp.F90 b/src/base/shellutils/shellInterp.F90 index a784ee8b..327b40db 100644 --- a/src/base/shellutils/shellInterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -11,13 +11,14 @@ module shellInterp contains - ! Interpolate on grid shGr a cell-centered variable (Q) at point t(heta),p(hi) + ! Interpolate on grid shGr a cell-centered variable at point t(heta),p(hi) + ! The cell-centered variable is selected via the Qind parameter ! Result is Qinterp ! Optional : isGood (Nt,Np), a mask for good/bad data ! Optional : isGoodP, whether Qinterp is a good value - subroutine InterpShell(shGr,Q,t,pin,Qinterp,isGoodP,isGood) + subroutine InterpShell(shGr,Qind,t,pin,Qinterp,isGoodP,isGood) type(ShellGrid_T), intent(in) :: shGr - real(rp), intent(in) :: Q(shGr%Nt,shGr%Np) + integer, intent(in) :: Qind real(rp), intent(out) :: Qinterp real(rp), intent(in) :: t,pin logical , intent(out), optional :: isGoodP @@ -99,7 +100,7 @@ module shellInterp if (ip<1) ip = 1 if (ip>shGr%Nt) ip = shGr%Nt - Qs(n) = Q(ip,jp) + Qs(n) = shGr%shellVars(Qind)%cellData(ip,jp) Ws(n) = wE(di)*wZ(dj) if (present(isGood)) then From ce110267d098ce27c661a7d5a02cc68e33785fa5 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Mon, 20 Mar 2023 15:36:41 -0600 Subject: [PATCH 011/365] Adding the driver modifications to accompany the previous commit. --- src/drivers/gremix.F90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/drivers/gremix.F90 b/src/drivers/gremix.F90 index 61bdad19..e4aea35f 100644 --- a/src/drivers/gremix.F90 +++ b/src/drivers/gremix.F90 @@ -9,7 +9,7 @@ program gremixx real(rp) :: LowLatBoundary, HighLatBoundary,QQ real(rp), allocatable, dimension(:) :: t,p real(rp), allocatable, dimension(:,:) :: Q,Qinterp - integer :: i,j, Nt=180, Np=360 + integer :: i,j, Nt=181, Np=361 type(ShellGrid_T) :: shGr LowLatBoundary = PI !180.*deg2rad @@ -34,11 +34,12 @@ program gremixx end do end do - call GenShellGrid(shGr,t,p) + call GenShellGrid(shGr,t,p,nGhosts=[0,0,4,4]) + shGr%shellVars(1)%cellData = Q do i=2,Nt-2 do j=2,Np-2 - call InterpShell(shGr,Q,0.5*(t(i)+t(i+1)),0.5*(p(j)+p(j+1)),Qinterp(i-1,j-1)) - write(*,*) 0.5*(t(i)+t(i+1))*rad2deg,0.5*(p(j)+p(j+1))*rad2deg,Qinterp(i-1,j-1) + call InterpShell(shGr,1,0.5*(t(i)+t(i+1)),0.5*(p(j)+p(j+1)),Qinterp(i-1,j-1)) +! write(*,*) 0.5*(t(i)+t(i+1))*rad2deg,0.5*(p(j)+p(j+1))*rad2deg,Qinterp(i-1,j-1) end do end do From e57673898d1ceb3acc5b917a9e66910a62e68a37 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Sat, 1 Apr 2023 05:35:33 -0600 Subject: [PATCH 012/365] Adding min/max grid bounds including ghosts. Adding NSEW enumerator. Fixing indentation. --- src/base/shellutils/shellGrid.F90 | 147 ++++++++++++++++-------------- 1 file changed, 80 insertions(+), 67 deletions(-) diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 index 4e5ace75..938bcdea 100644 --- a/src/base/shellutils/shellGrid.F90 +++ b/src/base/shellutils/shellGrid.F90 @@ -2,12 +2,18 @@ module shellGrid use kdefs use math - + + ! note, this will conflict with mixdefs + ! but NORTH, SOUTH are defined the same way + enum, bind(C) + enumerator :: NORTH=1,SOUTH,EAST,WEST + end enum + ! Data type for holding 2D spherical shell grid type ShellGrid_T integer :: nShellVars = 1 ! number of shell grid functions type(shellGridFunction_T), dimension(:), allocatable :: shellVars - + integer :: Np,Nt ! Number of lon/lat cells (phi/theta) ! xxc = centers (Nx) ! xx = corners (Nx+1) @@ -18,6 +24,7 @@ module shellGrid logical :: doSP = .false., doNP = .false. ! Whether grid contains south/north pole real(rp) :: minTheta, maxTheta, minPhi, maxPhi + real(rp) :: minGTheta, maxGTheta, minGPhi, maxGPhi ! Local indices, active region integer :: is=1,ie,js=1,je @@ -55,56 +62,56 @@ module shellGrid ! Parse optional parameters if (present(nGhosts)) then - shGr%Ngn = nGhosts(1) ! otherwise, always 0 as set in ShellGrid type - shGr%Ngs = nGhosts(2) ! otherwise, always 0 as set in ShellGrid type - shGr%Nge = nGhosts(3) ! otherwise, always 1 as set in ShellGrid type - shGr%Ngw = nGhosts(4) ! otherwise, always 1 as set in ShellGrid type + shGr%Ngn = nGhosts(NORTH) ! otherwise, always 0 as set in ShellGrid type + shGr%Ngs = nGhosts(SOUTH) ! otherwise, always 0 as set in ShellGrid type + shGr%Nge = nGhosts(EAST) ! otherwise, always 1 as set in ShellGrid type + shGr%Ngw = nGhosts(WEST) ! otherwise, always 1 as set in ShellGrid type end if ! do some checks first if (.not.(isAscending(Theta))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array must be ascending. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array must be ascending. Quitting..." + stop end if if (.not.(isAscending(Phi))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Phi array must be ascending. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array must be ascending. Quitting..." + stop end if if (any(Theta<0.).or.(any(Theta>PI))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Theta array should be in the range [0,PI]. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Theta array should be in the range [0,PI]. Quitting..." + stop end if if (any(Phi<0.).or.(any(Phi>2*PI))) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Phi array should be in the range [0,2*PI]. Quitting..." + stop end if ! decide if the grid is periodic if ( ( minval(Phi) <= TINY ).and.( maxval(Phi) >= 2*PI - TINY) ) then - shGr%isPeriodic = .True. + shGr%isPeriodic = .True. else - shGr%isPeriodic = .False. + shGr%isPeriodic = .False. end if if ( (shGr%isPeriodic).and.(shGr%Nge/=shGr%Ngw) ) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Periodic grid must have the same number of ghosts on low/high phi boundaries. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Periodic grid must have the same number of ghosts on low/high phi boundaries. Quitting..." + stop end if if ( (.not.(shGr%isPeriodic)).and.(shGr%Nge/=shGr%Ngw) ) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Grids with Nge/=Ngw have not been implemented. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Grids with Nge/=Ngw have not been implemented. Quitting..." + stop end if - + ! Decide if this has north/south pole shGr%doNP = .false. shGr%doSP = .false. @@ -117,15 +124,15 @@ module shellGrid endif if ( (shGr%doNP).and.(shGr%Ngn/=0) ) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Grid containing the north pole can't have Ngn/=0. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Grid containing the north pole can't have Ngn/=0. Quitting..." + stop end if if ( (shGr%doSP).and.(shGr%Ngs/=0) ) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Grid containing the south pole can't have Ngs/=0. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Grid containing the south pole can't have Ngs/=0. Quitting..." + stop end if ! define various array indices @@ -146,14 +153,14 @@ module shellGrid allocate(dphi(shGr%Np)) ! helper dphi = Phi(2:shGr%Np+1) - Phi(1:shGr%Np) if ( all( dphi - dphi(1) <= TINY ) ) then - shGr%isPhiUniform = .True. + shGr%isPhiUniform = .True. else - shGr%isPhiUniform = .False. + shGr%isPhiUniform = .False. end if deallocate(dphi) associate(is=>shGr%is, ie=>shGr%ie, js=>shGr%js, je=>shGr%je, & - isg=>shGr%isg, ieg=>shGr%ieg, jsg=>shGr%jsg, jeg=>shGr%jeg) + isg=>shGr%isg, ieg=>shGr%ieg, jsg=>shGr%jsg, jeg=>shGr%jeg) ! Nuke arrays if already allocated if (allocated(shGr%th)) deallocate(shGr%th) @@ -175,51 +182,51 @@ module shellGrid ! Do north, unless it's the pole (no ghosts for poles) if ( (.not.shGr%doNP).and.(shGr%Ngn/=0) ) then - ! linearly extrapolate - do i=1,shGr%Ngn - shGr%th(is-i) = 2*shGr%th(is) - shGr%th(is+i) - end do + ! linearly extrapolate + do i=1,shGr%Ngn + shGr%th(is-i) = 2*shGr%th(is) - shGr%th(is+i) + end do - ! check if we got into negative thetas - ! then just fill the space with same size cells down to theta=0 - if ( shGr%th(is-shGr%Ngn) < 0 ) then - delta = shGr%th(is)/shGr%Ngn + ! check if we got into negative thetas + ! then just fill the space with same size cells down to theta=0 + if ( shGr%th(is-shGr%Ngn) < 0 ) then + delta = shGr%th(is)/shGr%Ngn - do i=1,shGr%Ngn - shGr%th(is-i) = shGr%th(is) - delta*i - end do - end if + do i=1,shGr%Ngn + shGr%th(is-i) = shGr%th(is) - delta*i + end do + end if end if ! Do south, unless it's the pole (no ghosts for poles) if ( (.not.shGr%doSP).and.(shGr%Ngs/=0) ) then - ! linearly extrapolate - do i=1,shGr%Ngs - shGr%th(ie+1+i) = 2*shGr%th(ie+1) - shGr%th(ie+1-i) - end do + ! linearly extrapolate + do i=1,shGr%Ngs + shGr%th(ie+1+i) = 2*shGr%th(ie+1) - shGr%th(ie+1-i) + end do - ! check if we got into thetas > pi - ! then just fill the space with same size cells up to theta=pi - if ( shGr%th(ie+1+shGr%Ngs) > PI ) then - delta = (PI - shGr%th(ie+1))/shGr%Ngs - do i=1,shGr%Ngs - shGr%th(ie+1+i) = shGr%th(ie+1) + delta*i - end do - end if + ! check if we got into thetas > pi + ! then just fill the space with same size cells up to theta=pi + if ( shGr%th(ie+1+shGr%Ngs) > PI ) then + delta = (PI - shGr%th(ie+1))/shGr%Ngs + do i=1,shGr%Ngs + shGr%th(ie+1+i) = shGr%th(ie+1) + delta*i + end do + end if end if ! if non-periodic grid is needed, can implement ghosts on E/W ends similarly to N/S above ! but we assume the grid is always periodic if ( (.not.shGr%isPeriodic) ) then - write(*,*) "Inside shell grid generator (GenShellGrid)." - write(*,*) "Non-periodic grids are not implemented. Quitting..." - stop + write(*,*) "Inside shell grid generator (GenShellGrid)." + write(*,*) "Non-periodic grids are not implemented. Quitting..." + stop endif ! Note, we already made sure that for a periodic grid Nge=Ngw, phi(js)=0 and phi(je+1)=pi do j=1,shGr%Nge - shGr%ph(js-j) = shGr%ph(je+1-j) - 2*PI - shGr%ph(je+1+j) = shGr%ph(js+j) + 2*PI + shGr%ph(js-j) = shGr%ph(je+1-j) - 2*PI + shGr%ph(je+1+j) = shGr%ph(js+j) + 2*PI end do ! Set centers @@ -232,6 +239,12 @@ module shellGrid shGr%minPhi = minval(shGr%ph(js:je+1)) shGr%maxPhi = maxval(shGr%ph(js:je+1)) + ! this includes ghosts + shGr%minGTheta = minval(shGr%th) + shGr%maxGTheta = maxval(shGr%th) + shGr%minGPhi = minval(shGr%ph) + shGr%maxGPhi = maxval(shGr%ph) + end associate ! finally, initialize the shell grid functions @@ -245,10 +258,10 @@ module shellGrid contains subroutine initShellGridFunctions(shellVar) type(shellGridFunction_T), intent(out) :: shellVar - + if (.not.allocated(shellVar%cellData)) allocate(shellVar%cellData(shGr%Nt,shGr%Np)) shellVar%cellData = 0. ! initialize to 0 - + ! unset all BC's shellVar%bcsApplied = .false. From 77bafb184324eb2084e53674b524b821b708cddf Mon Sep 17 00:00:00 2001 From: vmerkin Date: Sat, 1 Apr 2023 05:38:15 -0600 Subject: [PATCH 013/365] Finished changes to GetShellIJ, now returns meaningful ij0 even for points outside the boundary. --- src/base/shellutils/shellInterp.F90 | 78 +++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/src/base/shellutils/shellInterp.F90 b/src/base/shellutils/shellInterp.F90 index 327b40db..92aa3cd7 100644 --- a/src/base/shellutils/shellInterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -22,7 +22,7 @@ module shellInterp real(rp), intent(out) :: Qinterp real(rp), intent(in) :: t,pin logical , intent(out), optional :: isGoodP - logical , intent(in) , optional :: isGood(shGr%Nt,shGr%Np) + logical , intent(in) , optional :: isGood(shGr%Nt,shGr%Np) ! TODO: consider if this should include ghosts integer :: i0,j0,ij0(2),di,dj integer :: ip,jp,n @@ -31,6 +31,19 @@ module shellInterp logical , dimension(NumTSC) :: isGs real(rp), dimension(-1:+1) :: wE,wZ + + + ! if ( (t>shGr%maxTheta).and.(.not.shGr%bcsApplied(SOUTH)) ) then + ! ! Point inside ghosts but BCs not applied, get outta here + ! return + ! endif + + ! if ( (tPI) ) then + write(*,*) "Inside InterpShell." + write(*,*) "Theta should be in the range [0,PI]. Quitting..." + stop + end if + + ! Note, check inside if the point is in our grid (including ghosts) + ! Note, all points are within the longitude bounds because we only use periodic grids + ! If the point is outside of latitude bounds, ij0(1) will be set to a nominal cell number extrapolated outside of bounds + ! (i.e., how many cells outside of the boundary are we) call GetShellIJ(shGr,t,p,ij0) !Find the i,j cell this point is in i0 = ij0(1) @@ -156,13 +179,6 @@ module shellInterp real(rp) :: p,dp,dJ integer :: iX,jX - ! Do some short circuiting - ! TODO: treat points outside of the grid - if ( (t>shGr%maxTheta) .or. (tshGr%maxGTheta) ) then + ! Point outside ghost grid + ! get an idea of where we are using the last available deltaTheta + ! and get outta here + + iX = shGr%ieg+1+floor((t-shGr%maxGTheta)/(shGr%th(shGr%ieg+1)-shGr%th(shGr%ieg))) + return + endif + + if ( (t Date: Sat, 1 Apr 2023 09:33:56 -0400 Subject: [PATCH 014/365] Merging setupEnvironment.sh from development branch --- scripts/setupEnvironment.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/setupEnvironment.sh b/scripts/setupEnvironment.sh index cd75db86..8546d90b 100755 --- a/scripts/setupEnvironment.sh +++ b/scripts/setupEnvironment.sh @@ -6,12 +6,15 @@ # borrowed this one-liner to get the directory containing the script: # https://stackoverflow.com/questions/59895/how-can-i-get-the-source-directory-of-a-bash-script-from-within-the-script-itsel -SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +# modified to also work with zsh +# https://stackoverflow.com/questions/9901210/bash-source0-equivalent-in-zsh +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-${(%):-%x}}" )" &> /dev/null && pwd )" # strip off the "/scripts" folder to get to the root of the repository ROOT_DIR="$(echo "$SCRIPT_DIR" | sed 's:/scripts$::')" +# using only export= and not export+= to work with bash and zsh export KAIJUHOME="$ROOT_DIR" -export PYTHONPATH+=":$KAIJUHOME" -export PATH+=":$KAIJUHOME/scripts:$KAIJUHOME/scripts/datamodel:$KAIJUHOME/scripts/helio:$KAIJUHOME/scripts/legacy:$KAIJUHOME/scripts/preproc:$KAIJUHOME/scripts/postproc:$KAIJUHOME/scripts/quicklook" +export PYTHONPATH="$PYTHONPATH:$KAIJUHOME" +export PATH="$PATH:$KAIJUHOME/scripts:$KAIJUHOME/scripts/datamodel:$KAIJUHOME/scripts/helio:$KAIJUHOME/scripts/legacy:$KAIJUHOME/scripts/preproc:$KAIJUHOME/scripts/postproc:$KAIJUHOME/scripts/quicklook" From 00ab5a894b7160d26796228f5cc1854a2e6f9498 Mon Sep 17 00:00:00 2001 From: phamkh Date: Mon, 10 Jul 2023 10:39:14 -0600 Subject: [PATCH 015/365] MPI Coupling Test 1 --- src/remix/tgcm.F90 | 143 ++++++++++++++++++++++++++++--- src/voltron/mpi/voltapp_mpi.F90 | 9 ++ src/voltron/mpi/voltmpitypes.F90 | 3 + src/voltron/voltapp.F90 | 10 +-- 4 files changed, 150 insertions(+), 15 deletions(-) diff --git a/src/remix/tgcm.F90 b/src/remix/tgcm.F90 index 261c0b7d..722039ee 100644 --- a/src/remix/tgcm.F90 +++ b/src/remix/tgcm.F90 @@ -15,6 +15,13 @@ module gcminterp character(len=strLen), dimension(nVars), private :: mixVarNames character(len=strLen), dimension(nVars), private :: mixUnitNames + interface exportgcm + module procedure exportgcmfile, exportgcmmpi + end interface + interface importgcm + module procedure importgcmfile, importgcmmpi + end interface + contains subroutine init_gcm(gcm,ion,isRestart) @@ -220,11 +227,13 @@ module gcminterp enddo end subroutine init_gcm_mix - subroutine coupleGCM2MIX(gcm,ion,do2way,mjd,time) + subroutine coupleGCM2MIX(gcm,ion,do2way,mjd,time,gcmCplComm,myRank) + use mpi_f08 type(mixIon_T),dimension(:),intent(inout) :: ion type(gcm_T), intent(inout) :: gcm - logical,optional,intent(in) :: do2way - real(rp), optional, intent(in) :: time, mjd + real(rp), intent(in) :: time, mjd + type(MPI_Comm), optional :: gcmCplComm + integer, optional, intent(in) :: myRank ! maybe create the SM and GEO list of points here ! since the destination grid does not need to be structured @@ -235,12 +244,20 @@ module gcminterp !Must MIX export first. TIEGCM will also import first. call Tic("Export") - call exportgcm(ion,gcm,mjd,time) + if (present(gcmCplComm)) then + call exportgcm(ion,gcm,mjd,time,gcmCplComm,myRank) + else + call exportgcm(ion,gcm,mjd,time) + endif call Toc("Export") !Import gcm data call Tic("Import") - call importgcm(gcm, ion) + if (present(gcmCplComm)) then + call importgcm(gcm, ion, gcmCplComm,myRank) + else + call importgcm(gcm, ion) + endif call Toc("Import") if (gcm%isRestart) gcm%isRestart=.false. @@ -251,7 +268,7 @@ module gcminterp end subroutine coupleGCM2MIX - subroutine importgcm(gcm,ion) + subroutine importgcmfile(gcm,ion) type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion integer :: h,ymod1 @@ -276,12 +293,12 @@ module gcminterp !Map the data to MIX grid call mapGCM2MIX(gcm,ion) - end subroutine + end subroutine importgcmfile - subroutine exportgcm(ion,gcm,mjd,time) + subroutine exportgcmfile(ion,gcm,mjd,time) type(gcm_T), intent(inout) :: gcm type(mixIon_T), dimension(:),intent(inout) :: ion - real(rp), optional, intent(in) :: time, mjd + real(rp), intent(in) :: time, mjd integer :: h,ymod2 ! The weird ymod here is to undo the funky southern hemisphere colat issue. @@ -303,7 +320,113 @@ module gcminterp ! write the coupling file call writeMIX2GCM(ion,gcm,mjd,time) - end subroutine + end subroutine exportgcmfile + + subroutine importgcmmpi(gcm,ion, gcmCplComm,myRank) + use mpi_f08 + type(gcm_T), intent(inout) :: gcm + type(mixIon_T),dimension(:),intent(inout) :: ion + type(MPI_Comm) :: gcmCplComm + integer, intent(in) :: myRank + + real(rp), dimension(:,:,:), allocatable :: var2d + + integer :: v,i,h,ymod1,ierr + + if (.not. allocated(var2d)) allocate(var2d(gcm%nlat,gcm%nlon,gcm2mix_nvar)) + + call Tic("MpiExchange") + + call mpi_send(testArray, 10*worldSize, MPI_DOUBLE_PRECISION, 1, 1001, interComm, ierror) + if(ierror /= MPI_Success) then + call MPI_Error_string( ierror, message, length, ierror) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierror) + end if + + ! Split global GCM array into two hemispheres + ! then shift the array so that longitude starts at 0 + do v=1,gcm2mix_nvar + do i=1,gcm%nhlat + gcm%gcmInput(GCMNORTH,v)%var(:gcm%nlon-1,i) = cshift(var2d(gcm%t2N(gcm%nhlat-i+1),:gcm%nlon-1,v),gcm%lonshift) + gcm%gcmInput(GCMSOUTH,v)%var(:gcm%nlon-1,i) = cshift(var2d(gcm%t2S(i),:gcm%nlon-1,v),gcm%lonshift) + gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) + gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) + enddo + !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) + !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v + !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v + !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v + end do + if (allocated(var2d)) deallocate(var2d) + call Toc("MpiExchange") + + ! The weird ymod here is to undo the funky southern hemisphere colat issue. + do h=1,size(ion) + if (h == GCMSOUTH) then + ymod1 = -1 + else + ymod1 = 1 + endif + !ymod1 = 1 + !write(*,*) "SM -> GEO START:",h,ymod1 + call transform_grid(ion(h)%G,ion(h)%Ggeo,iSMtoGEO,h,ym1=ymod1) + !write(*,*) "SM -> GEO END: ",h + end do + + !Map the data to MIX grid + call mapGCM2MIX(gcm,ion) + + end subroutine importgcmmpi + + subroutine exportgcmmpi(ion,gcm,mjd,time,gcmCplComm,myRank) + use mpi_f08 + type(gcm_T), intent(inout) :: gcm + type(mixIon_T), dimension(:),intent(inout) :: ion + real(rp), intent(in) :: time, mjd + type(MPI_Comm) :: gcmCplComm + integer, intent(in) :: myRank + integer :: h,ymod2,v,ierr,length + character( len = MPI_MAX_ERROR_STRING) :: message + + ! The weird ymod here is to undo the funky southern hemisphere colat issue. + do h=1,gcm%nhemi + if (h == GCMSOUTH) then + ymod2 = -1 + else + ymod2 = 1 + endif + !ymod2 = 1 + !write(*,*) "GEO -> SM START:",h,ymod2 + call transform_grid(gcm%GEO(h),gcm%SM(h),iGEOtoSM,h,ym2=ymod2) + !write(*,*) "GEO -> SM END: ",h + end do + + ! Map from mix grid to gcm grid + call mapMIX2GCM(ion,gcm) + + ! Prepare the export data + if (.not. allocated(var2d)) allocate(var2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) + var2d = 0. + ! Before we start, we collapse to 1 globe instead of 2 hemispheres + do v=1,mix2gcm_nvar + var2d(gcm%t2N(gcm%nhlat:1:-1),:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMNORTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) + var2d(gcm%t2S,:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMSOUTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) + var2d(gcm%t2N,gcm%nlon,v) = var2d(gcm%t2N,1,v) + var2d(gcm%t2S,gcm%nlon,v) = var2d(gcm%t2S,1,v) + end do + + ! Send the coupling data + do v=1,gcm2mix_nvar + call mpi_send(var2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, 1, 1000+v, gcmCplComm, ierr) + if(ierr /= MPI_Success) then + call MPI_Error_string( ierr, message, length, ierr) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierr) + end if + enddo + + end subroutine exportgcmmpi subroutine ReadH5gcm(gcm) type(gcm_T), intent(inout) :: gcm diff --git a/src/voltron/mpi/voltapp_mpi.F90 b/src/voltron/mpi/voltapp_mpi.F90 index 285593f5..eb604997 100644 --- a/src/voltron/mpi/voltapp_mpi.F90 +++ b/src/voltron/mpi/voltapp_mpi.F90 @@ -516,6 +516,12 @@ module voltapp_mpi call convertGameraToRemix(vApp%mhd2mix, vApp%gAppLocal, vApp%remixApp) call Toc("G2R") + if (vApp%doGCM .and. vApp%time >=0) then + call Tic("GCM2MIX") + call coupleGCM2MIX(vApp%gcm,vApp%remixApp%ion,vApp%MJD,vApp%time,vApp%gcmCplComm,vApp%myRank) + call Toc("GCM2MIX") + end if + ! run remix call Tic("ReMIX") call runRemix(vApp) @@ -1015,5 +1021,8 @@ module voltapp_mpi end subroutine + subroutine exportGCM2MIX(vApp) + type + end module voltapp_mpi diff --git a/src/voltron/mpi/voltmpitypes.F90 b/src/voltron/mpi/voltmpitypes.F90 index cb8e63e9..53b9c2dc 100644 --- a/src/voltron/mpi/voltmpitypes.F90 +++ b/src/voltron/mpi/voltmpitypes.F90 @@ -25,6 +25,9 @@ module voltmpitypes logical :: doSerialVoltron = .false., doAsyncCoupling = .true. logical :: firstDeepUpdate = .true., firstStepUpdate = .true. + ! coupling comms variables to be done on volt rank + type(MPI_Comm) :: gcmCplComm + ! array of all zeroes to simplify various send/receive calls integer, dimension(:), allocatable :: zeroArrayCounts type(MPI_Datatype), dimension(:), allocatable :: zeroArrayTypes diff --git a/src/voltron/voltapp.F90 b/src/voltron/voltapp.F90 index 7fe0da72..97b5f6e7 100644 --- a/src/voltron/voltapp.F90 +++ b/src/voltron/voltapp.F90 @@ -440,11 +440,11 @@ module voltapp ! determining the current dipole tilt call vApp%tilt%getValue(vApp%time,curTilt) - if (vApp%doGCM .and. vApp%time >=0) then - call Tic("GCM2MIX") - call coupleGCM2MIX(vApp%gcm,vApp%remixApp%ion,vApp%doGCM,mjd=vApp%MJD,time=vApp%time) - call Toc("GCM2MIX") - end if + !if (vApp%doGCM .and. vApp%time >=0) then + ! call Tic("GCM2MIX") + ! call coupleGCM2MIX(vApp%gcm,vApp%remixApp%ion,mjd=vApp%MJD,time=vApp%time) + ! call Toc("GCM2MIX") + !end if ! solve for remix output if (vApp%time<=0) then From c2c47260e798ed7316795dc2572de1b7251a0138 Mon Sep 17 00:00:00 2001 From: phamkh Date: Tue, 8 Aug 2023 22:09:16 -0600 Subject: [PATCH 016/365] Working MPI Coupling but end program has bug --- src/base/kdefs.F90 | 7 + src/drivers/voltron_mpix.F90 | 43 ++++- src/remix/tgcm.F90 | 272 +++++++++++++++++++++------ src/voltron/mpi/gam2VoltComm_mpi.F90 | 12 +- src/voltron/mpi/voltapp_mpi.F90 | 13 +- src/voltron/mpi/voltmpitypes.F90 | 1 + src/voltron/voltapp.F90 | 3 +- 7 files changed, 283 insertions(+), 68 deletions(-) diff --git a/src/base/kdefs.F90 b/src/base/kdefs.F90 index 719e4087..9b6b7be6 100644 --- a/src/base/kdefs.F90 +++ b/src/base/kdefs.F90 @@ -85,6 +85,13 @@ module kdefs real(rp), parameter :: Rsolar = 6.956D5 ! [km] Solar radius real(rp), parameter :: Msolar = 1.98847D30 ! [kg] Solar mass +!MPI Communicator Id + integer, parameter :: voltId = 116 + integer, parameter :: gamId = 45 + integer, parameter :: rcmId = 34 + integer, parameter :: tgcmId = 57 + integer, parameter :: hidraId = 40 + !Numbered accessors !Directions enum, bind(C) diff --git a/src/drivers/voltron_mpix.F90 b/src/drivers/voltron_mpix.F90 index cba304df..aa0c12c6 100644 --- a/src/drivers/voltron_mpix.F90 +++ b/src/drivers/voltron_mpix.F90 @@ -33,6 +33,7 @@ program voltron_mpix logical :: useHelpers, helperQuit integer(KIND=MPI_AN_MYADDR) :: tagMax logical :: tagSet + integer :: divideSize ! initialize MPI !Set up MPI with or without thread support @@ -89,7 +90,7 @@ program voltron_mpix if(worldRank .ge. (worldSize-1-numHelpers)) then ! voltron rank isGamera = .false. - call MPI_Comm_Split(MPI_COMM_WORLD, 1, worldRank, voltComm, ierror) + call MPI_Comm_Split(MPI_COMM_WORLD, voltId, worldRank, voltComm, ierror) if(ierror /= MPI_Success) then call MPI_Error_string( ierror, message, length, ierror) print *,message(1:length) @@ -98,7 +99,7 @@ program voltron_mpix else ! gamera rank isGamera = .true. - call MPI_Comm_Split(MPI_COMM_WORLD, 0, worldRank, gamComm, ierror) + call MPI_Comm_Split(MPI_COMM_WORLD, gamId, worldRank, gamComm, ierror) if(ierror /= MPI_Success) then call MPI_Error_string( ierror, message, length, ierror) print *,message(1:length) @@ -106,10 +107,46 @@ program voltron_mpix end if endif + if (worldRank .eq. worldSize-1-numHelpers) then + call MPI_Comm_Split(MPI_COMM_WORLD, tgcmId+voltId, 0, vApp%gcmCplComm, ierror) + call MPI_Comm_Rank(vApp%gcmCplComm, vApp%gcmCplRank, ierror) + call MPI_Comm_Size(vApp%gcmCplComm, divideSize, ierror) + if(vApp%gcmCplComm /= MPI_COMM_NULL) then + print *,'VOLTRON has created an gcmCplComm with ', divideSize-1, ' other app(s)' + print *,'VOLTRON using gcmCplComm tag ', tgcmId+voltId + write(*,*) "VOLTRON CPLCOMM: ",vApp%gcmCplComm,vApp%gcmCplRank + + ! This figures out what rank GCM should be on + if (vApp%gcmCplRank == 0) then + vApp%gcmCplRank = 1 + else + vApp%gcmCplRank = 0 + endif + + endif + if(divideSize == 1) then + write(*,*) "MIX: We're not coupling" + call mpi_comm_free(vApp%gcmCplComm, ierror) + if(ierror /= MPI_Success) then + call MPI_Error_string( ierror, message, length, ierror) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierror) + end if + vApp%gcmCplComm = MPI_COMM_NULL + endif + else + call MPI_Comm_Split(MPI_COMM_WORLD, MPI_UNDEFINED, worldRank, vApp%gcmCplComm, ierror) + endif + if(ierror /= MPI_Success) then + call MPI_Error_string( ierror, message, length, ierror) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierror) + end if + if(isGamera) then call Tic("Omega") call initGamera_mpi(gApp,userInitFunc,gamComm,doIO=.false.) - call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD) + call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD,gamId=(gamId+voltId)) call Toc("Omega") do while (g2vComm%time < g2vComm%tFin) diff --git a/src/remix/tgcm.F90 b/src/remix/tgcm.F90 index 722039ee..b643f7b2 100644 --- a/src/remix/tgcm.F90 +++ b/src/remix/tgcm.F90 @@ -13,7 +13,8 @@ module gcminterp type(mixGrid_T),allocatable,private :: gcmG character(len=strLen), dimension(nVars), private :: mixVarNames - character(len=strLen), dimension(nVars), private :: mixUnitNames + character(len=strLen), dimension(nVars), private :: mixUnitNames + real(rp), dimension(:,:,:), allocatable, private :: invar2d,outvar2d interface exportgcm module procedure exportgcmfile, exportgcmmpi @@ -24,7 +25,7 @@ module gcminterp contains - subroutine init_gcm(gcm,ion,isRestart) + subroutine init_gcm_file(gcm,ion,isRestart) type(gcm_T),intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion logical, intent(in) :: isRestart @@ -39,10 +40,28 @@ module gcminterp gcm%isRestart = isRestart - call init_gcm_mix(gcm,ion) + call init_gcm_mix_file(gcm,ion) + end subroutine init_gcm_file + + subroutine init_gcm_mpi(gcm,ion,isRestart) + type(gcm_T),intent(inout) :: gcm + type(mixIon_T),dimension(:),intent(inout) :: ion + logical, intent(in) :: isRestart + + write(*,*) "start init_gcm" + + if (.not. allocated(gcm%outlist)) allocate(gcm%outlist(mix2gcm_nvar)) + gcm%outlist(1) = POT + gcm%outlist(2) = AVG_ENG + gcm%outlist(3) = NUM_FLUX + call initGCMNames() + + gcm%isRestart = isRestart + + call init_gcm_grid(gcm,ion) - end subroutine init_gcm + end subroutine init_gcm_mpi subroutine initGCMNames() ! NOTE: these have to be in the same order as the @@ -100,7 +119,7 @@ module gcminterp mixUnitNames(IM_INFLX) = "1/cm^2 s" end subroutine initGCMNames - subroutine init_gcm_mix(gcm,ion)!,remixApp + subroutine init_gcm_mix_file(gcm,ion)!,remixApp type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion !type(mixApp_T), intent(in) :: remixApp @@ -225,9 +244,9 @@ module gcminterp if (.not. allocated(gcm%gcmOutput(h,v)%var)) allocate(gcm%gcmOutput(h,v)%var(gcm%nlon,gcm%nhlat)) end do enddo - end subroutine init_gcm_mix + end subroutine init_gcm_mix_file - subroutine coupleGCM2MIX(gcm,ion,do2way,mjd,time,gcmCplComm,myRank) + subroutine coupleGCM2MIX(gcm,ion,mjd,time,gcmCplComm,myRank) use mpi_f08 type(mixIon_T),dimension(:),intent(inout) :: ion type(gcm_T), intent(inout) :: gcm @@ -241,10 +260,18 @@ module gcminterp ! transform all remix points to GEO ! transform all gcm points to SM - + !Skip MPI exchange on first restart + if (present(gcmCplComm) .and. gcm%isRestart) then + gcm%isRestart = .False. + return + endif !Must MIX export first. TIEGCM will also import first. call Tic("Export") if (present(gcmCplComm)) then + if (gcm%isRestart) then + gcm%isRestart = .False. + return + endif call exportgcm(ion,gcm,mjd,time,gcmCplComm,myRank) else call exportgcm(ion,gcm,mjd,time) @@ -322,43 +349,163 @@ module gcminterp end subroutine exportgcmfile - subroutine importgcmmpi(gcm,ion, gcmCplComm,myRank) +! +!--------------------- START MPI ROUTINES HERE --------------------- +! + + subroutine init_gcm_mix_mpi(gcm,gcmCplComm,gcmCplRank)!,remixApp + use mpi_f08 + type(gcm_T), intent(inout) :: gcm + type(MPI_Comm) :: gcmCplComm + integer, intent(in) :: gcmCplRank + + integer :: nlon,nlat,Nh,ierr + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !Find grid dimension + + write(*,*) "MIXCPL Waiting for GCM Grid" + + call mpi_recv(nlat, 1, MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100+1, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT NLAT: ",nlat + + call mpi_recv(nlon, 1, MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100+2, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT NLON: ",nlon + + Nh = GCMhemispheres + + if (.not.allocated(gcm%lat)) allocate(gcm%lat(nlat)) + if (.not.allocated(gcm%lon)) allocate(gcm%lon(nlon)) + + + call mpi_recv(gcm%lat, nlat, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+3, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT GLAT: ",gcm%lat + call mpi_recv(gcm%lon, nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+4, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT GLON: ",gcm%lon + + write(*,*) "MIXCPL GOT GRID INFO: ",nlat, nlon + + !Save dimension information + gcm%nlon = nlon + gcm%nlat = nlat + gcm%nhemi = Nh + + end subroutine init_gcm_mix_mpi + + subroutine init_gcm_grid(gcm,ion) + type(gcm_T), intent(inout) :: gcm + type(mixIon_T),dimension(:),intent(in) :: ion + + integer :: Np, Nt, Nh, i,j,h,v + Np = gcm%nlon + Nt = gcm%nhlat + Nh = gcm%nhemi + + ! Let's try something + ! Build a list of index that says these are N and these are S + ! Then use that to map to N/S in gcm + !indicesN = merge( 0, [ ( i, i = 1, size( gcm%lat ) ) ], gcm%lat < 0 ) + !indicesS = merge( 0, [ ( i, i = 1, size( gcm%lat ) ) ], gcm%lat > 0 ) + !nhlatN = count(indicesN) + !nhlatS = count(indicesS) + !if ( .not. allocated(gcm%t2N) ) allocate(gcm%t2N(nhlatN)) + !if ( .not. allocated(gcm%t2S) ) allocate(gcm%t2S(nhlatS)) + !gcm%t2N = pack( indicesN, indicesN /= 0 ) + !gcm%t2S = pack( indicesS, indicesS /= 0 ) + gcm%t2N = pack([(i,i=1,gcm%nlat)], gcm%lat > 10) + gcm%t2S = pack([(i,i=1,gcm%nlat)], gcm%lat < -10) + gcm%lonshift = findloc(gcm%lon(:gcm%nlon-1),0.,1)-1 + + ! Going to assume the hemispheres are symetrically sized for now + if ( size(gcm%t2N) /= size(gcm%t2S) ) write(*,*) 'WE GOT ASYMMETRY' + gcm%nhlat = size(gcm%t2N) + + if (.not.allocated(gcm%gclat)) allocate(gcm%gclat(gcm%nlon,gcm%nhlat,Nh)) + if (.not.allocated(gcm%glon)) allocate(gcm%glon(gcm%nlon,gcm%nhlat,Nh)) + + ! Convert things from latitude to colatitude (and funky colat for south) + do i=1,gcm%nlon + gcm%gclat(i,:,GCMNORTH) = 90.-gcm%lat(gcm%t2N) + gcm%gclat(i,:,GCMNORTH) = gcm%gclat(i,gcm%nhlat:1:-1,GCMNORTH) ! reverse order. Ascending. + gcm%gclat(i,:,GCMSOUTH) = 90.+gcm%lat(gcm%t2S) !For mapping to work, we're going to use remix's funky southern colat + enddo + + ! This complicated looking thing converts [-180,180] longitude into [0,360] and also shifts it so that longitude starts at 0 + do j=1,gcm%nhlat + gcm%glon(:gcm%nlon-1,j,GCMNORTH) = modulo(cshift(gcm%lon(:gcm%nlon-1),gcm%lonshift)+360.,360.)!modulo((atan2(G2%y,G2%x)+2*pi),(2*pi)) + gcm%glon(:gcm%nlon-1,j,GCMSOUTH) = modulo(cshift(gcm%lon(:gcm%nlon-1),gcm%lonshift)+360.,360.) + ! cover periodic longitude point + gcm%glon(gcm%nlon,j,GCMNORTH) = 360. ! no matter what, last point is periodic point + gcm%glon(gcm%nlon,j,GCMSOUTH) = 360. ! hard coding the value of last point for now + enddo + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + !Now do remix mapping + if (.not.allocated(gcm%GEO)) allocate(gcm%GEO(Nh)) + if (.not.allocated(gcm%SM)) allocate(gcm%SM(Nh)) + if (.not.allocated(gcm%r2tMaps)) allocate(gcm%r2tMaps(Nh)) + if (.not.allocated(gcm%t2rMaps)) allocate(gcm%t2rMaps(Nh)) + + call init_grid_fromTP(gcm%GEO(GCMNORTH),gcm%gclat(:,:,GCMNORTH)*deg2rad,gcm%glon(:,:,GCMNORTH)*deg2rad,isSolverGrid=.true.) + call init_grid_fromTP(gcm%GEO(GCMSOUTH),gcm%gclat(:,:,GCMSOUTH)*deg2rad,gcm%glon(:,:,GCMSOUTH)*deg2rad,isSolverGrid=.true.) + + do h=1,Nh + do v=1,gcm2mix_nvar + if (.not.allocated(gcm%mixInput(h,v)%var)) allocate(gcm%mixInput(h,v)%var(ion(h)%G%Np,ion(h)%G%Nt)) + if (.not.allocated(gcm%gcmInput(h,v)%var)) allocate(gcm%gcmInput(h,v)%var(gcm%nlon,gcm%nhlat)) + end do + do v=1,mix2gcm_nvar + if (.not. allocated(gcm%gcmOutput(h,v)%var)) allocate(gcm%gcmOutput(h,v)%var(gcm%nlon,gcm%nhlat)) + end do + enddo + end subroutine init_gcm_grid + + subroutine importgcmmpi(gcm,ion, gcmCplComm,gcmCplRank) use mpi_f08 type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion type(MPI_Comm) :: gcmCplComm - integer, intent(in) :: myRank + integer, intent(in) :: gcmCplRank - real(rp), dimension(:,:,:), allocatable :: var2d - integer :: v,i,h,ymod1,ierr - - if (.not. allocated(var2d)) allocate(var2d(gcm%nlat,gcm%nlon,gcm2mix_nvar)) + integer :: v,i,h,ymod1,ierr,length + character( len = MPI_MAX_ERROR_STRING) :: message call Tic("MpiExchange") + write(*,*) "MIX: IMPORT GCM" - call mpi_send(testArray, 10*worldSize, MPI_DOUBLE_PRECISION, 1, 1001, interComm, ierror) - if(ierror /= MPI_Success) then - call MPI_Error_string( ierror, message, length, ierror) - print *,message(1:length) - call mpi_Abort(MPI_COMM_WORLD, 1, ierror) - end if - - ! Split global GCM array into two hemispheres - ! then shift the array so that longitude starts at 0 - do v=1,gcm2mix_nvar - do i=1,gcm%nhlat - gcm%gcmInput(GCMNORTH,v)%var(:gcm%nlon-1,i) = cshift(var2d(gcm%t2N(gcm%nhlat-i+1),:gcm%nlon-1,v),gcm%lonshift) - gcm%gcmInput(GCMSOUTH,v)%var(:gcm%nlon-1,i) = cshift(var2d(gcm%t2S(i),:gcm%nlon-1,v),gcm%lonshift) - gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) - gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) + if (gcmCplComm /= MPI_COMM_NULL) then + call Tic("Passing") + if (.not. allocated(invar2d)) allocate(invar2d(gcm%nlat,gcm%nlon,gcm2mix_nvar)) + do v=1,gcm2mix_nvar + call mpi_recv(invar2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+v, gcmCplComm, MPI_STATUS_IGNORE,ierr) + if(ierr /= MPI_Success) then + call MPI_Error_string( ierr, message, length, ierr) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierr) + end if enddo - !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) - !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v - !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v - !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v - end do - if (allocated(var2d)) deallocate(var2d) + call Toc("Passing") + call Tic("Shifting") + ! Split global GCM array into two hemispheres + ! then shift the array so that longitude starts at 0 + do v=1,gcm2mix_nvar + do i=1,gcm%nhlat + gcm%gcmInput(GCMNORTH,v)%var(:gcm%nlon-1,i) = cshift(invar2d(gcm%t2N(gcm%nhlat-i+1),:gcm%nlon-1,v),gcm%lonshift) + gcm%gcmInput(GCMSOUTH,v)%var(:gcm%nlon-1,i) = cshift(invar2d(gcm%t2S(i),:gcm%nlon-1,v),gcm%lonshift) + gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) + gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) + enddo + !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) + !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v + !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v + !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v + end do + call Toc("Shifting") + endif call Toc("MpiExchange") ! The weird ymod here is to undo the funky southern hemisphere colat issue. @@ -379,16 +526,19 @@ module gcminterp end subroutine importgcmmpi - subroutine exportgcmmpi(ion,gcm,mjd,time,gcmCplComm,myRank) + subroutine exportgcmmpi(ion,gcm,mjd,time,gcmCplComm,gcmCplRank) use mpi_f08 type(gcm_T), intent(inout) :: gcm type(mixIon_T), dimension(:),intent(inout) :: ion real(rp), intent(in) :: time, mjd type(MPI_Comm) :: gcmCplComm - integer, intent(in) :: myRank + integer, intent(in) :: gcmCplRank + integer :: h,ymod2,v,ierr,length character( len = MPI_MAX_ERROR_STRING) :: message + write(*,*) "MIX: EXPORT GCM" + ! The weird ymod here is to undo the funky southern hemisphere colat issue. do h=1,gcm%nhemi if (h == GCMSOUTH) then @@ -405,27 +555,37 @@ module gcminterp ! Map from mix grid to gcm grid call mapMIX2GCM(ion,gcm) - ! Prepare the export data - if (.not. allocated(var2d)) allocate(var2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) - var2d = 0. - ! Before we start, we collapse to 1 globe instead of 2 hemispheres - do v=1,mix2gcm_nvar - var2d(gcm%t2N(gcm%nhlat:1:-1),:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMNORTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) - var2d(gcm%t2S,:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMSOUTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) - var2d(gcm%t2N,gcm%nlon,v) = var2d(gcm%t2N,1,v) - var2d(gcm%t2S,gcm%nlon,v) = var2d(gcm%t2S,1,v) - end do + if (gcmCplComm /= MPI_COMM_NULL) then - ! Send the coupling data - do v=1,gcm2mix_nvar - call mpi_send(var2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, 1, 1000+v, gcmCplComm, ierr) - if(ierr /= MPI_Success) then - call MPI_Error_string( ierr, message, length, ierr) - print *,message(1:length) - call mpi_Abort(MPI_COMM_WORLD, 1, ierr) - end if - enddo + v = 22 + write(*,*) "MIXCPL: SENDING SMALL NUMBER:",v + call mpi_send(v,1,MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100, gcmCplComm, ierr) + write(*,*) "MIXCPL: SENDING SMALL NUMBER: DONE ", ierr + + ! Prepare the export data + if (.not. allocated(outvar2d)) allocate(outvar2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) + outvar2d = 0. + ! Before we start, we collapse to 1 globe instead of 2 hemispheres + do v=1,mix2gcm_nvar + outvar2d(gcm%t2N(gcm%nhlat:1:-1),:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMNORTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) + outvar2d(gcm%t2S,:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMSOUTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) + outvar2d(gcm%t2N,gcm%nlon,v) = outvar2d(gcm%t2N,1,v) + outvar2d(gcm%t2S,gcm%nlon,v) = outvar2d(gcm%t2S,1,v) + end do + ! Send the coupling data + do v=1,mix2gcm_nvar + write(*,*) " MIXCPL: ", gcmCplRank,(tgcmId+voltId)*100+v,gcmCplComm,gcm%nlat,gcm%nlon + call mpi_send(outvar2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+v, gcmCplComm, ierr) + if(ierr /= MPI_Success) then + call MPI_Error_string( ierr, message, length, ierr) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierr) + end if + enddo + + endif + end subroutine exportgcmmpi subroutine ReadH5gcm(gcm) diff --git a/src/voltron/mpi/gam2VoltComm_mpi.F90 b/src/voltron/mpi/gam2VoltComm_mpi.F90 index d7148306..1b1dc759 100644 --- a/src/voltron/mpi/gam2VoltComm_mpi.F90 +++ b/src/voltron/mpi/gam2VoltComm_mpi.F90 @@ -48,14 +48,16 @@ module gam2VoltComm_mpi contains ! setup the MPI communicator to talk to voltron, and send grid data - subroutine initGam2Volt(g2vComm, gApp, allComm, optFilename, doIO) + subroutine initGam2Volt(g2vComm, gApp, allComm, optFilename, doIO,gamId) type(gam2VoltCommMpi_T), intent(inout) :: g2vComm type(gamAppMpi_T), intent(inout) :: gApp type(MPI_Comm), intent(in) :: allComm character(len=*), optional, intent(in) :: optFilename logical, optional, intent(in) :: doIO + integer, optional, intent(in) :: gamId integer :: length, commSize, ierr, numCells, dataCount, numInNeighbors, numOutNeighbors + integer :: commId type(MPI_Comm) :: voltComm character( len = MPI_MAX_ERROR_STRING) :: message logical :: reorder, wasWeighted, doIOX @@ -63,6 +65,12 @@ module gam2VoltComm_mpi type(XML_Input_T) :: xmlInp integer, dimension(1) :: rankArray, weightArray + if (present(gamId)) then + commId = gamId + else + commId = 0 + endif + ! initialize F08 MPI objects g2vComm%voltMpiComm = MPI_COMM_NULL g2vComm%zeroArraytypes = (/ MPI_DATATYPE_NULL /) @@ -70,7 +78,7 @@ module gam2VoltComm_mpi ! split voltron helpers off of the communicator ! split allComm into a communicator with only the non-helper voltron rank call MPI_Comm_rank(allComm, commSize, ierr) - call MPI_comm_split(allComm, 0, commSize, voltComm, ierr) + call MPI_comm_split(allComm, commId, commSize, voltComm, ierr) if(present(optFilename)) then ! read from the prescribed file diff --git a/src/voltron/mpi/voltapp_mpi.F90 b/src/voltron/mpi/voltapp_mpi.F90 index eb604997..5d92d186 100644 --- a/src/voltron/mpi/voltapp_mpi.F90 +++ b/src/voltron/mpi/voltapp_mpi.F90 @@ -63,7 +63,7 @@ module voltapp_mpi type(XML_Input_T) :: xmlInp integer :: commSize, ierr, numCells, length, ic, numInNeighbors, numOutNeighbors type(MPI_Comm) :: voltComm - integer :: nHelpers, gamNRES + integer :: nHelpers, gamNRES, commId character( len = MPI_MAX_ERROR_STRING) :: message logical :: reorder, wasWeighted integer, allocatable, dimension(:) :: neighborRanks, inData, outData @@ -95,10 +95,11 @@ module voltapp_mpi ! split allComm into a communicator with only the non-helper voltron rank call MPI_Comm_rank(allComm, commSize, ierr) + commId = gamId+voltId if(vApp%amHelper) then call MPI_comm_split(allComm, MPI_UNDEFINED, commSize, voltComm, ierr) else - call MPI_comm_split(allComm, 0, commSize, voltComm, ierr) + call MPI_comm_split(allComm, commId, commSize, voltComm, ierr) endif ! helpers don't do full voltron initialization @@ -319,6 +320,9 @@ module voltapp_mpi vApp%gAppLocal%oState,vAPp%gApplocal%State,xmlInp,userInitFunc) ! now initialize basic voltron structures from gamera data + if(vApp%gcmCplComm /= MPI_COMM_NULL) then + call init_gcm_mix_mpi(vApp%gcm,vApp%gcmCplComm,vApp%gcmCplRank) + endif if(present(optFilename)) then call initVoltron(vApp, vApp%gAppLocal, optFilename) else @@ -518,7 +522,7 @@ module voltapp_mpi if (vApp%doGCM .and. vApp%time >=0) then call Tic("GCM2MIX") - call coupleGCM2MIX(vApp%gcm,vApp%remixApp%ion,vApp%MJD,vApp%time,vApp%gcmCplComm,vApp%myRank) + call coupleGCM2MIX(vApp%gcm,vApp%remixApp%ion,vApp%MJD,vApp%time,vApp%gcmCplComm,vApp%gcmCplRank) call Toc("GCM2MIX") end if @@ -1021,8 +1025,5 @@ module voltapp_mpi end subroutine - subroutine exportGCM2MIX(vApp) - type - end module voltapp_mpi diff --git a/src/voltron/mpi/voltmpitypes.F90 b/src/voltron/mpi/voltmpitypes.F90 index 53b9c2dc..9181817d 100644 --- a/src/voltron/mpi/voltmpitypes.F90 +++ b/src/voltron/mpi/voltmpitypes.F90 @@ -27,6 +27,7 @@ module voltmpitypes ! coupling comms variables to be done on volt rank type(MPI_Comm) :: gcmCplComm + integer :: gcmCplRank ! array of all zeroes to simplify various send/receive calls integer, dimension(:), allocatable :: zeroArrayCounts diff --git a/src/voltron/voltapp.F90 b/src/voltron/voltapp.F90 index 97b5f6e7..95a067b4 100644 --- a/src/voltron/voltapp.F90 +++ b/src/voltron/voltapp.F90 @@ -355,7 +355,8 @@ module voltapp vApp%remixApp%ion%rad_iono_m = vApp%planet%ri_m if (vApp%doGCM) then write(*,*) "Initializing GCM ..." - call init_gcm(vApp%gcm,vApp%remixApp%ion,gApp%Model%isRestart) + !call init_gcm_file(vApp%gcm,vApp%remixApp%ion,gApp%Model%isRestart) + call init_gcm_mpi(vApp%gcm,vApp%remixApp%ion,gApp%Model%isRestart) end if !Ensure remix and voltron restart numbers match if (isRestart .and. vApp%IO%nRes /= vApp%remixApp%ion(1)%P%nRes) then From 61ae89427fc2b019e0e74cd873a16dc9cce13d89 Mon Sep 17 00:00:00 2001 From: phamkh Date: Thu, 10 Aug 2023 12:15:14 -0600 Subject: [PATCH 017/365] Fixed hanging bug --- src/drivers/voltron_mpix.F90 | 3 ++- src/voltron/mpi/voltapp_mpi.F90 | 9 ++++++++- src/voltron/voltapp.F90 | 5 ----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/drivers/voltron_mpix.F90 b/src/drivers/voltron_mpix.F90 index aa0c12c6..23834bad 100644 --- a/src/drivers/voltron_mpix.F90 +++ b/src/drivers/voltron_mpix.F90 @@ -146,7 +146,8 @@ program voltron_mpix if(isGamera) then call Tic("Omega") call initGamera_mpi(gApp,userInitFunc,gamComm,doIO=.false.) - call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD,gamId=(gamId+voltId)) + !call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD,gamId=(gamId+voltId)) + call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD,gamId=gamId) call Toc("Omega") do while (g2vComm%time < g2vComm%tFin) diff --git a/src/voltron/mpi/voltapp_mpi.F90 b/src/voltron/mpi/voltapp_mpi.F90 index 5d92d186..463458a1 100644 --- a/src/voltron/mpi/voltapp_mpi.F90 +++ b/src/voltron/mpi/voltapp_mpi.F90 @@ -95,7 +95,8 @@ module voltapp_mpi ! split allComm into a communicator with only the non-helper voltron rank call MPI_Comm_rank(allComm, commSize, ierr) - commId = gamId+voltId + !commId = gamId+voltId + commId = gamId if(vApp%amHelper) then call MPI_comm_split(allComm, MPI_UNDEFINED, commSize, voltComm, ierr) else @@ -329,6 +330,12 @@ module voltapp_mpi call initVoltron(vApp, vApp%gAppLocal) endif + if ( ( vApp%doGCM ) .and. (vApp%gcmCplComm /= MPI_COMM_NULL) ) then + write(*,*) "Initializing GCM ..." + !call init_gcm_file(vApp%gcm,vApp%remixApp%ion,gApp%Model%isRestart) + call init_gcm_mpi(vApp%gcm,vApp%remixApp%ion,vApp%gAppLocal%Model%isRestart) + end if + ! Receive Gamera's restart number and ensure Voltron has the same restart number call mpi_recv(gamNRES, 1, MPI_INTEGER, MPI_ANY_SOURCE, 97520, vApp%voltMpiComm, MPI_STATUS_IGNORE, ierr) if (vApp%gAppLocal%Model%isRestart .and. vApp%IO%nRes /= gamNRES) then diff --git a/src/voltron/voltapp.F90 b/src/voltron/voltapp.F90 index 95a067b4..2872f6cd 100644 --- a/src/voltron/voltapp.F90 +++ b/src/voltron/voltapp.F90 @@ -353,11 +353,6 @@ module voltapp endif vApp%remixApp%ion%rad_iono_m = vApp%planet%ri_m - if (vApp%doGCM) then - write(*,*) "Initializing GCM ..." - !call init_gcm_file(vApp%gcm,vApp%remixApp%ion,gApp%Model%isRestart) - call init_gcm_mpi(vApp%gcm,vApp%remixApp%ion,gApp%Model%isRestart) - end if !Ensure remix and voltron restart numbers match if (isRestart .and. vApp%IO%nRes /= vApp%remixApp%ion(1)%P%nRes) then write(*,*) "Voltron and Remix disagree on restart number, you should sort that out." From a442316bafabf9970d36f2c9f845018794a889e7 Mon Sep 17 00:00:00 2001 From: phamkh Date: Sun, 13 Aug 2023 23:58:25 -0600 Subject: [PATCH 018/365] Refactored GCM MPI into separate module --- src/base/types/gcmtypes.F90 | 1 + src/remix/tgcm.F90 | 243 ++++---------------------------- src/voltron/mpi/gcm_mpi.F90 | 218 ++++++++++++++++++++++++++++ src/voltron/mpi/voltapp_mpi.F90 | 3 +- 4 files changed, 247 insertions(+), 218 deletions(-) create mode 100644 src/voltron/mpi/gcm_mpi.F90 diff --git a/src/base/types/gcmtypes.F90 b/src/base/types/gcmtypes.F90 index 29d105e2..432af041 100644 --- a/src/base/types/gcmtypes.F90 +++ b/src/base/types/gcmtypes.F90 @@ -29,6 +29,7 @@ module gcmtypes real(rp), dimension(:), allocatable :: time,lev,lon,clat,lat real(rp),dimension(:,:),allocatable :: gx,gy real(rp),dimension(:,:,:),allocatable :: glon,gclat + real(rp), dimension(:,:,:), allocatable :: invar2d, outvar2d integer :: cplStep = 1 !character(len=strlen) :: mix2gcmH5,gcm2mixH5,mix2gcmLock,gcm2mixLock character(len=strlen) :: mix2gcmH5 = "mix4gcm.h5" diff --git a/src/remix/tgcm.F90 b/src/remix/tgcm.F90 index b643f7b2..e79851bc 100644 --- a/src/remix/tgcm.F90 +++ b/src/remix/tgcm.F90 @@ -14,13 +14,12 @@ module gcminterp type(mixGrid_T),allocatable,private :: gcmG character(len=strLen), dimension(nVars), private :: mixVarNames character(len=strLen), dimension(nVars), private :: mixUnitNames - real(rp), dimension(:,:,:), allocatable, private :: invar2d,outvar2d interface exportgcm - module procedure exportgcmfile, exportgcmmpi + module procedure exportgcmfile end interface interface importgcm - module procedure importgcmfile, importgcmmpi + module procedure importgcmfile end interface contains @@ -44,25 +43,6 @@ module gcminterp end subroutine init_gcm_file - subroutine init_gcm_mpi(gcm,ion,isRestart) - type(gcm_T),intent(inout) :: gcm - type(mixIon_T),dimension(:),intent(inout) :: ion - logical, intent(in) :: isRestart - - write(*,*) "start init_gcm" - - if (.not. allocated(gcm%outlist)) allocate(gcm%outlist(mix2gcm_nvar)) - gcm%outlist(1) = POT - gcm%outlist(2) = AVG_ENG - gcm%outlist(3) = NUM_FLUX - call initGCMNames() - - gcm%isRestart = isRestart - - call init_gcm_grid(gcm,ion) - - end subroutine init_gcm_mpi - subroutine initGCMNames() ! NOTE: these have to be in the same order as the ! variable enumerator in mixdefs @@ -246,55 +226,6 @@ module gcminterp enddo end subroutine init_gcm_mix_file - subroutine coupleGCM2MIX(gcm,ion,mjd,time,gcmCplComm,myRank) - use mpi_f08 - type(mixIon_T),dimension(:),intent(inout) :: ion - type(gcm_T), intent(inout) :: gcm - real(rp), intent(in) :: time, mjd - type(MPI_Comm), optional :: gcmCplComm - integer, optional, intent(in) :: myRank - - ! maybe create the SM and GEO list of points here - ! since the destination grid does not need to be structured - ! can do a simple loop over all grid points and transform - ! transform all remix points to GEO - ! transform all gcm points to SM - - !Skip MPI exchange on first restart - if (present(gcmCplComm) .and. gcm%isRestart) then - gcm%isRestart = .False. - return - endif - !Must MIX export first. TIEGCM will also import first. - call Tic("Export") - if (present(gcmCplComm)) then - if (gcm%isRestart) then - gcm%isRestart = .False. - return - endif - call exportgcm(ion,gcm,mjd,time,gcmCplComm,myRank) - else - call exportgcm(ion,gcm,mjd,time) - endif - call Toc("Export") - - !Import gcm data - call Tic("Import") - if (present(gcmCplComm)) then - call importgcm(gcm, ion, gcmCplComm,myRank) - else - call importgcm(gcm, ion) - endif - call Toc("Import") - - if (gcm%isRestart) gcm%isRestart=.false. - ! We have a separate internal counter for coupling here. - ! This may be used later on for WACCM-X coupling which is desync from remix coupling time - ! TIEGCM coupling time is also 5s while WACCM-X will couple at 1 min default - gcm%cplStep = gcm%cplStep + 1 - - end subroutine coupleGCM2MIX - subroutine importgcmfile(gcm,ion) type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion @@ -349,49 +280,6 @@ module gcminterp end subroutine exportgcmfile -! -!--------------------- START MPI ROUTINES HERE --------------------- -! - - subroutine init_gcm_mix_mpi(gcm,gcmCplComm,gcmCplRank)!,remixApp - use mpi_f08 - type(gcm_T), intent(inout) :: gcm - type(MPI_Comm) :: gcmCplComm - integer, intent(in) :: gcmCplRank - - integer :: nlon,nlat,Nh,ierr - - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - !Find grid dimension - - write(*,*) "MIXCPL Waiting for GCM Grid" - - call mpi_recv(nlat, 1, MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100+1, gcmCplComm, MPI_STATUS_IGNORE,ierr) - write(*,*) "MIXCPL GOT NLAT: ",nlat - - call mpi_recv(nlon, 1, MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100+2, gcmCplComm, MPI_STATUS_IGNORE,ierr) - write(*,*) "MIXCPL GOT NLON: ",nlon - - Nh = GCMhemispheres - - if (.not.allocated(gcm%lat)) allocate(gcm%lat(nlat)) - if (.not.allocated(gcm%lon)) allocate(gcm%lon(nlon)) - - - call mpi_recv(gcm%lat, nlat, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+3, gcmCplComm, MPI_STATUS_IGNORE,ierr) - write(*,*) "MIXCPL GOT GLAT: ",gcm%lat - call mpi_recv(gcm%lon, nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+4, gcmCplComm, MPI_STATUS_IGNORE,ierr) - write(*,*) "MIXCPL GOT GLON: ",gcm%lon - - write(*,*) "MIXCPL GOT GRID INFO: ",nlat, nlon - - !Save dimension information - gcm%nlon = nlon - gcm%nlat = nlat - gcm%nhemi = Nh - - end subroutine init_gcm_mix_mpi subroutine init_gcm_grid(gcm,ion) type(gcm_T), intent(inout) :: gcm @@ -463,51 +351,32 @@ module gcminterp enddo end subroutine init_gcm_grid - subroutine importgcmmpi(gcm,ion, gcmCplComm,gcmCplRank) - use mpi_f08 + + subroutine process_gcmimport(gcm,ion) type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion - type(MPI_Comm) :: gcmCplComm - integer, intent(in) :: gcmCplRank + + integer :: v, i, h, ymod1 - - integer :: v,i,h,ymod1,ierr,length - character( len = MPI_MAX_ERROR_STRING) :: message - - call Tic("MpiExchange") - write(*,*) "MIX: IMPORT GCM" - - if (gcmCplComm /= MPI_COMM_NULL) then - call Tic("Passing") - if (.not. allocated(invar2d)) allocate(invar2d(gcm%nlat,gcm%nlon,gcm2mix_nvar)) - do v=1,gcm2mix_nvar - call mpi_recv(invar2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+v, gcmCplComm, MPI_STATUS_IGNORE,ierr) - if(ierr /= MPI_Success) then - call MPI_Error_string( ierr, message, length, ierr) - print *,message(1:length) - call mpi_Abort(MPI_COMM_WORLD, 1, ierr) - end if + call Tic("Processing") + call Tic("Shifting") + ! Split global GCM array into two hemispheres + ! then shift the array so that longitude starts at 0 + do v=1,gcm2mix_nvar + do i=1,gcm%nhlat + gcm%gcmInput(GCMNORTH,v)%var(:gcm%nlon-1,i) = cshift(gcm%invar2d(gcm%t2N(gcm%nhlat-i+1),:gcm%nlon-1,v),gcm%lonshift) + gcm%gcmInput(GCMSOUTH,v)%var(:gcm%nlon-1,i) = cshift(gcm%invar2d(gcm%t2S(i),:gcm%nlon-1,v),gcm%lonshift) + gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) + gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) enddo - call Toc("Passing") - call Tic("Shifting") - ! Split global GCM array into two hemispheres - ! then shift the array so that longitude starts at 0 - do v=1,gcm2mix_nvar - do i=1,gcm%nhlat - gcm%gcmInput(GCMNORTH,v)%var(:gcm%nlon-1,i) = cshift(invar2d(gcm%t2N(gcm%nhlat-i+1),:gcm%nlon-1,v),gcm%lonshift) - gcm%gcmInput(GCMSOUTH,v)%var(:gcm%nlon-1,i) = cshift(invar2d(gcm%t2S(i),:gcm%nlon-1,v),gcm%lonshift) - gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) - gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) - enddo - !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) - !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v - !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v - !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v - end do - call Toc("Shifting") - endif - call Toc("MpiExchange") + !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) + !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v + !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v + !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v + end do + call Toc("Shifting") + call Tic("Mapping") ! The weird ymod here is to undo the funky southern hemisphere colat issue. do h=1,size(ion) if (h == GCMSOUTH) then @@ -523,70 +392,10 @@ module gcminterp !Map the data to MIX grid call mapGCM2MIX(gcm,ion) + call Toc("Mapping") + call Toc("Processing") - end subroutine importgcmmpi - - subroutine exportgcmmpi(ion,gcm,mjd,time,gcmCplComm,gcmCplRank) - use mpi_f08 - type(gcm_T), intent(inout) :: gcm - type(mixIon_T), dimension(:),intent(inout) :: ion - real(rp), intent(in) :: time, mjd - type(MPI_Comm) :: gcmCplComm - integer, intent(in) :: gcmCplRank - - integer :: h,ymod2,v,ierr,length - character( len = MPI_MAX_ERROR_STRING) :: message - - write(*,*) "MIX: EXPORT GCM" - - ! The weird ymod here is to undo the funky southern hemisphere colat issue. - do h=1,gcm%nhemi - if (h == GCMSOUTH) then - ymod2 = -1 - else - ymod2 = 1 - endif - !ymod2 = 1 - !write(*,*) "GEO -> SM START:",h,ymod2 - call transform_grid(gcm%GEO(h),gcm%SM(h),iGEOtoSM,h,ym2=ymod2) - !write(*,*) "GEO -> SM END: ",h - end do - - ! Map from mix grid to gcm grid - call mapMIX2GCM(ion,gcm) - - if (gcmCplComm /= MPI_COMM_NULL) then - - v = 22 - write(*,*) "MIXCPL: SENDING SMALL NUMBER:",v - call mpi_send(v,1,MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100, gcmCplComm, ierr) - write(*,*) "MIXCPL: SENDING SMALL NUMBER: DONE ", ierr - - ! Prepare the export data - if (.not. allocated(outvar2d)) allocate(outvar2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) - outvar2d = 0. - ! Before we start, we collapse to 1 globe instead of 2 hemispheres - do v=1,mix2gcm_nvar - outvar2d(gcm%t2N(gcm%nhlat:1:-1),:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMNORTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) - outvar2d(gcm%t2S,:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMSOUTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) - outvar2d(gcm%t2N,gcm%nlon,v) = outvar2d(gcm%t2N,1,v) - outvar2d(gcm%t2S,gcm%nlon,v) = outvar2d(gcm%t2S,1,v) - end do - - ! Send the coupling data - do v=1,mix2gcm_nvar - write(*,*) " MIXCPL: ", gcmCplRank,(tgcmId+voltId)*100+v,gcmCplComm,gcm%nlat,gcm%nlon - call mpi_send(outvar2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+v, gcmCplComm, ierr) - if(ierr /= MPI_Success) then - call MPI_Error_string( ierr, message, length, ierr) - print *,message(1:length) - call mpi_Abort(MPI_COMM_WORLD, 1, ierr) - end if - enddo - - endif - - end subroutine exportgcmmpi + end subroutine process_gcmimport subroutine ReadH5gcm(gcm) type(gcm_T), intent(inout) :: gcm diff --git a/src/voltron/mpi/gcm_mpi.F90 b/src/voltron/mpi/gcm_mpi.F90 new file mode 100644 index 00000000..0ccfa672 --- /dev/null +++ b/src/voltron/mpi/gcm_mpi.F90 @@ -0,0 +1,218 @@ +module gcm_mpi + use gcminterp + use mpi_f08 + + implicit none + + interface exportgcm + module procedure exportgcmmpi + end interface + interface importgcm + module procedure importgcmmpi + end interface + +contains + +! +!--------------------- START MPI ROUTINES HERE --------------------- +! + + subroutine init_gcm_mpi(gcm,ion,isRestart) + type(gcm_T),intent(inout) :: gcm + type(mixIon_T),dimension(:),intent(inout) :: ion + logical, intent(in) :: isRestart + + write(*,*) "start init_gcm" + + if (.not. allocated(gcm%outlist)) allocate(gcm%outlist(mix2gcm_nvar)) + gcm%outlist(1) = POT + gcm%outlist(2) = AVG_ENG + gcm%outlist(3) = NUM_FLUX + call initGCMNames() + + gcm%isRestart = isRestart + + call init_gcm_grid(gcm,ion) + + end subroutine init_gcm_mpi + + subroutine init_gcm_mix_mpi(gcm,gcmCplComm,gcmCplRank) + type(gcm_T), intent(inout) :: gcm + type(MPI_Comm) :: gcmCplComm + integer, intent(in) :: gcmCplRank + + integer :: nlon,nlat,Nh,ierr + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !Find grid dimension + + write(*,*) "MIXCPL Waiting for GCM Grid" + + call mpi_recv(nlat, 1, MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100+1, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT NLAT: ",nlat + + call mpi_recv(nlon, 1, MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100+2, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT NLON: ",nlon + + Nh = GCMhemispheres + + if (.not.allocated(gcm%lat)) allocate(gcm%lat(nlat)) + if (.not.allocated(gcm%lon)) allocate(gcm%lon(nlon)) + + + call mpi_recv(gcm%lat, nlat, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+3, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT GLAT: ",gcm%lat + call mpi_recv(gcm%lon, nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+4, gcmCplComm, MPI_STATUS_IGNORE,ierr) + write(*,*) "MIXCPL GOT GLON: ",gcm%lon + + write(*,*) "MIXCPL GOT GRID INFO: ",nlat, nlon + + !Save dimension information + gcm%nlon = nlon + gcm%nlat = nlat + gcm%nhemi = Nh + + end subroutine init_gcm_mix_mpi + + subroutine coupleGCM2MIX(gcm,ion,mjd,time,gcmCplComm,myRank) + type(mixIon_T),dimension(:),intent(inout) :: ion + type(gcm_T), intent(inout) :: gcm + real(rp), intent(in) :: time, mjd + type(MPI_Comm), optional :: gcmCplComm + integer, optional, intent(in) :: myRank + + ! maybe create the SM and GEO list of points here + ! since the destination grid does not need to be structured + ! can do a simple loop over all grid points and transform + ! transform all remix points to GEO + ! transform all gcm points to SM + + !Skip MPI exchange on first restart + if (present(gcmCplComm) .and. gcm%isRestart) then + gcm%isRestart = .False. + return + endif + !Must MIX export first. TIEGCM will also import first. + call Tic("Export") + if (present(gcmCplComm)) then + if (gcm%isRestart) then + gcm%isRestart = .False. + return + endif + call exportgcm(ion,gcm,mjd,time,gcmCplComm,myRank) + else + call exportgcm(ion,gcm,mjd,time) + endif + call Toc("Export") + + !Import gcm data + call Tic("Import") + if (present(gcmCplComm)) then + call importgcm(gcm, ion, gcmCplComm,myRank) + else + call importgcm(gcm, ion) + endif + call process_gcmimport(gcm,ion) + call Toc("Import") + + if (gcm%isRestart) gcm%isRestart=.false. + ! We have a separate internal counter for coupling here. + ! This may be used later on for WACCM-X coupling which is desync from remix coupling time + ! TIEGCM coupling time is also 5s while WACCM-X will couple at 1 min default + gcm%cplStep = gcm%cplStep + 1 + + end subroutine coupleGCM2MIX + + + subroutine importgcmmpi(gcm,ion, gcmCplComm,gcmCplRank) + type(gcm_T), intent(inout) :: gcm + type(mixIon_T),dimension(:),intent(inout) :: ion + type(MPI_Comm) :: gcmCplComm + integer, intent(in) :: gcmCplRank + + integer :: v,ierr,length + character( len = MPI_MAX_ERROR_STRING) :: message + + call Tic("MpiExchange") + write(*,*) "MIX: IMPORT GCM" + + if (gcmCplComm /= MPI_COMM_NULL) then + call Tic("Passing") + if (.not. allocated(gcm%invar2d)) allocate(gcm%invar2d(gcm%nlat,gcm%nlon,gcm2mix_nvar)) + do v=1,gcm2mix_nvar + call mpi_recv(gcm%invar2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+v, gcmCplComm, MPI_STATUS_IGNORE,ierr) + if(ierr /= MPI_Success) then + call MPI_Error_string( ierr, message, length, ierr) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierr) + end if + enddo + call Toc("Passing") + + endif + call Toc("MpiExchange") + + end subroutine importgcmmpi + + subroutine exportgcmmpi(ion,gcm,mjd,time,gcmCplComm,gcmCplRank) + type(gcm_T), intent(inout) :: gcm + type(mixIon_T), dimension(:),intent(inout) :: ion + real(rp), intent(in) :: time, mjd + type(MPI_Comm) :: gcmCplComm + integer, intent(in) :: gcmCplRank + + integer :: h,ymod2,v,ierr,length + character( len = MPI_MAX_ERROR_STRING) :: message + + write(*,*) "MIX: EXPORT GCM" + + ! The weird ymod here is to undo the funky southern hemisphere colat issue. + do h=1,gcm%nhemi + if (h == GCMSOUTH) then + ymod2 = -1 + else + ymod2 = 1 + endif + !ymod2 = 1 + !write(*,*) "GEO -> SM START:",h,ymod2 + call transform_grid(gcm%GEO(h),gcm%SM(h),iGEOtoSM,h,ym2=ymod2) + !write(*,*) "GEO -> SM END: ",h + end do + + ! Map from mix grid to gcm grid + call mapMIX2GCM(ion,gcm) + + if (gcmCplComm /= MPI_COMM_NULL) then + + v = 22 + write(*,*) "MIXCPL: SENDING SMALL NUMBER:",v + call mpi_send(v,1,MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100, gcmCplComm, ierr) + write(*,*) "MIXCPL: SENDING SMALL NUMBER: DONE ", ierr + + ! Prepare the export data + if (.not. allocated(gcm%outvar2d)) allocate(gcm%outvar2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) + gcm%outvar2d = 0. + ! Before we start, we collapse to 1 globe instead of 2 hemispheres + do v=1,mix2gcm_nvar + gcm%outvar2d(gcm%t2N(gcm%nhlat:1:-1),:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMNORTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) + gcm%outvar2d(gcm%t2S,:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMSOUTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) + gcm%outvar2d(gcm%t2N,gcm%nlon,v) = gcm%outvar2d(gcm%t2N,1,v) + gcm%outvar2d(gcm%t2S,gcm%nlon,v) = gcm%outvar2d(gcm%t2S,1,v) + end do + + ! Send the coupling data + do v=1,mix2gcm_nvar + write(*,*) " MIXCPL: ", gcmCplRank,(tgcmId+voltId)*100+v,gcmCplComm,gcm%nlat,gcm%nlon + call mpi_send(gcm%outvar2d(:,:,v), gcm%nlat*gcm%nlon, MPI_DOUBLE_PRECISION, gcmCplRank, (tgcmId+voltId)*100+v, gcmCplComm, ierr) + if(ierr /= MPI_Success) then + call MPI_Error_string( ierr, message, length, ierr) + print *,message(1:length) + call mpi_Abort(MPI_COMM_WORLD, 1, ierr) + end if + enddo + + endif + + end subroutine exportgcmmpi +end module gcm_mpi diff --git a/src/voltron/mpi/voltapp_mpi.F90 b/src/voltron/mpi/voltapp_mpi.F90 index 463458a1..6750c4ce 100644 --- a/src/voltron/mpi/voltapp_mpi.F90 +++ b/src/voltron/mpi/voltapp_mpi.F90 @@ -9,6 +9,7 @@ module voltapp_mpi use ebsquish, only : SquishBlocksRemain, DoSquishBlock use, intrinsic :: ieee_arithmetic, only: IEEE_VALUE, IEEE_SIGNALING_NAN, IEEE_QUIET_NAN use volthelpers_mpi + use gcm_mpi implicit none @@ -527,7 +528,7 @@ module voltapp_mpi call convertGameraToRemix(vApp%mhd2mix, vApp%gAppLocal, vApp%remixApp) call Toc("G2R") - if (vApp%doGCM .and. vApp%time >=0) then + if (vApp%doGCM .and. vApp%time >=0 .and. vApp%gcmCplComm /= MPI_COMM_NULL) then call Tic("GCM2MIX") call coupleGCM2MIX(vApp%gcm,vApp%remixApp%ion,vApp%MJD,vApp%time,vApp%gcmCplComm,vApp%gcmCplRank) call Toc("GCM2MIX") From 0042163acdd2fcd6baa7b6b9948c9f84acc79e92 Mon Sep 17 00:00:00 2001 From: phamkh Date: Mon, 14 Aug 2023 10:02:35 -0600 Subject: [PATCH 019/365] The Big Delete --- src/remix/tgcm.F90 | 466 ------------------------------------ src/voltron/mpi/gcm_mpi.F90 | 14 +- 2 files changed, 2 insertions(+), 478 deletions(-) diff --git a/src/remix/tgcm.F90 b/src/remix/tgcm.F90 index e79851bc..8360b89a 100644 --- a/src/remix/tgcm.F90 +++ b/src/remix/tgcm.F90 @@ -11,276 +11,8 @@ module gcminterp implicit none - type(mixGrid_T),allocatable,private :: gcmG - character(len=strLen), dimension(nVars), private :: mixVarNames - character(len=strLen), dimension(nVars), private :: mixUnitNames - - interface exportgcm - module procedure exportgcmfile - end interface - interface importgcm - module procedure importgcmfile - end interface - contains - subroutine init_gcm_file(gcm,ion,isRestart) - type(gcm_T),intent(inout) :: gcm - type(mixIon_T),dimension(:),intent(inout) :: ion - logical, intent(in) :: isRestart - - write(*,*) "start init_gcm" - - if (.not. allocated(gcm%outlist)) allocate(gcm%outlist(mix2gcm_nvar)) - gcm%outlist(1) = POT - gcm%outlist(2) = AVG_ENG - gcm%outlist(3) = NUM_FLUX - call initGCMNames() - - gcm%isRestart = isRestart - - call init_gcm_mix_file(gcm,ion) - - end subroutine init_gcm_file - - subroutine initGCMNames() - ! NOTE: these have to be in the same order as the - ! variable enumerator in mixdefs - ! LAZY COPY PASTE HERE - mixVarNames(POT) = "Potential" - mixUnitNames(POT) = "kV" - mixVarNames(FAC) = "Field-aligned current" - mixUnitNames(FAC) = "muA/m**2" - mixVarNames(SIGMAP) = "Pedersen conductance" - mixUnitNames(SIGMAP) = "S" - mixVarNames(SIGMAH) = "Hall conductance" - mixUnitNames(SIGMAH) = "S" - mixVarNames(SOUND_SPEED) = "Sound speed" - mixUnitNames(SOUND_SPEED) = "cm/s" - mixVarNames(DENSITY) = "Density" - mixUnitNames(DENSITY) = "g/cm^3" - mixVarNames(AVG_ENG) = "Average energy" - mixUnitNames(AVG_ENG) = "keV" - mixVarNames(NUM_FLUX) = "Number flux" - mixUnitNames(NUM_FLUX) = "1/cm^2 s" - mixVarNames(NEUTRAL_WIND) = "Neutral wind" - mixUnitNames(NEUTRAL_WIND) = "cm/s" - mixVarNames(EFIELD) = "Electric field" - mixUnitNames(EFIELD) = "mV/m" - mixVarNames(IM_EFLUX) = "IM Energy flux" - mixUnitNames(IM_EFLUX) = "ergs/cm^2 s" - mixVarNames(IM_EAVG) = "IM average energy" - mixUnitNames(IM_EAVG) = "keV" ! add *1e-3 in rcm_mix_interface.F90 - mixVarNames(IM_IFLUX) = "IM Energy flux proton" - mixUnitNames(IM_IFLUX) = "ergs/cm^2 s" - mixVarNames(IM_IAVG) = "IM average energy proton" - mixUnitNames(IM_IAVG) = "keV" ! add *1e-3 in rcm_mix_interface.F90 - mixVarNames(Z_NFLUX) = "Zhang number flux" - mixUnitNames(Z_NFLUX) = "1/cm^2 s" - mixVarNames(Z_EAVG) = "Zhang average energy" - mixUnitNames(Z_EAVG) = "keV" - mixVarNames(CRPOT) = "Corotation Potential" - mixUnitNames(CRPOT) = "kV" - mixVarNames(TPOT) = "Total Potential" - mixUnitNames(TPOT) = "kV" - mixVarNames(IM_GTYPE) = "RCM grid type" - mixUnitNames(IM_GTYPE) = "0-1" - mixVarNames(AUR_TYPE) = "Auroral model type" - mixUnitNames(AUR_TYPE) = "Zhang Fedder RCM RCMZ" - mixVarNames(IM_BETA) = "RCM beta" - mixUnitNames(IM_BETA) = "0-1" - mixVarNames(IM_EDEN) = "RCM electron density" - mixUnitNames(IM_EDEN) = "#/m^3" - mixVarNames(IM_EPRE) = "RCM electron pressure" - mixUnitNames(IM_EPRE) = "Pa" - mixVarNames(IM_ENFLX) = "IM Number flux" - mixUnitNames(IM_ENFLX) = "1/cm^2 s" - mixVarNames(IM_INFLX) = "IM Number flux proton" - mixUnitNames(IM_INFLX) = "1/cm^2 s" - end subroutine initGCMNames - - subroutine init_gcm_mix_file(gcm,ion)!,remixApp - type(gcm_T), intent(inout) :: gcm - type(mixIon_T),dimension(:),intent(inout) :: ion - !type(mixApp_T), intent(in) :: remixApp - real(rp), dimension(:,:), allocatable :: gcmp,gcmt - integer :: i,j,h,v,Np,Nt,Nh,N,nlon,nlat,nhlatN,nhlatS - integer, allocatable :: positive_values(:) - integer, dimension(:), allocatable :: indicesN,indicesS - character (len=strLen) :: H5file, lockStr - logical :: fExist = .false., doSP = .false. - type(IOVAR_T), dimension(MAXMIXIOVAR) :: IOVars - - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !Prepare for reading - H5file = "init_"//trim(gcm%gcm2mixH5) - lockStr = "init_"//trim(gcm%gcm2mixLock) - write(*,*) "mix: waiting for ",trim(lockStr)," to be appear" - do while (.not.fExist) - inquire(file=lockStr,exist=fExist) - call sleep(1) - end do - write(*,*) "mix: ",trim(lockStr)," is here!" - - write(*,*) 'mix: Reading in gcm file: ', trim(H5file)," step: init" - call CheckFileOrDie(H5file,"Couldn't find input h5 file. Exiting...") - call ClearIO(IOVars) !Reset IO chain - - !write(*,*) 'Start scalar read' - !List variables to read - - !Scalars (need to specify integers), order doesn't matter - - !Arrays - - !1D Arrays - call AddInVar(IOVars,"Lat") - call AddInVar(IOVars,"Lon") - - !Now do actual reading - call ReadVars(IOVars,doSP,H5File) - - !Find grid dimension - N = FindIO(IOVars,"Lon") - nlon = IOVars(N)%dims(1) - N = FindIO(IOVars,"Lat") - nlat = IOVars(N)%dims(1) - Nh = GCMhemispheres - - if (.not.allocated(gcm%lat)) allocate(gcm%lat(nlat)) - if (.not.allocated(gcm%lon)) allocate(gcm%lon(nlon)) - - call IOArray1DFill(IOVars,"Lat",gcm%lat) - call IOArray1DFill(IOVars,"Lon",gcm%lon) - - !call CheckAndKill(lockStr) - - write(*,*) "mix: Done init GCM read" - - !Save dimension information - gcm%nlon = nlon - gcm%nlat = nlat - gcm%nhemi = Nh - - ! Let's try something - ! Build a list of index that says these are N and these are S - ! Then use that to map to N/S in gcm - !indicesN = merge( 0, [ ( i, i = 1, size( gcm%lat ) ) ], gcm%lat < 0 ) - !indicesS = merge( 0, [ ( i, i = 1, size( gcm%lat ) ) ], gcm%lat > 0 ) - !nhlatN = count(indicesN) - !nhlatS = count(indicesS) - !if ( .not. allocated(gcm%t2N) ) allocate(gcm%t2N(nhlatN)) - !if ( .not. allocated(gcm%t2S) ) allocate(gcm%t2S(nhlatS)) - !gcm%t2N = pack( indicesN, indicesN /= 0 ) - !gcm%t2S = pack( indicesS, indicesS /= 0 ) - gcm%t2N = pack([(i,i=1,nlat)], gcm%lat > 10) - gcm%t2S = pack([(i,i=1,nlat)], gcm%lat < -10) - gcm%lonshift = findloc(gcm%lon(:nlon-1),0.,1)-1 - - ! Going to assume the hemispheres are symetrically sized for now - if ( size(gcm%t2N) /= size(gcm%t2S) ) write(*,*) 'WE GOT ASYMMETRY' - gcm%nhlat = size(gcm%t2N) - - if (.not.allocated(gcm%gclat)) allocate(gcm%gclat(gcm%nlon,gcm%nhlat,Nh)) - if (.not.allocated(gcm%glon)) allocate(gcm%glon(gcm%nlon,gcm%nhlat,Nh)) - - ! Convert things from latitude to colatitude (and funky colat for south) - do i=1,gcm%nlon - gcm%gclat(i,:,GCMNORTH) = 90.-gcm%lat(gcm%t2N) - gcm%gclat(i,:,GCMNORTH) = gcm%gclat(i,gcm%nhlat:1:-1,GCMNORTH) ! reverse order. Ascending. - gcm%gclat(i,:,GCMSOUTH) = 90.+gcm%lat(gcm%t2S) !For mapping to work, we're going to use remix's funky southern colat - enddo - - ! This complicated looking thing converts [-180,180] longitude into [0,360] and also shifts it so that longitude starts at 0 - do j=1,gcm%nhlat - gcm%glon(:gcm%nlon-1,j,GCMNORTH) = modulo(cshift(gcm%lon(:nlon-1),gcm%lonshift)+360.,360.)!modulo((atan2(G2%y,G2%x)+2*pi),(2*pi)) - gcm%glon(:gcm%nlon-1,j,GCMSOUTH) = modulo(cshift(gcm%lon(:nlon-1),gcm%lonshift)+360.,360.) - ! cover periodic longitude point - gcm%glon(gcm%nlon,j,GCMNORTH) = 360. ! no matter what, last point is periodic point - gcm%glon(gcm%nlon,j,GCMSOUTH) = 360. ! hard coding the value of last point for now - enddo - - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - Np = gcm%nlon - Nt = gcm%nhlat - Nh = gcm%nhemi - - !Now do remix mapping - if (.not.allocated(gcm%GEO)) allocate(gcm%GEO(Nh)) - if (.not.allocated(gcm%SM)) allocate(gcm%SM(Nh)) - if (.not.allocated(gcm%r2tMaps)) allocate(gcm%r2tMaps(Nh)) - if (.not.allocated(gcm%t2rMaps)) allocate(gcm%t2rMaps(Nh)) - - call init_grid_fromTP(gcm%GEO(GCMNORTH),gcm%gclat(:,:,GCMNORTH)*deg2rad,gcm%glon(:,:,GCMNORTH)*deg2rad,isSolverGrid=.true.) - call init_grid_fromTP(gcm%GEO(GCMSOUTH),gcm%gclat(:,:,GCMSOUTH)*deg2rad,gcm%glon(:,:,GCMSOUTH)*deg2rad,isSolverGrid=.true.) - - do h=1,Nh - do v=1,gcm2mix_nvar - if (.not.allocated(gcm%mixInput(h,v)%var)) allocate(gcm%mixInput(h,v)%var(ion(h)%G%Np,ion(h)%G%Nt)) - if (.not.allocated(gcm%gcmInput(h,v)%var)) allocate(gcm%gcmInput(h,v)%var(gcm%nlon,gcm%nhlat)) - end do - do v=1,mix2gcm_nvar - if (.not. allocated(gcm%gcmOutput(h,v)%var)) allocate(gcm%gcmOutput(h,v)%var(gcm%nlon,gcm%nhlat)) - end do - enddo - end subroutine init_gcm_mix_file - - subroutine importgcmfile(gcm,ion) - type(gcm_T), intent(inout) :: gcm - type(mixIon_T),dimension(:),intent(inout) :: ion - integer :: h,ymod1 - - call Tic("Read") - call ReadH5gcm(gcm) - call Toc("Read") - - ! The weird ymod here is to undo the funky southern hemisphere colat issue. - do h=1,size(ion) - if (h == GCMSOUTH) then - ymod1 = -1 - else - ymod1 = 1 - endif - !ymod1 = 1 - !write(*,*) "SM -> GEO START:",h,ymod1 - call transform_grid(ion(h)%G,ion(h)%Ggeo,iSMtoGEO,h,ym1=ymod1) - !write(*,*) "SM -> GEO END: ",h - end do - - !Map the data to MIX grid - call mapGCM2MIX(gcm,ion) - - end subroutine importgcmfile - - subroutine exportgcmfile(ion,gcm,mjd,time) - type(gcm_T), intent(inout) :: gcm - type(mixIon_T), dimension(:),intent(inout) :: ion - real(rp), intent(in) :: time, mjd - integer :: h,ymod2 - - ! The weird ymod here is to undo the funky southern hemisphere colat issue. - do h=1,gcm%nhemi - if (h == GCMSOUTH) then - ymod2 = -1 - else - ymod2 = 1 - endif - !ymod2 = 1 - !write(*,*) "GEO -> SM START:",h,ymod2 - call transform_grid(gcm%GEO(h),gcm%SM(h),iGEOtoSM,h,ym2=ymod2) - !write(*,*) "GEO -> SM END: ",h - end do - - ! Map from mix grid to gcm grid - call mapMIX2GCM(ion,gcm) - - ! write the coupling file - call writeMIX2GCM(ion,gcm,mjd,time) - - end subroutine exportgcmfile - - subroutine init_gcm_grid(gcm,ion) type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(in) :: ion @@ -293,14 +25,6 @@ module gcminterp ! Let's try something ! Build a list of index that says these are N and these are S ! Then use that to map to N/S in gcm - !indicesN = merge( 0, [ ( i, i = 1, size( gcm%lat ) ) ], gcm%lat < 0 ) - !indicesS = merge( 0, [ ( i, i = 1, size( gcm%lat ) ) ], gcm%lat > 0 ) - !nhlatN = count(indicesN) - !nhlatS = count(indicesS) - !if ( .not. allocated(gcm%t2N) ) allocate(gcm%t2N(nhlatN)) - !if ( .not. allocated(gcm%t2S) ) allocate(gcm%t2S(nhlatS)) - !gcm%t2N = pack( indicesN, indicesN /= 0 ) - !gcm%t2S = pack( indicesS, indicesS /= 0 ) gcm%t2N = pack([(i,i=1,gcm%nlat)], gcm%lat > 10) gcm%t2S = pack([(i,i=1,gcm%nlat)], gcm%lat < -10) gcm%lonshift = findloc(gcm%lon(:gcm%nlon-1),0.,1)-1 @@ -351,7 +75,6 @@ module gcminterp enddo end subroutine init_gcm_grid - subroutine process_gcmimport(gcm,ion) type(gcm_T), intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion @@ -369,10 +92,6 @@ module gcminterp gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) enddo - !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) - !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v - !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v - !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v end do call Toc("Shifting") @@ -397,110 +116,6 @@ module gcminterp end subroutine process_gcmimport - subroutine ReadH5gcm(gcm) - type(gcm_T), intent(inout) :: gcm - type(IOVAR_T), dimension(MAXMIXIOVAR) :: IOVars - character (len=strLen) :: H5file, lockStr - integer :: ntime,nlon,nlat,nlev,nlathemi,nhemi - integer :: i,j,t,v,ordering - integer :: N,Nr,Ndim4 - real(rp), dimension(:), allocatable :: time,lev,etac - real(rp), dimension(:,:),allocatable :: x,y - real(rp), dimension(:,:,:), allocatable :: var2d - - logical :: doSP = .false. !Restarts are always double precision !doSP = Do single precision - logical :: fExist = .false. - - !Prepare for reading - call Tic("Wait4File") - H5file = gcm%gcm2mixH5 - lockStr = gcm%gcm2mixLock - write(*,*) "mix: waiting for ",trim(lockStr)," to be appear" - do while (.not.fExist) - inquire(file=lockStr,exist=fExist) - call sleep(1) - end do - write(*,*) "mix: ",trim(lockStr)," is here!" - call Toc("Wait4File") - - call Tic("Load_Arrays") - write(*,*) 'mix: Reading in gcm file: ', trim(H5file)," step: ",gcm%cplStep - call CheckFileOrDie(H5file,"Couldn't find input h5 file. Exiting...") - call ClearIO(IOVars) !Reset IO chain - - - !write(*,*) 'Start scalar read' - !List variables to read - - !Scalars (need to specify integers), order doesn't matter - - !Arrays - - !1D Arrays - call AddInVar(IOVars,"Lat") - call AddInVar(IOVars,"Lon") - - !2D Arrays - call AddInVar(IOVars,"Pedersen Conductance") - call AddInVar(IOVars,"Hall Conductance") - - !3D Arrays - - !4D Arrays - - !Now do actual reading - call ReadVars(IOVars,doSP,H5File) - - !The arrays are read in in the opposite order as listed by netcdf. - !The time array is the last dimension - if (.not. allocated(var2d)) allocate(var2d(gcm%nlat,gcm%nlon,gcm2mix_nvar)) - - !Pull Scalars - - !Pull 1D Arrays - - !Pull 2D Arrays - call IOArray2DFill(IOVars,"Pedersen Conductance",var2d(:,:,GCMSIGMAP)) - call IOArray2DFill(IOVars,"Hall Conductance",var2d(:,:,GCMSIGMAH)) - - !Pull 3D Arrays - - !Pull 4D Arrays - - ! Split global GCM array into two hemispheres - ! then shift the array so that longitude starts at 0 - do v=1,gcm2mix_nvar - do i=1,gcm%nhlat - gcm%gcmInput(GCMNORTH,v)%var(:gcm%nlon-1,i) = cshift(var2d(gcm%t2N(gcm%nhlat-i+1),:gcm%nlon-1,v),gcm%lonshift) - gcm%gcmInput(GCMSOUTH,v)%var(:gcm%nlon-1,i) = cshift(var2d(gcm%t2S(i),:gcm%nlon-1,v),gcm%lonshift) - gcm%gcmInput(GCMNORTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMNORTH,v)%var(1,i) - gcm%gcmInput(GCMSOUTH,v)%var(gcm%nlon,i) = gcm%gcmInput(GCMSOUTH,v)%var(1,i) - enddo - !write(*,*) "SHAPES: ",shape(var2d),shape(gcm%gcmInput(GCMNORTH,v)%var) - !write(*,*) "var2d: ",maxval(var2d(:,:,v)),minval(var2d(:,:,v)),v - !write(*,*) "GCMINPUT NORTH: ",maxval(gcm%gcmInput(GCMNORTH,v)%var),minval(gcm%gcmInput(GCMNORTH,v)%var),v - !write(*,*) "GCMINPUT SOUTH: ",maxval(gcm%gcmInput(GCMSOUTH,v)%var),minval(gcm%gcmInput(GCMSOUTH,v)%var),v - end do - - if (allocated(var2d)) deallocate(var2d) - call CheckAndKill(lockStr) - call Toc("Load_Arrays") - - !Noisy Diagnostics below - !write(*,*) 'Time array1: ',gcm%time - !write(*,*) 'Lon array1: ',gcm%glon(:,1)!modulo(deg2rad*gcm%glon+2*pi,(2*pi)) - !write(*,*) 'Lat array1: ',gcm%glat(1,:) - !write(*,*) 'Lat array2: ',gcm%glat(1,::-1) - !write(*,*) 'maxLat: ',maxval(gcm%glat) - !write(*,*) 'minLat: ',minval(gcm%glat) - !write(*,*) 'Colat array1: ',gcm%gcolat - !write(*,*) 'input shape1: ',shape(gcm%gcmInput(GCMNORTH,GCMSIGMAP)%var) - !write(*,*) 'input shape2: ',shape(var2d) - !write(*,*) 'SigmaP maxval: ',maxval(gcm%gcmInput(GCMNORTH,GCMSIGMAP)%var(:,:)),maxval(gcm%mixInput(GCMNORTH,GCMSIGMAP)%var(:,:)) - !write(*,*) 'SigmaH maxval: ',maxval(gcm%gcmInput(GCMNORTH,GCMSIGMAH)%var(:,:)),maxval(gcm%mixInput(GCMNORTH,GCMSIGMAH)%var(:,:)) - - end subroutine ReadH5gcm - subroutine mapGCM2MIX(gcm,ion) type(mixIon_T),dimension(:),intent(inout) :: ion type(gcm_T), intent(inout) :: gcm @@ -556,86 +171,5 @@ module gcminterp end subroutine apply_gcm2mix - subroutine writeMIX2GCM(I,gcm,mjd,time) - use mixio - type(mixIon_T),dimension(:),intent(in) :: I - type(gcm_T),intent(in) :: gcm - real(rp), optional, intent(in) :: time, mjd - character(len=strLen) :: vStr - - type(IOVAR_T), dimension(MAXMIXIOVAR) :: IOVars - - integer :: v,h,n0,cplStep - character(len=strLen) :: gStr,uStr,hStr - logical :: doDump = .true.,fExist=.true. - real(rp) :: cpcp = 0.0 - real(rp), dimension(:,:),allocatable :: xc,yc - real(rp), dimension(:,:,:),allocatable :: var2d - - character(len=strLen) :: cplStr,lockStr - - - if (.not. allocated(var2d)) allocate(var2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) - var2d = 0. - ! Before we start, we collapse to 1 globe instead of 2 hemispheres - do v=1,mix2gcm_nvar - var2d(gcm%t2N(gcm%nhlat:1:-1),:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMNORTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) - var2d(gcm%t2S,:gcm%nlon-1,v) = transpose(cshift(gcm%gcmOutput(GCMSOUTH,v)%var(:gcm%nlon-1,:),-1*gcm%lonshift)) - var2d(gcm%t2N,gcm%nlon,v) = var2d(gcm%t2N,1,v) - var2d(gcm%t2S,gcm%nlon,v) = var2d(gcm%t2S,1,v) - end do - - cplStr = gcm%mix2gcmH5 - lockStr = gcm%mix2gcmLock - cplStep = gcm%cplStep - - inquire(file=lockStr,exist=fExist) - - !write(*,*) "waiting for ",trim(lockStr)," to be disappear" - do while (fExist) - inquire(file=lockStr,exist=fExist) - call sleep(1) - end do - write(*,*) trim(lockStr)," is gone so creating ",trim(cplStr),' start coupling @ ',mjd - call CheckAndKill(cplStr) - - !Reset IO chain - call ClearIO(IOVars) - - do v=1,mix2gcm_nvar - ! NOTE: assuming initMIXNames got called before - vStr = trim(mixVarNames(gcm%outlist(v))) - uStr = trim(mixUnitNames(gcm%outlist(v))) - - call AddOutVar(IOVars,vStr,var2d(:,:,v),uStr) - enddo - - ! now add time - if (present(time)) call AddOutVar(IOVars,"time",time) - if (present(mjd)) call AddOutVar(IOVars,"MJD",mjd) - ! also add tilt - call AddOutVar(IOVars,"tilt",I(NORTH)%St%tilt) - - ! add grid info - call AddOutVar(IOVars,"lat",gcm%lat) - call AddOutVar(IOVars,"lon",gcm%lon) - - ! add cpcp - call AddOutVar(IOVars,"nCPCP",maxval(I(NORTH)%St%Vars(:,:,POT))-minval(I(NORTH)%St%Vars(:,:,POT))) - call AddOutVar(IOVars,"sCPCP",maxval(I(SOUTH)%St%Vars(:,:,POT))-minval(I(SOUTH)%St%Vars(:,:,POT))) - - !Write out the chain (to root) - write(gStr,'(A,I0)') "Step#", cplStep - call WriteVars(IOVars,.false.,cplStr,gStr) - - write(*,*) "Done making ",trim(cplStr)," so locking" - open(303,file=trim(lockStr)) - write(303,*) mjd - close(303) - write(*,*) trim(lockStr)," done, so go ahead" - - if (allocated(var2d)) deallocate(var2d) - - end subroutine writeMIX2GCM end module gcminterp diff --git a/src/voltron/mpi/gcm_mpi.F90 b/src/voltron/mpi/gcm_mpi.F90 index 0ccfa672..f714a95f 100644 --- a/src/voltron/mpi/gcm_mpi.F90 +++ b/src/voltron/mpi/gcm_mpi.F90 @@ -13,10 +13,6 @@ module gcm_mpi contains -! -!--------------------- START MPI ROUTINES HERE --------------------- -! - subroutine init_gcm_mpi(gcm,ion,isRestart) type(gcm_T),intent(inout) :: gcm type(mixIon_T),dimension(:),intent(inout) :: ion @@ -28,7 +24,6 @@ contains gcm%outlist(1) = POT gcm%outlist(2) = AVG_ENG gcm%outlist(3) = NUM_FLUX - call initGCMNames() gcm%isRestart = isRestart @@ -102,7 +97,7 @@ contains endif call exportgcm(ion,gcm,mjd,time,gcmCplComm,myRank) else - call exportgcm(ion,gcm,mjd,time) + write(*,*) "Are we trying to Export to Couple GCM?" endif call Toc("Export") @@ -111,7 +106,7 @@ contains if (present(gcmCplComm)) then call importgcm(gcm, ion, gcmCplComm,myRank) else - call importgcm(gcm, ion) + write(*,*) "Are we trying to Import to Couple GCM?" endif call process_gcmimport(gcm,ion) call Toc("Import") @@ -184,11 +179,6 @@ contains call mapMIX2GCM(ion,gcm) if (gcmCplComm /= MPI_COMM_NULL) then - - v = 22 - write(*,*) "MIXCPL: SENDING SMALL NUMBER:",v - call mpi_send(v,1,MPI_INTEGER, gcmCplRank, (tgcmId+voltId)*100, gcmCplComm, ierr) - write(*,*) "MIXCPL: SENDING SMALL NUMBER: DONE ", ierr ! Prepare the export data if (.not. allocated(gcm%outvar2d)) allocate(gcm%outvar2d(gcm%nlat,gcm%nlon,mix2gcm_nvar)) From 7f71ebe0c53e84c133eaaa667dfa4b32b2a2ab30 Mon Sep 17 00:00:00 2001 From: phamkh Date: Mon, 14 Aug 2023 12:01:03 -0600 Subject: [PATCH 020/365] No more 0 commId defaults --- scripts/legacy/mixmoviepar.py | 2 +- src/voltron/mpi/gam2VoltComm_mpi.F90 | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/scripts/legacy/mixmoviepar.py b/scripts/legacy/mixmoviepar.py index 9a4079f2..a8a8872b 100755 --- a/scripts/legacy/mixmoviepar.py +++ b/scripts/legacy/mixmoviepar.py @@ -136,7 +136,7 @@ def PlotStuff(i,remixFile,T,sIds,foundT,Time,args): #ncpus = 30 ncpus = args.ncpus -ag = ((i,remixFile,T,sIds,foundT,Time,args) for i in range(1,np.size(sorted(sIds))) ) +ag = ((i,remixFile,T,sIds,foundT,Time,args) for i in range(np.amin(sIds),np.amax(sIds)) ) print('This system has ',cpu_count(logical= False),' cpus.') ncpus = min(int(ncpus),cpu_count(logical=False)) print('We will use ',ncpus,' cpus for parallelization') diff --git a/src/voltron/mpi/gam2VoltComm_mpi.F90 b/src/voltron/mpi/gam2VoltComm_mpi.F90 index 1b1dc759..c7997cfa 100644 --- a/src/voltron/mpi/gam2VoltComm_mpi.F90 +++ b/src/voltron/mpi/gam2VoltComm_mpi.F90 @@ -48,16 +48,15 @@ module gam2VoltComm_mpi contains ! setup the MPI communicator to talk to voltron, and send grid data - subroutine initGam2Volt(g2vComm, gApp, allComm, optFilename, doIO,gamId) + subroutine initGam2Volt(g2vComm, gApp, allComm, optFilename, doIO,commId) type(gam2VoltCommMpi_T), intent(inout) :: g2vComm type(gamAppMpi_T), intent(inout) :: gApp type(MPI_Comm), intent(in) :: allComm character(len=*), optional, intent(in) :: optFilename logical, optional, intent(in) :: doIO - integer, optional, intent(in) :: gamId + integer, intent(in) :: commId integer :: length, commSize, ierr, numCells, dataCount, numInNeighbors, numOutNeighbors - integer :: commId type(MPI_Comm) :: voltComm character( len = MPI_MAX_ERROR_STRING) :: message logical :: reorder, wasWeighted, doIOX @@ -65,12 +64,6 @@ module gam2VoltComm_mpi type(XML_Input_T) :: xmlInp integer, dimension(1) :: rankArray, weightArray - if (present(gamId)) then - commId = gamId - else - commId = 0 - endif - ! initialize F08 MPI objects g2vComm%voltMpiComm = MPI_COMM_NULL g2vComm%zeroArraytypes = (/ MPI_DATATYPE_NULL /) From 71f3f91424e212b7aea03f231ad44549accfa614 Mon Sep 17 00:00:00 2001 From: phamkh Date: Wed, 16 Aug 2023 11:51:08 -0600 Subject: [PATCH 021/365] Moving mpiIDs to mpidefs --- src/base/kdefs.F90 | 7 ------- src/base/mpi/mpidefs.F90 | 7 +++++++ src/drivers/voltron_mpix.F90 | 13 +++++++++---- src/voltron/mpi/gam2VoltComm_mpi.F90 | 6 ++++-- src/voltron/mpi/gcm_mpi.F90 | 1 + src/voltron/mpi/voltapp_mpi.F90 | 3 +-- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/base/kdefs.F90 b/src/base/kdefs.F90 index 1fafb6a1..0a7fc50d 100644 --- a/src/base/kdefs.F90 +++ b/src/base/kdefs.F90 @@ -90,13 +90,6 @@ module kdefs real(rp), parameter :: Msolar = 1.98847D30 ! [kg] Solar mass real(rp), parameter :: Tsolar_synodic = 27.28 ![days] synodic Tsolar -!MPI Communicator Id - integer, parameter :: voltId = 116 - integer, parameter :: gamId = 45 - integer, parameter :: rcmId = 34 - integer, parameter :: tgcmId = 57 - integer, parameter :: hidraId = 40 - !Numbered accessors !Directions enum, bind(C) diff --git a/src/base/mpi/mpidefs.F90 b/src/base/mpi/mpidefs.F90 index d27caa02..ea1991a3 100644 --- a/src/base/mpi/mpidefs.F90 +++ b/src/base/mpi/mpidefs.F90 @@ -25,6 +25,13 @@ module mpidefs integer, parameter :: MPI_AN_MYADDR = MPI_ADDRESS_KIND ! this is the default #endif +!MPI Communicator Id + integer, parameter :: voltId = 116 + integer, parameter :: gamId = 45 + integer, parameter :: rcmId = 34 + integer, parameter :: tgcmId = 57 + integer, parameter :: hidraId = 40 + contains subroutine setMpiReal() diff --git a/src/drivers/voltron_mpix.F90 b/src/drivers/voltron_mpix.F90 index 23834bad..9e9fe2bf 100644 --- a/src/drivers/voltron_mpix.F90 +++ b/src/drivers/voltron_mpix.F90 @@ -72,6 +72,8 @@ program voltron_mpix call ReadXmlImmediate(trim(inpXML),'/Kaiju/Voltron/Helpers/numHelpers',helpersBuf,'0',.false.) read(helpersBuf,*) numHelpers if(.not. useHelpers) numHelpers = 0 + call ReadXmlImmediate(trim(inpXML),'/Kaiju/Voltron/coupling/doGCM',helpersBuf,'F',.false.) + read(helpersBuf,*) vApp%doGCM ! create a new MPI communicator for just Gamera ! for now this is always all ranks excep the last one (which is reserved for voltron) @@ -124,8 +126,8 @@ program voltron_mpix endif endif - if(divideSize == 1) then - write(*,*) "MIX: We're not coupling" + if(divideSize == 1 .and. vApp%doGCM .eq. .false.) then + write(*,*) "VOLTRON: We're not coupling to a GCM" call mpi_comm_free(vApp%gcmCplComm, ierror) if(ierror /= MPI_Success) then call MPI_Error_string( ierror, message, length, ierror) @@ -134,6 +136,10 @@ program voltron_mpix end if vApp%gcmCplComm = MPI_COMM_NULL endif + if (divideSize == 1 .and. vApp%doGCM .eq. .true.) then + write(*,*) "VOLTRON: Coupling to GCM Failed." + stop + endif else call MPI_Comm_Split(MPI_COMM_WORLD, MPI_UNDEFINED, worldRank, vApp%gcmCplComm, ierror) endif @@ -146,8 +152,7 @@ program voltron_mpix if(isGamera) then call Tic("Omega") call initGamera_mpi(gApp,userInitFunc,gamComm,doIO=.false.) - !call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD,gamId=(gamId+voltId)) - call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD,gamId=gamId) + call initGam2Volt(g2vComm,gApp,MPI_COMM_WORLD) call Toc("Omega") do while (g2vComm%time < g2vComm%tFin) diff --git a/src/voltron/mpi/gam2VoltComm_mpi.F90 b/src/voltron/mpi/gam2VoltComm_mpi.F90 index c7997cfa..d066b098 100644 --- a/src/voltron/mpi/gam2VoltComm_mpi.F90 +++ b/src/voltron/mpi/gam2VoltComm_mpi.F90 @@ -48,15 +48,15 @@ module gam2VoltComm_mpi contains ! setup the MPI communicator to talk to voltron, and send grid data - subroutine initGam2Volt(g2vComm, gApp, allComm, optFilename, doIO,commId) + subroutine initGam2Volt(g2vComm, gApp, allComm, optFilename, doIO) type(gam2VoltCommMpi_T), intent(inout) :: g2vComm type(gamAppMpi_T), intent(inout) :: gApp type(MPI_Comm), intent(in) :: allComm character(len=*), optional, intent(in) :: optFilename logical, optional, intent(in) :: doIO - integer, intent(in) :: commId integer :: length, commSize, ierr, numCells, dataCount, numInNeighbors, numOutNeighbors + integer :: commId type(MPI_Comm) :: voltComm character( len = MPI_MAX_ERROR_STRING) :: message logical :: reorder, wasWeighted, doIOX @@ -64,6 +64,8 @@ module gam2VoltComm_mpi type(XML_Input_T) :: xmlInp integer, dimension(1) :: rankArray, weightArray + commId = gamId + voltId + ! initialize F08 MPI objects g2vComm%voltMpiComm = MPI_COMM_NULL g2vComm%zeroArraytypes = (/ MPI_DATATYPE_NULL /) diff --git a/src/voltron/mpi/gcm_mpi.F90 b/src/voltron/mpi/gcm_mpi.F90 index f714a95f..404b12fc 100644 --- a/src/voltron/mpi/gcm_mpi.F90 +++ b/src/voltron/mpi/gcm_mpi.F90 @@ -1,6 +1,7 @@ module gcm_mpi use gcminterp use mpi_f08 + use mpidefs implicit none diff --git a/src/voltron/mpi/voltapp_mpi.F90 b/src/voltron/mpi/voltapp_mpi.F90 index 6750c4ce..5ffb10e1 100644 --- a/src/voltron/mpi/voltapp_mpi.F90 +++ b/src/voltron/mpi/voltapp_mpi.F90 @@ -96,8 +96,7 @@ module voltapp_mpi ! split allComm into a communicator with only the non-helper voltron rank call MPI_Comm_rank(allComm, commSize, ierr) - !commId = gamId+voltId - commId = gamId + commId = gamId+voltId if(vApp%amHelper) then call MPI_comm_split(allComm, MPI_UNDEFINED, commSize, voltComm, ierr) else From 4308324f9163f70c5a43e48599808e10b9ac3e18 Mon Sep 17 00:00:00 2001 From: "Anthony M. Sciola" Date: Wed, 23 Aug 2023 11:48:22 -0400 Subject: [PATCH 022/365] testing PIT in slice.x, WIP script to link PIT output within 1 h5 file --- scripts/postproc/pitLinker.py | 32 ++++++++++++++++++++++++++++++++ src/chimp/sliceio.F90 | 5 ++++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 scripts/postproc/pitLinker.py diff --git a/scripts/postproc/pitLinker.py b/scripts/postproc/pitLinker.py new file mode 100644 index 00000000..4f667476 --- /dev/null +++ b/scripts/postproc/pitLinker.py @@ -0,0 +1,32 @@ +# Creates a single file that links to a series of slice / chop h5 files generated using Parallel in Time feature + +import h5py as h5 +import argparse +import os + + +if __name__=="__main__": + + fTag = 'ebSlc.eb' + outfname = "" + numEb = 8 + + MainS = """Takes a series of Parallel in Time - generated eb files and makes a new h5 file linking to them + """ + + parser = argparse.ArgumentParser(description="Generates XDMF file from Gamera HDF5 output") + parser.add_argument('-outname',type=str,default=outfname,help="Name of generated h5 file") + parser.add_argument('-id',type=str,default=fTag,help=" Input Run ID (default: %(default)s)") + parser.add_argument('-numEb',type=int,default=numEb,help=" Number of EB files to join (default: %(default)s)") + args = parser.parse_args() + + fTag = args.id + outfname = args.outname + numEb = args.numEb + + + """ + myfile = h5py.File('foo.hdf5','w') + myfile['ext link'] = h5py.ExternalLink("otherfile.hdf5", "/path/to/resource") + """ + diff --git a/src/chimp/sliceio.F90 b/src/chimp/sliceio.F90 index e014f49c..c3143ae3 100644 --- a/src/chimp/sliceio.F90 +++ b/src/chimp/sliceio.F90 @@ -8,6 +8,7 @@ module sliceio use xml_input use files use plasmaputils + use parintime implicit none @@ -42,7 +43,9 @@ module sliceio character(len=strLen) :: idStr logical :: doLog - write(ebOutF,'(2a)') trim(adjustl(Model%RunID)),'.eb.h5' + !write(ebOutF,'(2a)') trim(adjustl(Model%RunID)),'.eb.h5' + !Check for time parallelism + call InitParInTime(Model,inpXML,"eb",ebOutF) associate( ebGr=>ebState%ebGr ) From 736f5e1eebb5105b3e2fbd5c17a46c12321b6351 Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 23 Aug 2023 13:18:41 -0600 Subject: [PATCH 023/365] renaming dbCat to pitmerge to make it more general --- scripts/postproc/{dbCat.py => pitmerge.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/postproc/{dbCat.py => pitmerge.py} (100%) diff --git a/scripts/postproc/dbCat.py b/scripts/postproc/pitmerge.py similarity index 100% rename from scripts/postproc/dbCat.py rename to scripts/postproc/pitmerge.py From 2df45744fd81d79ebdb7143f17ccbc46f41079af Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 23 Aug 2023 14:58:18 -0600 Subject: [PATCH 024/365] adding option to do ExternalLink instead of full copy --- scripts/postproc/pitLinker.py | 32 --------------------- scripts/postproc/pitmerge.py | 54 +++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 53 deletions(-) delete mode 100644 scripts/postproc/pitLinker.py diff --git a/scripts/postproc/pitLinker.py b/scripts/postproc/pitLinker.py deleted file mode 100644 index 4f667476..00000000 --- a/scripts/postproc/pitLinker.py +++ /dev/null @@ -1,32 +0,0 @@ -# Creates a single file that links to a series of slice / chop h5 files generated using Parallel in Time feature - -import h5py as h5 -import argparse -import os - - -if __name__=="__main__": - - fTag = 'ebSlc.eb' - outfname = "" - numEb = 8 - - MainS = """Takes a series of Parallel in Time - generated eb files and makes a new h5 file linking to them - """ - - parser = argparse.ArgumentParser(description="Generates XDMF file from Gamera HDF5 output") - parser.add_argument('-outname',type=str,default=outfname,help="Name of generated h5 file") - parser.add_argument('-id',type=str,default=fTag,help=" Input Run ID (default: %(default)s)") - parser.add_argument('-numEb',type=int,default=numEb,help=" Number of EB files to join (default: %(default)s)") - args = parser.parse_args() - - fTag = args.id - outfname = args.outname - numEb = args.numEb - - - """ - myfile = h5py.File('foo.hdf5','w') - myfile['ext link'] = h5py.ExternalLink("otherfile.hdf5", "/path/to/resource") - """ - diff --git a/scripts/postproc/pitmerge.py b/scripts/postproc/pitmerge.py index 28c4921b..8a90024b 100755 --- a/scripts/postproc/pitmerge.py +++ b/scripts/postproc/pitmerge.py @@ -1,5 +1,6 @@ #!/usr/bin/env python -#Joins decomposed DB files into single +# Joins decomposed eb files generated using "Parallel In Time" into a single file +# Example: bit.ly/3OQg71F import argparse from argparse import RawTextHelpFormatter @@ -12,7 +13,7 @@ import glob tEps = 1.0e-3 #Small time #Create new file w/ same root vars/attributes as old -def createfile(fIn,fOut): +def createfile(fIn,fOut,doLink=False): print('Creating new output file:',fOut) iH5 = h5py.File(fIn,'r') oH5 = h5py.File(fOut,'w') @@ -23,13 +24,16 @@ def createfile(fIn,fOut): aStr = str(k) oH5.attrs.create(k,iH5.attrs[aStr]) print("\t%s"%(aStr)) - #Copy root groups + #Copy root groups print("Copying root variables ...") for Q in iH5.keys(): sQ = str(Q) #Don't include stuff that starts with "Step" if "Step" not in sQ: - oH5.create_dataset(sQ,data=iH5[sQ]) + if doLink: + oH5[sQ] = h5py.ExternalLink(fIn, sQ) + else: + oH5.create_dataset(sQ,data=iH5[sQ]) print("\t%s"%(sQ)) iH5.close() @@ -49,16 +53,22 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description=MainS, formatter_class=RawTextHelpFormatter) parser.add_argument('-runid',type=str,metavar="runid",default=runid,help="Input run ID (default: %(default)s)") parser.add_argument('-typeid',type=str,metavar="typeid",default=typeid,help="Input type ID (default: %(default)s)") + parser.add_argument('--link',action='store_true',help="Create links to existing files rather than copy data (default: %(default)s)") #Finalize parsing args = parser.parse_args() runid = args.runid typeid = args.typeid - - dbIns = glob.glob('%s.????.%s.h5'%(runid,typeid)) + doLink = args.link + + globStr = '%s.????.%s.h5'%(runid,typeid) + dbIns = glob.glob(globStr) dbIns.sort() - - fOut = "%s.%s.h5"%(runid,typeid) + + if doLink: + fOut = "%s.%s.link.h5"%(runid,typeid) + else: + fOut = "%s.%s.h5"%(runid,typeid) N = len(dbIns) print("Found %d files, writing output to %s"%(N,fOut)) @@ -66,7 +76,7 @@ if __name__ == "__main__": print("No files found, exiting") exit() #Create file w/ attributes and root variables as first file - oH5 = createfile(dbIns[0],fOut) + oH5 = createfile(dbIns[0],fOut, doLink) s0 = 0 #Current step nowTime = 0.0 @@ -100,20 +110,22 @@ if __name__ == "__main__": #Good value, update old time oldTime = nowTime - oH5.create_group(ogStr) - print("Copying %s to %s"%(igStr,ogStr)) + if doLink: + oH5[ogStr] = h5py.ExternalLink(fIn, igStr) + else: + oH5.create_group(ogStr) + print("Copying %s to %s"%(igStr,ogStr)) - #Group atts - for k in iH5[igStr].attrs.keys(): + #Group atts + for k in iH5[igStr].attrs.keys(): - aStr = str(k) - oH5[ogStr].attrs.create(k,iH5[igStr].attrs[aStr]) - #print(aStr) - #Group vars - for Q in iH5[igStr].keys(): - sQ = str(Q) - #print("\tCopying %s"%(sQ)) - oH5[ogStr].create_dataset(sQ,data=iH5[igStr][sQ]) + aStr = str(k) + oH5[ogStr].attrs.create(k,iH5[igStr].attrs[aStr]) + #print(aStr) + #Group vars + for Q in iH5[igStr].keys(): + sQ = str(Q) + oH5[ogStr].create_dataset(sQ,data=iH5[igStr][sQ]) #Update s0 s0 = s0 + 1 From 7330086157bd7d2500815511522b24e37ff407c5 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 24 Aug 2023 09:21:58 -0600 Subject: [PATCH 025/365] Updated genXDMF.py to be able to make xmf from link.h5 file, needs cleanup and consideration of path complexity --- scripts/postproc/genXDMF.py | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/scripts/postproc/genXDMF.py b/scripts/postproc/genXDMF.py index 6b9a707a..4b33b1d1 100755 --- a/scripts/postproc/genXDMF.py +++ b/scripts/postproc/genXDMF.py @@ -8,7 +8,7 @@ import xml.etree.ElementTree as et import xml.dom.minidom import numpy as np -presets = {"gam", "mhdrcm_iono", "mhdrcm_eq", "mhdrcm_bmin", "rcm3D"} +presets = {"gam", "mhdrcm_eq", "mhdrcm_bmin"} def getDimInfo(h5fname,s0IDstr,preset): @@ -197,17 +197,29 @@ if __name__ == "__main__": rcmInfo['rcmKs'] = rcmKs #Get variable information + print("Getting variable information") + Nt = len(sIDstrs) #T = kh5.getTs(h5fname,sIDs) - T = np.array([]) + #T = np.array([]) + T = np.zeros(Nt) + # Also get file info, in case any of the steps are ExternalLinks to other files + # Assume this is done at the step level + fNames_link = [""]*Nt + steps_link = [""]*Nt with h5.File(h5fname,'r') as f5: #T = np.array([f5[k].attrs['time'] for k in f5.keys() if "Step" in k]) - for sIDstr in sIDstrs: + for i,sIDstr in enumerate(sIDstrs): if 'time' in f5[sIDstr].attrs.keys(): - T = np.append(T, f5[sIDstr].attrs['time']) + #T = np.append(T, f5[sIDstr].attrs['time']) + T[i] = f5[sIDstr].attrs['time'] else: - T = np.append(T, int(sIDstr.split("#")[1])) + #T = np.append(T, int(sIDstr.split("#")[1])) + T[i] = int(sIDstr.split("#")[1]) + fNames_link[i] = f5[sIDstr].file.filename.split('/')[-1] # !!NOTE: This means the files linked to must be in the same directory + steps_link[i] = int(f5[sIDstr].name.split('#')[1]) + #steps = np.array([k for k in f5.keys() if "Step" in k]) - Nt = len(T) + print("Getting Vars and RootVars") vIds ,vLocs = kxmf.getVars(h5fname,s0str,gDims) rvIds,rvLocs = kxmf.getRootVars(h5fname,gDims) @@ -236,6 +248,7 @@ if __name__ == "__main__": TGrid.set("CollectionType","Temporal") #Loop over time slices + print("Writing info for each step") for n in range(Nt): nStp = sIDs[n] Grid = et.SubElement(TGrid,"Grid") @@ -253,9 +266,11 @@ if __name__ == "__main__": if doAppendStep: stepStr = sIDstrs[n] sgVars = [os.path.join(stepStr, v) for v in gridVars] - kxmf.AddGrid(h5fname,Geom,gDimStr,sgVars) + #kxmf.AddGrid(h5fname,Geom,gDimStr,sgVars) + kxmf.AddGrid(fNames_link[n],Geom,gDimStr,sgVars) else: - kxmf.AddGrid(h5fname,Geom,gDimStr,gridVars) + #kxmf.AddGrid(h5fname,Geom,gDimStr,gridVars) + kxmf.AddGrid(fNames_link[n],Geom,gDimStr,gridVars) Time = et.SubElement(Grid,"Time") Time.set("Value","%f"%T[n]) @@ -268,11 +283,13 @@ if __name__ == "__main__": #-------------------------------- #Step variables for v in range(Nv): - kxmf.AddData(Grid,h5fname,vIds[v],vLocs[v],vDimStr,nStp) + #kxmf.AddData(Grid,h5fname,vIds[v],vLocs[v],vDimStr,nStp) + kxmf.AddData(Grid,fNames_link[n],vIds[v],vLocs[v],vDimStr,steps_link[n]) #-------------------------------- #Base grid variables for v in range(Nrv): - kxmf.AddData(Grid,h5fname,rvIds[v],rvLocs[v],vDimStr) + #kxmf.AddData(Grid,h5fname,rvIds[v],rvLocs[v],vDimStr) + kxmf.AddData(Grid,fNames_link[n],rvIds[v],rvLocs[v],vDimStr) if doAddRCMVars: addRCMVars(Grid, dimInfo, rcmInfo, sIDs[n]) From 442bad86c15bafd14e2c8df6975c91ff46c80267 Mon Sep 17 00:00:00 2001 From: phamkh Date: Fri, 25 Aug 2023 11:13:32 -0600 Subject: [PATCH 026/365] Adding scaling history arrays --- kaipy/gamera/magsphereRescale.py | 40 ++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/kaipy/gamera/magsphereRescale.py b/kaipy/gamera/magsphereRescale.py index 0b5b43e4..8f80479b 100644 --- a/kaipy/gamera/magsphereRescale.py +++ b/kaipy/gamera/magsphereRescale.py @@ -276,18 +276,34 @@ def downVolt(G): #Upscale gBAvg variable (G) on grid X,Y,Z to doubled grid with simple linear interpolation. #Typical size at quad for example: gBAvg Dataset {3, 128, 96, 1} def upVolt(G): - Nd,Nk,Nj,Ni = G.shape - Gu = np.zeros((Nd,2*Nk,2*Nj,Ni)) - print("Upscaling volt variables gBAvg etc...") - #Loop over coarse grid - for d in range(Nd): - for i in range(Ni): - for k in range(Nk): - for j in range(Nj): - Gjk = G[d,k,j,i] - j0 = 2*j - k0 = 2*k - Gu[d,k0:k0+2,j0:j0+2,i] = Gjk + try: + Nd,Nk,Nj,Ni = G.shape + Gu = np.zeros((Nd,2*Nk,2*Nj,Ni)) + print("Upscaling volt variables gBAvg etc...") + #Loop over coarse grid + for d in range(Nd): + for i in range(Ni): + for k in range(Nk): + for j in range(Nj): + Gjk = G[d,k,j,i] + j0 = 2*j + k0 = 2*k + Gu[d,k0:k0+2,j0:j0+2,i] = Gjk + except: + Nd,Nk,Nj,Ni,Nh = G.shape + Gu = np.zeros((Nd,2*Nk,2*Nj,Ni,Nh)) + print("Upscaling volt variables gBHist etc...") + print(G.shape) + #Loop over coarse grid + for h in range(Nh): + for d in range(Nd): + for i in range(Ni): + for k in range(Nk): + for j in range(Nj): + Gjk = G[d,k,j,i,h] + j0 = 2*j + k0 = 2*k + Gu[d,k0:k0+2,j0:j0+2,i,h] = Gjk return Gu #Downscale gas variable (G) on grid X,Y,Z (w/ ghosts) to halved grid From f1b808e389ad2a84d3c95eb43eaf62e12b1f91ae Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Tue, 29 Aug 2023 20:25:39 -0600 Subject: [PATCH 027/365] Branch off development for a clean version --- kaipy/rcm/wmutils/genWM.py | 85 ++++----- kaipy/rcm/wmutils/wmData.py | 6 +- scripts/preproc/genRCM.py | 4 +- src/rcm/lossutils.F90 | 278 +--------------------------- src/rcm/modules.F90 | 13 +- src/rcm/rcm_subs.F90 | 353 ++++++++++++------------------------ 6 files changed, 166 insertions(+), 573 deletions(-) diff --git a/kaipy/rcm/wmutils/genWM.py b/kaipy/rcm/wmutils/genWM.py index d8c1e5f7..25c26575 100644 --- a/kaipy/rcm/wmutils/genWM.py +++ b/kaipy/rcm/wmutils/genWM.py @@ -1,5 +1,6 @@ import numpy as np import h5py as h5 +from scipy.interpolate import RectBivariateSpline from kaipy.rcm.wmutils.wmData import wmParams # def genWM(params, useWM=True): @@ -11,15 +12,10 @@ def genWM(params, useWM=True): fInChorus = os.path.join(__location__,fInChorus) - fInTDS = 'tauTDS.txt' - __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) - - fInTDS = os.path.join(__location__,fInTDS) - - print("Reading %s and %s"%(fInChorus,fInTDS)) + print("Reading %s"%fInChorus) if useWM: - return readWM(params,fInChorus,fInTDS) + return readWM(params,fInChorus) else: return toyWM(params) @@ -38,55 +34,58 @@ def genh5(fIn, fOut, inputParams, useWM=True): else: oH5 = h5.File(fOut, 'r+') - if not ('Tau1i' in oH5.keys()): - kpi, mlti, li, eki, tau1i, tau2i, ekTDSi, tauTDSi = genWM(inputParams, useWM = useWM) + if not ('Taui' in oH5.keys()): + kpi, mlti, li, eki, taui = genWM(inputParams, useWM = useWM) attrs = inputParams.getAttrs() oH5.create_dataset('Kpi', data=kpi) oH5.create_dataset('MLTi', data=mlti) oH5.create_dataset('Li', data=li) oH5.create_dataset('Eki', data=eki) - oH5.create_dataset('Tau1i', data=tau1i) - oH5.create_dataset('Tau2i', data=tau2i) - oH5.create_dataset('EkTDSi', data=ekTDSi) - oH5.create_dataset('TauTDSi', data=tauTDSi) + oH5.create_dataset('Taui', data=taui) for key in attrs.keys(): oH5.attrs[key] = attrs[key] oH5.close() -def readWM(params,fInChorus,fInTDS): +def ReSample(cL,cK,Qp,s): + Q = np.log10(Qp) + SmthQ = RectBivariateSpline(cL,cK,Q.T,s=s) + Qs = SmthQ(cL,cK).T + return 10.0**(Qs) + +def readWM(params,fInChorus): # add electron lifetime for the chorus wave loss f5 = h5.File(fInChorus, 'r') kpi=f5['Kp_1D'][:][0] - mlti=np.append(f5['MLT_1D'][:][0],24.) + mlti=np.append(f5['MLT_1D'][:][0],24.) # 0, 1,...,24 li=f5['L_1D'][:][0] eki=10.**(f5['E_1D'][:][0]) # in MeV - print('shape of eki:',eki.shape) - tau1i=(10.**(f5['Tau1_4D'][:]))*24.*3600. # in second - tau2i=(10.**(f5['Tau2_4D'][:]))*24.*3600. + taui=(10.**(f5['Tau2_4D'][:]))*24.*3600. # in second, use method 2 data #print ("kpi",kpi,"mlti",mlti,"li",li,"eki",eki) - nk,nm,nl,ne = tau1i.shape - #expand mlt from 0:23 to 0:24 - tau1ai = np.array([np.append(tau1i[0,:,:,:],np.array([tau1i[0,0,:,:]]),0)]) - tau2ai = np.array([np.append(tau2i[0,:,:,:],np.array([tau2i[0,0,:,:]]),0)]) - for i in range(1,7): - tau1ai=np.append(tau1ai,np.array([np.append(tau1i[i,:,:,:],np.array([tau1i[i,0,:,:]]),0)]),0) - tau2ai=np.append(tau2ai,np.array([np.append(tau2i[i,:,:,:],np.array([tau2i[i,0,:,:]]),0)]),0) - tau1ai = tau1ai.T - tau2ai = tau2ai.T f5.close() + nk,nm,nl,ne = taui.shape + #expand mlt from 0:23 to 0:24 + tauEi = np.array([np.append(taui[0,:,:,:],np.array([taui[0,0,:,:]]),0)]) + for i in range(1,nk): + tauEi=np.append(tauEi,np.array([np.append(taui[i,:,:,:],np.array([taui[i,0,:,:]]),0)]),0) + tauEi = tauEi.T + #Smooth the expanded tau table + ne1,nl1,nm1,nk1 = tauEi.shape + tauSi = np.zeros((ne1,nl1,nm1,nk1)) #create new smoothed tau + s0 = 250 #Magic smoothing number + for m in range(nm1): + for k in range(nk1): + MLT0 = mlti[m] + Kp0 = kpi[k] + #print("MLT,Kp = %f,%f"%(MLT0,Kp0)) + m0 = np.abs(mlti-MLT0).argmin() + k0 = np.abs(kpi-Kp0).argmin() + tauMK = tauEi[:,:,m0,k0] + tauMKs = ReSample(li,eki*1e3,tauMK,s0) + tauSi[:,:,m,k] = tauMKs - #add electron lifetime for the Time Domain Structure loss - tdmArrays=np.loadtxt(fInTDS) - #print(tdm_arrays) - ekTDSi = tdmArrays[:,0].T/1.e6 #in MeV - print ('shape of ekiTDS',ekTDSi.shape) - tauTDSi = tdmArrays[:,2].T*24.*3600. #in second, read in the electron lifetime against TDS with Ew= 4mV/m - print ('tauTDSi[0]',tauTDSi[0]) - return kpi,mlti,li,eki,tau1ai,tau2ai,ekTDSi,tauTDSi - - + return kpi,mlti,li,eki,tauSi def toyWM(params): nKpi = params.nKp @@ -98,13 +97,9 @@ def toyWM(params): mlti = np.linspace(0,24,nMLTi) #Note the dimension of MLT is 25 li = np.linspace(3.,7.,nLi) eki = np.exp(np.linspace(-3,0.1,nEki)) #in MeV - #print ("kpi",kpi,"mlti",mlti,"li",li,"eki",eki) - tau1i = np.zeros((nKpi,nMLTi,nLi,nEki)) - tau2i = np.zeros((nKpi,nMLTi,nLi,nEki)).T - tau1i = kpi[:,None,None,None]*mlti[None,:,None,None]*li[None,None,:,None]*eki[None,None,None,:] - tau1i = tau1i.T - - return kpi,mlti,li,eki,tau1i,tau2i - + taui = kpi[:,None,None,None]*mlti[None,:,None,None]*li[None,None,:,None]*eki[None,None,None,:] + taui = taui.T + + return kpi,mlti,li,eki,taui diff --git a/kaipy/rcm/wmutils/wmData.py b/kaipy/rcm/wmutils/wmData.py index d723fef7..52906bd9 100644 --- a/kaipy/rcm/wmutils/wmData.py +++ b/kaipy/rcm/wmutils/wmData.py @@ -4,14 +4,12 @@ import numpy as np class wmParams: #All energies in eV - def __init__(self, dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155, dimTDS = 1, nEkTDS = 109): + def __init__(self, dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155): self.dim = dim self.nKp = nKp self.nMLT = nMLT self.nL = nL self.nEk = nEk - self.dimTDS = dimTDS - self.nEkTDS = nEkTDS def getAttrs(self): return { 'tauDim': self.dim, @@ -19,7 +17,5 @@ class wmParams: 'nMLT': self.nMLT, 'nL': self.nL, 'nEk': self.nEk, - 'tauTDSDim': self.dimTDS, - 'nEkTDS': self.nEkTDS } diff --git a/scripts/preproc/genRCM.py b/scripts/preproc/genRCM.py index 47cc32bb..45467dfd 100755 --- a/scripts/preproc/genRCM.py +++ b/scripts/preproc/genRCM.py @@ -78,7 +78,7 @@ if __name__ == "__main__": plotType = args.plotType if addWM: - tauParams = wmParams(dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155) + tauParams = wmParams(dim = 4, nKp = 7, nMLT = 25, nL = 161, nEk = 155) genWM.genh5(fIn,fOut,tauParams,useWM = True) else: # Determine proton channel limits based on resolving a certain (proton) temperature at given L @@ -106,7 +106,7 @@ if __name__ == "__main__": fileIO.saveRCMConfig(alamData,params=alamParams,fname=fOut) # Add data needed for wavemodel if not noWaveModel: - tauParams = wmParams(dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155, dimTDS = 1, nEkTDS = 109) + tauParams = wmParams(dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155) genWM.genh5(fOut,fOut,tauParams,useWM = True) print("Wrote RCM configuration to %s"%(fOut)) diff --git a/src/rcm/lossutils.F90 b/src/rcm/lossutils.F90 index 638565c8..8d580c00 100644 --- a/src/rcm/lossutils.F90 +++ b/src/rcm/lossutils.F90 @@ -164,79 +164,6 @@ MODULE lossutils END FUNCTION RatefnC_tau_s - FUNCTION Ratefn_sub_1keV (kpx,MLT,L,Ek,vm,beq,lossc,tau_ss) result(tau) - !Function return electron lifetime in sub-1keV energy range - - IMPLICIT NONE - REAL (rprec), INTENT (IN) :: kpx,MLT,L,Ek,vm,beq,lossc,tau_ss - REAL (rprec) :: tau, tau_c_1keV,tau_ss_1keV,ratio_c2s - REAL (rprec) :: E_1keV = 1.0e-3 !in MeV - - tau_c_1keV = RatefnDW_tau_c(kpx, MLT,L,E_1keV) ! 1keV = 1e-3MeV - tau_ss_1keV = RatefnC_tau_s(E_1keV*1.0e6/vm,vm,beq,lossc) - if (tau_c_1keV < Tiny) then !L > max(Li)in DW model,replace 0 by strong scattering - tau_c_1keV = tau_ss_1keV - endif - - ratio_c2s = tau_c_1keV/tau_ss_1keV !ratio between chorus wave model and strong scattering at 1keV - - tau = tau_ss*ratio_c2s !use scaled strong scattering lifetime to ensure smooth transition at 1keV - - END FUNCTION Ratefn_sub_1keV - - FUNCTION Ratefn_sub_1keV_linear (kpx,MLT,L,Ek,vm,beq,lossc,tau_ss) result(tau) - !Function return electron lifetime in sub-1keV energy range - - IMPLICIT NONE - REAL (rprec), INTENT (IN) :: kpx,MLT,L,Ek,vm,beq,lossc,tau_ss - REAL (rprec) :: tau, tau_E_thres_ss, tau_c_1keV !in MeV - REAL (rprec) :: E_thres = 0.8e-3, E_1keV = 1.0e-3 !in MeV, strong scattering energy limit 0.8keV, if E < 0.8keV, strong scattering only - ! if E_thres < E < 1keV, use linear ramping in log10(tau) between E_thres and 1keV - - tau_c_1keV = RatefnDW_tau_c(kpx, MLT,L,E_1keV) ! 1keV = 1e-3MeV - if (tau_c_1keV < Tiny) then !L > max(Li)in DW model,replace 0 by strong scattering - tau_c_1keV = RatefnC_tau_s(E_1keV*1.0e6/vm,vm,beq,lossc) - endif - tau_E_thres_ss = RatefnC_tau_s(E_thres*1.0e6/vm,vm,beq,lossc) ! strong scattering lifetime at E_thres - if (tau_ss <= tau_c_1keV) then - tau = tau_c_1keV ! use tau_c_1keV as the lower bound of strong scattering lifetime - else ! tau_ss > tau_c_1keV - if (Ek < E_thres) then - tau = tau_ss - else ! E_thres <= Ek < 1keV - tau = EXP(log(tau_E_thres_ss)+(Ek-E_thres)*log(tau_c_1keV/tau_E_thres_ss)/(E_1keV-E_thres)) ! linear ramping algorithm - endif - endif - - END FUNCTION Ratefn_sub_1keV_linear - - FUNCTION Ratefn_tau_TDS(Ekx) result(tau) - ! linearly interpolate tau from EWMTauInput to Ekx value - USE rice_housekeeping_module, ONLY: EWMTauInput - IMPLICIT NONE - REAL (rprec), INTENT (IN) :: Ekx - REAL(rprec) :: taui,tau - REAL(rprec) :: dE,wE - INTEGER :: eL,eU - - associate(Ne=>EWMTauInput%TDSTauInput%NeTDS,& - Eki=>EWMTauInput%TDSTauInput%EkTDSi,& - taui=>EWMTauInput%TDSTauInput%tauTDSi) - - if (Ekx < minval(Eki)) then - tau = taui(1) - else if (Ekx > maxval(Eki)) then ! default lifetime is 10^10s ~ 10^3 years. - tau = 1.D10 - else - eL = maxloc(Eki,dim=1,mask=(EkiEWMTauInput%ChorusTauInput%Nm,Nl=>EWMTauInput%ChorusTauInput%Nl,Nk=>EWMTauInput%ChorusTauInput%Nk,Ne=>EWMTauInput%ChorusTauInput%Ne,& Kpi=>EWMTauInput%ChorusTauInput%Kpi,MLTi=>EWMTauInput%ChorusTauInput%MLTi,Li=>EWMTauInput%ChorusTauInput%Li,Eki=>EWMTauInput%ChorusTauInput%Eki,& - taui=>EWMTauInput%ChorusTauInput%tau2i) ! using method 2 - !taui=>EWMTauInput%ChorusTauInput%tau1i) + taui=>EWMTauInput%ChorusTauInput%taui) ! look up in Kp !iK = minloc(abs(Kpi-Kpx),dim=1) @@ -397,208 +323,6 @@ MODULE lossutils END FUNCTION RatefnDW_tau_c - FUNCTION RatefnC_tau_C05(mltx,engx,Lshx) result(tau) - ! default scattering rate lambda based on Chen et al. 2005. - ! lambda(E,L,phi) = (1+a1*sin(phi+phi0)+a2*cos(2*(phi+phi0)))*lambda0, - ! where lambda0 = min(0.08*E[MeV]^(-1.32), 0.4*10^(2L-6+0.4*log_2(E)))/day, a1=1.2, a2=-0.25*a1, phi0=pi/6. - ! lambda max = 2.6*lambda0 at 04MLT, and min = 0.6 lambda0 at 22 MLT. - IMPLICIT NONE - REAL (rprec), INTENT (IN) :: mltx,engx,Lshx - REAL (rprec) :: lambda, lambda0, a1, a2, phi0, phi, tau - - lambda0 = min(0.08*engx**(-1.32), 0.4*10**(2*Lshx-6.D0+0.4*dlog(engx)/dlog(2.D0)))/86400.D0 ! 1/s. - a1 = 1.2D0 - a2 = -0.25D0*a1 - phi0 = pi/6.D0 - phi = (mltx-0.D0)/12.D0*pi+phi0 - lambda = (1.D0+a1*sin(phi)+a2*cos(2.D0*phi))*lambda0 - tau = 1.D0/lambda - END FUNCTION RatefnC_tau_C05 - - FUNCTION RatefnC_tau_w(mltx,engx,Lshx,kpx) result(tau) - ! Empirical electron lifetime against whistler mode chorus inside the plasmasphere, based on Orlova and Shprits, 2013JA019596. - ! log10(tau[days]) = a1+...+a33*E^4, R=R0, K=Kp+1, E=Ek[MeV], valid for 2115.0) then - return - endif - a1_33 = 0.D0 - if((mltx>21.0 .or. mltx<=3.0)) then ! Night side - if(engx<=10e-3) then ! Night side Ek <= 10 keV - a1_33 = [ & - -6.29800e+00, 2.47510e+00, -7.26390e-01, 8.69730e+01, -1.57180e+03, -1.53970e-02, -1.25300e+00, 1.16620e-03, -3.19940e+02, -5.98660e+00, & - 0.00000e+00, -3.27510e-01, 9.05410e-02, -4.77420e+00, 1.56930e-03, 1.18960e+01, 1.07320e+03, 1.50100e-02, -3.96060e-03, 0.00000e+00, & - 2.11370e+00, -4.24700e+02, 2.64410e+04, -9.88010e+05, -8.82250e-02, 8.66970e+00, 4.19100e+02, 4.70090e-03, -7.94090e-01, 2.02540e+03, & - -1.47880e+05, 5.62920e+06, 0.00000e+00] - else if(engx>10e-3 .and. engx<0.5) then ! Night side 10 keV < Ek < 0.5 MeV - if(kpx<=3.0) then ! Night side 10 keV < Ek < 0.5 MeV and Kp<=3 - a1_33 = [ & - 9.24560e+00, -3.76850e+00, 1.25590e+00, 4.02960e-02, 1.18330e+00, -1.08430e-01, 0.00000e+00, 1.36980e-02, 1.60750e+01, -1.98660e+01, & - 8.13400e+00, 5.44060e-01, -1.66160e-01, -9.18410e-02, 0.00000e+00, -1.69710e+00, 9.91020e-01, -2.81450e-02, 9.26060e-03, 5.88920e-02, & - -3.56740e+00, -9.05730e+00, -1.76860e+01, 1.00290e+01, 6.32200e-01, 7.36800e+00, 0.00000e+00, -8.55330e-02, -1.00050e+00, -3.13980e+01, & - 8.88950e+01, -5.97370e+01, 0.00000e+00] - else !% Night side 10 keV < Ek < 0.5 MeV and Kp>3 - a1_33 = [ & - -3.80640e+00, -1.06160e+00, -5.02820e-01, -9.34780e-01, 0.00000e+00, 1.59690e-01, -8.14210e-02, -8.10140e-03, 2.72620e+01, -1.05540e+01, & - 2.35650e+00, 4.42100e-01, -5.90540e-02, 1.55420e-01, -2.73460e-03, -4.05000e+00, 9.05600e-01, -2.84240e-02, 4.96020e-03, 1.48180e-01, & - 4.10080e+00, -5.58120e+01, 1.03920e+01, 0.00000e+00, -8.51250e-01, 9.90870e+00, -8.34090e-01, 4.40160e-02, -5.34040e-01, 8.23600e+01, & - -7.64720e+01, 1.58860e+02, -1.48750e+02] - endif - else !% Night side Ek >= 0.5 MeV - if(kpx<=3.0) then !% Night side Ek >= 0.5 MeV and Kp<=3 - a1_33 = [ & - 9.49410e+00, -4.60800e+00, 1.98140e+00, -3.54580e-01, 1.07230e-01, 1.54310e-01, -1.54080e-02, -1.13010e-02, 3.12460e+00, -6.73170e-02, & - -8.89080e-02, 9.11370e-01, -3.74740e-01, 6.32320e-03, -6.35990e-03, -3.68530e-01, 0.00000e+00, -6.11110e-02, 2.10430e-02, 1.80110e-02, & - -7.95890e+00, -2.00000e+00, 9.35410e-01, -4.25710e-01, 2.03700e+00, 1.06340e+00, 0.00000e+00, -3.05470e-01, -1.32250e-01, 9.44100e+00, & - -1.22310e+01, 5.00080e+00, -5.05700e-01] - else !% Night side Ek >= 0.5 MeV and Kp>3 - a1_33 = [ & - -2.26310e+01, 1.16930e+01, -4.69090e+00, -2.47010e-01, 3.34580e-02, 9.81050e-01, 0.00000e+00, -5.58680e-02, 3.83790e+00, -8.52390e-01, & - 0.00000e+00, -9.06580e-02, -1.58690e-01, 9.00520e-03, -7.96750e-03, -3.59650e-01, 6.80280e-02, -3.44430e-02, 1.87950e-02, 5.11320e-03, & - 1.06470e+01, -1.13320e+01, 1.39530e-01, -6.84300e-02, -2.16830e+00, 2.09080e+00, 0.00000e+00, 1.28550e-01, -1.21910e-01, 1.99560e+01, & - 3.90780e-01, -2.71180e-01, 1.92860e-01] - endif - endif - else if((mltx>3.0 .and. mltx<=9.0)) then !% Dawn side - if(engx<7e-3) then !% Dawn side Ek < 7 keV - a1_33 = [ & - 1.30230e+01, -5.13970e+00, 3.66290e-01, -1.14330e+02, 5.23120e+03, -4.32600e-03, 1.79870e+00, 0.00000e+00, 2.36470e+03, -2.37100e+05, & - 8.16800e+06, 6.61540e-01, -3.53860e-02, 4.47500e+00, 0.00000e+00, -2.23630e+02, 9.03540e+03, -2.98050e-02, 1.35540e-03, 8.04240e+00, & - -1.78800e+00, 5.68060e+02, -6.35360e+04, 2.30770e+06, 7.67790e-02, -1.68380e+01, 1.33480e+03, 0.00000e+00, 0.00000e+00, -6.51880e+03, & - 9.23510e+05, -4.27880e+07, 0.00000e+00] - else if(engx>=7e-3 .and. engx<90e-3) then !% Dawn side 7 keV <= Ek < 90 keV - a1_33 = [ & - 3.08720e+00, 4.40920e-01, -3.48240e-01, 2.83500e+00, -1.08380e+01, 1.37250e-02, -6.88500e-02, 0.00000e+00, 2.32830e+01, -4.26740e+02, & - 1.21980e+03, -1.41190e-01, 3.97480e-02, -1.01110e-01, -7.46140e-04, -1.04010e+00, 2.47740e+01, 1.07430e-02, -1.61820e-03, -9.16570e-02, & - 1.56840e-02, -1.22210e+01, 2.26340e+01, 3.96600e+02, 3.73680e-03, 7.93640e-01, -3.80690e+00, 1.16140e-03, 0.00000e+00, -1.70600e+02, & - 4.94760e+03, -4.94390e+04, 1.81330e+05] - else !% Dawn side Ek >= 90 keV - a1_33 = [ & - 4.01120e+00, -3.90940e-01, -3.25600e-02, -3.79590e-02, -6.26790e-03, 5.22710e-03, 1.26300e-03, -1.76010e-04, 1.00320e-01, 7.42100e-02, & - 1.13470e-02, 4.03350e-02, 5.57050e-03, 3.34900e-03, -3.85910e-04, -2.60900e-02, -6.55570e-03, -2.42570e-03, -2.71600e-04, 1.59150e-03, & - -9.36750e-01, 5.75970e-02, 5.61290e-02, -8.76050e-03, 5.90570e-02, -6.86030e-03, 1.47720e-03, 1.07560e-03, -3.19070e-04, 3.41930e+00, & - -2.95490e+00, 1.25700e+00, -2.41230e-01] - endif - else if((mltx>9.0 .and. mltx<=12.0)) then !% Prenoon side - if(engx<7e-3) then !% Prenoon side Ek < 7 keV - a1_33 = [ & - 9.37910e+00, -3.38010e+00, 2.29500e-01, -2.10440e+01, 3.23730e+03, -1.49830e-02, -1.37450e+00, 0.00000e+00, 1.95630e+03, -2.60190e+05, & - 1.25510e+07, 4.05490e-01, -1.64040e-02, 0.00000e+00, 1.40030e-03, -1.80880e+02, 6.90820e+03, -1.82190e-02, 0.00000e+00, 7.95340e+00, & - -1.44820e+00, 2.80490e+02, -3.29820e+04, 0.00000e+00, 1.09630e-01, -1.94360e+01, 2.54150e+03, 0.00000e+00, 0.00000e+00, -4.98720e+03, & - 8.43540e+05, -4.44550e+07, 0.00000e+00] - else if(engx>=7e-3 .and. engx<0.1) then !% Prenoon side 7 keV <= Ek < 0.1 MeV - a1_33 = [ & - 6.88650e+00, -1.66320e+00, -1.87690e-02, -4.04800e-01, -4.01940e+00, -4.79730e-03, 0.00000e+00, 4.97590e-04, 3.74930e+01, -1.84030e+02, & - 0.00000e+00, 1.48470e-01, 9.90130e-03, 8.86630e-02, 0.00000e+00, -3.66650e+00, 1.61190e+01, -2.52420e-03, -9.83090e-04, 4.54560e-02, & - -3.00720e-01, 3.96140e+00, -4.95480e+01, 3.76000e+02, 1.89350e-02, -7.31370e-02, 0.00000e+00, -1.26880e-03, 0.00000e+00, -2.17920e+02, & - 3.72960e+03, -3.41250e+04, 1.27070e+05] - else !% Prenoon side Ek >= 0.1 MeV - a1_33 = [ & - 3.77720e-01, 3.19090e-01, -9.72410e-02, 5.28290e-02, -5.49410e-03, 1.25200e-02, -4.55740e-03, -1.44610e-04, -1.16590e-01, 9.03070e-02, & - 4.56800e-03, -5.65820e-02, 7.94530e-03, -1.95700e-03, -9.02940e-04, -1.63340e-02, -4.90140e-03, 1.45370e-03, 0.00000e+00, 3.07330e-03, & - 5.18830e-02, -2.32240e-01, 8.30020e-02, -1.89770e-02, -3.38290e-02, 1.88780e-02, -1.07900e-03, 4.20820e-04, 0.00000e+00, 4.70710e+00, & - -4.06830e+00, 1.84240e+00, -3.35050e-01] - endif - else if((mltx>12.0 .and. mltx<=15.0)) then !% Postnoon side - if(engx<6e-3) then !% Postnoon side Ek < 6 keV - a1_33 = [ & - 5.49270e+00, -1.22390e+00, 1.67600e-01, -3.24120e+01, 4.52400e+03, -1.39930e-02, -1.13610e+00, 0.00000e+00, 1.18180e+03, -2.11750e+05, & - 1.39230e+07, 7.95320e-02, -7.59730e-03, 0.00000e+00, 9.95900e-04, -8.14630e+01, 1.78080e+03, -3.80660e-03, 0.00000e+00, 4.75400e+00, & - -1.40150e+00, 4.78460e+02, -8.44970e+04, 3.56970e+06, 1.40880e-01, -2.63470e+01, 3.59790e+03, -2.24530e-03, 0.00000e+00, -3.68170e+03, & - 8.58490e+05, -6.09680e+07, 0.00000e+00] - else if(engx>=6e-3 .and. engx<0.1) then !% Postnoon side 6 keV <= Ek < 0.1 MeV - a1_33 = [ & - 5.25830e+00, -9.93980e-01, -1.03430e-01, 2.15500e-01, -5.89310e+00, -6.02980e-03, 1.83200e-02, 5.13720e-04, 3.45720e+01, -1.67450e+02, & - -2.08860e+02, 6.29100e-02, 2.26720e-02, 4.07410e-02, 0.00000e+00, -3.92410e+00, 1.98090e+01, 1.48700e-03, -1.54070e-03, 5.62200e-02, & - -5.01280e-02, -1.31250e-01, -1.27710e+01, 2.93380e+02, 2.57500e-02, -1.60430e-02, -1.25290e+00, -1.76190e-03, 0.00000e+00, -1.74200e+02, & - 3.26740e+03, -3.24120e+04, 1.30380e+05] - else !% Postnoon side Ek >= 0.1 MeV - a1_33 = [ & - 8.56720e-01, 4.13830e-01, -1.14870e-01, 5.24810e-02, -5.71760e-03, 1.33180e-02, -5.20970e-03, -1.26850e-04, -6.66380e-02, 1.57840e-01, & - -4.22140e-03, -7.55980e-02, 1.08770e-02, -1.55610e-03, -9.77010e-04, -3.50350e-02, -8.20200e-03, 2.57510e-03, -1.58690e-04, 4.45860e-03, & - 1.40840e-01, -2.57110e-01, 9.20020e-02, -1.76770e-02, -3.68530e-02, 2.54710e-02, -3.38180e-03, 3.24390e-04, 0.00000e+00, 4.83550e+00, & - -4.46000e+00, 1.98870e+00, -3.56050e-01] - endif - endif - R = Lshx - K = kpx+1.0 - E = engx ! [MeV] - RK = R*K - KE = K*E - RE = R*E - RKE= RK*E - R2 = R*R - K2 = K*K - E2 = E*E - rke_pol = (/1.D0,R,RK,RKE,RKE*E,RK*K,RK*KE,RK*K2,RE,RE*E,RE*E2,R2,R*RK,R*RKE,RK*RK,R*RE,RE*RE,R*R2,R2*RK,R2*RE,K,KE,KE*E,KE*E2,K2,K*KE,KE*KE,K*K2,K2*KE,E,E2,E*E2,E2*E2/) - ! log(tau) = a1+a2*R+..., note tau is in days. - tau = 10.0**(dot_product(a1_33,rke_pol))*86400.D0 ! seconds. - lambda = 1.D0/tau ! 1/s - END FUNCTION RatefnC_tau_w - - FUNCTION RatefnC_tau_h(mltx,engx,Lshx,kpx) result(tau) - ! Empirical lifetime against plasmaspheric hiss pitch angle diffusion, based on Orlova et al. 2014GL060100. - ! 6-21 MLT, dayside: log10(tau) = g(E,L)+y(Kp), valid for L=3-6, Kp<=6, Ek=1keV-10MeV, log10(Ek[MeV])>f(L). - ! 21-6 MLT, nightside: log10(tau) = a1+...+a59*L*E^7*Kp, valid for L=3-6, Kp<=6, Ek<=10MeV, log10(Ek[MeV])>f(L). - ! For Kp>6, lifetimes are >100 days. Loss rate becomes negligible. - IMPLICIT NONE - REAL (rprec), INTENT (IN) :: mltx,engx,kpx,Lshx ! engx in MeV. - REAL (rprec) :: lambda, tau, gEL, yKp, fL - REAL (rprec) :: L, E, K, L2, L3, L4, E2, E3, E4, E5, K2, LE, LK, EK, LEK - REAL (rprec), DIMENSION(28) :: a1_28, le_pol - REAL (rprec), DIMENSION(59) :: a1_59, lek_pol - - lambda = 0.D0 - tau = 1.D10 - L = Lshx ! L=3-6 - E = log10(engx) ! engx is Ek in MeV - K = min(kpx,6.0) ! use Kp6 results for Kp>6. - L2 = L*L - L3 = L2*L - L4 = L3*L - fL = -0.2573*L4 + 4.2781*L3 - 25.9348*L2 + 66.8113*L - 66.1182 + 3.0 ! Need to add 3 to the equation from Orlova14 which was actually based on GeV. - if(L>6.0 .or. L<3.0 .or. K>6.0 .or. E>1.0 .or. E<-3.0 .or. E=f(L), 1keV=6) then ! Dayside, 6<=MLT<=21 - a1_28 = [ & - 4.32340e+01, -3.70570e+01, -1.09340e+02, 1.16160e+01, 8.29830e+01, 3.57260e+01, -1.58030e+00, -2.26630e+01, -1.60060e+01, 3.51250e+01, & - 7.99240e-02, 2.67740e+00, 2.13960e+00, -2.41070e+01, -3.50920e+00, -1.15310e-01, -7.43760e-02, 5.16590e+00, -2.04880e+00, -4.52050e+00, & - -3.52900e-01, 1.25340e+00, 2.32240e+00, 7.00390e-01, -1.36660e-01, -2.71340e-01, -1.41940e-01, -2.09270e-02 ] - le_pol = (/1.D0,L,E,L2,LE,E2,L3,L2*E,L*E2,E3,L4,L3*E,L2*E2,L*E3,E4,L3*LE,L3*E2,L2*E3,L*E4,E5,L3*E3,L2*E4,L*E5,E3*E3,L3*E4,L2*E5,LE*E5,E2*E5/) - gEL = dot_product(a1_28,le_pol) - yKp = 0.015465*kpx*kpx - 0.26074*kpx + 1.0077 - tau = 10**(gEL+yKp)*86400.D0 ! seconds - else ! Nightside, 21 EWMTauInput%TDSTauInput%EkTDSi(NeTDS)) then - write(*,*) "EkTDS: ",EWMTauInput%TDSTauInput%EkTDSi - write(*,*) "reorder wave model so EkTDS is in ascending order" - stop - end if - endif @@ -2221,23 +2202,15 @@ RETURN END FUNCTION G3ntrp ! -! -! -! -! -! -! -! !========================================================================= - !========================================================================= ! !Advance eeta by dt nstep times, dtcpl=dt x nstep SUBROUTINE Move_plasma_grid_MHD (dt,nstep) - use rice_housekeeping_module, ONLY : LowLatMHD,doNewCX,ELOSSMETHOD,doTDSLoss,doFLCLoss,dp_on,doPPRefill,doSmoothDDV,staticR,NowKp + use rice_housekeeping_module, ONLY : LowLatMHD,doNewCX,ELOSSMETHOD,doFLCLoss,dp_on,doPPRefill,doSmoothDDV,staticR,NowKp use math, ONLY : SmoothOpTSC,SmoothOperator33 use lossutils, ONLY : CXKaiju,FLCRat use planethelper, ONLY : DipFTV_colat,DerivDipFTV @@ -2476,7 +2449,7 @@ SUBROUTINE Move_plasma_grid_MHD (dt,nstep) !Calculate losses and keep track of total losses/precip losses if ( (ie == RCMELECTRON) .and. (kc /= 1) ) then !Do electron losses - lossFT = Ratefn(xmin(i,j),ymin(i,j),alamc(kc),vm(i,j),bmin(i,j),losscone(i,j),Dpp(i,j),dble(NowKp),fudgec(kc),sini(i,j),bir(i,j),mass_factor,ELOSSMETHOD,doTDSLoss) + lossFT = Ratefn(xmin(i,j),ymin(i,j),alamc(kc),vm(i,j),bmin(i,j),losscone(i,j),Dpp(i,j),dble(NowKp),fudgec(kc),sini(i,j),bir(i,j),mass_factor,ELOSSMETHOD) lossratep(i,j,kc) = lossratep(i,j,kc) + lossFT(1) lossmodel(i,j,kc) = lossFT(2) rate(i,j) = rate(i,j) + lossFT(1) @@ -2509,6 +2482,7 @@ SUBROUTINE Move_plasma_grid_MHD (dt,nstep) !--- !Main channel loop !NOTE: T1k/T2k need to be private b/c they're altered by claw2ez + !$OMP PARALLEL DO if (L_doOMPClaw) & !$OMP schedule(dynamic) & !$OMP DEFAULT(SHARED) & @@ -2522,17 +2496,7 @@ SUBROUTINE Move_plasma_grid_MHD (dt,nstep) ENDIF eeta_avg(:,:,kc) = 0.0 - - if ( (kc==1) .and. dp_on == .false.) then ! We are plasmasphere and we don't want to evolve it - ! Just set eeta_avg to whatever eta is there, and leave - !! Note: Regions that have ever been outside of active domain will never have psph again, based on current implementation (2023-08-24) - eeta_avg(:,:,kc) = eeta(:,:,kc) - cycle - else ! We are hot channel or we are evolving plasmasphere channel - ! Add current weighted eeta as first contribution - eeta_avg(:,:,kc) = eeta(:,:,kc)/(nstep+1) - endif - + eeta_avg(:,:,kc) = eeta_avg(:,:,kc) + eeta(:,:,kc)/(nstep+1) !Sub-step nstep times do n=1,nstep !--- @@ -3100,12 +3064,11 @@ FUNCTION RatefnFDG (fudgx, alamx, sinix, birx, vmx, xmfact) RETURN END FUNCTION RatefnFDG -FUNCTION Ratefn (xx,yy,alamx,vmx,beqx,losscx,nex,kpx,fudgxO,sinixO,birxO,xmfactO,ELOSSMETHOD,doTDSLoss) +FUNCTION Ratefn (xx,yy,alamx,vmx,beqx,losscx,nex,kpx,fudgxO,sinixO,birxO,xmfactO,ELOSSMETHOD) - use lossutils, ONLY : RatefnC_tau_s,RatefnC_tau_C05 + use lossutils, ONLY : RatefnC_tau_s IMPLICIT NONE INTEGER (iprec), INTENT (IN) :: ELOSSMETHOD - LOGICAL,INTENT (IN) :: doTDSLoss !Use TDS losses REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,beqx,losscx,nex,kpx REAL (rprec), INTENT (IN), OPTIONAL :: fudgxO,sinixO,birxO,xmfactO REAL (rprec) :: fudgx,sinix,birx,xmfact @@ -3141,18 +3104,9 @@ FUNCTION Ratefn (xx,yy,alamx,vmx,beqx,losscx,nex,kpx,fudgxO,sinixO,birxO,xmfactO tau = RatefnC_tau_s(alamx,vmx,beqx,losscx) Ratefn(1) = 1.D0/tau !/s Ratefn(2) = -1.0 - case (ELOSS_C05) - L = sqrt(xx**2+yy**2) - MLT = atan2(yy,xx)/pi*12.D0+12.D0 - K = abs(alamx*vmx*1.0e-6) !Energy [MeV] - tau = RatefnC_tau_s(alamx,vmx,beqx,losscx) + RatefnC_tau_C05(MLT,K,L) - Ratefn(1) = 1.D0/tau !/s - Ratefn(2) = 0.0 - case (ELOSS_C19) - Ratefn = RatefnC19S(xx,yy,alamx,vmx,beqx,losscx,nex,kpx) case (ELOSS_WM) if (EWMTauInput%useWM) then - Ratefn = RatefnWM(xx,yy,alamx,vmx,nex,kpx,beqx,losscx,doTDSLoss) + Ratefn = RatefnWM(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) else write(*,*) "Wave models are missing in rcmconfig.h5" stop @@ -3164,185 +3118,118 @@ FUNCTION Ratefn (xx,yy,alamx,vmx,beqx,losscx,nex,kpx,fudgxO,sinixO,birxO,xmfactO END FUNCTION Ratefn -FUNCTION RatefnWM(xx,yy,alamx,vmx,nex,kpx,bqx,losscx,doTDSLoss) - !Function to calculate diffuse electron precipitation loss rate using - ! 1. Dedong Wang's chorus wave model - ! 2. Orlova16 hiss wave model - ! 3. Shen21 electron loss for time domain structures with Ew =4mV/m - - use lossutils, ONLY: RatefnC_tau_s, RatefnDW_tau_c,RatefnC_tau_h16,Ratefn_tau_TDS - !Wave type in RatefnWM(2) - Hiss: 1.0; Chorus: 2.0; TDS: 3.0; strong scattering: 6.0; tau > 1.e10: -10.0 - IMPLICIT NONE - REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,nex,kpx,bqx,losscx - LOGICAL,INTENT (IN) :: doTDSLoss !Use TDS losses - REAL (rprec), dimension(2) :: RatefnWM - REAL (rprec) :: nhigh, nlow, L, MLT, E, tau, tau_s, tau_c, tau_h, tau_TDS, tau1, tau2, R1, R2 +FUNCTION RatefnWM(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + use lossutils, ONLY: RatefnC_tau_s, RatefnDW_tau_c,RatefnC_tau_h16 + IMPLICIT NONE + !Wave type: Hiss: 1.0; Chorus: 2.0 (Inner Mag,L<7); strong scattering: 3.0 (Plasmasheet,L>8); Blending zone, 7nhigh indicates inside plasmasphere. - nlow = 10.D0 ! [/cc] nenhigh) then ! Region inside the plasmasphere, wave candidates: Hiss - tau_h = RatefnC_tau_h16(MLT,E,L,kpx) - if (tau_h < 1.e10) then - tau = tau_h - RatefnWM(1) = 1./tau - RatefnWM(2) = 1.0 - else ! No wave mode triggered - tau = 1.e10 - RatefnWM(1) = 1./tau - RatefnWM(2) = -10.0 - endif - else ! nlow <= nex <= nhigh, at the plume region, wave candidates: Chorus and Hiss - tau_c = RatefnDW_tau_c(kpx,MLT,L,E) - tau_h = RatefnC_tau_h16(MLT,E,L,kpx) - if ((tau_c < 1.e10) .and. (tau_h < 1.e10)) then - tau1 = tau_c - R1 = 2.0 - tau2 = tau_h - R2 = 1.0 - RatefnWM(1) = (dlog(nhigh/nex)/tau1 + dlog(nex/nlow)/tau2)/dlog(nhigh/nlow) ! use density-weighted loss rate - RatefnWM(2) = (dlog(nhigh/nex)*R1 + dlog(nex/nlow)*R2)/dlog(nhigh/nlow) - elseif (tau_c < 1.e10) then - tau = tau_c - RatefnWM(1) = 1./tau - RatefnWM(2) = 2.0 - elseif (tau_h < 1.e10) then - tau = tau_h - RatefnWM(1) = 1./tau - RatefnWM(2) = 1.0 - else ! No wave mode triggered - tau = 1.e10 - RatefnWM(1) = 1./tau - RatefnWM(2) = -10.0 - endif - endif + + Lo = 8.0 !Outer L + Li = 7.0 !Middle L + + L = sqrt(xx**2+yy**2) + + if (L > Lo) then + !Only plasma sheet + rPS = LossR8_PSHEET(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + RatefnWM(1) = rPS + RatefnWM(2) = 3.0 + else if (L < Li) then + !IMAG only + rIMAG = LossR8_IMAG(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + RatefnWM(1) = rIMAG(1) + RatefnWM(2) = rIMAG(2) + + else + !Middle + rIMAG = LossR8_IMAG (xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + rPS = LossR8_PSHEET(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + wIMAG = RampDown(L,Li,Lo-Li) !Ramp from 1 to 0 + RatefnWM(1) = wIMAG*rIMAG(1) + (1-wIMAG)*rPS + RatefnWM(2) = 3.0-wIMAG + endif + + contains + + FUNCTION LossR8_IMAG(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,nex,kpx,beqx,losscx + REAL (rprec), dimension(2) :: LossR8_IMAG + + REAL (rprec) :: nhigh,nlow,MLT,K,E,tau_c,tau_h + REAL (rprec) :: kev0,E0,tScl !Energy min + + !NOTE: These values should be moved somewhere more appropriate + kev0 = 1.1 !Min value to allow [keV] + nhigh = 100.D0 ! [/cc] ne>nhigh indicates inside plasmasphere. + nlow = 10.D0 ! [/cc] nenhigh) then + ! Region inside the plasmasphere, wave candidates: Hiss + tau_h = tScl*RatefnC_tau_h16(MLT,E,L,kpx) + LossR8_IMAG(1) = 1.0/tau_h + LossR8_IMAG(2) = 1.0 !wave type number for hiss + else + ! nlow <= nex <= nhigh, at the plume region, wave candidates: + ! Chorus and Hiss + tau_c = tScl*RatefnDW_tau_c(kpx,MLT,L,E) + tau_h = tScl*RatefnC_tau_h16(MLT,E,L,kpx) + + LossR8_IMAG(1) = (dlog(nhigh/nex)/tau_c + dlog(nex/nlow)/tau_h)/dlog(nhigh/nlow) ! use density-weighted loss rate + LossR8_IMAG(2) = (dlog(nhigh/nex)*2.0 + dlog(nex/nlow)*1.0)/dlog(nhigh/nlow) + endif + + END FUNCTION LossR8_IMAG + + FUNCTION LossR8_PSHEET(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,nex,kpx,beqx,losscx + REAL (rprec) :: LossR8_PSHEET + REAL (rprec) :: TauSS + + TauSS = RatefnC_tau_s(alamx,vmx,beqx,losscx) + LossR8_PSHEET = 1.0/TauSS + LossR8_PSHEET = LossR8_PSHEET/3.0 !1/3 SS rate + END FUNCTION LossR8_PSHEET + + !Calculate speed (Re/s) from energy [keV] for ELECTRONS + FUNCTION kev2V(keV) result(V) + REAL (rprec), INTENT (IN) :: keV + REAL (rprec) :: V + + REAL (rprec) :: E,gammar + + E = keV*1.0e-3 !Energy [MeV] + gammar = 1.0 + E/mec2 + + V = vc_cgs*sqrt(1.0-1.0/gammar**2)/Re_cgs ! Re/s + + END FUNCTION kev2V END FUNCTION RatefnWM - -FUNCTION RatefnC19 (xx,yy,alamx,vmx,beqx,losscx,nex,kpx) -! Function to calculate diffuse electron precipitation loss rate using eq(10) of MW Chen et al. 2019. -! loss rate = 1/tau. Need to find tau. -! tau = (1+lambda_w*tau_s)/lambda_w for ne<10/cc, 0<=MLT<=15 and 21<=MLT<=24, (outside PP). -! tau = (1+lambda_h*tau_s)/lambda_h for ne>100/cc, 3<=R0<=6 and all MLTs, (inside PP). -! tau = (log(100)-1og(ne))/(log(100)-log(10))*(1+tau_w*tau_s)/lambda_w + (log(ne)-log(10))/(log(100)-log(10))*(1+lambda_h*tau_s)/lambda_h, for 10/ccnhigh indicates inside plasmasphere. - nlow = 10.D0 ! [/cc] ne6 or L<3 or Kp>6 or Ek>10MeV or Ek<1keV or log10(Ek[MeV])nhigh) then - tau = tau_s + RatefnC_tau_h16(MLT,K,L,kpx) ! mltx,engx,kpx,Lshx - RatefnC19(2) = 2.0 - else - tau = tau_s + (dlog(nhigh/nex)*RatefnC_tau_w(MLT,K,L,kpx) + dlog(nex/nlow)*RatefnC_tau_h16(MLT,K,L,kpx))/dlog(nhigh/nlow) - RatefnC19(2) = 3.0 - endif - - ! default MLT dependent scattering rate based on Chen+2005 for non-specified MLTs etc. -! if(tau>1.D10) then - E = log10(K) -! fL = -0.2573*L**4 + 4.2781*L**3 - 25.9348*L*L + 66.8113*L - 66.1182 -! if((nex15.0).or.(nex>nhigh.and.(L>6.0 .or. L<3.0 .or. E>1.0 .or. E<-3.0 .or. E6.0 - fL = 0.1328*L*L - 2.1463*L + 3.7857 - if(((nex15.0)).or.((nex>nhigh) .and. ((L>5.5) .or. (L<1.5) .or. (E>1.0) .or. (E<-3.0) .or. (E Date: Wed, 30 Aug 2023 08:04:28 -0600 Subject: [PATCH 028/365] fix type for h5 id --- src/base/ioH5.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/ioH5.F90 b/src/base/ioH5.F90 index 3377db4f..005c4a48 100644 --- a/src/base/ioH5.F90 +++ b/src/base/ioH5.F90 @@ -662,7 +662,7 @@ contains !> subroutine CheckAttCacheSize(stepStr, cacheId, cacheExist, cacheCreated) character(len=*), intent(in) :: stepStr - integer, intent(in) :: cacheId + integer(HID_T), intent(in) :: cacheId logical, intent(in) :: cacheExist logical, intent(in) :: cacheCreated integer :: nStep From 749c08d7b659b534cabaaf0282a0cb31778d5d8d Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Fri, 1 Sep 2023 15:05:16 -0600 Subject: [PATCH 029/365] Refine the 4D interpolation for chours wave. --- src/rcm/lossutils.F90 | 53 +++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/rcm/lossutils.F90 b/src/rcm/lossutils.F90 index 8d580c00..f2d7a509 100644 --- a/src/rcm/lossutils.F90 +++ b/src/rcm/lossutils.F90 @@ -175,20 +175,25 @@ MODULE lossutils REAL(rprec) :: dK,wK,dM,wM,dL,wL,dE,wE INTEGER :: iK,kL,kU,mL,mU,lL,lU,eL,eU LOGICAL :: Lflag = .false. - + LOGICAL :: isMin(4) = [.false.,.false.,.false.,.false.] !index 1-4 correspond to Kp,mlt,L and E + LOGICAL :: isMax(4) = [.false.,.false.,.false.,.false.] associate(Nm=>EWMTauInput%ChorusTauInput%Nm,Nl=>EWMTauInput%ChorusTauInput%Nl,Nk=>EWMTauInput%ChorusTauInput%Nk,Ne=>EWMTauInput%ChorusTauInput%Ne,& Kpi=>EWMTauInput%ChorusTauInput%Kpi,MLTi=>EWMTauInput%ChorusTauInput%MLTi,Li=>EWMTauInput%ChorusTauInput%Li,Eki=>EWMTauInput%ChorusTauInput%Eki,& taui=>EWMTauInput%ChorusTauInput%taui) + taui = log10(taui) !Interpolation in log10 space + ! look up in Kp !iK = minloc(abs(Kpi-Kpx),dim=1) ! Find the nearest neighbors in Kp if (Kpx >= maxval(Kpi)) then + isMax(1) = .true. kL = Nk kU = Nk else if (Kpx <= minval(Kpi)) then + isMin(1) = .true. kL = 1 kU = 1 else @@ -198,8 +203,10 @@ MODULE lossutils ! Find the nearest neighbours in MLT if ((mltx >= maxval(MLTi)) .or. (mltx <= minval(MLTi))) then ! maxval of MLT is 24, minval of MLT is 0 - mL = 1 - mU = 1 + isMax(1) = .true. + isMin(2) = .true. + mL = 1 + mU = 1 ! equivalent to mL = Nm, mU = Nm else mL = maxloc(MLTi,dim=1,mask=(MLTi= maxval(Li)) then + isMax(3) = .true. lL = Nl ! tau_c is 0, total tau = tau_s lU = Nl - Lflag = .true. else if (Lx <= minval(Li)) then - lL = 0 ! Lx < min(Li) is treated like min(Li) - lU = 0 + isMin(3) = .true. + lL = 1 ! Lx < min(Li) is treated like min(Li) + lU = 1 else lL = maxloc(Li,dim=1,mask=(Li= maxval(Eki)) then + if (Ekx >= maxval(Eki)) then + isMax(4) = .true. eL = Ne ! Ekx > max(Eki) is treated like max(Eki) eU = Ne + else if (Ekx < minval(Eki)) then + isMin(4) = .true. + !indices are not needed. else eL = maxloc(Eki,dim=1,mask=(Eki max(Li), assign 0 for now and replace it by strong scattering later - ! !write(*,*)"Corner case1,tau=tau_ss" - ! return - if (eU == -1) then + !Corner case: if Ek < min(Eki), use a large lifetime 10^10s ~ 10^3 years + if (isMin(4)) then tau = 1.D10 - !write(*,*)"Corner case2,tau=1e10" return end if !linear interpolation in Kp - if (kL == kU) then + if (isMax(1) .or. isMin(1)) then + ! In this case, kL = kU tauMLE(1,1,1) = taui(kL,mL,lL,eL) tauMLE(1,1,2) = taui(kL,mL,lL,eU) tauMLE(1,2,1) = taui(kL,mL,lU,eL) @@ -281,7 +286,8 @@ MODULE lossutils end if ! linear interpolation in mlt - if (mL == mU) then + if (isMax(2) .or. isMin(2)) then + ! In this case, degenerate in MLT dimension tauLE(1,1) = tauMLE(2,1,1) tauLE(1,2) = tauMLE(2,1,2) tauLE(2,1) = tauMLE(2,2,1) @@ -296,10 +302,11 @@ MODULE lossutils end if ! linear interpolation in L - if (lL == lU) then + if (isMax(3) .or. isMin(3)) then + ! In this case, degenerate in L dimension tauE(1) = tauLE(2,1) tauE(2) = tauLE(2,2) - if (Lflag) then ! use gaussian decay for L > maxval(Li) (7Re) + if (isMax(3)) then ! use gaussian decay for L > maxval(Li) (7Re) tauE(1)=tauE(1)/exp(-(Lx-maxval(Li))**2) tauE(2)=tauE(2)/exp(-(Lx-maxval(Li))**2) endif @@ -311,7 +318,8 @@ MODULE lossutils end if ! linear interpolation in Ek - if (eL == eU) then + if (isMax(4)) then + ! In this case, degenerate in Ek dimension tau = tauE(1) else dE = Eki(eU)-Eki(eL) @@ -319,6 +327,7 @@ MODULE lossutils tau = tauE(1) + wE*(tauE(2)-tauE(1)) end if end associate + tau = 10.**tau ! in seconds END FUNCTION RatefnDW_tau_c From 53ad593340e89baf1cf1138183671ddb19579c18 Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Tue, 5 Sep 2023 21:39:33 -0600 Subject: [PATCH 030/365] New chorus wave with polynomial fit and MLT interpolation. --- kaipy/rcm/wmutils/chorus_polynomial.txt | 169 ++++++++++++++++++++ kaipy/rcm/wmutils/genWM.py | 201 ++++++++++++++++-------- kaipy/rcm/wmutils/wmData.py | 2 +- scripts/preproc/genRCM.py | 4 +- 4 files changed, 311 insertions(+), 65 deletions(-) create mode 100644 kaipy/rcm/wmutils/chorus_polynomial.txt diff --git a/kaipy/rcm/wmutils/chorus_polynomial.txt b/kaipy/rcm/wmutils/chorus_polynomial.txt new file mode 100644 index 00000000..0f22683b --- /dev/null +++ b/kaipy/rcm/wmutils/chorus_polynomial.txt @@ -0,0 +1,169 @@ +MLT Kp Intercept L log10(E) L^2 log10(E)^2 L^3 log10(E)^3 log10(E)*L log10(E)*L^2 log10(E)^2*L Adjusted R2 +0 1 -9.717181e+00 7.204274e+00 -3.773921e+00 -1.496923e+00 -8.897443e-01 1.122206e-01 1.720684e-01 7.319817e-01 7.886410e-02 3.525678e-01 0.82448 +0 2 -2.945411e+00 3.066352e+00 -3.141152e+00 -6.402161e-01 -1.039579e+00 5.404518e-02 1.168904e-01 9.266642e-01 5.994503e-02 3.966304e-01 0.93159 +0 3 9.890714e-01 6.886358e-01 -1.466153e+00 -1.451928e-01 -4.153113e-01 2.069758e-02 1.924237e-01 9.012235e-01 5.583555e-02 3.945901e-01 0.95829 +0 4 3.852039e+00 -1.176268e+00 -4.278696e-01 2.504448e-01 9.717711e-02 -5.547521e-03 2.740005e-01 9.408564e-01 5.187616e-02 4.049745e-01 0.96932 +0 5 6.006599e+00 -2.177471e+00 1.988820e+00 4.797396e-01 1.590386e+00 -2.211532e-02 5.205646e-01 1.016683e+00 3.518574e-02 3.863031e-01 0.97468 +0 6 9.362199e+00 -4.631816e+00 2.171715e+00 9.973425e-01 1.628007e+00 -5.584455e-02 5.182470e-01 9.777994e-01 3.918595e-02 3.857173e-01 0.97183 +0 7 1.407725e+01 -7.921180e+00 2.665594e+00 1.685897e+00 1.719137e+00 -1.007732e-01 5.024012e-01 8.108719e-01 4.952734e-02 3.557380e-01 0.96121 +1 1 -7.359401e+00 5.884180e+00 -2.919064e+00 -1.280964e+00 -7.194898e-01 1.004658e-01 1.848896e-01 4.476771e-01 9.795562e-02 3.246588e-01 0.8113 +1 2 -9.211592e-01 1.879919e+00 -2.331452e+00 -4.555934e-01 -9.799721e-01 4.440510e-02 9.607246e-02 5.884254e-01 8.034954e-02 3.569996e-01 0.93061 +1 3 2.214357e+00 -1.302451e-01 -1.253408e+00 -2.825422e-02 -6.760874e-01 1.478623e-02 1.114119e-01 6.211463e-01 6.977352e-02 3.569444e-01 0.95885 +1 4 4.602497e+00 -1.757413e+00 -5.268525e-01 3.225730e-01 -3.095982e-01 -8.982241e-03 1.642781e-01 7.068561e-01 5.973179e-02 3.663537e-01 0.96771 +1 5 5.647597e+00 -2.090594e+00 2.072220e+00 4.521858e-01 1.630797e+00 -2.093337e-02 5.073641e-01 9.731945e-01 2.705271e-02 3.561372e-01 0.9769 +1 6 9.041642e+00 -4.539197e+00 2.387552e+00 9.639003e-01 1.697711e+00 -5.406964e-02 5.076273e-01 8.951338e-01 3.382134e-02 3.521105e-01 0.97444 +1 7 1.358006e+01 -7.724426e+00 2.883817e+00 1.632235e+00 1.787948e+00 -9.760212e-02 4.934266e-01 7.232415e-01 4.596935e-02 3.247448e-01 0.96432 +2 1 -3.874943e+00 4.102231e+00 -1.373509e+00 -1.041292e+00 -4.183570e-01 9.227025e-02 2.361563e-01 -1.138304e-01 1.485082e-01 3.037375e-01 0.7916 +2 2 1.135135e+00 8.053878e-01 -1.219720e+00 -3.253353e-01 -8.572429e-01 4.055670e-02 9.456559e-02 1.233136e-01 1.146499e-01 3.188955e-01 0.92407 +2 3 3.470548e+00 -8.068562e-01 -5.982210e-01 3.832536e-02 -7.467759e-01 1.380794e-02 7.074457e-02 2.502543e-01 9.399586e-02 3.198763e-01 0.95775 +2 4 5.298927e+00 -2.116907e+00 -7.789224e-02 3.310106e-01 -4.724148e-01 -6.974028e-03 9.885310e-02 3.806822e-01 7.604378e-02 3.223885e-01 0.96672 +2 5 5.390708e+00 -2.080829e+00 1.977300e+00 4.387184e-01 1.568525e+00 -2.012174e-02 4.844172e-01 9.273081e-01 2.346362e-02 3.360744e-01 0.97834 +2 6 8.740577e+00 -4.493841e+00 2.354275e+00 9.429094e-01 1.665505e+00 -5.276517e-02 4.895662e-01 8.388821e-01 3.088775e-02 3.306408e-01 0.97598 +2 7 1.312859e+01 -7.579869e+00 2.884980e+00 1.591603e+00 1.778583e+00 -9.498086e-02 4.806610e-01 6.638055e-01 4.400758e-02 3.043388e-01 0.96631 +3 1 -3.007360e+00 3.878241e+00 -2.503594e-01 -1.104900e+00 -2.463186e-01 1.027349e-01 2.620712e-01 -5.812605e-01 1.875912e-01 2.802112e-01 0.76887 +3 2 1.428129e+00 7.946179e-01 -4.736181e-01 -4.029787e-01 -8.095079e-01 5.002179e-02 9.131136e-02 -2.468938e-01 1.429859e-01 2.920961e-01 0.91249 +3 3 3.492771e+00 -7.037845e-01 -6.385505e-02 -4.812099e-02 -7.847110e-01 2.270458e-02 4.455866e-02 -6.340309e-02 1.147853e-01 2.888142e-01 0.95362 +3 4 5.076508e+00 -1.881944e+00 3.548632e-01 2.252584e-01 -5.466503e-01 2.435504e-03 5.898316e-02 1.014577e-01 9.081530e-02 2.853255e-01 0.96397 +3 5 5.003611e+00 -1.994916e+00 1.763108e+00 4.165897e-01 1.480360e+00 -1.889926e-02 4.619276e-01 9.120572e-01 1.959584e-02 3.219342e-01 0.97861 +3 6 8.287509e+00 -4.367077e+00 2.157762e+00 9.136333e-01 1.595472e+00 -5.121285e-02 4.693425e-01 8.258174e-01 2.614249e-02 3.146205e-01 0.97598 +3 7 1.256192e+01 -7.383831e+00 2.689740e+00 1.549352e+00 1.722078e+00 -9.259352e-02 4.643921e-01 6.559621e-01 3.934115e-02 2.896038e-01 0.96627 +4 1 1.050305e+00 7.848777e-01 9.479916e-01 -3.083059e-01 1.306091e+00 3.153739e-02 4.688779e-01 -2.334505e-01 6.162030e-02 7.291783e-02 0.68054 +4 2 4.040210e+00 -1.134349e+00 -1.503822e-01 7.706201e-02 -9.964354e-02 6.942414e-03 1.651045e-01 -2.702495e-02 5.874122e-02 1.475941e-01 0.89639 +4 3 4.404270e+00 -1.469072e+00 1.979190e-03 1.664247e-01 -4.765612e-01 -5.731605e-05 3.152533e-02 7.061707e-02 5.191952e-02 1.669078e-01 0.95584 +4 4 4.323673e+00 -1.548876e+00 2.847488e-01 2.024510e-01 -4.088867e-01 -3.419682e-03 -1.370144e-03 2.202261e-01 3.701387e-02 1.739477e-01 0.96832 +4 5 2.515772e+00 -4.929388e-01 1.519877e+00 1.045296e-01 1.563366e+00 1.304891e-03 4.611017e-01 9.923663e-01 8.081022e-03 3.041743e-01 0.97983 +4 6 3.614171e+00 -1.434655e+00 2.191388e+00 3.093934e-01 2.156221e+00 -1.268924e-02 5.007234e-01 1.031511e+00 -8.471922e-03 2.473691e-01 0.97928 +4 7 1.181002e+00 8.305165e-02 4.211134e+00 5.334156e-04 3.472403e+00 6.208793e-03 4.135005e-01 8.435557e-01 -4.340292e-02 -2.193345e-02 0.90882 +5 1 -3.635509e-01 1.537124e+00 6.839448e-01 -4.374485e-01 1.477097e+00 3.619611e-02 5.420787e-01 -3.200998e-02 4.117443e-02 1.085585e-01 0.66573 +5 2 2.730877e+00 -4.037838e-01 -3.262462e-01 -5.815076e-02 8.927091e-02 1.267726e-02 2.225624e-01 1.105053e-01 3.799409e-02 1.519576e-01 0.85975 +5 3 3.130800e+00 -7.408152e-01 -8.019317e-03 2.688265e-02 -2.800136e-01 6.361500e-03 6.581481e-02 1.283701e-01 3.327723e-02 1.422884e-01 0.94739 +5 4 2.955802e+00 -7.650633e-01 2.303200e-01 5.182572e-02 -2.653012e-01 3.848474e-03 1.075704e-02 2.684518e-01 1.739558e-02 1.381535e-01 0.96545 +5 5 2.124358e+00 -4.028353e-01 1.101238e+00 9.212299e-02 1.452351e+00 2.396013e-03 4.613711e-01 1.037621e+00 1.078170e-02 3.217369e-01 0.97517 +5 6 5.591571e+00 -2.882277e+00 1.412788e+00 6.001848e-01 1.516588e+00 -3.005855e-02 4.286400e-01 9.915549e-01 1.723768e-02 3.235609e-01 0.95989 +5 7 4.501496e-01 3.031204e-01 3.365214e+00 -1.985564e-02 3.409760e+00 6.876650e-03 4.318068e-01 1.082152e+00 -5.730041e-02 1.509701e-03 0.90629 +6 1 -1.082603e+00 1.875584e+00 9.310370e-01 -4.950986e-01 1.787906e+00 3.858447e-02 6.521055e-01 1.961963e-02 4.202852e-02 1.523830e-01 0.71376 +6 2 2.226915e+00 -1.704689e-01 -3.963971e-01 -1.029844e-01 3.216038e-01 1.479182e-02 3.368555e-01 1.955210e-01 3.696059e-02 1.988433e-01 0.8417 +6 3 2.709992e+00 -5.353846e-01 -5.663882e-02 -1.585543e-02 -1.096716e-01 8.494261e-03 1.598446e-01 1.609246e-01 3.461982e-02 1.766625e-01 0.9334 +6 4 2.586263e+00 -5.847657e-01 1.943038e-01 1.230286e-02 -1.479929e-01 5.925418e-03 8.955435e-02 2.580448e-01 2.255908e-02 1.664718e-01 0.95531 +6 5 1.504292e+00 -1.921066e-01 4.736238e-01 4.748769e-02 1.344912e+00 6.977116e-03 5.404359e-01 1.107407e+00 2.810924e-02 4.008609e-01 0.96374 +6 6 4.466400e+00 -2.382881e+00 6.159318e-01 5.076358e-01 1.471883e+00 -2.276222e-02 5.221047e-01 1.166062e+00 2.602738e-02 4.047381e-01 0.94311 +6 7 2.351494e-01 1.666321e-01 2.214849e+00 2.901656e-02 3.314877e+00 4.447027e-03 5.253594e-01 1.391421e+00 -5.814033e-02 9.645109e-02 0.91061 +7 1 -1.609647e+00 2.138282e+00 9.978059e-01 -5.454796e-01 2.010335e+00 4.129492e-02 7.025682e-01 7.441586e-02 3.843694e-02 1.605779e-01 0.74681 +7 2 1.205435e+00 4.161637e-01 -6.240078e-01 -2.239074e-01 5.056336e-01 2.247393e-02 4.051097e-01 2.968045e-01 3.199971e-02 2.171849e-01 0.82391 +7 3 1.735397e+00 2.397207e-02 -2.982400e-01 -1.309153e-01 3.509832e-02 1.574091e-02 2.127733e-01 2.436606e-01 2.895244e-02 1.849069e-01 0.91497 +7 4 1.747765e+00 -1.078381e-01 -6.498805e-02 -8.557804e-02 -6.364141e-02 1.200978e-02 1.234992e-01 3.194689e-01 1.761723e-02 1.681927e-01 0.94378 +7 5 5.800442e-01 2.740269e-01 -1.060418e-01 -3.751396e-02 1.318171e+00 1.332550e-02 5.758035e-01 1.244108e+00 3.008228e-02 4.363843e-01 0.95472 +7 6 3.240336e+00 -1.725721e+00 5.217625e-02 3.879902e-01 1.535061e+00 -1.431980e-02 5.734181e-01 1.338752e+00 2.441768e-02 4.362915e-01 0.9364 +7 7 -3.419454e-01 3.364636e-01 1.162533e+00 2.115812e-02 3.261958e+00 5.064395e-03 5.677576e-01 1.717444e+00 -7.004800e-02 1.465979e-01 0.91279 +8 1 -1.308247e+00 1.888381e+00 9.812949e-01 -4.930137e-01 2.128228e+00 3.763054e-02 7.285720e-01 1.276625e-01 3.701688e-02 1.744106e-01 0.78102 +8 2 6.496435e-01 7.090310e-01 -9.210358e-01 -2.847685e-01 6.275309e-01 2.650405e-02 4.616605e-01 4.027105e-01 2.954339e-02 2.436590e-01 0.81642 +8 3 1.224925e+00 2.956671e-01 -5.856631e-01 -1.880907e-01 1.435859e-01 1.951742e-02 2.621966e-01 3.290408e-01 2.645165e-02 2.028016e-01 0.89064 +8 4 1.362127e+00 9.646588e-02 -2.953666e-01 -1.299670e-01 2.750683e-02 1.496309e-02 1.621236e-01 3.674189e-01 1.694558e-02 1.768604e-01 0.92377 +8 5 4.360637e-02 5.273507e-01 -5.878643e-01 -8.277919e-02 1.336013e+00 1.756848e-02 6.253131e-01 1.358198e+00 3.779334e-02 4.803578e-01 0.94084 +8 6 2.251631e+00 -1.218175e+00 -5.475816e-01 3.014285e-01 1.634329e+00 -7.652272e-03 6.444620e-01 1.540731e+00 2.635283e-02 4.850513e-01 0.92387 +8 7 -6.076540e-01 3.305252e-01 1.344023e-01 4.882027e-02 3.284135e+00 3.630749e-03 6.318609e-01 2.061262e+00 -8.015682e-02 2.045887e-01 0.91829 +9 1 -5.066641e-01 1.329285e+00 9.461658e-01 -3.763959e-01 2.240101e+00 2.978094e-02 7.368882e-01 1.972342e-01 3.197202e-02 1.768036e-01 0.81157 +9 2 9.160371e-02 1.003259e+00 -1.122999e+00 -3.433058e-01 8.014770e-01 3.033769e-02 5.132520e-01 4.999701e-01 2.610510e-02 2.586349e-01 0.82344 +9 3 6.441270e-01 6.074673e-01 -8.280261e-01 -2.502328e-01 3.001515e-01 2.354826e-02 3.102515e-01 4.242577e-01 2.217524e-02 2.137150e-01 0.86982 +9 4 9.621762e-01 3.067436e-01 -4.664940e-01 -1.727513e-01 1.712550e-01 1.775645e-02 2.018135e-01 4.221124e-01 1.508525e-02 1.801912e-01 0.90089 +9 5 -5.807546e-01 8.358583e-01 -9.227595e-01 -1.324629e-01 1.486863e+00 2.185117e-02 6.931474e-01 1.487158e+00 4.400470e-02 5.199000e-01 0.92804 +9 6 9.998263e-01 -5.206567e-01 -9.205089e-01 1.792725e-01 1.868123e+00 1.036909e-03 7.270077e-01 1.728786e+00 2.663989e-02 5.212735e-01 0.91573 +9 7 -1.395993e+00 6.936340e-01 -5.686723e-01 6.332513e-04 3.424236e+00 7.041325e-03 7.011817e-01 2.342476e+00 -8.797079e-02 2.473096e-01 0.92407 +10 1 6.079346e-01 5.764501e-01 8.115558e-01 -2.184695e-01 2.348472e+00 1.915197e-02 7.311287e-01 3.106179e-01 2.082615e-02 1.686596e-01 0.838 +10 2 -7.268649e-01 1.466423e+00 -1.092733e+00 -4.325192e-01 1.038170e+00 3.593924e-02 5.489411e-01 5.537068e-01 2.192478e-02 2.517090e-01 0.83772 +10 3 2.556828e-01 7.956948e-01 -1.003445e+00 -2.837742e-01 5.126386e-01 2.553391e-02 3.518927e-01 5.288852e-01 1.452997e-02 2.116595e-01 0.86064 +10 4 7.065774e-01 4.202723e-01 -6.093885e-01 -1.918654e-01 3.621433e-01 1.879915e-02 2.357079e-01 5.007935e-01 8.970089e-03 1.736803e-01 0.88263 +10 5 -9.592237e-01 1.000408e+00 -1.082641e+00 -1.462954e-01 1.769092e+00 2.324966e-02 7.676456e-01 1.630683e+00 4.522151e-02 5.448054e-01 0.92157 +10 6 -5.821246e-02 8.253724e-02 -1.046605e+00 8.037739e-02 2.225443e+00 7.622167e-03 8.093918e-01 1.903019e+00 2.274182e-02 5.378206e-01 0.91615 +10 7 -2.338184e+00 1.223269e+00 -7.855223e-01 -8.592414e-02 3.651338e+00 1.259107e-02 7.501100e-01 2.502162e+00 -9.368997e-02 2.579936e-01 0.93182 +11 1 1.584285e+00 -9.233146e-02 4.913702e-01 -7.566464e-02 2.481888e+00 9.490070e-03 7.329837e-01 4.912006e-01 3.914822e-03 1.591060e-01 0.85812 +11 2 -1.233141e+00 1.724787e+00 -9.581509e-01 -4.738511e-01 1.320496e+00 3.796105e-02 5.644950e-01 6.102217e-01 1.219256e-02 2.222982e-01 0.85947 +11 3 1.827068e-01 7.765828e-01 -1.183786e+00 -2.699423e-01 7.669048e-01 2.408920e-02 3.812356e-01 6.690887e-01 2.258100e-04 1.942093e-01 0.86762 +11 4 6.871972e-01 3.742267e-01 -7.970033e-01 -1.730136e-01 5.907723e-01 1.701475e-02 2.586978e-01 6.313598e-01 -4.919297e-03 1.544219e-01 0.87667 +11 5 -9.381048e-01 9.140218e-01 -1.186769e+00 -1.013428e-01 2.127841e+00 1.981853e-02 8.292878e-01 1.814266e+00 3.555110e-02 5.461664e-01 0.92408 +11 6 -4.972398e-01 3.141483e-01 -1.051545e+00 5.992790e-02 2.625577e+00 8.281900e-03 8.692018e-01 2.075338e+00 1.109089e-02 5.285473e-01 0.92303 +11 7 -2.912724e+00 1.566532e+00 -7.009429e-01 -1.358704e-01 3.905999e+00 1.504579e-02 7.656498e-01 2.588276e+00 -1.026651e-01 2.357367e-01 0.94093 +12 1 3.795952e+00 -1.609479e+00 -3.957772e-01 2.542400e-01 2.626393e+00 -1.324950e-02 7.607905e-01 8.914563e-01 -3.213962e-02 1.607167e-01 0.81496 +12 2 9.414399e-01 7.276113e-03 -2.638211e+00 -4.937976e-02 2.464290e+00 5.299087e-03 5.154030e-01 1.694110e+00 -1.217717e-01 1.060381e-02 0.92512 +12 3 -1.826336e+00 2.001220e+00 -1.062499e+00 -5.044658e-01 1.421460e+00 3.846524e-02 3.475318e-01 8.563474e-01 -3.515948e-02 7.200227e-02 0.93612 +12 4 1.091590e-01 6.744938e-01 -1.006791e+00 -2.180554e-01 1.035742e+00 1.896149e-02 2.513562e-01 8.626383e-01 -3.680987e-02 8.137101e-02 0.90779 +12 5 -2.650530e-01 3.934711e-01 -1.363908e+00 4.468812e-02 2.491388e+00 8.265016e-03 8.734229e-01 2.064115e+00 1.198487e-02 5.295803e-01 0.92943 +12 6 1.403355e+00 -9.923685e-01 -1.340616e+00 3.576970e-01 2.578394e+00 -1.248317e-02 9.303741e-01 2.201778e+00 1.047900e-02 5.851626e-01 0.89932 +12 7 2.509748e+00 -1.973148e+00 -1.179463e+00 5.883023e-01 2.987827e+00 -2.935602e-02 1.016046e+00 2.393637e+00 -1.646417e-02 5.747707e-01 0.92602 +13 1 4.746942e+00 -2.558156e+00 -1.186727e+00 4.880320e-01 2.900345e+00 -2.996777e-02 6.265849e-01 1.273604e+00 -7.816415e-02 5.587602e-02 0.88353 +13 2 4.831451e+00 -2.725641e+00 -2.085771e+00 5.398800e-01 2.502417e+00 -3.465279e-02 3.921621e-01 1.614453e+00 -1.319834e-01 -6.435277e-02 0.95219 +13 3 3.992977e+00 -2.157449e+00 -1.657250e+00 4.149461e-01 1.869796e+00 -2.561423e-02 2.871417e-01 1.343384e+00 -1.034915e-01 -3.756972e-02 0.95133 +13 4 2.400486e+00 -1.090502e+00 -1.266064e+00 1.786835e-01 1.361613e+00 -8.450192e-03 2.460354e-01 1.080190e+00 -6.583955e-02 2.563368e-02 0.92219 +13 5 -9.144487e-01 8.800053e-01 -1.485896e+00 -2.665061e-02 2.909180e+00 1.033890e-02 9.077201e-01 2.325317e+00 -2.244458e-02 4.854397e-01 0.94403 +13 6 -3.069663e+00 2.301743e+00 -2.144761e+00 -3.388892e-01 2.369229e+00 3.262324e-02 9.135671e-01 2.383983e+00 -4.136037e-03 5.984553e-01 0.9073 +13 7 -1.111552e+01 7.990864e+00 -3.070002e+00 -1.549400e+00 1.867375e+00 1.118725e-01 9.435024e-01 2.560170e+00 -1.446240e-02 6.814598e-01 0.88757 +14 1 1.242540e+00 -2.369845e-01 -1.419802e+00 8.260015e-03 2.972485e+00 1.156809e-03 5.212952e-01 1.408200e+00 -1.014738e-01 -2.296493e-02 0.89436 +14 2 1.368811e+00 -4.145601e-01 -2.280856e+00 5.740560e-02 2.450007e+00 -2.985853e-03 3.109832e-01 1.682819e+00 -1.424004e-01 -1.039245e-01 0.96163 +14 3 1.228625e+00 -3.074402e-01 -1.908024e+00 3.084710e-02 1.861609e+00 -7.324499e-04 2.322341e-01 1.454456e+00 -1.170960e-01 -6.583994e-02 0.95624 +14 4 9.318505e-01 -8.583122e-02 -1.478347e+00 -2.767564e-02 1.499320e+00 4.344851e-03 2.301372e-01 1.230955e+00 -8.575766e-02 -3.931169e-03 0.94057 +14 5 1.432796e+00 -7.039165e-01 -1.539426e+00 3.447957e-01 3.325479e+00 -1.743970e-02 9.270213e-01 2.570657e+00 -6.012586e-02 4.282312e-01 0.94925 +14 6 8.747952e+00 -5.660286e+00 -1.996150e+00 1.383810e+00 2.770045e+00 -8.538469e-02 9.037252e-01 2.494688e+00 -4.085627e-02 5.001229e-01 0.86508 +14 7 2.967365e+00 -1.483125e+00 -2.468505e+00 4.985097e-01 2.507725e+00 -2.839179e-02 9.571531e-01 2.559916e+00 -4.860509e-02 5.434484e-01 0.85267 +15 1 1.994942e+00 -6.555317e-01 -9.786397e-01 9.222377e-02 2.990598e+00 -4.051119e-03 5.103009e-01 1.267481e+00 -9.162911e-02 -3.369420e-02 0.89008 +15 2 1.963584e+00 -7.859760e-01 -2.306874e+00 1.416005e-01 2.554944e+00 -8.779408e-03 3.022099e-01 1.737735e+00 -1.513754e-01 -1.268836e-01 0.9563 +15 3 1.458040e+00 -4.180364e-01 -1.778768e+00 5.630375e-02 1.907537e+00 -2.330213e-03 2.224492e-01 1.426373e+00 -1.165456e-01 -7.909000e-02 0.95286 +15 4 2.745900e+00 -1.327290e+00 -1.972995e+00 2.563148e-01 1.682334e+00 -1.611474e-02 2.078639e-01 1.520069e+00 -1.218943e-01 -4.948951e-02 0.93794 +15 5 1.826867e+00 -9.040947e-01 -1.520564e+00 4.006556e-01 3.544015e+00 -2.263840e-02 9.401753e-01 2.685085e+00 -7.891802e-02 4.005182e-01 0.94983 +15 6 9.225378e+00 -5.938465e+00 -2.074143e+00 1.459699e+00 3.004200e+00 -9.190359e-02 9.271197e-01 2.649451e+00 -6.093322e-02 4.793848e-01 0.86549 +15 7 3.091311e+00 -1.498489e+00 -2.306911e+00 5.168763e-01 2.754097e+00 -3.127343e-02 9.674050e-01 2.639684e+00 -6.627824e-02 5.084716e-01 0.85807 +16 1 3.455794e+00 -1.486545e+00 -4.241646e-01 2.553252e-01 2.897685e+00 -1.393926e-02 4.953749e-01 1.043180e+00 -7.239104e-02 -3.229877e-02 0.88572 +16 2 2.605198e+00 -1.151861e+00 -2.244961e+00 2.195486e-01 2.583749e+00 -1.359540e-02 2.940331e-01 1.720754e+00 -1.512312e-01 -1.393042e-01 0.94717 +16 3 1.819715e+00 -5.552250e-01 -1.492429e+00 7.627047e-02 1.815396e+00 -2.575816e-03 2.228693e-01 1.269107e+00 -9.778961e-02 -6.373164e-02 0.94071 +16 4 1.261123e+00 -1.728350e-01 -1.180981e+00 -1.378674e-02 1.472255e+00 4.495729e-03 2.288470e-01 1.093459e+00 -6.965027e-02 1.820913e-03 0.92354 +16 5 1.662075e+00 -5.909674e-01 -1.032808e+00 3.036510e-01 3.420686e+00 -1.432329e-02 9.422668e-01 2.419683e+00 -5.111377e-02 4.185730e-01 0.93755 +16 6 9.062144e+00 -5.641461e+00 -1.647969e+00 1.368319e+00 2.893126e+00 -8.392754e-02 9.367054e-01 2.409603e+00 -3.379991e-02 5.021036e-01 0.83891 +16 7 2.972117e+00 -1.238725e+00 -1.911060e+00 4.349748e-01 2.623632e+00 -2.402190e-02 9.727758e-01 2.407711e+00 -3.981335e-02 5.321525e-01 0.8466 +17 1 6.065370e+00 -3.036624e+00 -1.204084e-02 5.641127e-01 2.756971e+00 -3.286474e-02 4.653054e-01 8.509755e-01 -5.683971e-02 -3.862627e-02 0.87655 +17 2 3.923084e+00 -1.907470e+00 -2.128049e+00 3.679667e-01 2.450187e+00 -2.189545e-02 2.936385e-01 1.601658e+00 -1.351094e-01 -1.236702e-01 0.92472 +17 3 1.621678e+00 -2.015437e-01 -6.733390e-01 -3.357035e-02 1.440304e+00 8.215388e-03 2.342461e-01 7.653045e-01 -3.439944e-02 5.107264e-03 0.91385 +17 4 1.111911e+00 1.243552e-01 -5.019693e-01 -1.052095e-01 1.134048e+00 1.355940e-02 2.308674e-01 6.698652e-01 -1.594878e-02 5.972407e-02 0.88394 +17 5 1.171040e+00 2.179109e-02 -3.418401e-01 1.133921e-01 2.971660e+00 2.502135e-03 9.291163e-01 1.896026e+00 9.636920e-03 4.748118e-01 0.90471 +17 6 8.870233e+00 -5.244293e+00 -1.112737e+00 1.223429e+00 2.451975e+00 -6.975656e-02 9.381103e-01 1.935719e+00 2.655594e-02 5.699057e-01 0.78699 +17 7 3.603072e+00 -1.434442e+00 -1.722606e+00 4.197760e-01 2.101419e+00 -1.864621e-02 9.686834e-01 2.030916e+00 1.464855e-02 6.122669e-01 0.83498 +18 1 1.082397e+01 -5.971812e+00 -4.770886e-01 1.146059e+00 2.577005e+00 -6.751868e-02 4.057286e-01 8.848197e-01 -6.116616e-02 -7.655496e-02 0.83153 +18 2 6.135939e+00 -3.064357e+00 -1.681550e+00 5.476164e-01 1.634596e+00 -2.726348e-02 2.771841e-01 1.006313e+00 -5.079006e-02 -1.432417e-02 0.83838 +18 3 1.724760e+00 -8.414072e-03 -3.017019e-01 -1.301847e-01 7.400475e-01 2.104870e-02 2.109209e-01 2.444144e-01 4.343080e-02 1.012454e-01 0.85289 +18 4 2.442864e+00 -5.483793e-01 -5.906341e-01 -6.476063e-03 5.333321e-01 1.231103e-02 2.145601e-01 3.817825e-01 3.833219e-02 1.472210e-01 0.83846 +18 5 1.344505e+00 9.699000e-02 -6.337698e-01 5.032382e-02 2.277124e+00 1.129115e-02 9.031503e-01 1.626972e+00 5.876497e-02 5.640278e-01 0.85781 +18 6 9.111154e+00 -5.207996e+00 -1.319588e+00 1.167587e+00 1.705285e+00 -6.148092e-02 8.926992e-01 1.622487e+00 7.799110e-02 6.537055e-01 0.76933 +18 7 3.367097e+00 -1.055645e+00 -1.444817e+00 2.936776e-01 1.358158e+00 -6.841480e-03 8.764732e-01 1.570444e+00 6.559029e-02 6.528755e-01 0.85086 +19 1 1.045173e+01 -4.941345e+00 2.088544e+00 7.367673e-01 1.576796e+00 -2.060349e-02 3.211369e-01 -6.732945e-01 1.313307e-01 3.047173e-02 0.62546 +19 2 3.703805e+00 -5.375542e-01 8.605968e-01 -2.386394e-01 2.000571e-01 5.240058e-02 2.518747e-01 -8.312531e-01 2.203020e-01 2.530480e-01 0.76811 +19 3 3.272628e+00 -4.558256e-01 -2.863843e-01 -2.107743e-01 -2.212628e-01 4.758636e-02 2.495467e-01 -3.954864e-01 1.979005e-01 3.445951e-01 0.83723 +19 4 4.151799e+00 -1.163107e+00 -7.815812e-01 -3.602281e-02 -3.369410e-01 3.421682e-02 2.939125e-01 -1.301308e-01 1.840421e-01 4.095479e-01 0.84295 +19 5 2.430916e+00 -5.853717e-01 -1.651001e+00 1.615683e-01 1.360993e+00 7.354574e-03 8.027559e-01 1.550617e+00 8.437972e-02 6.310538e-01 0.8633 +19 6 9.589982e+00 -5.442344e+00 -1.902050e+00 1.178300e+00 7.118181e-01 -5.898198e-02 7.425192e-01 1.372019e+00 1.104244e-01 6.923491e-01 0.81967 +19 7 3.876753e+00 -1.278652e+00 -1.571642e+00 3.014910e-01 3.879600e-01 -5.220906e-03 6.825008e-01 1.184419e+00 9.635829e-02 6.463116e-01 0.89373 +20 1 8.818423e+00 -3.885616e+00 1.977768e+00 5.171642e-01 3.360926e-01 -6.969479e-05 3.518455e-01 -1.178708e+00 2.501245e-01 2.894164e-01 0.40912 +20 2 4.558966e+00 -1.402672e+00 -9.888905e-01 3.233388e-02 -4.743974e-01 3.207056e-02 1.957514e-01 -3.101404e-01 2.026337e-01 3.518889e-01 0.78776 +20 3 5.278691e+00 -1.976263e+00 -2.136653e+00 1.696426e-01 -8.774690e-01 2.264980e-02 2.230293e-01 1.211223e-01 1.908473e-01 4.719043e-01 0.87646 +20 4 7.510426e+00 -3.536111e+00 -2.728126e+00 5.091674e-01 -9.617025e-01 -2.305694e-05 2.979294e-01 4.199695e-01 1.829638e-01 5.617737e-01 0.8876 +20 5 4.348244e+00 -1.800377e+00 -2.704540e+00 3.893456e-01 3.900327e-01 -2.537683e-03 6.766795e-01 1.545482e+00 1.164300e-01 7.199897e-01 0.91058 +20 6 1.124045e+01 -6.484624e+00 -2.881991e+00 1.373009e+00 -2.577640e-01 -6.712893e-02 6.072741e-01 1.352019e+00 1.407729e-01 7.735385e-01 0.8799 +20 7 4.717043e+00 -1.737761e+00 -1.978561e+00 3.722787e-01 -4.753988e-01 -6.761770e-03 5.151032e-01 1.002508e+00 1.231074e-01 6.697817e-01 0.92448 +21 1 4.640154e+00 -2.058466e+00 -2.997508e+00 3.229845e-01 -3.267520e-01 7.138257e-03 5.035649e-01 4.081604e-01 1.794630e-01 5.391301e-01 0.67759 +21 2 2.121799e+00 -2.683458e-01 -3.333377e+00 -5.632149e-02 -8.802042e-01 2.931274e-02 1.548270e-01 6.114660e-01 1.282823e-01 4.094238e-01 0.86667 +21 3 6.281104e+00 -3.060090e+00 -3.968978e+00 5.414357e-01 -1.146769e+00 -1.021923e-02 1.901006e-01 9.614629e-01 1.213459e-01 5.239261e-01 0.92484 +21 4 8.247573e+00 -4.368089e+00 -3.711893e+00 8.203851e-01 -1.261375e+00 -2.889684e-02 2.156578e-01 9.815061e-01 1.295587e-01 5.859233e-01 0.94245 +21 5 6.556615e+00 -3.025109e+00 -2.490098e+00 6.514720e-01 -2.943759e-02 -2.238848e-02 5.300817e-01 1.512397e+00 1.036178e-01 6.936618e-01 0.94549 +21 6 6.664013e+00 -3.151148e+00 -2.756030e+00 6.730895e-01 -5.035543e-01 -2.392248e-02 5.015257e-01 1.426664e+00 1.177137e-01 7.459815e-01 0.93986 +21 7 2.741656e+00 -2.924058e-01 -2.782497e+00 4.167455e-02 -1.626785e+00 1.572766e-02 2.898142e-01 9.446596e-01 1.393182e-01 7.278788e-01 0.93601 +22 1 -4.590363e+00 3.849984e+00 -3.307862e+00 -8.018508e-01 -5.969935e-01 6.933581e-02 2.171315e-01 6.565482e-01 9.535360e-02 3.544051e-01 0.77153 +22 2 -2.609181e+00 2.507249e+00 -5.141995e+00 -5.146901e-01 -1.571547e+00 5.116362e-02 1.732032e-01 1.201708e+00 8.957430e-02 5.433813e-01 0.9206 +22 3 1.080075e+00 3.236284e-01 -3.718565e+00 -5.764888e-02 -1.178401e+00 1.959957e-02 1.928291e-01 1.188282e+00 8.092215e-02 5.341144e-01 0.95301 +22 4 4.479736e+00 -1.791747e+00 -2.497527e+00 3.837625e-01 -7.002120e-01 -9.574992e-03 2.565598e-01 1.158360e+00 8.196728e-02 5.367337e-01 0.96423 +22 5 7.115847e+00 -2.980620e+00 8.689305e-02 6.478047e-01 7.610591e-01 -2.795117e-02 4.940494e-01 1.193656e+00 7.286485e-02 5.257920e-01 0.96623 +22 6 1.034411e+01 -5.385961e+00 1.065337e-01 1.161385e+00 7.716018e-01 -6.171584e-02 4.895997e-01 1.208054e+00 7.274434e-02 5.283104e-01 0.96174 +22 7 1.527996e+01 -8.823876e+00 5.233917e-01 1.885894e+00 9.122497e-01 -1.095329e-01 4.840952e-01 1.107045e+00 7.496147e-02 4.965770e-01 0.94807 +23 1 -9.118623e+00 6.724055e+00 -3.965887e+00 -1.375352e+00 -8.335226e-01 1.046972e-01 2.009562e-01 8.231081e-01 8.124472e-02 3.770911e-01 0.80809 +23 2 -3.740883e+00 3.467194e+00 -3.997971e+00 -7.001114e-01 -1.237937e+00 5.863242e-02 1.334926e-01 1.117128e+00 6.114471e-02 4.509570e-01 0.93032 +23 3 5.376906e-01 9.692099e-01 -2.061007e+00 -1.823208e-01 -5.029704e-01 2.387347e-02 2.247389e-01 1.071661e+00 5.850010e-02 4.461086e-01 0.95698 +23 4 3.849287e+00 -1.117338e+00 -7.892561e-01 2.559385e-01 8.074780e-02 -5.009557e-03 3.151658e-01 1.066045e+00 5.871764e-02 4.531396e-01 0.96842 +23 5 6.541330e+00 -2.454719e+00 1.481125e+00 5.404290e-01 1.340032e+00 -2.428085e-02 5.163963e-01 1.067777e+00 5.079943e-02 4.374805e-01 0.9716 +23 6 9.778494e+00 -4.861683e+00 1.537440e+00 1.053167e+00 1.355536e+00 -5.796366e-02 5.118758e-01 1.069339e+00 5.137129e-02 4.387966e-01 0.96811 +23 7 1.467645e+01 -8.275620e+00 1.985977e+00 1.769856e+00 1.458738e+00 -1.049781e-01 4.988052e-01 9.306013e-01 5.826336e-02 4.082278e-01 0.95666 diff --git a/kaipy/rcm/wmutils/genWM.py b/kaipy/rcm/wmutils/genWM.py index 25c26575..ed7b078c 100644 --- a/kaipy/rcm/wmutils/genWM.py +++ b/kaipy/rcm/wmutils/genWM.py @@ -2,40 +2,38 @@ import numpy as np import h5py as h5 from scipy.interpolate import RectBivariateSpline from kaipy.rcm.wmutils.wmData import wmParams -# + def genWM(params, useWM=True): import os - - fInChorus = 'DWang_chorus_lifetime.h5' + + fInChorus = 'chorus_polynomial.txt' __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) fInChorus = os.path.join(__location__,fInChorus) print("Reading %s"%fInChorus) - + if useWM: - return readWM(params,fInChorus) + return genChorus(params,fInChorus) else: return toyWM(params) - - # Add wpi-induced electron lifetime model to input file and create an output file # Writes arrays to file in rcmconfig.h5 format def genh5(fIn, fOut, inputParams, useWM=True): - if fIn != fOut: + if fIn != fOut: oH5 = h5.File(fOut, 'w') - iH5 = h5.File(fIn,'r') + iH5 = h5.File(fIn,'r') for Q in iH5.keys(): sQ = str(Q) oH5.create_dataset(sQ, data=iH5[sQ]) else: oH5 = h5.File(fOut, 'r+') - + if not ('Taui' in oH5.keys()): - kpi, mlti, li, eki, taui = genWM(inputParams, useWM = useWM) + kpi, mlti, li, eki, taui = genWM(inputParams, useWM = useWM) attrs = inputParams.getAttrs() oH5.create_dataset('Kpi', data=kpi) @@ -47,59 +45,138 @@ def genh5(fIn, fOut, inputParams, useWM=True): oH5.attrs[key] = attrs[key] oH5.close() -def ReSample(cL,cK,Qp,s): - Q = np.log10(Qp) - SmthQ = RectBivariateSpline(cL,cK,Q.T,s=s) - Qs = SmthQ(cL,cK).T - return 10.0**(Qs) +#read parameters of the polynomial fit, Wang+,2023 +def readPoly(fIn): + table = [] + with open(fIn, 'r') as file: + # Skip the first row + next(file) + for line in file: + row = line.strip().split('\t')[2:-1] # Discard the first two elements of each row + row = [float(x) for x in row] # Convert the strings to float + rowLen = len(row) + table.append(np.array(row)) + return (rowLen,np.array(table)) + +#Chorus polynomial fit for the electron lifetime +def ChorusPoly(Lpoly,Kpoly,polyTgt): + + c0 = polyTgt[0]#Intercept + c1 = polyTgt[1] #L + c2 = polyTgt[2]#log10(E) + c3 = polyTgt[3]# L^2 + c4 = polyTgt[4]#log10(E)^2 + c5 = polyTgt[5]#L^3 + c6 = polyTgt[6]#log10(E)^3 + c7 = polyTgt[7]#log10(E)*L + c8 = polyTgt[8]#log10(E)*L^2 + c9 = polyTgt[9]#log10(E)^2*L + + lenL = len(Lpoly) + lenK = len(Kpoly) + + tau = np.ones((lenL,lenK)) + # Duplicating the array in columns + L = np.tile(Lpoly, (lenK, 1)).T + + # Duplicating the array in rows + K = np.tile(Kpoly, (lenL, 1)) + + tau = c0*tau+\ + c1*L+c2*K+\ + c3*np.power(L,2)+\ + c4*np.power(K,2)+\ + c5*np.power(L,3)+\ + c6*np.power(K,3)+\ + c7*L*K+\ + c8*np.power(L,2)*K+\ + c9*L*np.power(K,2) #in log10(days) + + tau = 10.0**tau*(60.*60.*24.) #in seconds + + return tau + +def ReSample(L,MLT,Qp,xMLT): + Nr,Np = Qp.shape + #Add ghosts in MLT to handle periodic boundary + Ng = 2 + Npg = Np+Ng*2 + gMLT = np.arange(0-Ng,24+Ng+1) + Qpg = np.zeros((Nr,Npg)) + #Set center and then left/right strips + Qpg[:,2:-2] = Qp + Qpg[:,1] = Qp[:,-1] + Qpg[:,0] = Qp[:,-2] + Qpg[:,-1] = Qp[:,0] + Qpg[:,-2] = Qp[:,1] + + Q = np.log10(Qpg) + upQ = RectBivariateSpline(L,gMLT,Q,s=10) + + Qu = upQ(L,xMLT) + xQp = 10.0**(Qu) + #Enforce equality at overlap point + tauP = 0.5*(xQp[:,0]+xQp[:,-1]) + xQp[:, 0] = tauP + xQp[:,-1] = tauP + + return xQp + +def genChorus(params,fInChorus): + rowLen,paramArray = readPoly(fInChorus) + polyArray = paramArray.reshape(24,7,rowLen) #Dim MLT: 24, Dim Kp: 7 + lenMLT = 24 + #Kpi + startValue = 1.0 #in Re + endValue = 7.0 + lenKp = 7 + Kpi = np.linspace(startValue, endValue, num=lenKp) + #Eki + startValue = 1.0e-3 #in MeV + endValue = 2.0 + lenEk = 155 + Eki = np.linspace(np.log10(startValue), np.log10(endValue), lenEk) #in log10(MeV) + #Li + startValue = 3.0 #in Re + endValue = 7.0 + lenL = 161 + Li = np.linspace(startValue, endValue, num=lenL) + #Tau from polynomial fit + tauP = np.zeros((lenKp,lenMLT,lenL,lenEk)) + for k in range(0,lenKp): # Kp: 1,2,...,7 + for m in range(0,lenMLT): # MLT: 0,1,...,23 + #print("xMLT,xKp",xMLT,xKp) + polyKM = polyArray[m,k,:] + #print('polyTgt',polyTgt) + tauPolyKM = ChorusPoly(Li,Eki,polyKM) + tauP[k,m,:,:] = tauPolyKM[:,:] + #expand MLT from 0-23 to 0-24 + tauE = np.array([np.append(tauP[0,:,:,:],np.array([tauP[0,0,:,:]]),0)]) + for i in range(1,lenKp): + tauE=np.append(tauE,np.array([np.append(tauP[i,:,:,:],np.array([tauP[i,0,:,:]]),0)]),0) + tauE = tauE.T + #Interpolation in the MLT dimesion + xFac = 4 + lenMLTx = lenMLT*xFac+1 # 97 + MLTi = np.linspace(0,24,lenMLT+1) + xMLTi = np.linspace(0,24,lenMLTx) + tauX = np.zeros((lenEk,lenL,lenMLTx,lenKp)) + for i in range(lenEk): + for j in range(lenKp): + Q = tauE[i,:,:,j] + tauX[i,:,:,j] = ReSample(Li,MLTi,Q,xMLTi) + Eki = 10.0**Eki #in MeV + return Kpi,xMLTi,Li,Eki,tauX + + -def readWM(params,fInChorus): - # add electron lifetime for the chorus wave loss - f5 = h5.File(fInChorus, 'r') - kpi=f5['Kp_1D'][:][0] - mlti=np.append(f5['MLT_1D'][:][0],24.) # 0, 1,...,24 - li=f5['L_1D'][:][0] - eki=10.**(f5['E_1D'][:][0]) # in MeV - taui=(10.**(f5['Tau2_4D'][:]))*24.*3600. # in second, use method 2 data - #print ("kpi",kpi,"mlti",mlti,"li",li,"eki",eki) - f5.close() - nk,nm,nl,ne = taui.shape - #expand mlt from 0:23 to 0:24 - tauEi = np.array([np.append(taui[0,:,:,:],np.array([taui[0,0,:,:]]),0)]) - for i in range(1,nk): - tauEi=np.append(tauEi,np.array([np.append(taui[i,:,:,:],np.array([taui[i,0,:,:]]),0)]),0) - tauEi = tauEi.T - #Smooth the expanded tau table - ne1,nl1,nm1,nk1 = tauEi.shape - tauSi = np.zeros((ne1,nl1,nm1,nk1)) #create new smoothed tau - s0 = 250 #Magic smoothing number - for m in range(nm1): - for k in range(nk1): - MLT0 = mlti[m] - Kp0 = kpi[k] - #print("MLT,Kp = %f,%f"%(MLT0,Kp0)) - m0 = np.abs(mlti-MLT0).argmin() - k0 = np.abs(kpi-Kp0).argmin() - tauMK = tauEi[:,:,m0,k0] - tauMKs = ReSample(li,eki*1e3,tauMK,s0) - tauSi[:,:,m,k] = tauMKs - return kpi,mlti,li,eki,tauSi -def toyWM(params): - nKpi = params.nKp - nMLTi = params.nMLT - nLi = params.nL - nEki = params.nEk - - kpi = np.linspace(1,7,nKpi) - mlti = np.linspace(0,24,nMLTi) #Note the dimension of MLT is 25 - li = np.linspace(3.,7.,nLi) - eki = np.exp(np.linspace(-3,0.1,nEki)) #in MeV - taui = kpi[:,None,None,None]*mlti[None,:,None,None]*li[None,None,:,None]*eki[None,None,None,:] - taui = taui.T - - return kpi,mlti,li,eki,taui - + + + + + + diff --git a/kaipy/rcm/wmutils/wmData.py b/kaipy/rcm/wmutils/wmData.py index 52906bd9..f59442c1 100644 --- a/kaipy/rcm/wmutils/wmData.py +++ b/kaipy/rcm/wmutils/wmData.py @@ -4,7 +4,7 @@ import numpy as np class wmParams: #All energies in eV - def __init__(self, dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155): + def __init__(self, dim = 4, nKp = 7, nMLT = 97, nL = 161, nEk = 155): self.dim = dim self.nKp = nKp self.nMLT = nMLT diff --git a/scripts/preproc/genRCM.py b/scripts/preproc/genRCM.py index 45467dfd..5b926e5c 100755 --- a/scripts/preproc/genRCM.py +++ b/scripts/preproc/genRCM.py @@ -78,7 +78,7 @@ if __name__ == "__main__": plotType = args.plotType if addWM: - tauParams = wmParams(dim = 4, nKp = 7, nMLT = 25, nL = 161, nEk = 155) + tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 161, nEk = 155) genWM.genh5(fIn,fOut,tauParams,useWM = True) else: # Determine proton channel limits based on resolving a certain (proton) temperature at given L @@ -106,7 +106,7 @@ if __name__ == "__main__": fileIO.saveRCMConfig(alamData,params=alamParams,fname=fOut) # Add data needed for wavemodel if not noWaveModel: - tauParams = wmParams(dim = 4, nKp = 7, nMLT = 25, nL = 41, nEk = 155) + tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 161, nEk = 155) genWM.genh5(fOut,fOut,tauParams,useWM = True) print("Wrote RCM configuration to %s"%(fOut)) From c5943a2cc7ff23844c636a20db567913bf061546 Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Fri, 8 Sep 2023 18:44:41 -0600 Subject: [PATCH 031/365] Minor fix --- src/base/defs/rcmdefs.F90 | 2 +- src/rcm/modules.F90 | 10 +++------- src/rcm/rcm_subs.F90 | 3 ++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/base/defs/rcmdefs.F90 b/src/base/defs/rcmdefs.F90 index e1b92f0c..d4c51dda 100644 --- a/src/base/defs/rcmdefs.F90 +++ b/src/base/defs/rcmdefs.F90 @@ -38,7 +38,7 @@ module rcmdefs REAL(krp), PARAMETER :: tiote_RCM = 4.0 enum, bind(C) - enumerator :: ELOSS_FDG=1,ELOSS_SS,ELOSS_C05,ELOSS_C19,ELOSS_WM !Choice of electron loss model + enumerator :: ELOSS_FDG=1,ELOSS_SS,ELOSS_WM !Choice of electron loss model end enum REAL(krp), PARAMETER :: bMin_C_DEF = 1.0 ![nT], default min allowable field strength diff --git a/src/rcm/modules.F90 b/src/rcm/modules.F90 index 5867137b..a9d3f6d9 100644 --- a/src/rcm/modules.F90 +++ b/src/rcm/modules.F90 @@ -27,7 +27,7 @@ MODULE rice_housekeeping_module use kronos use strings use earthhelper, ONLY : SetKp0 - use rcmdefs, ONLY : DenPP0, ELOSS_FDG, ELOSS_SS, ELOSS_C05, ELOSS_C19, ELOSS_WM + use rcmdefs, ONLY : DenPP0, ELOSS_FDG, ELOSS_SS, ELOSS_WM IMPLICIT NONE @@ -77,7 +77,7 @@ MODULE rice_housekeeping_module end type RCMEllipse_T type ChorusTauIn_T !electron lifetime for Chorus wave - integer(iprec) :: Nm=24, Nl=20, Nk=7 ,Ne=155 + integer(iprec) :: Nm=97, Nl=161, Nk=7 ,Ne=155 real(rprec), ALLOCATABLE :: MLTi(:), Li(:), Kpi(:), Eki(:) real(rprec), ALLOCATABLE :: taui(:,:,:,:) end type ChorusTauIn_T @@ -147,14 +147,10 @@ MODULE rice_housekeeping_module ELOSSMETHOD = ELOSS_FDG case ("SS") ELOSSMETHOD = ELOSS_SS - case ("C05") - ELOSSMETHOD = ELOSS_C05 - case ("C19") - ELOSSMETHOD = ELOSS_C19 case ("WM") ELOSSMETHOD = ELOSS_WM case default - stop "The electron loss type entered is not supported (Available options: WM, FDG, SS, C05, C19)." + stop "The electron loss type entered is not supported (Available options: WM, FDG, SS)." end select call xmlInp%Set_Val(doNewCX ,"loss/doNewCX" ,doNewCX ) diff --git a/src/rcm/rcm_subs.F90 b/src/rcm/rcm_subs.F90 index fa5aa472..8f92b863 100644 --- a/src/rcm/rcm_subs.F90 +++ b/src/rcm/rcm_subs.F90 @@ -1216,7 +1216,8 @@ integer :: nvari, tauDim, Nk, Nm,Nl,Ne integer :: dims(4) ! update when add higher dimensions - + write(*,*) "Read rcmconfig.h5.... +" doSP = .false. call ClearIO(IOVars) !Reset IO chain call AddInVar(IOVars,"alamc") !1 From fcd5e3edf5dd6b82d0806881acd061fdef3ec3c0 Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Fri, 8 Sep 2023 23:04:59 -0600 Subject: [PATCH 032/365] Minor fix --- src/rcm/lossutils.F90 | 140 ++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 81 deletions(-) diff --git a/src/rcm/lossutils.F90 b/src/rcm/lossutils.F90 index f2d7a509..912eb561 100644 --- a/src/rcm/lossutils.F90 +++ b/src/rcm/lossutils.F90 @@ -164,49 +164,38 @@ MODULE lossutils END FUNCTION RatefnC_tau_s - FUNCTION RatefnDW_tau_c(Kpx,mltx,Lx,Ekx) result(tau) ! linearly interpolate tau from EWMTauInput to current MLT,L,Kp,Ek value USE rice_housekeeping_module, ONLY: EWMTauInput IMPLICIT NONE REAL (rprec), INTENT (IN) :: Kpx, mltx,Lx,Ekx - REAL(rprec) :: taui,tau - REAL(rprec) :: tauKMLE(2,2,2,2),tauMLE(2,2,2),tauLE(2,2),tauE(2)! tauKMLE(1,2,2,2) means tauKlMuLuEu, l:lower bound, u: upper bound in the NN methond + REAL(rprec) :: tau + REAL(rprec) :: tauKMLE(2,2,2,2),tauMLE(2,2,2),tauLE(2,2),tauE(2)! tauKMLE(1,2,2,2) means tauKlMuLuEu, l:lower bound, u: upper bound in the NN methond REAL(rprec) :: dK,wK,dM,wM,dL,wL,dE,wE INTEGER :: iK,kL,kU,mL,mU,lL,lU,eL,eU LOGICAL :: Lflag = .false. - LOGICAL :: isMin(4) = [.false.,.false.,.false.,.false.] !index 1-4 correspond to Kp,mlt,L and E - LOGICAL :: isMax(4) = [.false.,.false.,.false.,.false.] + associate(Nm=>EWMTauInput%ChorusTauInput%Nm,Nl=>EWMTauInput%ChorusTauInput%Nl,Nk=>EWMTauInput%ChorusTauInput%Nk,Ne=>EWMTauInput%ChorusTauInput%Ne,& Kpi=>EWMTauInput%ChorusTauInput%Kpi,MLTi=>EWMTauInput%ChorusTauInput%MLTi,Li=>EWMTauInput%ChorusTauInput%Li,Eki=>EWMTauInput%ChorusTauInput%Eki,& - taui=>EWMTauInput%ChorusTauInput%taui) - - taui = log10(taui) !Interpolation in log10 space - - ! look up in Kp - !iK = minloc(abs(Kpi-Kpx),dim=1) + taui=>EWMTauInput%ChorusTauInput%taui) ! Find the nearest neighbors in Kp if (Kpx >= maxval(Kpi)) then - isMax(1) = .true. - kL = Nk + kL = Nk !use Kp maximum kU = Nk else if (Kpx <= minval(Kpi)) then - isMin(1) = .true. - kL = 1 + kL = 1 !use Kp minimum kU = 1 else kL = maxloc(Kpi,dim=1,mask=(Kpi= maxval(MLTi)) .or. (mltx <= minval(MLTi))) then ! maxval of MLT is 24, minval of MLT is 0 - isMax(1) = .true. - isMin(2) = .true. - mL = 1 - mU = 1 ! equivalent to mL = Nm, mU = Nm + if ((mltx >= maxval(MLTi)) .or. (mltx <= minval(MLTi))) then ! maxval of MLT is 24, minval of MLT is 0 + mL = 1 !use MLT = 0 + mU = 1 else mL = maxloc(MLTi,dim=1,mask=(MLTi= maxval(Li)) then - isMax(3) = .true. - lL = Nl ! tau_c is 0, total tau = tau_s + lL = Nl !use L maximum lU = Nl + Lflag = .true. else if (Lx <= minval(Li)) then - isMin(3) = .true. - lL = 1 ! Lx < min(Li) is treated like min(Li) + lL = 1 ! use L minimum lU = 1 else lL = maxloc(Li,dim=1,mask=(Li= maxval(Eki)) then - isMax(4) = .true. - eL = Ne ! Ekx > max(Eki) is treated like max(Eki) + if (Ekx < minval(Eki)) then + tau = 1.D10 ! For low energies, assign a huge lifetime is 10^10s ~ 10^3 years. + else if (Ekx >= maxval(Eki)) then + eL = Ne !use Ek maximum eU = Ne - else if (Ekx < minval(Eki)) then - isMin(4) = .true. - !indices are not needed. else eL = maxloc(Eki,dim=1,mask=(Eki maxval(Li) (7Re) + if (Lflag) then ! use gaussian decay for L > maxval(Li) (7Re) tauE(1)=tauE(1)/exp(-(Lx-maxval(Li))**2) tauE(2)=tauE(2)/exp(-(Lx-maxval(Li))**2) - endif + endif else dL = Li(lU)-Li(lL) wL = (Lx-Li(lL))/dL tauE(1) = tauLE(1,1)+ wL*(tauLE(2,1)-tauLE(1,1)) tauE(2) = tauLE(1,2)+ wL*(tauLE(2,2)-tauLE(1,2)) - end if - + end if + ! linear interpolation in Ek - if (isMax(4)) then - ! In this case, degenerate in Ek dimension + if (eL == eU) then tau = tauE(1) else dE = Eki(eU)-Eki(eL) - wE = (Ekx-Eki(eL))/dE - tau = tauE(1) + wE*(tauE(2)-tauE(1)) - end if + wE = (Ekx-Eki(eL))/dE + tau = tauE(1) + wE*(tauE(2)-tauE(1)) + end if + + tau = 10.0**tau !convert back to tau in seconds + end associate - tau = 10.**tau ! in seconds - + END FUNCTION RatefnDW_tau_c From f793e4cb82001c84cea6fabb9276fdcc335d593f Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Fri, 8 Sep 2023 23:07:44 -0600 Subject: [PATCH 033/365] Minor fix --- src/rcm/rcm_subs.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcm/rcm_subs.F90 b/src/rcm/rcm_subs.F90 index 8f92b863..6c22150a 100644 --- a/src/rcm/rcm_subs.F90 +++ b/src/rcm/rcm_subs.F90 @@ -1216,8 +1216,8 @@ integer :: nvari, tauDim, Nk, Nm,Nl,Ne integer :: dims(4) ! update when add higher dimensions - write(*,*) "Read rcmconfig.h5.... -" + write(*,*) "Read rcmconfig.h5...." + doSP = .false. call ClearIO(IOVars) !Reset IO chain call AddInVar(IOVars,"alamc") !1 From 60c21d97406ef281be36982ad1673ff5c17ea515 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Wed, 13 Sep 2023 08:47:52 -0600 Subject: [PATCH 034/365] Add linearized mono in mixconductance. --- src/base/defs/mixdefs.F90 | 6 +- src/remix/mixconductance.F90 | 179 +++++++++++++++++++++++++++++++++-- src/remix/mixio.F90 | 6 ++ src/remix/mixparams.F90 | 4 +- src/remix/tgcm.F90 | 2 + 5 files changed, 185 insertions(+), 12 deletions(-) diff --git a/src/base/defs/mixdefs.F90 b/src/base/defs/mixdefs.F90 index 19acf879..fec3309f 100644 --- a/src/base/defs/mixdefs.F90 +++ b/src/base/defs/mixdefs.F90 @@ -4,9 +4,9 @@ module mixdefs use kdefs implicit none - integer, parameter :: nVars = 25 ! change together wiht the enumerator below + integer, parameter :: nVars = 26 ! change together wiht the enumerator below enum, bind(C) - enumerator :: POT=1,FAC,SIGMAP,SIGMAH,SOUND_SPEED,DENSITY,AVG_ENG,NUM_FLUX,NEUTRAL_WIND,EFIELD,IM_EAVG,IM_EFLUX,IM_IAVG,IM_IFLUX,Z_EAVG,Z_NFLUX,CRPOT,TPOT,IM_GTYPE,AUR_TYPE,IM_BETA,IM_EDEN,IM_EPRE,IM_ENFLX,IM_INFLX + enumerator :: POT=1,FAC,SIGMAP,SIGMAH,SOUND_SPEED,DENSITY,AVG_ENG,NUM_FLUX,NEUTRAL_WIND,EFIELD,IM_EAVG,IM_EFLUX,IM_IAVG,IM_IFLUX,Z_EAVG,Z_NFLUX,CRPOT,TPOT,IM_GTYPE,AUR_TYPE,IM_BETA,IM_EDEN,IM_EPRE,IM_ENFLX,IM_INFLX,DELTAE end enum ! enumerator for MHD->MIX variables @@ -28,7 +28,7 @@ module mixdefs end enum enum, bind(C) - enumerator :: FEDDER=1,ZHANG,RCMHD,RCMONO,RCMFED + enumerator :: FEDDER=1,ZHANG,RCMHD,RCMONO,RCMFED,LINMRG end enum enum, bind(C) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index e22a62ae..86996a19 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -254,6 +254,7 @@ module mixconductance end subroutine conductance_fedder95 subroutine conductance_zhang15(conductance,G,St,dorcmO) + ! Nonlinear Fridman-Lemaire relation for mono. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St @@ -261,7 +262,7 @@ module mixconductance real(rp) :: signOfY, signOfJ logical :: dorcm - real(rp), dimension(G%Np,G%Nt) :: Pe_MHD, Ne_MHD, Pe_RMD, Ne_RMD + real(rp), dimension(G%Np,G%Nt) :: Pe_MHD, Ne_MHD, Pe_RMD, Ne_RMD, ceV if(present(dorcmO)) then dorcm = dorcmO @@ -290,8 +291,8 @@ module mixconductance if (conductance%doChill) then ! MHD density replaced with gallagher where it's lower ! and temperature changed correspondingly - tmpD = max(G%D0*Mp_cgs,St%Vars(:,:,DENSITY)) - tmpC = St%Vars(:,:,SOUND_SPEED)*sqrt(St%Vars(:,:,DENSITY)/tmpD) + tmpD = max(G%D0*Mp_cgs,St%Vars(:,:,DENSITY)) ! [g/cm^3] + tmpC = St%Vars(:,:,SOUND_SPEED)*sqrt(St%Vars(:,:,DENSITY)/tmpD) ! [cm/s] else tmpD = St%Vars(:,:,DENSITY) tmpC = St%Vars(:,:,SOUND_SPEED) @@ -299,7 +300,7 @@ module mixconductance call conductance_auroralmask(conductance,G,signOfY) - Pe_MHD = 0.1/MIXgamma*alpha_RCM*tmpD*tmpC**2 ! electron pressure from MHD side in [Pa]. + Pe_MHD = 0.1/MIXgamma*alpha_RCM*tmpD*tmpC**2 ! electron pressure from MHD side in [Pa]. 0.1 is to convert [g/cm^3]*[cm/s]^2=[g/cm/s^2] to [Pa]. Ne_MHD = tmpD/(Mp_cgs*heFrac)*1.0D6 ! electron number density from MHD side in [/m^3]. if(.not.dorcm) then ! default Zhang15 using MHD thermal flux only. conductance%E0 = 2.0/kev2J*Pe_MHD/Ne_MHD ! Mean energy from MHD electron fluxes in [keV]. E_avg = 2*kT for Maxwellian. @@ -324,6 +325,7 @@ module mixconductance where ( JF0 > 1. ) ! limit the max potential energy drop to 20 [keV] + ! deltaE, or eV=kB*Te*(RM-1)*ln((RM-1)/(RM-J/e/F0)) when 1<=J/e/F0<=RM. conductance%deltaE = min( 0.5*conductance%E0*(RM - 1.D0)*dlog((RM-1.D0)/(RM-JF0)),maxDrop) St%Vars(:,:,Z_NFLUX) = JF0*conductance%phi0 elsewhere @@ -332,15 +334,94 @@ module mixconductance end where ! floor on total energy - St%Vars(:,:,Z_EAVG) = max(2.0*conductance%E0 + conductance%deltaE,1.D-8) - St%Vars(:,:,Z_NFLUX) = St%Vars(:,:,Z_NFLUX)!*conductance%AuroraMask + ! Eavg=2*kB*Te + eV*(1-exp(-eV/(RM-1)/kB/Te))/(1-(1-1/RM)*exp(-eV/(RM-1)/kB/Te)) + ceV = 0.D0 + ceV = exp(-conductance%deltaE/(0.5*conductance%E0*(RM-1))) + St%Vars(:,:,Z_EAVG) = max(conductance%E0 + conductance%deltaE*(1-ceV)/(1-(1-1/RM)*ceV),1.D-8) ! Apply Zhang15 precipitation to main arrays St%Vars(:,:,AVG_ENG) = St%Vars(:,:,Z_EAVG) ! [keV] St%Vars(:,:,NUM_FLUX) = St%Vars(:,:,Z_NFLUX)! [#/cm^2/s] + St%Vars(:,:,DELTAE) = conductance%deltaE ! [kV] end subroutine conductance_zhang15 + subroutine conductance_linmono(conductance,G,St) + ! Linearized Fridman-Lemaire relation for mono. + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + type(mixState_T), intent(inout) :: St + + real(rp) :: signOfY, signOfJ + integer :: i,j + real(rp) :: D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT + real(rp) :: eTINY + + if (St%hemisphere==NORTH) then + signOfY = -1 ! note, factor2 (dawn-dusk asymmetry is not + ! implemented since factor2 in the old + ! fedder95 code was removed, i.e., set to + ! 1, anyway). I think Mike did this when he + ! implemented his ramp function. + signOfJ = -1 + elseif (St%hemisphere==SOUTH) then + signOfY = 1 + signOfJ = 1 + else + stop 'Wrong hemisphere label. Stopping...' + endif + + eTINY = 1.D-8 + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT) + do j=1,G%Nt + do i=1,G%Np + if (conductance%doChill) then + ! MHD density replaced with gallagher where it's lower + ! and temperature changed correspondingly + D = max(G%D0(i,j)*Mp_cgs, St%Vars(i,j,DENSITY)) ! [g/cm^3] + Cs = St%Vars(i,j,SOUND_SPEED)*sqrt(St%Vars(i,j,DENSITY)/D) ! [cm/s] + else + D = St%Vars(i,j,DENSITY) + Cs = St%Vars(i,j,SOUND_SPEED) + endif + ! electron pressure from MHD side in [Pa] + ! 0.1 is to convert [g/cm^3]*[cm/s]^2=[g/cm/s^2] to [Pa]. + Pe = 0.1/MIXgamma*alpha_RCM(i,j)*D*(Cs**2) + ! electron number density from MHD side in [/m^3]. + Ne = D/(Mp_cgs*heFrac)*1.0D6 + ! Mean energy from MHD electron fluxes in [keV]. E_avg = 2*kT for Maxwellian. + kT = Pe/Ne/kev2J + conductance%E0 (i,j) = 2.0*kT + ! Thermal number flux from MHD electron fluxes in [#/cm^2/s]. + phi0 = beta_RCM(i,j)* sqrt(Pe*Ne/(2.0D-3*PI*Me_cgs))*1.0D-4 + conductance%phi0(i,j) = phi0 + + ! Note JF0 may go inf where phi0/Pe_MHD/Ne_MHD is zero. + J2eF0 = 1.D-4*signOfJ*(St%Vars(i,j,FAC)*1.D-6)/(eCharge*phi0) + ! Linearize the Fridman-Lemaire relation: + ! eV = kB*Te*(RM-1)*ln((RM-1)/(RM-J/e/F0)) when 1<=J/e/F0<=RM. + ! eV ~ kB*Te*(J/e/F0-1) when RM>>J/e/F0 + if (J2eF0>1.0 .and. Ne>0.03e6) then + dE = kT*(J2eF0-1.0) + conductance%deltaE(i,j) = dE + eV2kT = min(dE,maxDrop)/kT + 1.0 + St%Vars(i,j,Z_NFLUX) = phi0*eV2kT + St%Vars(i,j,Z_EAVG) = kT*(eV2kT+1.0/eV2kT) + else + conductance%deltaE(i,j) = 0.0 + St%Vars(i,j,Z_NFLUX) = phi0 + St%Vars(i,j,Z_EAVG) = kT + endif + St%Vars(i,j,DELTAE) = conductance%deltaE(i,j) + St%Vars(i,j,AVG_ENG) = max(St%Vars(i,j,Z_EAVG),eTINY) ! [keV] + St%Vars(i,j,NUM_FLUX) = St%Vars(i,j,Z_NFLUX) ! [#/cm^2/s] + enddo ! i + enddo ! j + + end subroutine conductance_linmono + subroutine conductance_alpha_beta(conductance,G,St) type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G @@ -449,7 +530,9 @@ module mixconductance logical :: isMono real(rp) :: mhd_eavg, mhd_nflx, mhd_eflx, rmd_nflx, rmd_eflx, rmd_eavg real(rp) :: rmd_eavg_fin, rmd_nflx_fin, rmd_SigP, mhd_SigP - logical :: dorcm = .true. + ! Make dorcm=.false. to use pure MHD fluxes. + ! Tests show that RCM fluxes are so low that they easily trigger mono in the dawnside R2 FAC. + logical :: dorcm = .false. ! derive RCM grid weighting based on that passed from RCM and smooth it with five iterations of numerical diffusion. call conductance_IM_GTYPE(G,St) @@ -501,7 +584,7 @@ module mixconductance rmd_SigP = SigmaP_Robinson(rmd_eavg_fin,kev2erg*rmd_eavg_fin*rmd_nflx_fin) mhd_SigP = SigmaP_Robinson(mhd_eavg ,kev2erg*mhd_eavg *mhd_nflx ) - if (mhd_SigP>rmd_SigP) then + if (mhd_SigP>=rmd_SigP) then St%Vars(i,j,AVG_ENG ) = mhd_eavg St%Vars(i,j,NUM_FLUX) = mhd_nflx St%Vars(i,j,AUR_TYPE) = AT_RMono @@ -517,6 +600,86 @@ module mixconductance enddo end subroutine conductance_rcmhd + + subroutine conductance_linmrg(conductance,G,St) + ! Duplicate of kmerge except calling linmono to replace kmono. + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + type(mixState_T), intent(inout) :: St + + integer :: i,j + real(rp) :: wC1,wC2,wRCM,gtype + real(rp) :: mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE + real(rp) :: rcm_eavg,rcm_nflx,rcm_eflx + real(rp) :: mix_eavg,mix_nflx,mix_eflx + logical :: isRCM,isMHD,isMIX + St%Vars(:,:,AUR_TYPE) = 0 + + wC1 = 0.15 + wC2 = 1.0-wC1 + + !Get RCM grid weighting: 1=RCM and 0=MHD + call conductance_IM_GTYPE(G,St) + + !Precalc Fedder everywhere (yuck) + call conductance_linmono (conductance,G,St) + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,isRCM,isMHD,isMIX,wRCM,gtype) & + !$OMP private(mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE) & + !$OMP private(rcm_eavg,rcm_nflx,rcm_eflx ) & + !$OMP private(mix_eavg,mix_nflx,mix_eflx ) + do j=1,G%Nt + do i=1,G%Np + !Start by figuring out where we are + !gtype = St%Vars(i,j,IM_GTYPE) + gtype = gtype_RCM(i,j) + isRCM = gtype >= wC2 + isMHD = gtype <= wC1 + if ( (.not. isRCM) .and. (.not. isMHD) ) then + isMIX = .true. + endif + + !Grab values + mhd_eavg = St%Vars(i,j,AVG_ENG) + mhd_nflx = St%Vars(i,j,NUM_FLUX) + mhd_delE = conductance%deltaE(i,j) + mhd_eflx = St%Vars(i,j,AVG_ENG)*St%Vars(i,j,NUM_FLUX)*kev2erg + rcm_nflx = St%Vars(i,j,IM_ENFLX) + rcm_eflx = St%Vars(i,j,IM_EFLUX) + rcm_eavg = rcm_eflx/(rcm_nflx*kev2erg) !Back to keV + + if (isMHD) then + St%Vars(i,j,AVG_ENG ) = mhd_eavg + St%Vars(i,j,NUM_FLUX) = mhd_nflx + else if (isRCM) then + if (rcm_nflx <= TINY) then + rcm_eavg = 1.0e-8 + endif + St%Vars(i,j,AVG_ENG ) = rcm_eavg + St%Vars(i,j,NUM_FLUX) = rcm_nflx + conductance%deltaE(i,j) = 0.0 + else + !Mixture + wRCM = RampUp(gtype,wC1,wC2-wC1) + if ( (rcm_nflx > TINY) ) then + !Mix both + mix_nflx = wRCM*rcm_nflx + (1-wRCM)*mhd_nflx + mix_eflx = wRCM*rcm_eflx + (1-wRCM)*mhd_eflx + else + !RCM data wasn't good so just use MHD + mix_nflx = mhd_nflx + mix_eflx = mhd_eflx + endif + mix_eavg = mix_eflx/(mix_nflx*kev2erg) + + St%Vars(i,j,AVG_ENG ) = mix_eavg + St%Vars(i,j,NUM_FLUX) = mix_nflx + endif + enddo + enddo + + end subroutine conductance_linmrg subroutine conductance_rcmonoK(conductance,G,St) type(mixConductance_T), intent(inout) :: conductance diff --git a/src/remix/mixio.F90 b/src/remix/mixio.F90 index 32ec9e08..6816ab73 100644 --- a/src/remix/mixio.F90 +++ b/src/remix/mixio.F90 @@ -71,6 +71,8 @@ contains mixUnitNames(IM_ENFLX) = "1/cm^2 s" mixVarNames(IM_INFLX) = "IM Number flux proton" mixUnitNames(IM_INFLX) = "1/cm^2 s" + mixVarNames(DELTAE) = "Mono potential drop" + mixUnitNames(DELTAE) = "kV" end subroutine initMIXNames subroutine initMIXIO(I,RunID,isRestart,nRes) @@ -205,6 +207,8 @@ contains doDump = .true. case (IM_INFLX) doDump = .true. + case (DELTAE) + doDump = .true. case DEFAULT doDump = .false. ! only dump the variables explicitely set to be dumped above end select @@ -573,6 +577,8 @@ contains doDump = .true. case (IM_INFLX) doDump = .true. + case (DELTAE) + doDump = .true. case (NEUTRAL_WIND) doDump = .false. case (EFIELD) diff --git a/src/remix/mixparams.F90 b/src/remix/mixparams.F90 index b7ce8080..9c43d663 100644 --- a/src/remix/mixparams.F90 +++ b/src/remix/mixparams.F90 @@ -118,8 +118,10 @@ module mixparams Params%aurora_model_type = RCMONO case ("RCMFED") Params%aurora_model_type = RCMFED + case ("LINMRG") + Params%aurora_model_type = LINMRG case default - stop "The aurora model type entered is not supported (Available options: FEDDER, ZHANG, RCMHD, RCMONO, RCMFED)." + stop "The aurora model type entered is not supported (Available options: FEDDER, ZHANG, RCMHD, RCMONO, RCMFED, LINMRG)." end select ! =========== CONDUCTANCE MODEL PARAMETERS =================== ! diff --git a/src/remix/tgcm.F90 b/src/remix/tgcm.F90 index 261c0b7d..d34ead16 100644 --- a/src/remix/tgcm.F90 +++ b/src/remix/tgcm.F90 @@ -91,6 +91,8 @@ module gcminterp mixUnitNames(IM_ENFLX) = "1/cm^2 s" mixVarNames(IM_INFLX) = "IM Number flux proton" mixUnitNames(IM_INFLX) = "1/cm^2 s" + mixVarNames(DELTAE) = "Mono potential drop" + mixUnitNames(DELTAE) = "kV" end subroutine initGCMNames subroutine init_gcm_mix(gcm,ion)!,remixApp From 0251f07f6066308f670fd1797df1b2f216633d43 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Wed, 13 Sep 2023 09:34:25 -0600 Subject: [PATCH 035/365] Fix case selection in mixconducance for linmrg. --- src/remix/mixconductance.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 86996a19..3c12d046 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -961,6 +961,8 @@ module mixconductance call conductance_rcmhd(conductance,G,St) case (RCMFED) call conductance_rcmfed(conductance,G,St) + case (LINMRG) + call conductance_linmrg(conductance,G,St) case default stop "The aurora precipitation model type entered is not supported." end select From ae5cc5d24a6b7ec67976c00b38b5e1ed67f6928a Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Wed, 13 Sep 2023 10:37:53 -0600 Subject: [PATCH 036/365] Reduce chorus model resolution in L --- kaipy/rcm/wmutils/genWM.py | 2 +- kaipy/rcm/wmutils/wmData.py | 2 +- scripts/preproc/genRCM.py | 4 ++-- src/rcm/modules.F90 | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kaipy/rcm/wmutils/genWM.py b/kaipy/rcm/wmutils/genWM.py index ed7b078c..eb995949 100644 --- a/kaipy/rcm/wmutils/genWM.py +++ b/kaipy/rcm/wmutils/genWM.py @@ -139,7 +139,7 @@ def genChorus(params,fInChorus): #Li startValue = 3.0 #in Re endValue = 7.0 - lenL = 161 + lenL = 41 Li = np.linspace(startValue, endValue, num=lenL) #Tau from polynomial fit tauP = np.zeros((lenKp,lenMLT,lenL,lenEk)) diff --git a/kaipy/rcm/wmutils/wmData.py b/kaipy/rcm/wmutils/wmData.py index f59442c1..e92f3450 100644 --- a/kaipy/rcm/wmutils/wmData.py +++ b/kaipy/rcm/wmutils/wmData.py @@ -4,7 +4,7 @@ import numpy as np class wmParams: #All energies in eV - def __init__(self, dim = 4, nKp = 7, nMLT = 97, nL = 161, nEk = 155): + def __init__(self, dim = 4, nKp = 7, nMLT = 97, nL = 41, nEk = 155): self.dim = dim self.nKp = nKp self.nMLT = nMLT diff --git a/scripts/preproc/genRCM.py b/scripts/preproc/genRCM.py index 5b926e5c..7cf69869 100755 --- a/scripts/preproc/genRCM.py +++ b/scripts/preproc/genRCM.py @@ -78,7 +78,7 @@ if __name__ == "__main__": plotType = args.plotType if addWM: - tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 161, nEk = 155) + tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 41, nEk = 155) genWM.genh5(fIn,fOut,tauParams,useWM = True) else: # Determine proton channel limits based on resolving a certain (proton) temperature at given L @@ -106,7 +106,7 @@ if __name__ == "__main__": fileIO.saveRCMConfig(alamData,params=alamParams,fname=fOut) # Add data needed for wavemodel if not noWaveModel: - tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 161, nEk = 155) + tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 41, nEk = 155) genWM.genh5(fOut,fOut,tauParams,useWM = True) print("Wrote RCM configuration to %s"%(fOut)) diff --git a/src/rcm/modules.F90 b/src/rcm/modules.F90 index a9d3f6d9..ceda0bf2 100644 --- a/src/rcm/modules.F90 +++ b/src/rcm/modules.F90 @@ -77,7 +77,7 @@ MODULE rice_housekeeping_module end type RCMEllipse_T type ChorusTauIn_T !electron lifetime for Chorus wave - integer(iprec) :: Nm=97, Nl=161, Nk=7 ,Ne=155 + integer(iprec) :: Nm=97, Nl=41, Nk=7 ,Ne=155 real(rprec), ALLOCATABLE :: MLTi(:), Li(:), Kpi(:), Eki(:) real(rprec), ALLOCATABLE :: taui(:,:,:,:) end type ChorusTauIn_T From 5785d90724d573f249e6a0a61fa9dc7720a6465a Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Wed, 13 Sep 2023 16:51:18 -0600 Subject: [PATCH 037/365] Add rcmconfig.h5 check. Remove 1/3 in SS loss. --- src/rcm/rcm_subs.F90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rcm/rcm_subs.F90 b/src/rcm/rcm_subs.F90 index 6c22150a..a958f0cc 100644 --- a/src/rcm/rcm_subs.F90 +++ b/src/rcm/rcm_subs.F90 @@ -1233,6 +1233,12 @@ ! reset to make sure species if ikflav ==1 alamc is set to negative, for electrons where(ikflavc==1)alamc = -abs(alamc) + ! Check if rcmconfig.h5 is up-to-date + if (ioExist(RCMGAMConfig,"Tau1i")) then + write(*,*) "An old rcmconfig.h5 is used. Please make a new one using genRCM.py." + stop + endif + !Store data for wave models !Dimension check: only compatible with tau(MLT,L,Kp,Ek) if (ioExist(RCMGAMConfig,"Taui")) then @@ -3212,7 +3218,7 @@ FUNCTION RatefnWM(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) TauSS = RatefnC_tau_s(alamx,vmx,beqx,losscx) LossR8_PSHEET = 1.0/TauSS - LossR8_PSHEET = LossR8_PSHEET/3.0 !1/3 SS rate + LossR8_PSHEET = LossR8_PSHEET !SS rate END FUNCTION LossR8_PSHEET !Calculate speed (Re/s) from energy [keV] for ELECTRONS From f34510c2dc0e80e6cce5ea02f07152ed8906679e Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Wed, 13 Sep 2023 17:09:28 -0600 Subject: [PATCH 038/365] Bring back the block of code for eeta_avg with the option for fixed plamasphere --- src/rcm/rcm_subs.F90 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/rcm/rcm_subs.F90 b/src/rcm/rcm_subs.F90 index a958f0cc..730effdd 100644 --- a/src/rcm/rcm_subs.F90 +++ b/src/rcm/rcm_subs.F90 @@ -2503,8 +2503,17 @@ SUBROUTINE Move_plasma_grid_MHD (dt,nstep) ENDIF eeta_avg(:,:,kc) = 0.0 - eeta_avg(:,:,kc) = eeta_avg(:,:,kc) + eeta(:,:,kc)/(nstep+1) - !Sub-step nstep times + ! Just set eeta_avg to whatever eta is there, and leave + !! Note: Regions that have ever been outside of active domain will never have psph again, based on current implementation (2023-08-24) + if ( (kc==1) .and. dp_on == .false.) then ! We are plasmasphere and we don't want to evolve it + eeta_avg(:,:,kc) = eeta(:,:,kc) + cycle + else ! We are hot channel or we are evolving plasmasphere channel + ! Add current weighted eeta as first contribution + eeta_avg(:,:,kc) = eeta(:,:,kc)/(nstep+1) + endif + + !Sub-step nstep times do n=1,nstep !--- !Tally precipitation losses From 929c01b4c5f4a73414efe1b7f5d2b577b4cb20ef Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Wed, 13 Sep 2023 20:09:25 -0600 Subject: [PATCH 039/365] Restore the chorus lifetime file. From 9d63f661c214feca178fd6f993a1e93d96f2a551 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Fri, 15 Sep 2023 09:14:52 -0600 Subject: [PATCH 040/365] Remove RCMHD/RCMONO. Manually ororganize the subroutines in mixcnonductance module. --- src/base/defs/mixdefs.F90 | 2 +- src/remix/mixconductance.F90 | 1007 +++++++++++++--------------------- src/remix/mixparams.F90 | 8 +- 3 files changed, 391 insertions(+), 626 deletions(-) diff --git a/src/base/defs/mixdefs.F90 b/src/base/defs/mixdefs.F90 index fec3309f..b3db9d1c 100644 --- a/src/base/defs/mixdefs.F90 +++ b/src/base/defs/mixdefs.F90 @@ -28,7 +28,7 @@ module mixdefs end enum enum, bind(C) - enumerator :: FEDDER=1,ZHANG,RCMHD,RCMONO,RCMFED,LINMRG + enumerator :: FEDDER=1,ZHANG,LINMRG end enum enum, bind(C) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 3c12d046..ed879a6c 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -24,7 +24,9 @@ module mixconductance logical , private :: doDrift = .false. !Whether to add drift term from Zhang contains + subroutine conductance_init(conductance,Params,G) + ! Initialize precipitation and conductance variables. type(mixConductance_T), intent(inout) :: conductance type(mixParams_T) , intent(in) :: Params type(mixGrid_T) , intent(in) :: G @@ -72,9 +74,6 @@ module mixconductance ! these arrays are global and should not be! reallocate them if(allocated(tmpD)) deallocate(tmpD) if(allocated(tmpC)) deallocate(tmpC) - allocate(tmpD(G%Np,G%Nt)) - allocate(tmpC(G%Np,G%Nt)) - if(allocated(JF0)) deallocate(JF0) if(allocated(RM)) deallocate(RM) if(allocated(RRdi)) deallocate(RRdi) @@ -83,8 +82,11 @@ module mixconductance if(allocated(beta_RCM)) deallocate(beta_RCM) if(allocated(alpha_RCM)) deallocate(alpha_RCM) if(allocated(gtype_RCM)) deallocate(gtype_RCM) - allocate(JF0 (G%Np,G%Nt)) - allocate(RM (G%Np,G%Nt)) + + allocate(tmpD(G%Np,G%Nt)) + allocate(tmpC(G%Np,G%Nt)) + allocate(JF0(G%Np,G%Nt)) + allocate(RM(G%Np,G%Nt)) allocate(RRdi(G%Np,G%Nt)) allocate(tmpE(G%Np+4,G%Nt+4)) ! for boundary processing. allocate(tmpF(G%Np+4,G%Nt+4)) @@ -93,16 +95,121 @@ module mixconductance allocate(gtype_RCM(G%Np,G%Nt)) call SetMIXgamma(Params%gamma) + ! MHD inner boundary, used to calc mirror ratio. RinMHD = Params%RinMHD - ! alpha_RCM and alpha_beta replace conductance_alpha/beta in zhang15. - ! Use default xml input if not using rcmhd. + ! Te/Tmhd alpha_RCM = 1.0/(tiote_RCM+1.0) + ! Loss cone rate beta_RCM = conductance%beta - gtype_RCM = 0.0 ! if conductance_IM_GTYPE is not called, gtype_RCM has all zero, MHD values have a weight of 1. + ! RCM grid weight: 1. Totally on closed RCM; 0. Totally outside RCM. + ! if conductance_IM_GTYPE is not called, gtype_RCM has all zero, MHD values have a weight of 1. + gtype_RCM = 0.0 end subroutine conductance_init + subroutine conductance_total(conductance,G,St,gcm,h) + ! The main subroutine in module mixconductance. + ! It derives auroral precipitation and conductance caused by both solar EUV and aurora. + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + type(mixState_T), intent(inout) :: St + type(gcm_T),optional,intent(in) :: gcm + integer,optional,intent(in) :: h + + call GenMirrorRatio(G,St) + + ! Compute EUV though because it's used in fedder + call conductance_euv(conductance,G,St) + select case ( conductance%aurora_model_type ) + case (FEDDER) + call conductance_fedder95(conductance,G,St) + case (ZHANG) + doDrift = .true. + call conductance_zhang15(conductance,G,St) + case (LINMRG) + call conductance_linmrg(conductance,G,St) + case default + stop "The aurora precipitation model type entered is not supported." + end select + + ! Correct for multiple reflections if you're so inclined + if (conductance%doMR) call conductance_mr(conductance,G,St) + + ! Smooth precipitation energy and flux before calculating conductance. + if (conductance%doAuroralSmooth) call conductance_auroralsmooth(St,G,conductance) + + if (present(gcm)) then + ! Use GCM conductance. + call apply_gcm2mix(gcm,St,h) + St%Vars(:,:,SIGMAP) = max(conductance%pedmin,St%Vars(:,:,SIGMAP)) + St%Vars(:,:,SIGMAH) = max(conductance%hallmin,St%Vars(:,:,SIGMAH)) + else if (conductance%const_sigma) then + ! Use constant conductance. + St%Vars(:,:,SIGMAP) = conductance%ped0 + St%Vars(:,:,SIGMAH) = 0.D0 + else + ! Use vector addition of auroral and EUV conductance. + call conductance_aurora(conductance,G,St) + St%Vars(:,:,SIGMAP) = sqrt( conductance%euvSigmaP**2 + conductance%deltaSigmaP**2) + St%Vars(:,:,SIGMAH) = sqrt( conductance%euvSigmaH**2 + conductance%deltaSigmaH**2) + endif + + ! Apply cap + if ((conductance%apply_cap).and.(.not. conductance%const_sigma).and.(.not. present(gcm))) then + St%Vars(:,:,SIGMAP) = max(conductance%pedmin,St%Vars(:,:,SIGMAP)) + St%Vars(:,:,SIGMAH) = min(max(conductance%hallmin,St%Vars(:,:,SIGMAH)),& + St%Vars(:,:,SIGMAP)*conductance%sigma_ratio) + endif + + end subroutine conductance_total + + subroutine GenMirrorRatio(G,St,doIGRFO) + ! Calculate mirror ratio array RM(G%Np,G%Nt). + ! NOTE: Leaving this to be done every time at every lat/lon to accomodate improved model later + type(mixGrid_T) , intent(in) :: G + type(mixState_T), intent(in) :: St + logical,optional,intent(in) :: doIGRFO + real(rp) :: mlat,mlon + integer :: i,j + logical :: doIGRF + + if(present(doIGRFO)) then + doIGRF = doIGRFO + else ! default is using IGRF, i.e, default is unequal split. + doIGRF = .true. + endif + + if (RinMHD > 0) then + !Calculate actual mirror ratio + !NOTE: Should replace this w/ actual inner boundary field strength + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,mlat,mlon) + do j=1,G%Nt + do i=1,G%Np + mlat = PI/2 - G%t(i,j) + mlon = G%p(i,j) + + if(.not.doIGRF) then + RM(i,j) = MirrorRatio(mlat,RinMHD) + elseif (St%hemisphere==NORTH) then + RM(i,j) = IGRFMirrorRatio(+mlat,+mlon,RinMHD) + else + !Southern, always a right-hand system based on the local pole. + !SM phi (mlon) goes in clock-wise as opposed to counter-clockwise if looking down on the southern pole from above. + RM(i,j) = IGRFMirrorRatio(-mlat,-mlon,RinMHD) + endif + enddo + enddo !j loop + else + !Set mirror ratio everywhere no matter the inner boundary to 10 + !Note: This may have been okay for simulating magnetospheres 40 years ago, but it's 2021 now + RM = 10.0 + endif + end subroutine GenMirrorRatio + subroutine conductance_euv(conductance,G,St) + ! Derive solar EUV conductance: euvSigmaP, euvSigmaH. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St @@ -178,8 +285,8 @@ module mixconductance end subroutine conductance_euv - subroutine conductance_fedder95(conductance,G,St) + ! Derive electron precipitation energy flux and avg energy using Feder95 formula. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St @@ -254,7 +361,7 @@ module mixconductance end subroutine conductance_fedder95 subroutine conductance_zhang15(conductance,G,St,dorcmO) - ! Nonlinear Fridman-Lemaire relation for mono. + ! Derive electron precipitation energy flux and avg energy using the nonlinear Fridman-Lemaire relation [Zhang et al., 2014JA020615]. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St @@ -345,6 +452,87 @@ module mixconductance St%Vars(:,:,DELTAE) = conductance%deltaE ! [kV] end subroutine conductance_zhang15 + + subroutine conductance_linmrg(conductance,G,St) + ! Derive mono-diffuse electron precipitation where mono is based on linearized FL relation, + ! diffuse is derived from RCM. The merging code was duplicated from kmerge. + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + type(mixState_T), intent(inout) :: St + + integer :: i,j + real(rp) :: wC1,wC2,wRCM,gtype + real(rp) :: mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE + real(rp) :: rcm_eavg,rcm_nflx,rcm_eflx + real(rp) :: mix_eavg,mix_nflx,mix_eflx + logical :: isRCM,isMHD,isMIX + St%Vars(:,:,AUR_TYPE) = 0 + + wC1 = 0.15 + wC2 = 1.0-wC1 + + !Get RCM grid weighting: 1=RCM and 0=MHD + call conductance_IM_GTYPE(G,St) + + ! Derive mono using the linearized FL relation. + call conductance_linmono(conductance,G,St) + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,isRCM,isMHD,isMIX,wRCM,gtype) & + !$OMP private(mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE) & + !$OMP private(rcm_eavg,rcm_nflx,rcm_eflx ) & + !$OMP private(mix_eavg,mix_nflx,mix_eflx ) + do j=1,G%Nt + do i=1,G%Np + !Start by figuring out where we are + !gtype = St%Vars(i,j,IM_GTYPE) + gtype = gtype_RCM(i,j) + isRCM = gtype >= wC2 + isMHD = gtype <= wC1 + if ( (.not. isRCM) .and. (.not. isMHD) ) then + isMIX = .true. + endif + + !Grab values + mhd_eavg = St%Vars(i,j,AVG_ENG) + mhd_nflx = St%Vars(i,j,NUM_FLUX) + mhd_delE = conductance%deltaE(i,j) + mhd_eflx = St%Vars(i,j,AVG_ENG)*St%Vars(i,j,NUM_FLUX)*kev2erg + rcm_nflx = St%Vars(i,j,IM_ENFLX) + rcm_eflx = St%Vars(i,j,IM_EFLUX) + rcm_eavg = rcm_eflx/(rcm_nflx*kev2erg) !Back to keV + + if (isMHD) then + St%Vars(i,j,AVG_ENG ) = mhd_eavg + St%Vars(i,j,NUM_FLUX) = mhd_nflx + else if (isRCM) then + if (rcm_nflx <= TINY) then + rcm_eavg = 1.0e-8 + endif + St%Vars(i,j,AVG_ENG ) = rcm_eavg + St%Vars(i,j,NUM_FLUX) = rcm_nflx + conductance%deltaE(i,j) = 0.0 + else + !Mixture + wRCM = RampUp(gtype,wC1,wC2-wC1) + if ( (rcm_nflx > TINY) ) then + !Mix both + mix_nflx = wRCM*rcm_nflx + (1-wRCM)*mhd_nflx + mix_eflx = wRCM*rcm_eflx + (1-wRCM)*mhd_eflx + else + !RCM data wasn't good so just use MHD + mix_nflx = mhd_nflx + mix_eflx = mhd_eflx + endif + mix_eavg = mix_eflx/(mix_nflx*kev2erg) + + St%Vars(i,j,AVG_ENG ) = mix_eavg + St%Vars(i,j,NUM_FLUX) = mix_nflx + endif + enddo + enddo + + end subroutine conductance_linmrg subroutine conductance_linmono(conductance,G,St) ! Linearized Fridman-Lemaire relation for mono. @@ -422,35 +610,170 @@ module mixconductance end subroutine conductance_linmono - subroutine conductance_alpha_beta(conductance,G,St) + subroutine conductance_mr(conductance,G,St) + ! George Khazanov's multiple reflection(MR) corrections + ! Modify the diffuse precipitation energy and mean energy based on equation (5) in Khazanov et al. [2019JA026589] + ! Kc = 3.36-exp(0.597-0.37*Eavg+0.00794*Eavg^2) + ! Eavg_c = 0.073+0.933*Eavg-0.0092*Eavg^2 + ! Nflx_c = Eflx_c/Eavg_c = Eflx*Kc/Eavg_c = Nflx*Eavg*Kc/Eavg_c + ! where Eavg is the mean energy in [keV] before MR, Eavg_c is the modified mean energy. + ! Kc is the ratio between modified enflux and unmodified enflux. + ! No longer use the conductance modification factor in Khazanov et al. [2018SW001837] (eq 7). + type(mixConductance_T), intent(in) :: conductance + type(mixGrid_T), intent(in) :: G + type(mixState_T), intent(inout) :: St + real(rp) :: eavg,nflx,eavgMR,nflxMR + integer :: i,j + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,eavg,nflx,eavgMR,nflxMR) + do j=1,G%Nt + do i=1,G%Np + if(conductance%deltaE(i,j)<=0.0) then + ! Only modify diffuse precipitation. May modify mono precipitation when a formula is available. + eavg = St%Vars(i,j,AVG_ENG) + nflx = St%Vars(i,j,NUM_FLUX) + call AugmentMR(eavg,nflx,eavgMR,nflxMR) + St%Vars(i,j,AVG_ENG ) = eavgMR + St%Vars(i,j,NUM_FLUX) = nflxMR + endif + enddo + enddo + end subroutine conductance_mr + + subroutine AugmentMR(eavg,nflx,eavgMR,nflxMR) + ! Cleaned up routine to calculate MR-augmented eavg and nflx + real(rp), intent(in) :: eavg ,nflx + real(rp), intent(out) :: eavgMR,nflxMR + real(rp) :: eflux,Kc + + if ( (eavg<=30.0) .and. (eavg>=0.5) ) then + ! Kc becomes negative when E_avg > 47-48 keV. + Kc = 3.36 - exp(0.597-0.37*eavg+0.00794*eavg**2) + eflux = eavg*nflx + eavgMR = 0.073+0.933*eavg-0.0092*eavg**2 ! [keV] + nflxMR = Kc*eflux/eavgMR + else + !Nothing to do + eavgMR = eavg + nflxMR = nflx + endif + end subroutine AugmentMR + + subroutine conductance_auroralsmooth(St,G,conductance) + type(mixState_T), intent(inout) :: St + type(mixGrid_T), intent(in) :: G + type(mixConductance_T), intent(inout) :: conductance + integer :: i, j + logical :: smthDEPonly = .true. + integer :: smthE + smthE = 1 ! 1. smooth EFLUX; 2. smooth EAVG + + tmpC = 0.D0 + tmpD = 0.D0 + tmpE = 0.D0 + tmpF = 0.D0 + + ! Test only smoothing diffuse precipitation. + ! Diffuse mask is St%Vars(:,:,Z_NFLUX)<0. + ! Get AVG_ENG*mask and NUM_FLUX*mask for smoothing. + ! Fill AVG_ENG and NUM_FLUX with smoothed where mask is true. + ! this domain of conductance is never used. Here it's used for diffuse precipitation mask. Be careful if not in RCMONO mode. + conductance%PrecipMask = 1.D0 + if(smthDEPonly) then + where(St%Vars(:,:,Z_NFLUX)>=0) + conductance%PrecipMask = 0.D0 + end where + ! back up mono + tmpC = St%Vars(:,:,AVG_ENG) + tmpD = St%Vars(:,:,NUM_FLUX) + endif + + ! get expanded diffuse with margin grid (ghost cells) + if(smthE==1) then + call conductance_margin(G,St%Vars(:,:,AVG_ENG)*St%Vars(:,:,NUM_FLUX)*conductance%PrecipMask, tmpE) + else + call conductance_margin(G,St%Vars(:,:,AVG_ENG)*conductance%PrecipMask, tmpE) + end if + call conductance_margin(G,St%Vars(:,:,NUM_FLUX)*conductance%PrecipMask, tmpF) + + !$OMP PARALLEL DO default(shared) collapse(2) & + !$OMP private(i,j) + do j=1,G%Nt + do i=1,G%Np + ! SmoothOperator55(Q,isGO) + St%Vars(i,j,NUM_FLUX)=SmoothOperator55(tmpF(i:i+4,j:j+4)) + if(smthE==1) then + St%Vars(i,j,AVG_ENG) = max(SmoothOperator55(tmpE(i:i+4,j:j+4))/St%Vars(i,j,NUM_FLUX),1.D-8) + else + St%Vars(i,j,AVG_ENG) = max(SmoothOperator55(tmpE(i:i+4,j:j+4)),1.D-8) + end if + end do + end do + + if(smthDEPonly) then + ! combine smoothed diffuse and unsmoothed mono + where(St%Vars(:,:,Z_NFLUX)>=0) + St%Vars(:,:,AVG_ENG) = tmpC + St%Vars(:,:,NUM_FLUX) = tmpD + end where + endif + end subroutine conductance_auroralsmooth + + subroutine conductance_aurora(conductance,G,St) + ! Use Robinson formula to get Pedersen and Hall conductance from electron precipitation. + ! Diffuse precipitation from RCM has been divided by 2 in rcm_subs.F90 for each hemisphere. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - real(rp), dimension(G%Np,G%Nt) :: phi0_rcmz - integer :: i,j - ! In MHD, use the same alpha/beta with RCM. - ! Calculate beta from RCM fluxes. - ! Default values are from xml in conductance_init. - ! It's still necessary to initialize alpha/beta_RCM here because they may be using an old value at some points - ! if IM_EAVG or IM_EPRE or EM_EDEN no longer satisfies the if criteria. Better to use default background beta. - alpha_RCM = 1.0/(tiote_RCM+1.0) - beta_RCM = conductance%beta - phi0_rcmz = sqrt(St%Vars(:,:,IM_EPRE)*St%Vars(:,:,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 ! sqrt([Pa]*[#/m^3]/[kg]) = sqrt([#/m^4/s^2]) = 1e-4*[#/cm^2/s] - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j) - do j=1,G%Nt - do i=1,G%Np - if(phi0_rcmz(i,j)>TINY) then - beta_RCM(i,j) = St%Vars(i,j,IM_ENFLX)/phi0_rcmz(i,j) - elseif(St%Vars(i,j,IM_GTYPE) > 0.5) then - beta_RCM(i,j) = 0.0 - endif - enddo - enddo - St%Vars(:,:,IM_BETA) = min(beta_RCM,1.0) + conductance%engFlux = kev2erg*St%Vars(:,:,AVG_ENG)*St%Vars(:,:,NUM_FLUX) ! Energy flux in ergs/cm^2/s + conductance%deltaSigmaP = SigmaP_Robinson(St%Vars(:,:,AVG_ENG),conductance%engFlux) + conductance%deltaSigmaH = SigmaH_Robinson(St%Vars(:,:,AVG_ENG),conductance%engFlux) + end subroutine conductance_aurora - end subroutine conductance_alpha_beta + subroutine conductance_ramp(conductance,G,rPolarBound,rEquatBound,rLowLimit) + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + real(rp), intent(in) :: rPolarBound,rEquatBound,rLowLimit + + where (G%r < rPolarBound) + conductance%rampFactor = 1.0D0 + elsewhere ( (G%r > rPolarBound).and.(G%r <= rEquatBound) ) + conductance%rampFactor = 1.0D0+(rLowLimit - 1.0D0)*(G%r - rPolarBound)/(rEquatBound-rPolarBound) + elsewhere ! G%r > rEquatBound + conductance%rampFactor = rLowLimit + end where + end subroutine conductance_ramp + + subroutine conductance_auroralmask(conductance,G,signOfY) + ! Artificial auroral mask used in Zhang15 to represent dawnward shift of diffuse electron precipitation. + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + real(rp), intent(in) :: signOfY + real(rp) :: Rio, al0, alp, Radi, order, Rady + + Rio = 1.02 + al0 = -5.0*deg2rad + alp = 28*deg2rad + alp = min(28*deg2rad,alp) + Rady = Rio*sin(alp-al0) + Radi = Rady**2 ! Rio**2*(1-cos(alp-al0)*cos(alp-al0)) + order = 2.0 + + RRdi = 0.D0 + RRdi = (G%y-0.03*signOfY)**2 + ( G%x/cos(al0) - Rio*cos(alp-al0)*tan(al0) )**2 + where(RRdi < Radi) + conductance%AuroraMask = cos((RRdi/Radi)**order*PI/2)+0.D0 + elsewhere + conductance%AuroraMask = 0.D0 + end where + where(abs(G%y)TINY) then + beta_RCM(i,j) = St%Vars(i,j,IM_ENFLX)/phi0_rcmz(i,j) + elseif(St%Vars(i,j,IM_GTYPE) > 0.5) then + beta_RCM(i,j) = 0.0 + endif + enddo + enddo + St%Vars(:,:,IM_BETA) = min(beta_RCM,1.0) + + end subroutine conductance_alpha_beta + !Enforce pole condition that all values at the same point are equal subroutine FixPole(Gr,Q) type(mixGrid_T), intent(in) :: Gr @@ -521,590 +874,8 @@ module mixconductance Q(:,1) = Qavg end subroutine FixPole - subroutine conductance_rcmhd(conductance,G,St) - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(inout) :: St - - integer :: i,j - logical :: isMono - real(rp) :: mhd_eavg, mhd_nflx, mhd_eflx, rmd_nflx, rmd_eflx, rmd_eavg - real(rp) :: rmd_eavg_fin, rmd_nflx_fin, rmd_SigP, mhd_SigP - ! Make dorcm=.false. to use pure MHD fluxes. - ! Tests show that RCM fluxes are so low that they easily trigger mono in the dawnside R2 FAC. - logical :: dorcm = .false. - - ! derive RCM grid weighting based on that passed from RCM and smooth it with five iterations of numerical diffusion. - call conductance_IM_GTYPE(G,St) - - ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need IM_GTYPE. - call conductance_alpha_beta(conductance,G,St) - - ! derive MHD/mono precipitation with zhang15 but include RCM thermal flux to the source by using dorcm=.true. - call conductance_zhang15(conductance,G,St,dorcm) - - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,isMono) & - !$OMP private(mhd_eavg, mhd_nflx, mhd_eflx, rmd_nflx, rmd_eflx, rmd_eavg)& - !$OMP private(rmd_eavg_fin, rmd_nflx_fin, rmd_SigP, mhd_SigP) - do j=1,G%Nt - do i=1,G%Np - isMono = conductance%deltaE(i,j) > 0.0 !Potential drop - - mhd_eavg = St%Vars(i,j,Z_EAVG) - mhd_nflx = St%Vars(i,j,Z_NFLUX) - mhd_eflx = St%Vars(i,j,Z_EAVG)*St%Vars(i,j,Z_NFLUX)*kev2erg - - rmd_nflx = St%Vars(i,j,IM_ENFLX)*gtype_RCM(i,j)+mhd_nflx*(1.0-gtype_RCM(i,j)) - rmd_eflx = St%Vars(i,j,IM_EFLUX)*gtype_RCM(i,j)+mhd_eflx*(1.0-gtype_RCM(i,j)) - if(rmd_nflx>TINY) then - rmd_eavg = max(rmd_eflx/(rmd_nflx*kev2erg),1.0e-8) - else - rmd_eavg = 0.0 - endif - - if (.not. isMono) then - !No potential drop, just use merged precipitation. - St%Vars(i,j,AVG_ENG ) = rmd_eavg - St%Vars(i,j,NUM_FLUX) = rmd_nflx - St%Vars(i,j,AUR_TYPE) = AT_RMnoE - cycle - endif - - !If still here, we have a potential drop - !Decide between the two by taking one that gives highest Sig-P - if (conductance%doMR) then ! be careful here is using nflux. - call AugmentMR(rmd_eavg,rmd_nflx,rmd_eavg_fin,rmd_nflx_fin) !Correct for MR - else - !No corrections - rmd_eavg_fin = rmd_eavg - rmd_nflx_fin = rmd_nflx - endif - - rmd_SigP = SigmaP_Robinson(rmd_eavg_fin,kev2erg*rmd_eavg_fin*rmd_nflx_fin) - mhd_SigP = SigmaP_Robinson(mhd_eavg ,kev2erg*mhd_eavg *mhd_nflx ) - - if (mhd_SigP>=rmd_SigP) then - St%Vars(i,j,AVG_ENG ) = mhd_eavg - St%Vars(i,j,NUM_FLUX) = mhd_nflx - St%Vars(i,j,AUR_TYPE) = AT_RMono - else - !RMD diffuse is still better than MHD + puny potential drop - St%Vars(i,j,AVG_ENG ) = rmd_eavg !Use un-augmented value since MR gets called later - St%Vars(i,j,NUM_FLUX) = rmd_nflx - conductance%deltaE(i,j) = 0.0 !Wipe out potential drop since it don't matter (otherwise MR won't happen if desired) - St%Vars(i,j,AUR_TYPE) = AT_RMfnE - endif - - enddo - enddo - - end subroutine conductance_rcmhd - - subroutine conductance_linmrg(conductance,G,St) - ! Duplicate of kmerge except calling linmono to replace kmono. - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(inout) :: St - - integer :: i,j - real(rp) :: wC1,wC2,wRCM,gtype - real(rp) :: mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE - real(rp) :: rcm_eavg,rcm_nflx,rcm_eflx - real(rp) :: mix_eavg,mix_nflx,mix_eflx - logical :: isRCM,isMHD,isMIX - St%Vars(:,:,AUR_TYPE) = 0 - - wC1 = 0.15 - wC2 = 1.0-wC1 - - !Get RCM grid weighting: 1=RCM and 0=MHD - call conductance_IM_GTYPE(G,St) - - !Precalc Fedder everywhere (yuck) - call conductance_linmono (conductance,G,St) - - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,isRCM,isMHD,isMIX,wRCM,gtype) & - !$OMP private(mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE) & - !$OMP private(rcm_eavg,rcm_nflx,rcm_eflx ) & - !$OMP private(mix_eavg,mix_nflx,mix_eflx ) - do j=1,G%Nt - do i=1,G%Np - !Start by figuring out where we are - !gtype = St%Vars(i,j,IM_GTYPE) - gtype = gtype_RCM(i,j) - isRCM = gtype >= wC2 - isMHD = gtype <= wC1 - if ( (.not. isRCM) .and. (.not. isMHD) ) then - isMIX = .true. - endif - - !Grab values - mhd_eavg = St%Vars(i,j,AVG_ENG) - mhd_nflx = St%Vars(i,j,NUM_FLUX) - mhd_delE = conductance%deltaE(i,j) - mhd_eflx = St%Vars(i,j,AVG_ENG)*St%Vars(i,j,NUM_FLUX)*kev2erg - rcm_nflx = St%Vars(i,j,IM_ENFLX) - rcm_eflx = St%Vars(i,j,IM_EFLUX) - rcm_eavg = rcm_eflx/(rcm_nflx*kev2erg) !Back to keV - - if (isMHD) then - St%Vars(i,j,AVG_ENG ) = mhd_eavg - St%Vars(i,j,NUM_FLUX) = mhd_nflx - else if (isRCM) then - if (rcm_nflx <= TINY) then - rcm_eavg = 1.0e-8 - endif - St%Vars(i,j,AVG_ENG ) = rcm_eavg - St%Vars(i,j,NUM_FLUX) = rcm_nflx - conductance%deltaE(i,j) = 0.0 - else - !Mixture - wRCM = RampUp(gtype,wC1,wC2-wC1) - if ( (rcm_nflx > TINY) ) then - !Mix both - mix_nflx = wRCM*rcm_nflx + (1-wRCM)*mhd_nflx - mix_eflx = wRCM*rcm_eflx + (1-wRCM)*mhd_eflx - else - !RCM data wasn't good so just use MHD - mix_nflx = mhd_nflx - mix_eflx = mhd_eflx - endif - mix_eavg = mix_eflx/(mix_nflx*kev2erg) - - St%Vars(i,j,AVG_ENG ) = mix_eavg - St%Vars(i,j,NUM_FLUX) = mix_nflx - endif - enddo - enddo - - end subroutine conductance_linmrg - - subroutine conductance_rcmonoK(conductance,G,St) - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(inout) :: St - - integer :: i,j - logical :: isRCM,isMono,isMHD - real(rp) :: rcm_eavg,rcm_nflx,mhd_eavg,mhd_nflx,rcm_eavg_fin,rcm_nflx_fin,rcm_SigP,mhd_SigP,rcm_eavg0,rcm_nflx0 - real(rp) :: pK = 0.5 !Cut-off for "interesting" energy for precipitation [keV] - - ! If using rcmono, beta will be uniform and specified in xml or default. - ! RCM thermal flux will NOT be used for mono by setting gtype_RCM = 0.0 - ! to be safe, make beta_RCM=specified beta although conductance_alpha_beta won't be called in rcmono mode. - beta_RCM = conductance%beta - call conductance_zhang15(conductance,G,St) - - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,isRCM,isMono,isMHD,rcm_SigP,mhd_SigP) & - !$OMP private(rcm_eavg,rcm_nflx,mhd_eavg,mhd_nflx,rcm_eavg_fin,rcm_nflx_fin) - do j=1,G%Nt - do i=1,G%Np - isMono = conductance%deltaE(i,j) > 0.0 !Potential drop - isMHD = conductance%E0(i,j) > pK !Have "hot" MHD information, ie worth putting into Robinson - ! IM_GTYPE>0.5 covers low lat RCM grid. IM_EAVG>1.0e-8 covers buffre region with meaningful RCM precipitation. - isRCM = St%Vars(i,j,IM_GTYPE) > 0.5 .or. St%Vars(i,j,IM_EAVG)>1.0e-8 - - !Cases: isMono/isRCM/isMHD = 8 cases - !- No RCM: - if (.not. isRCM) then - !*/F/* = 4 cases - !If we don't have RCM info then MHD is the only game in town, use Zhang - St%Vars(i,j,AVG_ENG ) = St%Vars(i,j,Z_EAVG) ! [keV] - St%Vars(i,j,NUM_FLUX) = St%Vars(i,j,Z_NFLUX)! [#/cm^2/s] - St%Vars(i,j,AUR_TYPE) = AT_MHD ! AT_MHD=1,AT_RCM,AT_RMnoE,AT_RMfnE,AT_RMono - cycle - !NOTE: Should we handle ~isRCM and ~isMHD case separately? - endif - - !If still here we have RCM information - ! Rcmono is obsolete here because it assumes rcm passes eflux and eavg, - ! and it does not involve any merging based on rcm grid. - ! It only chooses whichever is present and whichever gives higher SigP when both are present. - ! Keep it here as a historical record. Use rcmhd where nflux is merged. - rcm_eavg = St%Vars(i,j,IM_EAVG) - if(St%Vars(i,j,IM_EAVG)>TINY) then - rcm_nflx = St%Vars(i,j,IM_EFLUX)/(St%Vars(i,j,IM_EAVG)*kev2erg) - else - rcm_nflx = 0.0 - endif - mhd_eavg = St%Vars(i,j,Z_EAVG) - mhd_nflx = St%Vars(i,j,Z_NFLUX) - - !- No (hot) MHD, but RCM info (ie low-lat plasmasphere) - if (.not. isMHD) then !T/T/F & F/T/F - !NOTE: Split this case into isMono and apply drop to RCM? - !For now just use RCM - St%Vars(i,j,AVG_ENG ) = rcm_eavg - St%Vars(i,j,NUM_FLUX) = rcm_nflx - St%Vars(i,j,AUR_TYPE) = AT_RCM - cycle - endif - - !Remaining, have both RCM and (hot) MHD. May or may not have pot drop - !T/T/T and F/T/T - - if (.not. isMono) then - !F/T/T - !Have RCM info and no drop, just use RCM - St%Vars(i,j,AVG_ENG ) = rcm_eavg - St%Vars(i,j,NUM_FLUX) = rcm_nflx - St%Vars(i,j,AUR_TYPE) = AT_RMnoE - cycle - endif - - !If still here, we have both RCM info and a potential drop - !Decide between the two by taking one that gives highest Sig-P - if (conductance%doMR) then - call AugmentMR(rcm_eavg,rcm_nflx,rcm_eavg_fin,rcm_nflx_fin) !Correct for MR - else - !No corrections - rcm_eavg_fin = rcm_eavg - rcm_nflx_fin = rcm_nflx - endif - - rcm_SigP = SigmaP_Robinson(rcm_eavg_fin,kev2erg*rcm_eavg_fin*rcm_nflx_fin) - mhd_SigP = SigmaP_Robinson(mhd_eavg ,kev2erg*mhd_eavg *mhd_nflx ) - - if (mhd_SigP>rcm_SigP) then - St%Vars(i,j,AVG_ENG ) = mhd_eavg - St%Vars(i,j,NUM_FLUX) = mhd_nflx - St%Vars(i,j,AUR_TYPE) = AT_RMono - else - !RCM diffuse is still better than MHD + puny potential drop - St%Vars(i,j,AVG_ENG ) = rcm_eavg !Use un-augmented value since MR gets called later - St%Vars(i,j,NUM_FLUX) = rcm_nflx - conductance%deltaE(i,j) = 0.0 !Wipe out potential drop since it don't matter (otherwise MR won't happen if desired) - St%Vars(i,j,AUR_TYPE) = AT_RMfnE - endif - - enddo - enddo - - end subroutine conductance_rcmonoK - - subroutine conductance_rcmono(conductance,G,St) - ! Keep a record of even older rcmono in master as of 20220303. - ! Slight changes to AUR_TYPE assignment. Note alpha/beta are effectively diff by 2/gamma and 1/sqrt(2). - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(inout) :: St - - call conductance_zhang15(conductance,G,St) - ! If there is no potential drop OR mono eflux is too low, use RCM precipitation instead. - St%Vars(:,:,AUR_TYPE) = AT_MHD - where(conductance%deltaE<=0.0) - St%Vars(:,:,AVG_ENG) = max(St%Vars(:,:,IM_EAVG),1.D-8) ! [keV] - St%Vars(:,:,NUM_FLUX) = St%Vars(:,:,IM_EFLUX)/(St%Vars(:,:,AVG_ENG)*kev2erg) ! [ergs/cm^2/s] - St%Vars(:,:,AUR_TYPE) = AT_RCM - !St%Vars(:,:,Z_NFLUX) = -1.0 ! for diagnostic purposes since full Z15 does not currently work. - end where - - end subroutine conductance_rcmono - - subroutine conductance_rcmfed(conductance,G,St) - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(inout) :: St - real(rp) :: E2Th, E2Tl - E2Th = 2.0 - E2Tl = 1.0 - - ! Use totally Fedder precipitation if deltaE > 2Te. - call conductance_fedder95(conductance,G,St) - tmpC = conductance%deltaE/conductance%E0 - where(tmpC<=E2Th.and.tmpC>=E2Tl) ! Linearly combine RCM and Fedder where 1<=deltaE/Te<=2. - St%Vars(:,:,AVG_ENG) = max(((E2Th-tmpC)*St%Vars(:,:,IM_EAVG)+(tmpC-E2Tl)*St%Vars(:,:,AVG_ENG))/(E2Th-E2Tl),1.D-8) ! [keV] - St%Vars(:,:,NUM_FLUX) = ((E2Th-tmpC)*St%Vars(:,:,IM_EFLUX)/(St%Vars(:,:,AVG_ENG)*kev2erg)+(tmpC-E2Tl)*St%Vars(:,:,NUM_FLUX))/(E2Th-E2Tl) ! [ergs/cm^2/s]/[ergs] - St%Vars(:,:,Z_NFLUX) = -0.5 ! for diagnostic purposes since full Z15 does not currently work. - elsewhere(tmpC=0.5) - ! The formula was derived from E_avg between 0.5 and 30 keV. Kc becomes negative when E_avg > 47-48 keV. - Kc = 3.36 - exp(0.597-0.37*St%Vars(:,:,AVG_ENG)+0.00794*St%Vars(:,:,AVG_ENG)**2) - St%Vars(:,:,NUM_FLUX) = St%Vars(:,:,NUM_FLUX)*St%Vars(:,:,AVG_ENG) ! store Eflux - St%Vars(:,:,AVG_ENG) = 0.073+0.933*St%Vars(:,:,AVG_ENG)-0.0092*St%Vars(:,:,AVG_ENG)**2 ! [keV] - St%Vars(:,:,NUM_FLUX) = Kc*St%Vars(:,:,NUM_FLUX)/St%Vars(:,:,AVG_ENG) - end where -! conductance%deltaSigmaP = (2.16-0.87*exp(-0.16*St%Vars(:,:,AVG_ENG)))*conductance%deltaSigmaP -! conductance%deltaSigmaH = (1.87-0.54*exp(-0.16*St%Vars(:,:,AVG_ENG)))*conductance%deltaSigmaH - end subroutine conductance_mr - - !Cleaned up routine to calculate MR-augmented eavg and nflx - subroutine AugmentMR(eavg,nflx,eavgMR,nflxMR) - real(rp), intent(in) :: eavg ,nflx - real(rp), intent(out) :: eavgMR,nflxMR - - real(rp) :: eflux,Kc - - if ( (eavg<=30.0) .and. (eavg>=0.5) ) then - Kc = 3.36 - exp(0.597-0.37*eavg+0.00794*eavg**2) - eflux = eavg*nflx - eavgMR = 0.073+0.933*eavg-0.0092*eavg**2 ! [keV] - nflxMR = Kc*eflux/eavgMR - else - !Nothing to do - eavgMR = eavg - nflxMR = nflx - - endif - end subroutine AugmentMR - - !Calculate mirror ratio array - !NOTE: Leaving this to be done every time at every lat/lon to accomodate improved model later - subroutine GenMirrorRatio(G,St,doIGRFO) - type(mixGrid_T) , intent(in) :: G - type(mixState_T), intent(in) :: St - logical,optional,intent(in) :: doIGRFO - real(rp) :: mlat,mlon - integer :: i,j - logical :: doIGRF - - if(present(doIGRFO)) then - doIGRF = doIGRFO - else ! default is using IGRF, i.e, default is unequal split. - doIGRF = .true. - endif - - if (RinMHD > 0) then - !Calculate actual mirror ratio - !NOTE: Should replace this w/ actual inner boundary field strength - - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,mlat,mlon) - do j=1,G%Nt - do i=1,G%Np - mlat = PI/2 - G%t(i,j) - mlon = G%p(i,j) - - if(.not.doIGRF) then - RM(i,j) = MirrorRatio(mlat,RinMHD) - elseif (St%hemisphere==NORTH) then - RM(i,j) = IGRFMirrorRatio(+mlat,+mlon,RinMHD) - else - !Southern, always a right-hand system based on the local pole. - !SM phi (mlon) goes in clock-wise as opposed to counter-clockwise if looking down on the southern pole from above. - RM(i,j) = IGRFMirrorRatio(-mlat,-mlon,RinMHD) - endif - enddo - enddo !j loop - else - !Set mirror ratio everywhere no matter the inner boundary to 10 - !Note: This may have been okay for simulating magnetospheres 40 years ago, but it's 2021 now - RM = 10.0 - endif - end subroutine GenMirrorRatio - - subroutine conductance_total(conductance,G,St,gcm,h) - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(inout) :: St - type(gcm_T),optional,intent(in) :: gcm - integer,optional,intent(in) :: h - - call GenMirrorRatio(G,St) - - ! always call fedder to fill in AVG_ENERGY and NUM_FLUX - ! even if const_sigma, we still have the precip info that way - - ! compute EUV though because it's used in fedder - call conductance_euv(conductance,G,St) - select case ( conductance%aurora_model_type ) - case (FEDDER) - call conductance_fedder95(conductance,G,St) - case (ZHANG) - doDrift = .true. - call conductance_zhang15(conductance,G,St) - case (RCMONO) - doDrift = .false. - call conductance_rcmono(conductance,G,St) - case (RCMHD) - doDrift = .false. - call conductance_rcmhd(conductance,G,St) - case (RCMFED) - call conductance_rcmfed(conductance,G,St) - case (LINMRG) - call conductance_linmrg(conductance,G,St) - case default - stop "The aurora precipitation model type entered is not supported." - end select - - ! correct for multiple reflections if you're so inclined - if (conductance%doMR) call conductance_mr(conductance,G,St) - - ! Smooth precipitation energy and flux before calculating conductance. - if (conductance%doAuroralSmooth) call conductance_auroralsmooth(St,G,conductance) - - if (present(gcm)) then - !write(*,*) 'going to apply!' - call apply_gcm2mix(gcm,St,h) - St%Vars(:,:,SIGMAP) = max(conductance%pedmin,St%Vars(:,:,SIGMAP)) - St%Vars(:,:,SIGMAH) = max(conductance%hallmin,St%Vars(:,:,SIGMAH)) - !St%Vars(:,:,SIGMAH) = min(max(conductance%hallmin,St%Vars(:,:,SIGMAH)),& - ! St%Vars(:,:,SIGMAP)*conductance%sigma_ratio) - else if (conductance%const_sigma) then - !write(*,*) "conductance: const_sigma" - St%Vars(:,:,SIGMAP) = conductance%ped0 - St%Vars(:,:,SIGMAH) = 0.D0 - else - !write(*,*) "conductance: aurora" - call conductance_aurora(conductance,G,St) - - St%Vars(:,:,SIGMAP) = sqrt( conductance%euvSigmaP**2 + conductance%deltaSigmaP**2) - St%Vars(:,:,SIGMAH) = sqrt( conductance%euvSigmaH**2 + conductance%deltaSigmaH**2) - endif - - ! Apply cap - if ((conductance%apply_cap).and.(.not. conductance%const_sigma).and.(.not. present(gcm))) then - St%Vars(:,:,SIGMAP) = max(conductance%pedmin,St%Vars(:,:,SIGMAP)) - St%Vars(:,:,SIGMAH) = min(max(conductance%hallmin,St%Vars(:,:,SIGMAH)),& - St%Vars(:,:,SIGMAP)*conductance%sigma_ratio) - endif - - end subroutine conductance_total - - subroutine conductance_ramp(conductance,G,rPolarBound,rEquatBound,rLowLimit) - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - real(rp), intent(in) :: rPolarBound,rEquatBound,rLowLimit - - where (G%r < rPolarBound) - conductance%rampFactor = 1.0D0 - elsewhere ( (G%r > rPolarBound).and.(G%r <= rEquatBound) ) - conductance%rampFactor = 1.0D0+(rLowLimit - 1.0D0)*(G%r - rPolarBound)/(rEquatBound-rPolarBound) - elsewhere ! G%r > rEquatBound - conductance%rampFactor = rLowLimit - end where - end subroutine conductance_ramp - - subroutine conductance_auroralmask(conductance,G,signOfY) - type(mixConductance_T), intent(inout) :: conductance - type(mixGrid_T), intent(in) :: G - real(rp), intent(in) :: signOfY - real(rp) :: Rio, al0, alp, Radi, order, Rady - - Rio = 1.02 - al0 = -5.0*deg2rad - alp = 28*deg2rad - alp = min(28*deg2rad,alp) - Rady = Rio*sin(alp-al0) - Radi = Rady**2 ! Rio**2*(1-cos(alp-al0)*cos(alp-al0)) - order = 2.0 - - RRdi = 0.D0 - RRdi = (G%y-0.03*signOfY)**2 + ( G%x/cos(al0) - Rio*cos(alp-al0)*tan(al0) )**2 - where(RRdi < Radi) - conductance%AuroraMask = cos((RRdi/Radi)**order*PI/2)+0.D0 - elsewhere - conductance%AuroraMask = 0.D0 - end where - where(abs(G%y)=0) - conductance%PrecipMask = 0.D0 - end where - ! back up mono - tmpC = St%Vars(:,:,AVG_ENG) - tmpD = St%Vars(:,:,NUM_FLUX) - endif - - ! get expanded diffuse with margin grid (ghost cells) - if(smthE==1) then - call conductance_margin(G,St%Vars(:,:,AVG_ENG)*St%Vars(:,:,NUM_FLUX)*conductance%PrecipMask, tmpE) - else - call conductance_margin(G,St%Vars(:,:,AVG_ENG)*conductance%PrecipMask, tmpE) - end if - call conductance_margin(G,St%Vars(:,:,NUM_FLUX)*conductance%PrecipMask, tmpF) - - ! do smoothing - !$OMP PARALLEL DO default(shared) collapse(2) & - !$OMP private(i,j) - do j=1,G%Nt - do i=1,G%Np - ! SmoothOperator55(Q,isGO) - St%Vars(i,j,NUM_FLUX)=SmoothOperator55(tmpF(i:i+4,j:j+4)) - if(smthE==1) then - St%Vars(i,j,AVG_ENG) = max(SmoothOperator55(tmpE(i:i+4,j:j+4))/St%Vars(i,j,NUM_FLUX),1.D-8) - else - St%Vars(i,j,AVG_ENG) = max(SmoothOperator55(tmpE(i:i+4,j:j+4)),1.D-8) - end if - end do - end do - - if(smthDEPonly) then - ! combine smoothed diffuse and unsmoothed mono - where(St%Vars(:,:,Z_NFLUX)>=0) - St%Vars(:,:,AVG_ENG) = tmpC - St%Vars(:,:,NUM_FLUX) = tmpD - end where - endif - end subroutine conductance_auroralsmooth - subroutine conductance_margin(G,array,arraymar) + ! Conductance boundary proessing. type(mixGrid_T), intent(in) :: G real(rp),dimension(G%Np,G%Nt), intent(in) :: array real(rp),dimension(G%Np+4,G%Nt+4), intent(out) :: arraymar @@ -1119,8 +890,8 @@ module mixconductance arraymar(G%Np+3:G%Np+4,:) = arraymar(3:4,:) end subroutine conductance_margin - !Routine to change MIX-gamma on the fly if necessary subroutine SetMIXgamma(gamma) + !Routine to change MIX-gamma on the fly if necessary real(rp), intent(in) :: gamma MIXgamma = gamma end subroutine SetMIXgamma diff --git a/src/remix/mixparams.F90 b/src/remix/mixparams.F90 index 9c43d663..0f13323e 100644 --- a/src/remix/mixparams.F90 +++ b/src/remix/mixparams.F90 @@ -112,16 +112,10 @@ module mixparams Params%aurora_model_type = FEDDER case ("ZHANG") Params%aurora_model_type = ZHANG - case ("RCMHD") - Params%aurora_model_type = RCMHD - case ("RCMONO") - Params%aurora_model_type = RCMONO - case ("RCMFED") - Params%aurora_model_type = RCMFED case ("LINMRG") Params%aurora_model_type = LINMRG case default - stop "The aurora model type entered is not supported (Available options: FEDDER, ZHANG, RCMHD, RCMONO, RCMFED, LINMRG)." + stop "The aurora model type entered is not supported (Available options: FEDDER, ZHANG, LINMRG)." end select ! =========== CONDUCTANCE MODEL PARAMETERS =================== ! From 3b707301084017ea7566f2dfdb821945fba8a283 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 18 Sep 2023 10:36:30 -0400 Subject: [PATCH 041/365] Linting --- scripts/quicklook/heliopic.py | 161 ++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 78 deletions(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index f12e621a..3739ba95 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -48,14 +48,13 @@ import kaipy.gamhelio.heliosphere as hsph import kaipy.kaiH5 as kh5 import kaipy.kaiTools as ktools import kaipy.kaiViz as kv -import kaipy.kdefs as kdefs from kaipy.satcomp import scutils # Program constants and defaults # Program description. -description = """Creates multi-panel figure for Gamera heliosphere run +DESCRIPTION = """Creates multi-panel figure for Gamera heliosphere run Upper left - Solar wind speed Upper right - Solar wind number density Lower left - Solar wind temperature @@ -63,19 +62,20 @@ Lower right - Solar wind radial magnetic field """ # Default identifier for results to read. -default_runid = "wsa" +DEFAULT_RUNID = "wsa" # List of steps -default_steps = "-1" +DEFAULT_STEPS = "-1" # Default slices -default_slice = "1:2:1" +DEFAULT_SLICE = "1:2:1" # Code for default picture type. -default_pictype = "pic1" +DEFAULT_PICTYPE = "pic1" + +# Colors to use for spacecraft position symbols. +SPACECRAFT_COLORS = list(mpl.colors.TABLEAU_COLORS.keys()) -# Color to use for spacecraft position symbols. -SPACECRAFT_COLOR = "red" def create_command_line_parser(): """Create the command-line argument parser. @@ -92,7 +92,7 @@ def create_command_line_parser(): Command-line argument parser for this script. """ parser = argparse.ArgumentParser( - description=description, + description=DESCRIPTION, formatter_class=argparse.RawTextHelpFormatter ) parser.add_argument( @@ -104,37 +104,32 @@ def create_command_line_parser(): help="Directory containing data to read (default: %(default)s)" ) parser.add_argument( - "-id", type=str, metavar="runid", default=default_runid, + "-id", type=str, metavar="runid", default=DEFAULT_RUNID, help="Run ID of data (default: %(default)s)" ) parser.add_argument( - "--nlist", type=lambda n: [int(item) for item in n.split(',')], metavar="list of steps", default=default_steps, + "--nlist", type=lambda n: [int(item) for item in n.split(',')], + metavar="list of steps", default=DEFAULT_STEPS, help="List of time slice(s) to plot (default: %(default)s)" ) parser.add_argument( - "--nslice", type=lambda n: [int(item) for item in n.split(':')], metavar="step slice", default=default_slice, + "--nslice", type=lambda n: [int(item) for item in n.split(':')], + metavar="step slice", default=DEFAULT_SLICE, help="Slice for range of time slice(s) to plot (default: %(default)s)" ) parser.add_argument( - "-nompi", action="store_true", default=False, - help="Don't show MPI boundaries (default: %(default)s)." - ) - parser.add_argument( - "-pic", type=str, metavar="pictype", default=default_pictype, + "-pic", type=str, metavar="pictype", default=DEFAULT_PICTYPE, help="Code for plot type (default: %(default)s)" ) parser.add_argument( "--spacecraft", type=str, metavar="spacecraft", default=None, - help="Names of spacecraft to plot positions, separated by commas (default: %(default)s)" + help="Names of spacecraft to plot positions, separated by commas " + "(default: %(default)s)" ) parser.add_argument( "-v", "--verbose", action="store_true", default=False, help="Print verbose output (default: %(default)s)." ) - parser.add_argument( - "-o", "--outfile", type=str, metavar="outFile", default=fOut, - help="Output file name (default: %(default)s)." - ) parser.add_argument( "-p", "--parallel", action="store_true", default=False, help="Read from HDF5 in parallel (default: %(default)s)." @@ -149,8 +144,9 @@ def create_command_line_parser(): ) return parser + def initFig(pic): - # Determine figure size (width, height) (inches) based on the picture type. + """Determine figure size (width, height) (inches) based on the pic type.""" if pic == "pic1" or pic == "pic2" or pic == "pic7": figSz = (10, 12.5) elif pic == "pic3": @@ -159,20 +155,20 @@ def initFig(pic): figSz = (10, 6) elif pic == "pic5": figSz = (12, 12) - elif pic == "pic6": + elif pic == "pic6": figSz = (12.5, 12.5) # Create the figure. fig = plt.figure(figsize=figSz) return fig -# Name of plot output file. -def fOut(id, pic, nStp): - return "qkpic_{}_{}_n{}.png".format(id, pic, nStp) -if __name__ == "__main__": - from alive_progress import alive_bar +def fOut(runid, pic, nStp): + """Compute the name of the output file.""" + return "qkpic_{}_{}_n{}.png".format(runid, pic, nStp) + +def main(): """Make a quick figure of a Gamera heliosphere run.""" # Set up the command-line parser. @@ -180,25 +176,22 @@ if __name__ == "__main__": # Parse the command-line arguments. args = parser.parse_args() + if args.debug: + print(f"args = {args}") debug = args.debug fdir = args.d ftag = args.id - noMPI = args.nompi steps = args.nlist slices = args.nslice spacecraft = args.spacecraft verbose = args.verbose - fOut = args.outfile pic = args.pic doParallel = args.parallel nWorkers = args.nworkers pic2lon = args.lon - if debug: - print("args = %s" % args) if slices: - print("Slice selected {}".format(slice(slices[0],slices[1],slices[2]))) - # Invert the MPI flag for convenience. - doMPI = not noMPI + print("Slice selected {}".format(slice(slices[0], slices[1], + slices[2]))) tic = time.perf_counter() # Fetch the plot domain based on the picture type. @@ -214,17 +207,16 @@ if __name__ == "__main__": # Open a pipe to the results data. tic = time.perf_counter() - gsph = hsph.GamsphPipe(fdir, ftag, doFast=doFast, doParallel=doParallel, nWorkers=nWorkers) + gsph = hsph.GamsphPipe(fdir, ftag, doFast=doFast, doParallel=doParallel, + nWorkers=nWorkers) toc = time.perf_counter() - + print(f"Open pipe took {toc-tic} s") - if(slices and steps[0] == -1): - steps = range(gsph.sFin)[slice(slices[0],slices[1],slices[2])] + if (slices and steps[0] == -1): + steps = range(gsph.sFin)[slice(slices[0], slices[1], slices[2])] - nsteps = len(steps) - - for nStp in steps: + for nStp in steps: tic = time.perf_counter() print(f"Generating {pic} for time step {nStp}") fig = initFig(pic) @@ -235,7 +227,10 @@ if __name__ == "__main__": print(f"mjd = {mjd}") # Lay out the subplots. - if pic == "pic1" or pic == "pic2" or pic == "pic3" or pic == "pic6" or pic == "pic7": + if ( + pic == "pic1" or pic == "pic2" or pic == "pic3" or pic == "pic6" or + pic == "pic7" + ): gs = gridspec.GridSpec(4, 6, height_ratios=[20, 1, 20, 1]) # Axes for plots. AxL0 = fig.add_subplot(gs[0, 0:3]) @@ -259,7 +254,7 @@ if __name__ == "__main__": if nStp < 0: nStp = gsph.sFin - print("Using Step %d" % nStp) + print(f"Using Step {nStp}") # Now create the actual plots. if pic == "pic1": @@ -270,15 +265,19 @@ if __name__ == "__main__": hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) elif pic == "pic2": - hviz.PlotMerMagV(gsph ,nStp, xyBds, AxL0, AxC1_0,indx=(None,pic2lon)) - hviz.PlotMerDNorm(gsph, nStp, xyBds, AxR0, AxC2_0,indx=(None,pic2lon)) - hviz.PlotMerTemp(gsph, nStp, xyBds, AxL1, AxC1_1,indx=(None,pic2lon)) - hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1,indx=(None,pic2lon)) + hviz.PlotMerMagV(gsph, nStp, xyBds, AxL0, AxC1_0, + indx=(None, pic2lon)) + hviz.PlotMerDNorm(gsph, nStp, xyBds, AxR0, AxC2_0, + indx=(None, pic2lon)) + hviz.PlotMerTemp(gsph, nStp, xyBds, AxL1, AxC1_1, + indx=(None, pic2lon)) + hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1, + indx=(None, pic2lon)) elif pic == "pic3": - hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0,idx=0) - hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0,idx=0) - hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1,idx=0) - hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1,idx=0) + hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, idx=0) + hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, idx=0) + hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1, idx=0) + hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, idx=0) elif pic == "pic4": hviz.PlotiSlBrRotatingFrame(gsph, nStp, xyBds, Ax, AxC) elif pic == "pic5": @@ -291,12 +290,12 @@ if __name__ == "__main__": hviz.PlotEqBy(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBz(gsph, nStp, xyBds, AxR1, AxC2_1) elif pic == "pic7": - hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0,jidx=448) - hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0,jidx=448) - hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1,jidx=448) - hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1,jidx=448) + hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0, jidx=448) + hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=448) + hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=448) + hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1, jidx=448) else: - print ("Pic is empty. Choose pic1 or pic2 or pic3") + print("Pic is empty. Choose pic1 or pic2 or pic3") # Add time in the upper left. if pic == "pic1" or pic == "pic2" or pic == "pic6" or pic == "pic7": @@ -306,63 +305,65 @@ if __name__ == "__main__": elif pic == "pic4" or pic == "pic5": gsph.AddTime(nStp, Ax, xy=[0.015, 0.92], fs="small") else: - print ("Pic is empty. Choose pic1 or pic2 or pic3") + print("Pic is empty. Choose pic1 or pic2 or pic3") # Overlay the spacecraft trajectory, if needed. if spacecraft: - print("Overplotting spacecraft trajectories of %s." % spacecraft) + print(f"Overplotting spacecraft trajectories of {spacecraft}.") # Split the list into individual spacecraft names. spacecraft = spacecraft.split(',') if debug: - print("spacecraft = %s" % spacecraft) + print(f"spacecraft = {spacecraft}") # Fetch the MJD start and end time of the model results. fname = gsph.f0 if debug: - print("fname = %s" % fname) + print(f"fname = {fname}") MJD_start = kh5.tStep(fname, 0, aID="MJD") if debug: - print("MJD_start = %s" % MJD_start) + print(f"MJD_start = {MJD_start}") MJD_end = kh5.tStep(fname, gsph.sFin, aID="MJD") if debug: - print("MJD_end = %s" % MJD_end) + print(f"MJD_end = {MJD_end}") # Convert the start and stop MJD to a datetime object in UT. ut_start = ktools.MJD2UT(MJD_start) if debug: - print("ut_start = %s" % ut_start) + print(f"ut_start = {ut_start}") ut_end = ktools.MJD2UT(MJD_end) if debug: - print("ut_end = %s" % ut_end) + print(f"ut_end = {ut_end}") # Get the MJDc value for use in computing the gamhelio frame. MJDc = scutils.read_MJDc(fname) if debug: - print("mjdc = %s" % MJDc) + print(f"mjdc = {MJDc}") # Fetch and plot the trajectory of each spacecraft from CDAWeb. for (i_sc, sc_id) in enumerate(spacecraft): if verbose: - print("Fetching trajectory for %s." % sc_id) + print(f"Fetching trajectory for {sc_id}.") - # Fetch the spacecraft trajectory in whatever frame is available - # from CDAWeb. + # Fetch the spacecraft trajectory in whatever frame is + # available from CDAWeb. sc_data = cdaweb_utils.fetch_helio_spacecraft_trajectory( sc_id, ut_start, ut_end ) if sc_data is None: - print("No trajectory found for %s." % sc_id) + print(f"No trajectory found for {sc_id}.") continue # Ingest the trajectory by converting it to the GH(MJDc) frame. if verbose: - print("Converting ephemeris for %s into gamhelio format." % sc_id) + print(f"Converting ephemeris for {sc_id} into gamhelio " + "format.") t_strings = np.array([str(t) for t in sc_data["Epoch"]]) t = astropy.time.Time(t_strings, scale='utc').mjd - x, y, z = cdaweb_utils.ingest_helio_spacecraft_trajectory(sc_id, sc_data, MJDc) + x, y, z = cdaweb_utils.ingest_helio_spacecraft_trajectory( + sc_id, sc_data, MJDc) if debug: - print("t, x, y, z = %s, %s, %s, %s" % (t, x, y, z)) + print(f"t, x, y, z = {t}, {x}, {y}, {z}") # Interpolate the spacecraft position at the time for the plot. t_sc = mjd @@ -382,7 +383,6 @@ if __name__ == "__main__": # Plot a position of the spacecraft. # Left plot - SPACECRAFT_COLORS = list(mpl.colors.TABLEAU_COLORS.keys()) color = SPACECRAFT_COLORS[i_sc % len(SPACECRAFT_COLORS)] x_nudge = 0.0 y_nudge = 8.0 @@ -390,18 +390,19 @@ if __name__ == "__main__": for ax in (AxL0, AxR0, AxL1, AxR1): ax.plot(x_sc, y_sc, 'o', c=color) ax.plot(x_sc, y_sc, 'o', c="black", fillstyle="none") - ax.text(x_sc + x_nudge, y_sc+ y_nudge, sc_id, + ax.text(x_sc + x_nudge, y_sc + y_nudge, sc_id, c="black", horizontalalignment="center") elif pic == "pic2": for ax in (AxL0, AxR0, AxL1, AxR1): ax.plot(x_sc, y_sc, 'o', c=color) ax.plot(x_sc, y_sc, 'o', c="black", fillstyle="none") - ax.text(x_sc + x_nudge, y_sc+ y_nudge, sc_id, + ax.text(x_sc + x_nudge, y_sc + y_nudge, sc_id, c="black", horizontalalignment="center") elif pic == "pic3": for ax in (AxL0, AxR0, AxL1, AxR1): ax.plot(lon_sc, lat_sc, 'o', c=color) - ax.plot(lon_sc, lat_sc, 'o', c="black", fillstyle="none") + ax.plot(lon_sc, lat_sc, 'o', c="black", + fillstyle="none") ax.text(lon_sc + x_nudge, lat_sc + y_nudge, sc_id, c="black", horizontalalignment="center") elif pic == "pic4": @@ -419,3 +420,7 @@ if __name__ == "__main__": fig.clear() toc = time.perf_counter() print(f"Step {nStp} took {toc-tic} s") + + +if __name__ == "__main__": + main() From 839dd0c38955128d86f5bfe4b24832a677af6fb6 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 18 Sep 2023 19:44:32 -0400 Subject: [PATCH 042/365] Docstrings. --- scripts/quicklook/heliopic.py | 60 +++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 3739ba95..fbda78ff 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -1,30 +1,64 @@ #!/usr/bin/env python -"""Make a quick figure of a Gamera heliosphere run. +"""Make a quick-look figure of a Gamera heliosphere run. -Make a quick figure of a Gamera heliosphere run. +Make a quick-look figure of a Gamera heliosphere run. Five different sets of plots are supported, and are distinguished by the value of the "pic" argument. -pic1 (default): A 4-panel display showing radial speed, number -density*(r/r0)**2, temperature*(r/r0), and radial magnetic field*(r/r0)**2. -These plots are done in the XY plane of the gamhelio frame (which is a -Heliographic Stonyhurst (HGS) frame, modified with the +x reversed from -the usual HGS definition) +pic1 (default): A 4-panel display showing pcolormesh plots in the z = 0 +(equatorial) plane of the gamhelio frame used in the simulation. The plots +are: -pic2: + Upper left: Solar wind speed (km/s) + Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) + Lower left: Solar wind temperature scaled by r/r0 (MK) + Lower right: Solar wind radial magnetic field scaled by r/r0 (nT) -pic3: +pic2: A 4-panel display showing pcolormesh plots in the y = 0 (meridional, +containing Earth) plane of the gamhelio frame used in the simulation. The +plots are: -pic4: + Upper left: Solar wind speed (km/s) + Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) + Lower left: Solar wind temperature scaled by r/r0 (MK) + Lower right: Solar wind radial magnetic field scaled bryr/r0 (nT) -pic5: +pic3: A 4-panel display showing pcolormesh plots in the r = 1 AU slice of the +gamhelio frame used in the simulation. The plots are: -Author ------- + Upper left: Solar wind speed (km/s) + Upper right: Solar wind number density (cm**-3) + Lower left: Solar wind temperature (MK) + Lower right: Solar wind radial magnetic field (nT) + +pic4: A pcolormesh plot in the innermost radial slice (r = 22 Rsun) of the +gamhelio frame used in the simulation. The plot shows the radial magnetic +field in nT, in a coordinate frame rotating with the Sun. + +pic5: A 3-panel display showing line as a function of radius, +22 Rsun <= r <= 220 Rsun. The plots are: + + Upper left: Solar wind number density (cm**-3) + Upper right: Solar wind speed (km/s) + Lower left: Solar wind radial momentum flux (km**2/s**2/cm**3) + +pic6: A 4-panel display showing components of the solar wind magnetic field +in the solar equatorial plane (z=0), for -200 Rsun <= X, Y <= +200 Rsun. + + Upper left: Radial magnetic field (nT) + Upper right: x-component of magnetic field (nT) + Lower left: y-component of magnetic field (nT) + Lower right: z-component of magnetic field (nT) + +pic7: ??? + +Authors +------- Elena Provornikova (elena.provornikova@jhuapl.edu) +Andrew McCubbin (andrew.mccubbin@jhuapl.edu) Eric Winter (eric.winter@jhuapl.edu) """ From 771405f1254f79dd0b136dccb66802397387a40f Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 18 Sep 2023 20:02:50 -0400 Subject: [PATCH 043/365] Fixed bug in spacecraft position plot in pic2. --- scripts/quicklook/heliopic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index fbda78ff..ff8e704f 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -428,9 +428,9 @@ def main(): c="black", horizontalalignment="center") elif pic == "pic2": for ax in (AxL0, AxR0, AxL1, AxR1): - ax.plot(x_sc, y_sc, 'o', c=color) - ax.plot(x_sc, y_sc, 'o', c="black", fillstyle="none") - ax.text(x_sc + x_nudge, y_sc + y_nudge, sc_id, + ax.plot(x_sc, z_sc, 'o', c=color) + ax.plot(x_sc, z_sc, 'o', c="black", fillstyle="none") + ax.text(x_sc + x_nudge, z_sc + y_nudge, sc_id, c="black", horizontalalignment="center") elif pic == "pic3": for ax in (AxL0, AxR0, AxL1, AxR1): From 0614729c77217d00865a8be1c40c02a8d5069f43 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 18 Sep 2023 20:06:05 -0400 Subject: [PATCH 044/365] Added spacecraft positions to pic6 plots. --- scripts/quicklook/heliopic.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index ff8e704f..7a5b6be3 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -447,6 +447,12 @@ def main(): c="black", horizontalalignment="center") elif pic == "pic5": pass + elif pic == "pic6": + for ax in (AxL0, AxR0, AxL1, AxR1): + ax.plot(x_sc, y_sc, 'o', c=color) + ax.plot(x_sc, y_sc, 'o', c="black", fillstyle="none") + ax.text(x_sc + x_nudge, y_sc + y_nudge, sc_id, + c="black", horizontalalignment="center") # Save the figure to a file. path = os.path.join(fdir, fOut(ftag, pic, nStp)) From fffb0a6d72e0550fa57df4b39f77e5fb69875196 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 18 Sep 2023 20:07:37 -0400 Subject: [PATCH 045/365] Docstrings --- scripts/quicklook/heliopic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 7a5b6be3..35b995c2 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -48,7 +48,7 @@ pic5: A 3-panel display showing line as a function of radius, pic6: A 4-panel display showing components of the solar wind magnetic field in the solar equatorial plane (z=0), for -200 Rsun <= X, Y <= +200 Rsun. - Upper left: Radial magnetic field (nT) + Upper left: Radial component of magnetic field (nT) Upper right: x-component of magnetic field (nT) Lower left: y-component of magnetic field (nT) Lower right: z-component of magnetic field (nT) From 5f61a1df0d2dc894014df031ba18f13cdf297bec Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 19 Sep 2023 15:15:37 -0400 Subject: [PATCH 046/365] Minor linting and docstrings. --- scripts/quicklook/heliopic.py | 222 ++++++++++++++++++++-------------- 1 file changed, 129 insertions(+), 93 deletions(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 35b995c2..a703f082 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -1,11 +1,11 @@ #!/usr/bin/env python -"""Make a quick-look figure of a Gamera heliosphere run. +"""Make a quick-look plot for a gamhelio run. -Make a quick-look figure of a Gamera heliosphere run. +Make a quick-look plot for a gamhelio run. -Five different sets of plots are supported, and are distinguished by the +Several different sets of plots are supported, and are distinguished by the value of the "pic" argument. pic1 (default): A 4-panel display showing pcolormesh plots in the z = 0 @@ -24,26 +24,26 @@ plots are: Upper left: Solar wind speed (km/s) Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) Lower left: Solar wind temperature scaled by r/r0 (MK) - Lower right: Solar wind radial magnetic field scaled bryr/r0 (nT) + Lower right: Solar wind radial magnetic field scaled by r/r0 (nT) pic3: A 4-panel display showing pcolormesh plots in the r = 1 AU slice of the gamhelio frame used in the simulation. The plots are: Upper left: Solar wind speed (km/s) - Upper right: Solar wind number density (cm**-3) - Lower left: Solar wind temperature (MK) - Lower right: Solar wind radial magnetic field (nT) + Upper right: Solar wind number density (cm**-3) - SCALED? + Lower left: Solar wind temperature (MK) - SCALED? + Lower right: Solar wind radial magnetic field (nT) - SCALED? pic4: A pcolormesh plot in the innermost radial slice (r = 22 Rsun) of the gamhelio frame used in the simulation. The plot shows the radial magnetic -field in nT, in a coordinate frame rotating with the Sun. +field in nT, in a coordinate frame rotating with the Sun. - SCALED? -pic5: A 3-panel display showing line as a function of radius, +pic5: A 3-panel display showing solar wind variables as a function of radius, 22 Rsun <= r <= 220 Rsun. The plots are: - Upper left: Solar wind number density (cm**-3) - Upper right: Solar wind speed (km/s) - Lower left: Solar wind radial momentum flux (km**2/s**2/cm**3) + Upper left: Solar wind number density (cm**-3) - SCALED? + Upper right: Solar wind speed (km/s) - SCALED? + Lower left: Solar wind radial momentum flux (km**2/s**2/cm**3) - SCALED? pic6: A 4-panel display showing components of the solar wind magnetic field in the solar equatorial plane (z=0), for -200 Rsun <= X, Y <= +200 Rsun. @@ -88,12 +88,7 @@ from kaipy.satcomp import scutils # Program constants and defaults # Program description. -DESCRIPTION = """Creates multi-panel figure for Gamera heliosphere run -Upper left - Solar wind speed -Upper right - Solar wind number density -Lower left - Solar wind temperature -Lower right - Solar wind radial magnetic field -""" +DESCRIPTION = "Make a quicklook plot for a gamhelio run." # Default identifier for results to read. DEFAULT_RUNID = "wsa" @@ -124,36 +119,52 @@ def create_command_line_parser(): ------- parser : argparse.ArgumentParser Command-line argument parser for this script. + + Raises + ------ + None """ - parser = argparse.ArgumentParser( - description=DESCRIPTION, - formatter_class=argparse.RawTextHelpFormatter - ) + parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument( - "--debug", action="store_true", default=False, + "--debug", action="store_true", help="Print debugging output (default: %(default)s)." ) parser.add_argument( - "-d", type=str, metavar="directory", default=os.getcwd(), + "--directory", "-d", type=str, metavar="directory", + default=os.getcwd(), help="Directory containing data to read (default: %(default)s)" ) parser.add_argument( "-id", type=str, metavar="runid", default=DEFAULT_RUNID, help="Run ID of data (default: %(default)s)" ) + parser.add_argument( + "-lon", type=float, metavar="lon", default=0.0, + help="Longitude of meridian slice (pic2) (default: %(default)s)" + ) parser.add_argument( "--nlist", type=lambda n: [int(item) for item in n.split(',')], metavar="list of steps", default=DEFAULT_STEPS, - help="List of time slice(s) to plot (default: %(default)s)" + help="List of time slice(s) n1,n2,... to plot (default: %(default)s)" ) parser.add_argument( "--nslice", type=lambda n: [int(item) for item in n.split(':')], metavar="step slice", default=DEFAULT_SLICE, - help="Slice for range of time slice(s) to plot (default: %(default)s)" + help="Slice for range of time slice(s) n1:n2 to plot " + "(default: %(default)s)" ) parser.add_argument( - "-pic", type=str, metavar="pictype", default=DEFAULT_PICTYPE, - help="Code for plot type (default: %(default)s)" + "--nworkers", "-nw", type=int, metavar="nworkers", default=4, + help="Number of parallel workers (default: %(default)s)" + ) + parser.add_argument( + "--parallel", "-p", action="store_true", + help="Read from HDF5 in parallel (default: %(default)s)." + ) + parser.add_argument( + "-pic", type=str, metavar="pictype", + default=DEFAULT_PICTYPE, + help="Code for plot type (pic1-pic7) (default: %(default)s)" ) parser.add_argument( "--spacecraft", type=str, metavar="spacecraft", default=None, @@ -161,26 +172,34 @@ def create_command_line_parser(): "(default: %(default)s)" ) parser.add_argument( - "-v", "--verbose", action="store_true", default=False, + "--verbose", "-v", action="store_true", help="Print verbose output (default: %(default)s)." ) - parser.add_argument( - "-p", "--parallel", action="store_true", default=False, - help="Read from HDF5 in parallel (default: %(default)s)." - ) - parser.add_argument( - "-nw", "--nworkers", type=int, metavar="nworkers", default=4, - help="Number of parallel workers (default: %(default)s)" - ) - parser.add_argument( - "-lon", type=float, metavar="lon", default=0.0, - help="Longitude of meridian slice (pic2) (default: %(default)s)" - ) + + # Return the parser. return parser def initFig(pic): - """Determine figure size (width, height) (inches) based on the pic type.""" + """Create the matplotlib figure for a plot. + + Determine figure size (width, height) (inches) based on the pic type. + + Parameters + ---------- + pic : str + String representing picture type. + + Returns + ------- + fig : mpl.Figure + Matplotlib Figure to use for plots. + + Raises + ------ + None + """ + # Figure dimensions are in inches. if pic == "pic1" or pic == "pic2" or pic == "pic7": figSz = (10, 12.5) elif pic == "pic3": @@ -198,12 +217,33 @@ def initFig(pic): def fOut(runid, pic, nStp): - """Compute the name of the output file.""" - return "qkpic_{}_{}_n{}.png".format(runid, pic, nStp) + """Compute the name of the output file. + + Compute the name of the output file. + + Parameters + ---------- + runid : str + ID string for run + pic : str + String representing picture type. + nStp : int + Simulation step number used in plot. + + Returns + ------- + : str + Name of file to receive the plot. + + Raises + ------ + None + """ + return f"qkpic_{runid}_{pic}_n{nStp}.png" def main(): - """Make a quick figure of a Gamera heliosphere run.""" + """Make a quick-look plot for a gamhelio run.""" # Set up the command-line parser. parser = create_command_line_parser() @@ -213,46 +253,52 @@ def main(): if args.debug: print(f"args = {args}") debug = args.debug - fdir = args.d + fdir = args.directory ftag = args.id + pic2lon = args.lon steps = args.nlist slices = args.nslice + nWorkers = args.nworkers + doParallel = args.parallel + pic = args.pic spacecraft = args.spacecraft verbose = args.verbose - pic = args.pic - doParallel = args.parallel - nWorkers = args.nworkers - pic2lon = args.lon - if slices: - print("Slice selected {}".format(slice(slices[0], slices[1], - slices[2]))) - tic = time.perf_counter() + if slices: + print(f"Slice selected {slice(slices[0], slices[1], slices[2])}") + # Fetch the plot domain based on the picture type. + tic = time.perf_counter() xyBds = hviz.GetSizeBds(pic) toc = time.perf_counter() print(xyBds) - print(f"Get bounds took {toc-tic} s") + print(f"Get bounds took {toc - tic} s") + # Do work? doFast = False - # Create figures in a memory buffer. - mpl.use("Agg") - # Open a pipe to the results data. tic = time.perf_counter() gsph = hsph.GamsphPipe(fdir, ftag, doFast=doFast, doParallel=doParallel, nWorkers=nWorkers) toc = time.perf_counter() - print(f"Open pipe took {toc-tic} s") + # Compute the range of time steps to use. if (slices and steps[0] == -1): steps = range(gsph.sFin)[slice(slices[0], slices[1], slices[2])] + print(f"steps = {steps}") + # Create figures in a memory buffer. + mpl.use("Agg") + + # Make a plot for each time step in the list of time steps. for nStp in steps: + if debug: + print(f"nStp = {nStp}") + tic = time.perf_counter() - print(f"Generating {pic} for time step {nStp}") + print(f"Generating {pic} for time step {nStp}.") fig = initFig(pic) # Extract the MJD for the step. @@ -261,10 +307,7 @@ def main(): print(f"mjd = {mjd}") # Lay out the subplots. - if ( - pic == "pic1" or pic == "pic2" or pic == "pic3" or pic == "pic6" or - pic == "pic7" - ): + if pic in ["pic1", "pic2", "pic3", "pic6", "pic7"]: gs = gridspec.GridSpec(4, 6, height_ratios=[20, 1, 20, 1]) # Axes for plots. AxL0 = fig.add_subplot(gs[0, 0:3]) @@ -285,20 +328,25 @@ def main(): Ax = fig.add_subplot(gs[0, 0]) AxC = fig.add_subplot(gs[0, 1]) AxC1 = fig.add_subplot(gs[1, 0]) + else: + raise TypeError(f"Invalid figure type: {pic}!") + # If the step is -1, use the last step. if nStp < 0: nStp = gsph.sFin print(f"Using Step {nStp}") # Now create the actual plots. if pic == "pic1": - # These are all equatorial plots in the XY plane of the HGS frame - # used by gamhelio. + # These are all equatorial plots in the XY plane of the modified + # HGS frame used by gamhelio. hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) elif pic == "pic2": + # Meridional plots in the XZ plane of the modified HGS frame used + # by gamhelio. hviz.PlotMerMagV(gsph, nStp, xyBds, AxL0, AxC1_0, indx=(None, pic2lon)) hviz.PlotMerDNorm(gsph, nStp, xyBds, AxR0, AxC2_0, @@ -329,7 +377,7 @@ def main(): hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=448) hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1, jidx=448) else: - print("Pic is empty. Choose pic1 or pic2 or pic3") + raise TypeError(f"Invalid figure type: {pic}!") # Add time in the upper left. if pic == "pic1" or pic == "pic2" or pic == "pic6" or pic == "pic7": @@ -339,42 +387,29 @@ def main(): elif pic == "pic4" or pic == "pic5": gsph.AddTime(nStp, Ax, xy=[0.015, 0.92], fs="small") else: - print("Pic is empty. Choose pic1 or pic2 or pic3") + raise TypeError(f"Invalid figure type: {pic}!") - # Overlay the spacecraft trajectory, if needed. + # Overlay the spacecraft positions. if spacecraft: - print(f"Overplotting spacecraft trajectories of {spacecraft}.") # Split the list into individual spacecraft names. spacecraft = spacecraft.split(',') - if debug: - print(f"spacecraft = {spacecraft}") - # Fetch the MJD start and end time of the model results. + # Fetch the MJD at start and end of the model results. fname = gsph.f0 - if debug: - print(f"fname = {fname}") MJD_start = kh5.tStep(fname, 0, aID="MJD") - if debug: - print(f"MJD_start = {MJD_start}") MJD_end = kh5.tStep(fname, gsph.sFin, aID="MJD") - if debug: - print(f"MJD_end = {MJD_end}") # Convert the start and stop MJD to a datetime object in UT. ut_start = ktools.MJD2UT(MJD_start) - if debug: - print(f"ut_start = {ut_start}") ut_end = ktools.MJD2UT(MJD_end) - if debug: - print(f"ut_end = {ut_end}") # Get the MJDc value for use in computing the gamhelio frame. MJDc = scutils.read_MJDc(fname) - if debug: - print(f"mjdc = {MJDc}") - # Fetch and plot the trajectory of each spacecraft from CDAWeb. + # Fetch the trajectory of each spacecraft from CDAWeb. Then + # interpolate the position at the time of the plot, and plot the + # spacecraft at the interpolated position. for (i_sc, sc_id) in enumerate(spacecraft): if verbose: print(f"Fetching trajectory for {sc_id}.") @@ -390,14 +425,12 @@ def main(): # Ingest the trajectory by converting it to the GH(MJDc) frame. if verbose: - print(f"Converting ephemeris for {sc_id} into gamhelio " - "format.") + print(f"Converting ephemeris for {sc_id} to the gamhelio " + f"frame at MJD {MJDc}.") t_strings = np.array([str(t) for t in sc_data["Epoch"]]) t = astropy.time.Time(t_strings, scale='utc').mjd x, y, z = cdaweb_utils.ingest_helio_spacecraft_trajectory( sc_id, sc_data, MJDc) - if debug: - print(f"t, x, y, z = {t}, {x}, {y}, {z}") # Interpolate the spacecraft position at the time for the plot. t_sc = mjd @@ -405,7 +438,8 @@ def main(): y_sc = np.interp(t_sc, t, y) z_sc = np.interp(t_sc, t, z) - # If needed, compute heliocentric spherical coordinates. + # If needed, compute heliocentric spherical coordinates + # for the interpolated spacecraft position. if pic == "pic3" or pic == "pic4": rxy = np.sqrt(x_sc**2 + y_sc**2) theta = np.arctan2(rxy, z_sc) @@ -415,8 +449,8 @@ def main(): lat_sc = lat lon_sc = lon - # Plot a position of the spacecraft. - # Left plot + # Plot the position of the spacecraft at the plot time. Each + # spacecraft is plotted as a colored dot with a black outline. color = SPACECRAFT_COLORS[i_sc % len(SPACECRAFT_COLORS)] x_nudge = 0.0 y_nudge = 8.0 @@ -453,6 +487,8 @@ def main(): ax.plot(x_sc, y_sc, 'o', c="black", fillstyle="none") ax.text(x_sc + x_nudge, y_sc + y_nudge, sc_id, c="black", horizontalalignment="center") + else: + raise TypeError(f"Invalid figure type: {pic}!") # Save the figure to a file. path = os.path.join(fdir, fOut(ftag, pic, nStp)) From c4677b8a8b4ec8e1ef1e7c8b3a2324285f9747ad Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 09:22:47 -0400 Subject: [PATCH 047/365] Added support for hgsplot to 1st pic1 plot. --- kaipy/gamhelio/helioViz.py | 125 ++++++++++++++++++++++++++++++---- scripts/quicklook/heliopic.py | 43 ++++++++---- 2 files changed, 144 insertions(+), 24 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 1a40d275..4328acfd 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -9,6 +9,11 @@ import kaipy.kaiViz as kv import kaipy.gamhelio.heliosphere as hsph from kaipy.kdefs import * import os +from astropy.coordinates import SkyCoord +import astropy.units as u +from sunpy.coordinates import frames +import kaipy.kaiTools as ktools +import spacepy.datamodel as dm VMax = 800. @@ -86,28 +91,124 @@ def GetSizeBds(pic): return xyBds #Plot speed in equatorial plane -def PlotEqMagV(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): - vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) - - if (AxCB is not None): - #Add the colorbar to AxCB - AxCB.clear() - kv.genCB(AxCB,vMagV,"Speed [km/s]",cM=MagVCM,Ntk=7) +def PlotEqMagV( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot solar wind speed in the solar equatorial plane. - #Now do main plotting - if (doClear): + Plot solar wind speed in the solar equatorial plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. + vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) + + # Create the color bar. + if AxCB is not None: + AxCB.clear() + kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Fetch the solar wind speed at the specified step. MagV = gsph.eqMagV(nStp) - Ax.pcolormesh(gsph.xxi,gsph.yyi,MagV,cmap=MagVCM,norm=vMagV) - kv.SetAx(xyBds,Ax) + # Plot the solar wind speed in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, MagV, cmap=MagVCM, norm=vMagV) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, MagV, cmap=MagVCM, norm=vMagV) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # If requested, label the axes. + if doDeco: Ax.set_xlabel('X [R_S]') Ax.set_ylabel('Y [R_S]') + + # Return the solar wind speed data. return MagV + #Plot speed in j plane def PlotjMagV(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index a703f082..132a082b 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -134,6 +134,11 @@ def create_command_line_parser(): default=os.getcwd(), help="Directory containing data to read (default: %(default)s)" ) + parser.add_argument( + "--hgsplot", action="store_true", + help="Plot in the Heliographic Stonyhurst frame corresponding to the " + "date of the plot (default: %(default)s)." + ) parser.add_argument( "-id", type=str, metavar="runid", default=DEFAULT_RUNID, help="Run ID of data (default: %(default)s)" @@ -216,7 +221,7 @@ def initFig(pic): return fig -def fOut(runid, pic, nStp): +def fOut(runid, pic, nStp, hgsplot): """Compute the name of the output file. Compute the name of the output file. @@ -229,17 +234,23 @@ def fOut(runid, pic, nStp): String representing picture type. nStp : int Simulation step number used in plot. + hgsplot : bool + True if plot is in HGS frame at the date of the plot Returns ------- - : str + s : str Name of file to receive the plot. Raises ------ None """ - return f"qkpic_{runid}_{pic}_n{nStp}.png" + if hgsplot: + s = f"qkpic_{runid}_{pic}_n{nStp}_HGS.png" + else: + s = f"qkpic_{runid}_{pic}_n{nStp}.png" + return s def main(): @@ -254,6 +265,7 @@ def main(): print(f"args = {args}") debug = args.debug fdir = args.directory + hgsplot = args.hgsplot ftag = args.id pic2lon = args.lon steps = args.nlist @@ -289,6 +301,10 @@ def main(): steps = range(gsph.sFin)[slice(slices[0], slices[1], slices[2])] print(f"steps = {steps}") + # Get the MJDc value for use in computing the gamhelio frame. + fname = gsph.f0 + MJDc = scutils.read_MJDc(fname) + # Create figures in a memory buffer. mpl.use("Agg") @@ -340,10 +356,17 @@ def main(): if pic == "pic1": # These are all equatorial plots in the XY plane of the modified # HGS frame used by gamhelio. - hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) - hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) - hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) - hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) + if hgsplot: + hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) + hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) + hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) + else: + hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) + hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) + hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) + hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) elif pic == "pic2": # Meridional plots in the XZ plane of the modified HGS frame used # by gamhelio. @@ -396,7 +419,6 @@ def main(): spacecraft = spacecraft.split(',') # Fetch the MJD at start and end of the model results. - fname = gsph.f0 MJD_start = kh5.tStep(fname, 0, aID="MJD") MJD_end = kh5.tStep(fname, gsph.sFin, aID="MJD") @@ -404,9 +426,6 @@ def main(): ut_start = ktools.MJD2UT(MJD_start) ut_end = ktools.MJD2UT(MJD_end) - # Get the MJDc value for use in computing the gamhelio frame. - MJDc = scutils.read_MJDc(fname) - # Fetch the trajectory of each spacecraft from CDAWeb. Then # interpolate the position at the time of the plot, and plot the # spacecraft at the interpolated position. @@ -491,7 +510,7 @@ def main(): raise TypeError(f"Invalid figure type: {pic}!") # Save the figure to a file. - path = os.path.join(fdir, fOut(ftag, pic, nStp)) + path = os.path.join(fdir, fOut(ftag, pic, nStp, hgsplot)) kv.savePic(path, bLenX=40) fig.clear() toc = time.perf_counter() From cd3aca7958bdcdd522a6a4de1d46111fb8767210 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 09:59:25 -0400 Subject: [PATCH 048/365] Add HGS conversion of spacecraft position. --- kaipy/gamhelio/helioViz.py | 2 +- scripts/quicklook/heliopic.py | 67 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 4328acfd..86c9ccdb 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -90,7 +90,7 @@ def GetSizeBds(pic): raise RuntimeError("No compatible pic type specified.") return xyBds -#Plot speed in equatorial plane + def PlotEqMagV( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, MJDc=None, MJD_plot=None, hgsplot=False diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 132a082b..718a08c1 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -70,10 +70,14 @@ import time # Import supplemental modules. import astropy +from astropy.coordinates import SkyCoord +import astropy.units as u import matplotlib as mpl from matplotlib import gridspec import matplotlib.pyplot as plt import numpy as np +import spacepy.datamodel as dm +from sunpy.coordinates import frames # Import project-specific modules. from kaipy import cdaweb_utils @@ -253,6 +257,65 @@ def fOut(runid, pic, nStp, hgsplot): return s +def GHtoHGS(mjd_gh, x_gh, y_gh, z_gh, mjd_hgs): + """Convert Cartesin GH coordinates to HGS. + + Convert Cartesian coordinates in the gamhelio frame at time mjdc to + the Heliographic Sonyhurst frame at time mjd. + + NOTE: The gamhelio frame at time t is related to the Heliographic + Stonyhurst frame at time t by the reflection of the x- and y-axes: + + x_gh(t) = -x_hgs(t) + y_gh(t) = -y_hgs(t) + z_gh(t) = z_hgs(t) + + Since HGS is a time-dependent frame, a time must be provided for each set + of coordinates. + + Parameters + ---------- + mjd_gh : float + MJD of source gamhelio frame + x_gh, y_gh, z_gh : np.array of float (any shape) or scalar float + Cartesian coordinates in GH(mjdc) frame. All three arrays x, y, z must + have identical shapes. + mjd_hgs : float + MJD of target HGS frame + + Returns + ------- + x_hgs, y_hgs, z_hgs : np.array of float (same shape as x_gh, y_gh, z_gh) + Cartesian coordinates converted to HGS(mjd) frame. + + Raises + ------ + None + """ + # Load the source coordinates (originially in the GH(mjd_gh) frame) into + # the equivalent HGS(mjd_gh) frame. + c_gh = SkyCoord( + -x_gh*u.Rsun, -y_gh*u.Rsun, z_gh*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(mjd_gh), + representation_type="cartesian" + ) + + # Create the target Heliographic Stonyhurst frame. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(mjd_hgs) + ) + + # Convert the coordinates from GH(mjd_gh) to HGS(mjd_hgs). + c_hgs = c_gh.transform_to(hgs_frame) + + # Extract and return the converted coordinates. + x_hgs = dm.dmarray(c_hgs.cartesian.x) + y_hgs = dm.dmarray(c_hgs.cartesian.y) + z_hgs = dm.dmarray(c_hgs.cartesian.z) + return x_hgs, y_hgs, z_hgs + + def main(): """Make a quick-look plot for a gamhelio run.""" @@ -457,6 +520,10 @@ def main(): y_sc = np.interp(t_sc, t, y) z_sc = np.interp(t_sc, t, z) + # If needed, convert the position to HGS(mjd). + if hgsplot: + x_sc, y_sc, z_sc = GHtoHGS(MJDc, x_sc, y_sc, z_sc, mjd) + # If needed, compute heliocentric spherical coordinates # for the interpolated spacecraft position. if pic == "pic3" or pic == "pic4": From 47a2939d58ca838dc37336aac8872cbe31183ae4 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 10:20:40 -0400 Subject: [PATCH 049/365] Added HGS frame option for number density plot in pic1 --- kaipy/gamhelio/helioViz.py | 140 +++++++++++++++++++++++++++++----- scripts/quicklook/heliopic.py | 3 +- 2 files changed, 123 insertions(+), 20 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 86c9ccdb..b4755742 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -142,6 +142,9 @@ def PlotEqMagV( Returns ------- + MagV : np.array of float + Data for solar wind speed in equatorial plane, same shape as the + equatorial grid in the gamhelio results. Raises ------ @@ -202,8 +205,8 @@ def PlotEqMagV( # If requested, label the axes. if doDeco: - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + Ax.set_xlabel("$X [R_S]$") + Ax.set_ylabel("$Y [R_S]$") # Return the solar wind speed data. return MagV @@ -381,29 +384,128 @@ def PlotMerTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[None Ax.set_ylabel('Z [R_S]') return Tempr, Templ -#Plot normalized density in equatorial plane n(r/r0)^2 -def PlotEqD(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): - vD = kv.genNorm(DMin, DMax, doLog=False, midP=None) - - if (AxCB is not None): - #Add the colorbar to AxCB - AxCB.clear() - kv.genCB(AxCB,vD,r"Density n(r/r$_0)^2$ [cm$^{-3}$]",cM=DCM,Ntk=7) +# +def PlotEqD( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized density in the solar equatorial plane. - #Now do main plotting - if (doClear): + Plot normalized density in the solar equatorial plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The density is normalized with the factor (r/r0)**2. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + MagV : np.array of float + Data for solar wind speed in equatorial plane, same shape as the + equatorial grid in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. + vD = kv.genNorm(DMin, DMax, doLog=False, midP=None) + + # Create the color bar. + if AxCB is not None: + AxCB.clear() + kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [cm$^{-3}$]", cM=DCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Fetch the normalized number density at the specified step. NormD = gsph.eqNormD(nStp) - Ax.pcolormesh(gsph.xxi,gsph.yyi,NormD,cmap=DCM,norm=vD) - kv.SetAx(xyBds,Ax) + # Plot the normalized number density in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): - Ax.set_xlabel('R [R_S]') - Ax.set_ylabel('Y [R_S]') - Ax.yaxis.tick_right() - Ax.yaxis.set_label_position('right') + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, NormD, cmap=DCM, norm=vD) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, NormD, cmap=DCM, norm=vD) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # If requested, label the axes. + Ax.set_xlabel("$X [R_S]$") + Ax.set_ylabel("$Y [R_S]$") + Ax.yaxis.tick_right() + Ax.yaxis.set_label_position("right") + + # Return the normalized number density data. return NormD #Plot normalized density in equatorial plane n(r/r0)^2 diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 718a08c1..6542bcb2 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -422,7 +422,8 @@ def main(): if hgsplot: hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0, MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) + hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) else: From a5a1ae00190bc54164d4596b892c49dba4097ad9 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 10:31:14 -0400 Subject: [PATCH 050/365] Added hgsplot option for temperature in pic1. --- kaipy/gamhelio/helioViz.py | 134 ++++++++++++++++++++++++++++++---- scripts/quicklook/heliopic.py | 3 +- 2 files changed, 121 insertions(+), 16 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index b4755742..07b63de4 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -439,7 +439,7 @@ def PlotEqD( Returns ------- MagV : np.array of float - Data for solar wind speed in equatorial plane, same shape as the + Data for number density in equatorial plane, same shape as the equatorial grid in the gamhelio results. Raises @@ -500,10 +500,11 @@ def PlotEqD( kv.SetAx(xyBds, Ax) # If requested, label the axes. - Ax.set_xlabel("$X [R_S]$") - Ax.set_ylabel("$Y [R_S]$") - Ax.yaxis.tick_right() - Ax.yaxis.set_label_position("right") + if doDeco: + Ax.set_xlabel("$X [R_S]$") + Ax.set_ylabel("$Y [R_S]$") + Ax.yaxis.tick_right() + Ax.yaxis.set_label_position("right") # Return the normalized number density data. return NormD @@ -533,26 +534,129 @@ def PlotjD(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): Ax.yaxis.set_label_position('right') return NormD -#Plot normalized Temperature in equatorial plane T(r/r0) -def PlotEqTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): +def PlotEqTemp( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized temperature in the solar equatorial plane. + + Plot normalized temperature in the solar equatorial plane. By default, the + plot is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The temperature is normalized with the factor (r/r0). + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Temp : np.array of float + Data for temperature in equatorial plane, same shape as the + equatorial grid in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vT = kv.genNorm(TMin, TMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB is not None: AxCB.clear() - kv.genCB(AxCB,vT,r"Temperature T(r/r$_0$) [MK]",cM=TCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Fetch the normalized temperature at the specified step. Temp = gsph.eqTemp(nStp) - Ax.pcolormesh(gsph.xxi,gsph.yyi,Temp,cmap=TCM,norm=vT) - + + # Plot the normalized temperature in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, Temp, cmap=TCM, norm=vT) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, Temp, cmap=TCM, norm=vT) + + # Set the plot boundaries. kv.SetAx(xyBds,Ax) - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # If requested, label the axes. + if doDeco: + Ax.set_xlabel("$X [R_S]$") + Ax.set_ylabel("$Y [R_S]$") + + # Return the normalized temperature data. return Temp + #Plot normalized Temperature in equatorial plane T(r/r0) def PlotjTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): vT = kv.genNorm(TMin, TMax, doLog=False, midP=None) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 6542bcb2..637bc8ae 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -424,7 +424,8 @@ def main(): MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0, MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) + hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) else: hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) From f01914457e2bc26ec6026da3d62a0442ff45ba63 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 10:44:09 -0400 Subject: [PATCH 051/365] Added hgsplot option to pic1 Br plot. --- kaipy/gamhelio/helioViz.py | 145 +++++++++++++++++++++++++++++----- scripts/quicklook/heliopic.py | 3 +- 2 files changed, 126 insertions(+), 22 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 07b63de4..3f31733c 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -154,7 +154,7 @@ def PlotEqMagV( vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) # Create the color bar. - if AxCB is not None: + if AxCB: AxCB.clear() kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) @@ -205,8 +205,8 @@ def PlotEqMagV( # If requested, label the axes. if doDeco: - Ax.set_xlabel("$X [R_S]$") - Ax.set_ylabel("$Y [R_S]$") + Ax.set_xlabel(r"$X [R_S]$") + Ax.set_ylabel(r"$Y [R_S]$") # Return the solar wind speed data. return MagV @@ -450,7 +450,7 @@ def PlotEqD( vD = kv.genNorm(DMin, DMax, doLog=False, midP=None) # Create the color bar. - if AxCB is not None: + if AxCB: AxCB.clear() kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [cm$^{-3}$]", cM=DCM, Ntk=7) @@ -501,8 +501,8 @@ def PlotEqD( # If requested, label the axes. if doDeco: - Ax.set_xlabel("$X [R_S]$") - Ax.set_ylabel("$Y [R_S]$") + Ax.set_xlabel(r"$X [R_S]$") + Ax.set_ylabel(r"$Y [R_S]$") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") @@ -599,7 +599,7 @@ def PlotEqTemp( vT = kv.genNorm(TMin, TMax, doLog=False, midP=None) # Create the color bar. - if AxCB is not None: + if AxCB: AxCB.clear() kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) @@ -646,12 +646,12 @@ def PlotEqTemp( Ax.pcolormesh(gsph.xxi, gsph.yyi, Temp, cmap=TCM, norm=vT) # Set the plot boundaries. - kv.SetAx(xyBds,Ax) + kv.SetAx(xyBds, Ax) # If requested, label the axes. if doDeco: - Ax.set_xlabel("$X [R_S]$") - Ax.set_ylabel("$Y [R_S]$") + Ax.set_xlabel(r"$X [R_S]$") + Ax.set_ylabel(r"$Y [R_S]$") # Return the normalized temperature data. return Temp @@ -677,28 +677,131 @@ def PlotjTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): Ax.set_ylabel('Y [R_S]') return Temp -#Plor Br in equatorial plane -def PlotEqBr(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): +def PlotEqBr( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized radial magnetic field in the solar equatorial plane. + + Plot normalized radial magnetic field in the solar equatorial plane. By + default, the plot is produced in the GH(MJDc) frame (the gamhelio frame + used for the simulation results). If MJD_plot is specified, MJDc must also + be specified. In that case, the coordinates are mapped from the GH(MJDc) + frame to the HGS(MJD_plot) frame. + + The radial magnetic field is normalized with the factor (r/r0)**2. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Br : np.array of float + Data for radial magnetic field in equatorial plane, same shape as the + equatorial grid in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,r'Radial MF B$_r$(r/r$_0)^2$ [nT]',cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, r"Radial MF $B_r (r/r_0)^2$ [nT]", cM=BCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Fetch the normalized radial magnetic field at the specified step. Br = gsph.eqNormBr(nStp) - Ax.pcolormesh(gsph.xxi,gsph.yyi,Br,cmap=BCM,norm=vB) - kv.SetAx(xyBds,Ax) + # Plot the normalized radial magnetic field in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, Br, cmap=BCM, norm=vB) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, Br, cmap=BCM, norm=vB) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # If requested, label the axes. + if doDeco: + Ax.set_xlabel(r"$X [R_S]$") + Ax.set_ylabel(r"$Y [R_S]$") Ax.yaxis.tick_right() - Ax.yaxis.set_label_position('right') + Ax.yaxis.set_label_position("right") + + # Return the normalized radial magnetic field data. return Br + #Plor Br in equatorial plane def PlotjBr(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 637bc8ae..23c7061a 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -426,7 +426,8 @@ def main(): MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1, MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) + hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) else: hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) From 21aebdd2a228a836ccd21c743b7486be461e733b Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 10:59:18 -0400 Subject: [PATCH 052/365] Added frame and time in plot title. --- scripts/quicklook/heliopic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 23c7061a..488dbd5a 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -428,11 +428,13 @@ def main(): MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") else: hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) + fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic2": # Meridional plots in the XZ plane of the modified HGS frame used # by gamhelio. From d297646825d60d1810fd1d8452f675adbdea1b64 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 11:25:54 -0400 Subject: [PATCH 053/365] Added hgsplot option to PlotMerMagV. --- kaipy/gamhelio/helioViz.py | 168 +++++++++++++++++++++++++++------- scripts/quicklook/heliopic.py | 28 +++--- 2 files changed, 151 insertions(+), 45 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 3f31733c..6c1a104d 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -235,41 +235,145 @@ def PlotjMagV(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): Ax.set_ylabel('Y [R_S]') return MagV -#Plot speed in meridional plane Y=0 -def PlotMerMagV(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=(None,None)): - vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) - if (AxCB is not None): - #Add the colorbar to AxCB - AxCB.clear() - kv.genCB(AxCB,vMagV,"Speed [km/s]",cM=MagVCM,Ntk=7) - - if (doClear): - Ax.clear() - - phi = 0.0 - for idx in indx: - if type(idx) is not None: - if type(idx) is int: - phi = idx/gsph.Nk*2*np.pi - elif type(idx) is float: - phi = idx - else: - phi = "" - - #r is for +X plane and l is for -X plane - Vr, Vl = gsph.MerMagV(nStp,indx=indx) - #cell corners - xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - Ax.pcolormesh(np.sqrt(xr**2 + yr**2),zr,Vr,cmap=MagVCM,norm=vMagV) - Ax.pcolormesh(-np.sqrt(xl**2 + yl**2),zl,Vl,cmap=MagVCM,norm=vMagV) +def PlotMerMagV( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + indx=(None, None), + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot solar wind speed in a meridional plane. - kv.SetAx(xyBds,Ax) + Plot solar wind speed in a meridional plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. - if (doDeco): - Ax.set_xlabel(f"R_XY [R_S] Phi={phi:{2}.{2}} [rad]") - Ax.set_ylabel('Z [R_S]') - return Vr, Vl + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + indx : tuple of 2 int or float + Index or angle of meridional slice to plot + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Vr, Vl : np.array of float + Data for solar wind speed in meridional plane plane, for left and + right parts of plot. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. + vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) + + # Create the color bar. + if AxCB: + AxCB.clear() + kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: + Ax.clear() + + # Determine the angle of the meridional slice. + phi = 0.0 + for idx in indx: + if type(idx) is int: + phi = idx/gsph.Nk*2*np.pi + elif type(idx) is float: + phi = idx + else: + phi = "" + + # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) + # frame. + Vr, Vl = gsph.MerMagV(nStp,indx=indx) + + # Fetch the coordinates of the grid cell corners in the specified + # meridional plane, in the GH(mjd_gh) frame. + xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) + + # Plot the solar wind speed in the meridional plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + c = SkyCoord( + -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + xr = dm.dmarray(c.cartesian.x) + yr = dm.dmarray(c.cartesian.y) + zr = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Vr, cmap=MagVCM, norm=vMagV) + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Vl, cmap=MagVCM, norm=vMagV) + + else: + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Vr, cmap=MagVCM, norm=vMagV) + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Vl, cmap=MagVCM, norm=vMagV) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # If requested, label the axes. + if doDeco: + Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_ylabel("Z [$R_S$]") + + # Return the solar wind speed data. + return Vr, Vl #Plot normalized density n(r/r0)^2 in meridional plane Y=0 def PlotMerDNorm(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[None,None]): diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 488dbd5a..0b90a25e 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -419,33 +419,35 @@ def main(): if pic == "pic1": # These are all equatorial plots in the XY plane of the modified # HGS frame used by gamhelio. + hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) if hgsplot: - hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") else: - hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0) - hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0) - hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1) - hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1) fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic2": # Meridional plots in the XZ plane of the modified HGS frame used # by gamhelio. hviz.PlotMerMagV(gsph, nStp, xyBds, AxL0, AxC1_0, - indx=(None, pic2lon)) + indx=(None, pic2lon), + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot + ) hviz.PlotMerDNorm(gsph, nStp, xyBds, AxR0, AxC2_0, indx=(None, pic2lon)) hviz.PlotMerTemp(gsph, nStp, xyBds, AxL1, AxC1_1, indx=(None, pic2lon)) hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1, indx=(None, pic2lon)) + if hgsplot: + fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + else: + fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic3": hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, idx=0) hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, idx=0) From a5c7ba39a3f786a61ac9461aa64d581276f32f70 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 12:48:18 -0400 Subject: [PATCH 054/365] Fixed meridional plots for speed and density. --- kaipy/gamhelio/helioViz.py | 188 +++++++++++++++++++++++++++++----- scripts/quicklook/heliopic.py | 6 +- 2 files changed, 163 insertions(+), 31 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 6c1a104d..6670f50e 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -310,7 +310,7 @@ def PlotMerMagV( if doClear: Ax.clear() - # Determine the angle of the meridional slice. + # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: if type(idx) is int: @@ -322,7 +322,7 @@ def PlotMerMagV( # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) # frame. - Vr, Vl = gsph.MerMagV(nStp,indx=indx) + Vr, Vl = gsph.MerMagV(nStp, indx=indx) # Fetch the coordinates of the grid cell corners in the specified # meridional plane, in the GH(mjd_gh) frame. @@ -333,15 +333,21 @@ def PlotMerMagV( # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Load the equatorial grid cell vertex coordinates (originially in the + # Load the meridional grid cell vertex coordinates (originially in the # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values # to 0 since we are using the solar equatorial plane. - c = SkyCoord( + cr = SkyCoord( -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, frame=frames.HeliographicStonyhurst, obstime=ktools.MJD2UT(MJDc), representation_type="cartesian" ) + cl = SkyCoord( + -xl*u.Rsun, -yl*u.Rsun, zl*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) # Create a HGS frame for the plot time. hgs_frame = frames.HeliographicStonyhurst( @@ -349,16 +355,20 @@ def PlotMerMagV( ) # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). - c = c.transform_to(hgs_frame) + cr = cr.transform_to(hgs_frame) + cl = cl.transform_to(hgs_frame) # Extract the converted coordinates. - xr = dm.dmarray(c.cartesian.x) - yr = dm.dmarray(c.cartesian.y) - zr = dm.dmarray(c.cartesian.z) + xr = dm.dmarray(cr.cartesian.x) + yr = dm.dmarray(cr.cartesian.y) + zr = dm.dmarray(cr.cartesian.z) + xl = dm.dmarray(cl.cartesian.x) + yl = dm.dmarray(cl.cartesian.y) + zl = dm.dmarray(cl.cartesian.z) # Plot the data in the HGS(MJD_plot) frame. - Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Vr, cmap=MagVCM, norm=vMagV) - Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Vl, cmap=MagVCM, norm=vMagV) + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Vl, cmap=MagVCM, norm=vMagV) + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Vr, cmap=MagVCM, norm=vMagV) else: Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Vr, cmap=MagVCM, norm=vMagV) @@ -376,41 +386,163 @@ def PlotMerMagV( return Vr, Vl #Plot normalized density n(r/r0)^2 in meridional plane Y=0 -def PlotMerDNorm(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[None,None]): +def PlotMerDNorm( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + indx=(None, None), + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized solar wind number density in a meridional plane. + + Plot normalized solar wind number density in a meridional plane. By + default, the plot is produced in the GH(MJDc) frame (the gamhelio frame + used for the simulation results). If MJD_plot is specified, MJDc must also + be specified. In that case, the coordinates are mapped from the GH(MJDc) + frame to the HGS(MJD_plot) frame. + + The density is normalized with the factor (r/r0)**2. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + indx : tuple of 2 int or float + Index or angle of meridional slice to plot + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Dr, Dl : np.array of float + Data for solar wind normalized number density in meridional plane, for + left and right parts of plot. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vD = kv.genNorm(DMin, DMax, doLog=False, midP=None) - if (AxCB is not None): - #Add the colorbar to AxCB + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vD,r"Density n$(r/r_0)^2$ [cm$^{-3}$]",cM=DCM,Ntk=7) + kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [$cm^{-3}$]", cM=DCM, Ntk=7) - if (doClear): + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: - if type(idx) is not None: - if type(idx) is int: - phi = idx/gsph.Nk*2*np.pi - elif type(idx) is float: - phi = idx - else: + if type(idx) is int: + phi = idx/gsph.Nk*2*np.pi + elif type(idx) is float: + phi = idx + else: phi = "" - Dr, Dl = gsph.MerDNrm(nStp,indx=indx) + # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) + # frame. + Dr, Dl = gsph.MerDNrm(nStp, indx=indx) + + # Fetch the coordinates of the grid cell corners in the specified + # meridional plane, in the GH(mjd_gh) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - Ax.pcolormesh(np.sqrt(xr**2 + yr**2),zr,Dr,cmap=DCM,norm=vD, shading='auto') - Ax.pcolormesh(-np.sqrt(xl**2 + yl**2),zl,Dl,cmap=DCM,norm=vD, shading='auto') - kv.SetAx(xyBds,Ax) + # Plot the solar wind normalized number density in the meridional plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): - Ax.set_xlabel(f"R_XY [R_S] Phi={phi:{2}.{2}} [rad]") - Ax.set_ylabel('Z [R_S]') + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + cr = SkyCoord( + -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + cl = SkyCoord( + -xl*u.Rsun, -yl*u.Rsun, zl*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + cr = cr.transform_to(hgs_frame) + cl = cl.transform_to(hgs_frame) + + # Extract the converted coordinates. + xr = dm.dmarray(cr.cartesian.x) + yr = dm.dmarray(cr.cartesian.y) + zr = dm.dmarray(cr.cartesian.z) + xl = dm.dmarray(cl.cartesian.x) + yl = dm.dmarray(cl.cartesian.y) + zl = dm.dmarray(cl.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Dl, cmap=DCM, norm=vD, + shading='auto') + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Dr, cmap=DCM, norm=vD, + shading='auto') + + else: + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Dr, cmap=DCM, norm=vD, + shading='auto') + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Dl, cmap=DCM, norm=vD, + shading='auto') + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + if doDeco: + Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_ylabel("Z [$R_S$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the solar wind normalized number density. return Dr, Dl + #Plot normalized Br Br(r/r0)^2 in meridional plane Y=0 def PlotMerBrNorm(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[None,None]): vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 0b90a25e..2585eb6c 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -436,10 +436,10 @@ def main(): # by gamhelio. hviz.PlotMerMagV(gsph, nStp, xyBds, AxL0, AxC1_0, indx=(None, pic2lon), - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot - ) + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotMerDNorm(gsph, nStp, xyBds, AxR0, AxC2_0, - indx=(None, pic2lon)) + indx=(None, pic2lon), + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotMerTemp(gsph, nStp, xyBds, AxL1, AxC1_1, indx=(None, pic2lon)) hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1, From 8579efd231a757a72fd92c5fdec329ca505e6580 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 12:59:56 -0400 Subject: [PATCH 055/365] Added hgsplot option to PlotMerTemp(). --- kaipy/gamhelio/helioViz.py | 163 +++++++++++++++++++++++++++++----- scripts/quicklook/heliopic.py | 3 +- 2 files changed, 143 insertions(+), 23 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 6670f50e..70c6244e 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -291,8 +291,8 @@ def PlotMerMagV( Returns ------- Vr, Vl : np.array of float - Data for solar wind speed in meridional plane plane, for left and - right parts of plot. + Data for solar wind speed in meridional plane plane, for right and + left parts of plot. Raises ------ @@ -385,7 +385,7 @@ def PlotMerMagV( # Return the solar wind speed data. return Vr, Vl -#Plot normalized density n(r/r0)^2 in meridional plane Y=0 + def PlotMerDNorm( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, indx=(None, None), @@ -444,7 +444,7 @@ def PlotMerDNorm( ------- Dr, Dl : np.array of float Data for solar wind normalized number density in meridional plane, for - left and right parts of plot. + right and left parts of plot. Raises ------ @@ -588,36 +588,155 @@ def PlotMerBrNorm(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[No Ax.yaxis.set_label_position('right') return Br_r, Br_l -#Plot normalized temperature T(r/r0) in meridional plane -def PlotMerTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[None,None]): + +def PlotMerTemp( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + indx=(None, None), + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized solar wind temperature in a meridional plane. + + Plot normalized solar wind temperature in a meridional plane. By + default, the plot is produced in the GH(MJDc) frame (the gamhelio frame + used for the simulation results). If MJD_plot is specified, MJDc must also + be specified. In that case, the coordinates are mapped from the GH(MJDc) + frame to the HGS(MJD_plot) frame. + + The temperature is normalized with the factor (r/r0). + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + indx : tuple of 2 int or float + Index or angle of meridional slice to plot + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Tempr, Templ : np.array of float + Data for solar wind normalized temperature in meridional plane, for + right and left parts of plot. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vT = kv.genNorm(TMin, TMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vT, r'Temperature T(r/r$_0$) [MK]',cM=TCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vT, r'Temperature $T(r/r_0)$ [MK]', cM=TCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: - if type(idx) is not None: - if type(idx) is int: - phi = idx/gsph.Nk*2*np.pi - elif type(idx) is float: - phi = idx - else: + if type(idx) is int: + phi = idx/gsph.Nk*2*np.pi + elif type(idx) is float: + phi = idx + else: phi = "" - Tempr, Templ = gsph.MerTemp(nStp,indx=indx) + # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) + # frame. + Tempr, Templ = gsph.MerTemp(nStp, indx=indx) + + # Fetch the coordinates of the grid cell corners in the specified + # meridional plane, in the GH(mjd_gh) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - Ax.pcolormesh(np.sqrt(xr**2 + yr**2),zr,Tempr,cmap=TCM,norm=vT) - Ax.pcolormesh(-np.sqrt(xl**2 + yl**2),zl,Templ,cmap=TCM,norm=vT) - kv.SetAx(xyBds,Ax) + # Plot the solar wind normalized temperature in the meridional plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): - Ax.set_xlabel(f"R_XY [R_S] Phi={phi:{2}.{2}} [rad]") - Ax.set_ylabel('Z [R_S]') + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + cr = SkyCoord( + -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + cl = SkyCoord( + -xl*u.Rsun, -yl*u.Rsun, zl*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + cr = cr.transform_to(hgs_frame) + cl = cl.transform_to(hgs_frame) + + # Extract the converted coordinates. + xr = dm.dmarray(cr.cartesian.x) + yr = dm.dmarray(cr.cartesian.y) + zr = dm.dmarray(cr.cartesian.z) + xl = dm.dmarray(cl.cartesian.x) + yl = dm.dmarray(cl.cartesian.y) + zl = dm.dmarray(cl.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Templ, cmap=TCM, norm=vT) + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Tempr, cmap=TCM, norm=vT) + + else: + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Tempr, cmap=TCM, norm=vT) + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Templ, cmap=TCM, norm=vT) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + if doDeco: + Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_ylabel("Z [$R_S$]") + + # Return the solar wind normalized temperature. return Tempr, Templ # diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 2585eb6c..1f51a6cc 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -441,7 +441,8 @@ def main(): indx=(None, pic2lon), MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotMerTemp(gsph, nStp, xyBds, AxL1, AxC1_1, - indx=(None, pic2lon)) + indx=(None, pic2lon), + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1, indx=(None, pic2lon)) if hgsplot: From f04c3d9aaf4bfd9e7160ddc1cce1b72910614aeb Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 20 Sep 2023 14:27:59 -0400 Subject: [PATCH 056/365] Finished adding hgsplot option for pic2. --- kaipy/gamhelio/helioViz.py | 290 ++++++++++++++++++++++++++-------- scripts/quicklook/heliopic.py | 3 +- 2 files changed, 227 insertions(+), 66 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 70c6244e..6a1b8aaa 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -162,10 +162,10 @@ def PlotEqMagV( if doClear: Ax.clear() - # Fetch the solar wind speed at the specified step. + # Fetch the data at the specified step. MagV = gsph.eqMagV(nStp) - # Plot the solar wind speed in the solar equatorial plane. + # Plot the data in the solar equatorial plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -203,12 +203,12 @@ def PlotEqMagV( # Set the plot boundaries. kv.SetAx(xyBds, Ax) - # If requested, label the axes. + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") - # Return the solar wind speed data. + # Return the data. return MagV @@ -313,13 +313,15 @@ def PlotMerMagV( # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: - if type(idx) is int: - phi = idx/gsph.Nk*2*np.pi - elif type(idx) is float: - phi = idx - else: + if type(idx) is not None: + if type(idx) is int: + phi = idx/gsph.Nk*2*np.pi + elif type(idx) is float: + phi = idx + else: phi = "" + # Fetch the data at the specified step. # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) # frame. Vr, Vl = gsph.MerMagV(nStp, indx=indx) @@ -328,7 +330,7 @@ def PlotMerMagV( # meridional plane, in the GH(mjd_gh) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - # Plot the solar wind speed in the meridional plane. + # Plot the data in the meridional plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -377,12 +379,12 @@ def PlotMerMagV( # Set the plot boundaries. kv.SetAx(xyBds, Ax) - # If requested, label the axes. + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") Ax.set_ylabel("Z [$R_S$]") - # Return the solar wind speed data. + # Return the data. return Vr, Vl @@ -465,13 +467,15 @@ def PlotMerDNorm( # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: - if type(idx) is int: - phi = idx/gsph.Nk*2*np.pi - elif type(idx) is float: - phi = idx + if type(idx) is not None: + if type(idx) is int: + phi = idx/gsph.Nk*2*np.pi + elif type(idx) is float: + phi = idx else: phi = "" + # Fetch the data at the specified step. # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) # frame. Dr, Dl = gsph.MerDNrm(nStp, indx=indx) @@ -480,7 +484,7 @@ def PlotMerDNorm( # meridional plane, in the GH(mjd_gh) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - # Plot the solar wind normalized number density in the meridional plane. + # Plot the data in the meridional plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -533,59 +537,212 @@ def PlotMerDNorm( # Set the plot boundaries. kv.SetAx(xyBds, Ax) + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") Ax.set_ylabel("Z [$R_S$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') - # Return the solar wind normalized number density. + # Return the data. return Dr, Dl -#Plot normalized Br Br(r/r0)^2 in meridional plane Y=0 -def PlotMerBrNorm(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,indx=[None,None]): +def PlotMerBrNorm( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + indx=(None, None), + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized solar wind temperature in a meridional plane. + + Plot normalized solar wind temperature in a meridional plane. By + default, the plot is produced in the GH(MJDc) frame (the gamhelio frame + used for the simulation results). If MJD_plot is specified, MJDc must also + be specified. In that case, the coordinates are mapped from the GH(MJDc) + frame to the HGS(MJD_plot) frame. + + The temperature is normalized with the factor (r/r0)**2. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + indx : tuple of 2 int or float + Index or angle of meridional slice to plot + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Br_r, Br_l : np.array of float + Data for solar wind normalized radial magnetic field in meridional + plane, for right and left parts of plot. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) - if (AxCB is not None): - #Add the colorbar to AxCB + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,r'Radial MF B$_r$(r/r$_0)^2$ [nT]',cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, r'Radial MF $B_r (r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() - + + # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: if type(idx) is not None: - if type(idx) is int: + if type(idx) is int: phi = idx/gsph.Nk*2*np.pi elif type(idx) is float: phi = idx - else: + else: phi = "" - Br_r, Br_l = gsph.MerBrNrm(nStp,indx=indx) - xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - Ax.pcolormesh(np.sqrt(xr**2 + yr**2),zr,Br_r,cmap=BCM,norm=vB,shading='auto') - Ax.pcolormesh(-np.sqrt(xl**2 + yl**2),zl,Br_l,cmap=BCM,norm=vB,shading='auto') - #plot heliospheric current sheet - #cell-cent coords first - xr_c = 0.25*( xr[:-1,:-1]+xr[:-1,1:]+xr[1:,:-1]+xr[1:,1:] ) - yr_c = 0.25*( yr[:-1,:-1]+yr[:-1,1:]+yr[1:,:-1]+yr[1:,1:] ) - zr_c = 0.25*( zr[:-1,:-1]+zr[:-1,1:]+zr[1:,:-1]+zr[1:,1:] ) - xl_c = 0.25*( xl[:-1,:-1]+xl[:-1,1:]+xl[1:,:-1]+xl[1:,1:] ) - yl_c = 0.25*( yl[:-1,:-1]+yl[:-1,1:]+yl[1:,:-1]+yl[1:,1:] ) - zl_c = 0.25*( zl[:-1,:-1]+zl[:-1,1:]+zl[1:,:-1]+zl[1:,1:] ) - #plot Br=0 - Ax.contour(np.sqrt(xr_c**2 + yr_c**2),zr_c,Br_r,[0.],colors='black') - Ax.contour(-np.sqrt(xl_c**2 + yl_c**2),zl_c,Br_l,[0.],colors='black') - kv.SetAx(xyBds,Ax) + # Fetch the data at the specified step. + # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) + # frame. + Br_r, Br_l = gsph.MerBrNrm(nStp, indx=indx) - if (doDeco): - Ax.set_xlabel(f"R_XY [R_S] Phi={phi:{2}.{2}} [rad]") - Ax.set_ylabel('Z [R_S]') + # Fetch the coordinates of the grid cell corners in the specified + # meridional plane, in the GH(mjd_gh) frame. + xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) + + # Compute the cell center coordinates (used to plot current sheet). + xr_c = 0.25*(xr[:-1, :-1] + xr[:-1, 1:] + xr[1:, :-1] + xr[1:, 1:]) + yr_c = 0.25*(yr[:-1, :-1] + yr[:-1, 1:] + yr[1:, :-1] + yr[1:, 1:]) + zr_c = 0.25*(zr[:-1, :-1] + zr[:-1, 1:] + zr[1:, :-1] + zr[1:, 1:]) + xl_c = 0.25*(xl[:-1, :-1] + xl[:-1, 1:] + xl[1:, :-1] + xl[1:, 1:]) + yl_c = 0.25*(yl[:-1, :-1] + yl[:-1, 1:] + yl[1:, :-1] + yl[1:, 1:]) + zl_c = 0.25*(zl[:-1, :-1] + zl[:-1, 1:] + zl[1:, :-1] + zl[1:, 1:]) + + # Plot the data in the meridional plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + cr = SkyCoord( + -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + cl = SkyCoord( + -xl*u.Rsun, -yl*u.Rsun, zl*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + cr_c = SkyCoord( + -xr_c*u.Rsun, -yr_c*u.Rsun, zr_c*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + cl_c = SkyCoord( + -xl_c*u.Rsun, -yl_c*u.Rsun, zl_c*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + cr = cr.transform_to(hgs_frame) + cl = cl.transform_to(hgs_frame) + cr_c = cr_c.transform_to(hgs_frame) + cl_c = cl_c.transform_to(hgs_frame) + + # Extract the converted coordinates. + xr = dm.dmarray(cr.cartesian.x) + yr = dm.dmarray(cr.cartesian.y) + zr = dm.dmarray(cr.cartesian.z) + xl = dm.dmarray(cl.cartesian.x) + yl = dm.dmarray(cl.cartesian.y) + zl = dm.dmarray(cl.cartesian.z) + xr_c = dm.dmarray(cr_c.cartesian.x) + yr_c = dm.dmarray(cr_c.cartesian.y) + zr_c = dm.dmarray(cr_c.cartesian.z) + xl_c = dm.dmarray(cl_c.cartesian.x) + yl_c = dm.dmarray(cl_c.cartesian.y) + zl_c = dm.dmarray(cl_c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Br_l, cmap=BCM, norm=vB, + shading="auto") + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Br_r, cmap=BCM, norm=vB, + shading="auto") + + # Plot the heliospheric current sheet. + Ax.contour(np.sqrt(xr_c**2 + yr_c**2), zr_c, Br_r, [0.], + colors='black') + Ax.contour(-np.sqrt(xl_c**2 + yl_c**2), zl_c, Br_l, [0.], + colors='black') + + else: + Ax.pcolormesh(np.sqrt(xr**2 + yr**2), zr, Br_r, cmap=BCM, norm=vB, + shading='auto') + Ax.pcolormesh(-np.sqrt(xl**2 + yl**2), zl, Br_l, cmap=BCM, norm=vB, + shading='auto') + Ax.contour(np.sqrt(xr_c**2 + yr_c**2), zr_c, Br_r, [0.], + colors='black') + Ax.contour(-np.sqrt(xl_c**2 + yl_c**2), zl_c, Br_l, [0.], + colors='black') + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel(r"$R_XY [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_ylabel('Z [$R_S$]') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return Br_r, Br_l @@ -668,13 +825,15 @@ def PlotMerTemp( # Determine the angle of the meridional slice. phi = 0.0 for idx in indx: - if type(idx) is int: - phi = idx/gsph.Nk*2*np.pi - elif type(idx) is float: - phi = idx + if type(idx) is not None: + if type(idx) is int: + phi = idx/gsph.Nk*2*np.pi + elif type(idx) is float: + phi = idx else: phi = "" + # Fetch the data at the specified step. # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) # frame. Tempr, Templ = gsph.MerTemp(nStp, indx=indx) @@ -683,7 +842,7 @@ def PlotMerTemp( # meridional plane, in the GH(mjd_gh) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - # Plot the solar wind normalized temperature in the meridional plane. + # Plot the data in the meridional plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -732,14 +891,15 @@ def PlotMerTemp( # Set the plot boundaries. kv.SetAx(xyBds, Ax) + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") Ax.set_ylabel("Z [$R_S$]") - # Return the solar wind normalized temperature. + # Return the data. return Tempr, Templ -# + def PlotEqD( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, MJDc=None, MJD_plot=None, hgsplot=False @@ -813,10 +973,10 @@ def PlotEqD( if doClear: Ax.clear() - # Fetch the normalized number density at the specified step. + # Fetch the data at the specified step. NormD = gsph.eqNormD(nStp) - # Plot the normalized number density in the solar equatorial plane. + # Plot the data in the solar equatorial plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -854,14 +1014,14 @@ def PlotEqD( # Set the plot boundaries. kv.SetAx(xyBds, Ax) - # If requested, label the axes. + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") - # Return the normalized number density data. + # Return the data. return NormD #Plot normalized density in equatorial plane n(r/r0)^2 @@ -962,10 +1122,10 @@ def PlotEqTemp( if doClear: Ax.clear() - # Fetch the normalized temperature at the specified step. + # Fetch the data at the specified step. Temp = gsph.eqTemp(nStp) - # Plot the normalized temperature in the solar equatorial plane. + # Plot the data in the solar equatorial plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -1003,12 +1163,12 @@ def PlotEqTemp( # Set the plot boundaries. kv.SetAx(xyBds, Ax) - # If requested, label the axes. + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") - # Return the normalized temperature data. + # Return the data. return Temp @@ -1105,10 +1265,10 @@ def PlotEqBr( if doClear: Ax.clear() - # Fetch the normalized radial magnetic field at the specified step. + # Fetch the data at the specified step. Br = gsph.eqNormBr(nStp) - # Plot the normalized radial magnetic field in the solar equatorial plane. + # Plot the data in the solar equatorial plane. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -1146,14 +1306,14 @@ def PlotEqBr( # Set the plot boundaries. kv.SetAx(xyBds, Ax) - # If requested, label the axes. + # Decorate the plots. if doDeco: Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") - # Return the normalized radial magnetic field data. + # Return the data. return Br diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 1f51a6cc..e9209e80 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -444,7 +444,8 @@ def main(): indx=(None, pic2lon), MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1, - indx=(None, pic2lon)) + indx=(None, pic2lon), + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) if hgsplot: fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") else: From 5164c3128ac3af8171dab98b707cf0f14dd201ab Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Sun, 24 Sep 2023 20:31:35 -0600 Subject: [PATCH 057/365] Make them pythonic! --- kaipy/rcm/wmutils/genWM.py | 122 +++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 66 deletions(-) diff --git a/kaipy/rcm/wmutils/genWM.py b/kaipy/rcm/wmutils/genWM.py index eb995949..c65696bb 100644 --- a/kaipy/rcm/wmutils/genWM.py +++ b/kaipy/rcm/wmutils/genWM.py @@ -47,50 +47,57 @@ def genh5(fIn, fOut, inputParams, useWM=True): #read parameters of the polynomial fit, Wang+,2023 def readPoly(fIn): - table = [] - with open(fIn, 'r') as file: - # Skip the first row - next(file) - for line in file: - row = line.strip().split('\t')[2:-1] # Discard the first two elements of each row - row = [float(x) for x in row] # Convert the strings to float - rowLen = len(row) - table.append(np.array(row)) - return (rowLen,np.array(table)) + table = [] + with open(fIn, 'r') as file: + # Skip the first row + next(file) + for line in file: + row = line.strip().split('\t')[2:-1] # Discard the first two elements of each row + row = [float(x) for x in row] # Convert the strings to float + rowLen = len(row) + table.append(np.array(row)) + return (rowLen,np.array(table)) #Chorus polynomial fit for the electron lifetime -def ChorusPoly(Lpoly,Kpoly,polyTgt): +def ChorusPoly(Li,Eki,polyArray): +# The 3-rd Order Polynomial Fit Coefficients of Electron Lifetime Caused by Interaction with Chorus Waves +#(https://doi.org/will be provided) +# Dedong Wang et al., in preparation +# For each Kp (0,1,2...,7) and each MLT (0,1,2,...,23), tau has a polynomial fit of Ek and L. - c0 = polyTgt[0]#Intercept - c1 = polyTgt[1] #L - c2 = polyTgt[2]#log10(E) - c3 = polyTgt[3]# L^2 - c4 = polyTgt[4]#log10(E)^2 - c5 = polyTgt[5]#L^3 - c6 = polyTgt[6]#log10(E)^3 - c7 = polyTgt[7]#log10(E)*L - c8 = polyTgt[8]#log10(E)*L^2 - c9 = polyTgt[9]#log10(E)^2*L + lenKp,lenMLT,lenParam = polyArray.shape + #Extend polyArray + polyArrayX = polyArray[:,:,:,np.newaxis,np.newaxis] + #Extend Li and Ki + lenL = len(Li) + lenEki = len(Eki) + Lx = np.tile(Li, (lenEki, 1)).T + Lx = Lx[np.newaxis,np.newaxis,:,:] + Ex = np.tile(Eki, (lenL, 1)) + Ex = Ex[np.newaxis,np.newaxis,:,:] - lenL = len(Lpoly) - lenK = len(Kpoly) - - tau = np.ones((lenL,lenK)) - # Duplicating the array in columns - L = np.tile(Lpoly, (lenK, 1)).T - - # Duplicating the array in rows - K = np.tile(Kpoly, (lenL, 1)) - - tau = c0*tau+\ - c1*L+c2*K+\ - c3*np.power(L,2)+\ - c4*np.power(K,2)+\ - c5*np.power(L,3)+\ - c6*np.power(K,3)+\ - c7*L*K+\ - c8*np.power(L,2)*K+\ - c9*L*np.power(K,2) #in log10(days) + tau = np.ones((lenKp,lenMLT,lenL,lenEki)) + + c0 = polyArrayX[:,:,0,:,:]#Intercept + c1 = polyArrayX[:,:,1,:,:] #L + c2 = polyArrayX[:,:,2,:,:]#log10(E) + c3 = polyArrayX[:,:,3,:,:]# L^2 + c4 = polyArrayX[:,:,4,:,:]#log10(E)^2 + c5 = polyArrayX[:,:,5,:,:]#L^3 + c6 = polyArrayX[:,:,6,:,:]#log10(E)^3 + c7 = polyArrayX[:,:,7,:,:]#log10(E)*L + c8 = polyArrayX[:,:,8,:,:]#log10(E)*L^2 + c9 = polyArrayX[:,:,9,:,:]#log10(E)^2*L + + tau = c0*tau+\ + c1*Lx+c2*Ex+\ + c3*np.power(Lx,2)+\ + c4*np.power(Ex,2)+\ + c5*np.power(Lx,3)+\ + c6*np.power(Ex,3)+\ + c7*Lx*Ex+\ + c8*np.power(Lx,2)*Ex+\ + c9*Lx*np.power(Ex,2) #in log10(days) tau = 10.0**tau*(60.*60.*24.) #in seconds @@ -125,6 +132,7 @@ def ReSample(L,MLT,Qp,xMLT): def genChorus(params,fInChorus): rowLen,paramArray = readPoly(fInChorus) polyArray = paramArray.reshape(24,7,rowLen) #Dim MLT: 24, Dim Kp: 7 + polyArray = polyArray.transpose(1, 0, 2) # shape (7,24,rowLen) lenMLT = 24 #Kpi startValue = 1.0 #in Re @@ -142,18 +150,10 @@ def genChorus(params,fInChorus): lenL = 41 Li = np.linspace(startValue, endValue, num=lenL) #Tau from polynomial fit - tauP = np.zeros((lenKp,lenMLT,lenL,lenEk)) - for k in range(0,lenKp): # Kp: 1,2,...,7 - for m in range(0,lenMLT): # MLT: 0,1,...,23 - #print("xMLT,xKp",xMLT,xKp) - polyKM = polyArray[m,k,:] - #print('polyTgt',polyTgt) - tauPolyKM = ChorusPoly(Li,Eki,polyKM) - tauP[k,m,:,:] = tauPolyKM[:,:] + tauP = ChorusPoly(Li,Eki,polyArray) #expand MLT from 0-23 to 0-24 - tauE = np.array([np.append(tauP[0,:,:,:],np.array([tauP[0,0,:,:]]),0)]) - for i in range(1,lenKp): - tauE=np.append(tauE,np.array([np.append(tauP[i,:,:,:],np.array([tauP[i,0,:,:]]),0)]),0) + extraMLT0 = tauP[:, 0, :, :][:,np.newaxis,:,:] + tauE = np.concatenate((tauP, extraMLT0), axis=1) tauE = tauE.T #Interpolation in the MLT dimesion xFac = 4 @@ -161,22 +161,12 @@ def genChorus(params,fInChorus): MLTi = np.linspace(0,24,lenMLT+1) xMLTi = np.linspace(0,24,lenMLTx) tauX = np.zeros((lenEk,lenL,lenMLTx,lenKp)) - for i in range(lenEk): - for j in range(lenKp): - Q = tauE[i,:,:,j] - tauX[i,:,:,j] = ReSample(Li,MLTi,Q,xMLTi) + # Smoothing in MLT + for i, j in np.ndindex(tauX.shape[0], tauX.shape[3]): + Q = tauE[i, :, :, j] + tauX[i, :, :, j] = ReSample(Li, MLTi, Q, xMLTi) Eki = 10.0**Eki #in MeV + return Kpi,xMLTi,Li,Eki,tauX - - - - - - - - - - - From da21d522ce73d598981bef79afae05f8acf51ae0 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Sun, 24 Sep 2023 22:26:35 -0600 Subject: [PATCH 058/365] Add back rcmhd manually. --- src/base/defs/mixdefs.F90 | 2 +- src/remix/mixconductance.F90 | 83 +++++++++++++++++++++++++++++++++++- src/remix/mixparams.F90 | 4 +- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/base/defs/mixdefs.F90 b/src/base/defs/mixdefs.F90 index b3db9d1c..bf0c79b3 100644 --- a/src/base/defs/mixdefs.F90 +++ b/src/base/defs/mixdefs.F90 @@ -28,7 +28,7 @@ module mixdefs end enum enum, bind(C) - enumerator :: FEDDER=1,ZHANG,LINMRG + enumerator :: FEDDER=1,ZHANG,LINMRG,RCMHD end enum enum, bind(C) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index ed879a6c..cc1a0fc9 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -128,8 +128,10 @@ module mixconductance call conductance_zhang15(conductance,G,St) case (LINMRG) call conductance_linmrg(conductance,G,St) + case (RCMHD) + call conductance_rcmhd(conductance,G,St) case default - stop "The aurora precipitation model type entered is not supported." + stop "The auroral precipitation model type entered is not supported." end select ! Correct for multiple reflections if you're so inclined @@ -610,6 +612,85 @@ module mixconductance end subroutine conductance_linmono + subroutine conductance_rcmhd(conductance,G,St) + type(mixConductance_T), intent(inout) :: conductance + type(mixGrid_T), intent(in) :: G + type(mixState_T), intent(inout) :: St + + integer :: i,j + logical :: isMono + real(rp) :: mhd_eavg, mhd_nflx, mhd_eflx, rmd_nflx, rmd_eflx, rmd_eavg + real(rp) :: rmd_eavg_fin, rmd_nflx_fin, rmd_SigP, mhd_SigP + + ! Make dorcm=.false. to use pure MHD fluxes. + ! Tests show that RCM fluxes are so low that they easily trigger mono in the dawnside R2 FAC. + logical :: dorcm = .false. + + ! derive RCM grid weighting based on that passed from RCM and smooth it with five iterations of numerical diffusion. + call conductance_IM_GTYPE(G,St) + + ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need IM_GTYPE. + call conductance_alpha_beta(conductance,G,St) + + ! derive MHD/mono precipitation with zhang15 but include RCM thermal flux to the source by using dorcm=.true. + call conductance_zhang15(conductance,G,St,dorcm) + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,isMono) & + !$OMP private(mhd_eavg, mhd_nflx, mhd_eflx, rmd_nflx, rmd_eflx, rmd_eavg)& + !$OMP private(rmd_eavg_fin, rmd_nflx_fin, rmd_SigP, mhd_SigP) + do j=1,G%Nt + do i=1,G%Np + isMono = conductance%deltaE(i,j) > 0.05 .and. St%Vars(i,j,Z_NFLUX)>1e6 !Potential drop + + mhd_eavg = St%Vars(i,j,Z_EAVG) + mhd_nflx = St%Vars(i,j,Z_NFLUX) + mhd_eflx = St%Vars(i,j,Z_EAVG)*St%Vars(i,j,Z_NFLUX)*kev2erg + + rmd_nflx = St%Vars(i,j,IM_ENFLX)*gtype_RCM(i,j)+mhd_nflx*(1.0-gtype_RCM(i,j)) + rmd_eflx = St%Vars(i,j,IM_EFLUX)*gtype_RCM(i,j)+mhd_eflx*(1.0-gtype_RCM(i,j)) + if(rmd_nflx>TINY) then + rmd_eavg = max(rmd_eflx/(rmd_nflx*kev2erg),1.0e-8) + else + rmd_eavg = 0.0 + endif + + if (.not. isMono) then + !No potential drop, just use merged precipitation. + St%Vars(i,j,AVG_ENG ) = rmd_eavg + St%Vars(i,j,NUM_FLUX) = rmd_nflx + St%Vars(i,j,AUR_TYPE) = AT_RMnoE + cycle + endif + + !If still here, we have a potential drop + !Decide between the two by taking one that gives highest Sig-P + if (conductance%doMR) then ! be careful here is using nflux. + call AugmentMR(rmd_eavg,rmd_nflx,rmd_eavg_fin,rmd_nflx_fin) !Correct for MR + else + !No corrections + rmd_eavg_fin = rmd_eavg + rmd_nflx_fin = rmd_nflx + endif + + rmd_SigP = SigmaP_Robinson(rmd_eavg_fin,kev2erg*rmd_eavg_fin*rmd_nflx_fin) + mhd_SigP = SigmaP_Robinson(mhd_eavg ,kev2erg*mhd_eavg *mhd_nflx ) + + if (mhd_SigP>=rmd_SigP) then + St%Vars(i,j,AVG_ENG ) = mhd_eavg + St%Vars(i,j,NUM_FLUX) = mhd_nflx + St%Vars(i,j,AUR_TYPE) = AT_RMono + else + !RMD diffuse is still better than MHD + puny potential drop + St%Vars(i,j,AVG_ENG ) = rmd_eavg !Use un-augmented value since MR gets called later + St%Vars(i,j,NUM_FLUX) = rmd_nflx + conductance%deltaE(i,j) = 0.0 !Wipe out potential drop since it don't matter (otherwise MR won't happen if desired) + St%Vars(i,j,AUR_TYPE) = AT_RMfnE + endif + enddo + enddo + end subroutine conductance_rcmhd + subroutine conductance_mr(conductance,G,St) ! George Khazanov's multiple reflection(MR) corrections ! Modify the diffuse precipitation energy and mean energy based on equation (5) in Khazanov et al. [2019JA026589] diff --git a/src/remix/mixparams.F90 b/src/remix/mixparams.F90 index 0f13323e..49def790 100644 --- a/src/remix/mixparams.F90 +++ b/src/remix/mixparams.F90 @@ -114,8 +114,10 @@ module mixparams Params%aurora_model_type = ZHANG case ("LINMRG") Params%aurora_model_type = LINMRG + case ("RCMHD") + Params%aurora_model_type = RCMHD case default - stop "The aurora model type entered is not supported (Available options: FEDDER, ZHANG, LINMRG)." + stop "The auroral model type entered is not supported (Available options: FEDDER, ZHANG, LINMRG, RCMHD)." end select ! =========== CONDUCTANCE MODEL PARAMETERS =================== ! From ee0ba22858498232e5def27d9a62bf05eac4d596 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Sun, 24 Sep 2023 22:53:21 -0600 Subject: [PATCH 059/365] Minor fix of typos. --- src/base/defs/mixdefs.F90 | 2 +- src/remix/mixconductance.F90 | 36 ++++++++++++------------------------ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/base/defs/mixdefs.F90 b/src/base/defs/mixdefs.F90 index b3db9d1c..f246fa81 100644 --- a/src/base/defs/mixdefs.F90 +++ b/src/base/defs/mixdefs.F90 @@ -4,7 +4,7 @@ module mixdefs use kdefs implicit none - integer, parameter :: nVars = 26 ! change together wiht the enumerator below + integer, parameter :: nVars = 26 ! change together with the enumerator below enum, bind(C) enumerator :: POT=1,FAC,SIGMAP,SIGMAH,SOUND_SPEED,DENSITY,AVG_ENG,NUM_FLUX,NEUTRAL_WIND,EFIELD,IM_EAVG,IM_EFLUX,IM_IAVG,IM_IFLUX,Z_EAVG,Z_NFLUX,CRPOT,TPOT,IM_GTYPE,AUR_TYPE,IM_BETA,IM_EDEN,IM_EPRE,IM_ENFLX,IM_INFLX,DELTAE end enum diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index ed879a6c..e34b9b4f 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -19,6 +19,7 @@ module mixconductance !Replacing some hard-coded inline values (bad) w/ module private values (slightly less bad) real(rp), parameter, private :: maxDrop = 20.0 !Hard-coded max potential drop [kV] + real(rp), parameter, private :: eTINY = 1.D-8 ! Floor of average energy [keV] real(rp), private :: RinMHD = 0.0 !Rin of MHD grid (0 if not running w/ MHD) real(rp), private :: MIXgamma logical , private :: doDrift = .false. !Whether to add drift term from Zhang @@ -129,7 +130,7 @@ module mixconductance case (LINMRG) call conductance_linmrg(conductance,G,St) case default - stop "The aurora precipitation model type entered is not supported." + stop "The auroral precipitation model type entered is not supported." end select ! Correct for multiple reflections if you're so inclined @@ -286,7 +287,7 @@ module mixconductance end subroutine conductance_euv subroutine conductance_fedder95(conductance,G,St) - ! Derive electron precipitation energy flux and avg energy using Feder95 formula. + ! Derive electron precipitation energy flux and avg energy using Fedder95 formula. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St @@ -300,11 +301,7 @@ module mixconductance tmpD = 0.D0 if (St%hemisphere==NORTH) then - signOfY = -1 ! note, factor2 (dawn-dusk asymmetry is not - ! implemented since factor2 in the old - ! fedder95 code was removed, i.e., set to - ! 1, anyway). I think Mike did this when he - ! implemented his ramp function. + signOfY = -1 signOfJ = -1 elseif (St%hemisphere==SOUTH) then signOfY = 1 @@ -382,11 +379,7 @@ module mixconductance JF0 = 0.D0 if (St%hemisphere==NORTH) then - signOfY = -1 ! note, factor2 (dawn-dusk asymmetry is not - ! implemented since factor2 in the old - ! fedder95 code was removed, i.e., set to - ! 1, anyway). I think Mike did this when he - ! implemented his ramp function. + signOfY = -1 signOfJ = -1 elseif (St%hemisphere==SOUTH) then signOfY = 1 @@ -442,7 +435,6 @@ module mixconductance ! floor on total energy ! Eavg=2*kB*Te + eV*(1-exp(-eV/(RM-1)/kB/Te))/(1-(1-1/RM)*exp(-eV/(RM-1)/kB/Te)) - ceV = 0.D0 ceV = exp(-conductance%deltaE/(0.5*conductance%E0*(RM-1))) St%Vars(:,:,Z_EAVG) = max(conductance%E0 + conductance%deltaE*(1-ceV)/(1-(1-1/RM)*ceV),1.D-8) @@ -468,6 +460,7 @@ module mixconductance logical :: isRCM,isMHD,isMIX St%Vars(:,:,AUR_TYPE) = 0 + ! Two thresholds of rcm grid type between which both MHD and RCM precipitation will be merged. wC1 = 0.15 wC2 = 1.0-wC1 @@ -507,7 +500,7 @@ module mixconductance St%Vars(i,j,NUM_FLUX) = mhd_nflx else if (isRCM) then if (rcm_nflx <= TINY) then - rcm_eavg = 1.0e-8 + rcm_eavg = eTINY endif St%Vars(i,j,AVG_ENG ) = rcm_eavg St%Vars(i,j,NUM_FLUX) = rcm_nflx @@ -543,14 +536,10 @@ module mixconductance real(rp) :: signOfY, signOfJ integer :: i,j real(rp) :: D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT - real(rp) :: eTINY + real(rp) :: Ne_floor if (St%hemisphere==NORTH) then - signOfY = -1 ! note, factor2 (dawn-dusk asymmetry is not - ! implemented since factor2 in the old - ! fedder95 code was removed, i.e., set to - ! 1, anyway). I think Mike did this when he - ! implemented his ramp function. + signOfY = -1 signOfJ = -1 elseif (St%hemisphere==SOUTH) then signOfY = 1 @@ -559,7 +548,7 @@ module mixconductance stop 'Wrong hemisphere label. Stopping...' endif - eTINY = 1.D-8 + Ne_floor = 0.03e6 ! minimum Ne in [/m^3] when evaluating the linearized FL relation. !$OMP PARALLEL DO default(shared) & !$OMP private(i,j,D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT) @@ -591,7 +580,7 @@ module mixconductance ! Linearize the Fridman-Lemaire relation: ! eV = kB*Te*(RM-1)*ln((RM-1)/(RM-J/e/F0)) when 1<=J/e/F0<=RM. ! eV ~ kB*Te*(J/e/F0-1) when RM>>J/e/F0 - if (J2eF0>1.0 .and. Ne>0.03e6) then + if (J2eF0>1.0 .and. Ne>Ne_floor) then dE = kT*(J2eF0-1.0) conductance%deltaE(i,j) = dE eV2kT = min(dE,maxDrop)/kT + 1.0 @@ -722,7 +711,6 @@ module mixconductance subroutine conductance_aurora(conductance,G,St) ! Use Robinson formula to get Pedersen and Hall conductance from electron precipitation. - ! Diffuse precipitation from RCM has been divided by 2 in rcm_subs.F90 for each hemisphere. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St @@ -875,7 +863,7 @@ module mixconductance end subroutine FixPole subroutine conductance_margin(G,array,arraymar) - ! Conductance boundary proessing. + ! Conductance boundary processing. type(mixGrid_T), intent(in) :: G real(rp),dimension(G%Np,G%Nt), intent(in) :: array real(rp),dimension(G%Np+4,G%Nt+4), intent(out) :: arraymar From 09c0016b30373e2d3cdd498081115b7c793a335c Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 25 Sep 2023 10:04:52 -0400 Subject: [PATCH 060/365] pic6 updates --- kaipy/gamhelio/helioViz.py | 442 ++++++++++++++++++-- quickstart/geo_mpi/geo_mpi_template.pbs | 4 + quickstart/helio_mpi/helio_mpi_template.ini | 2 +- scripts/quicklook/heliopic.py | 23 +- 4 files changed, 417 insertions(+), 54 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 6a1b8aaa..f6f903d4 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -1192,6 +1192,7 @@ def PlotjTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): Ax.set_ylabel('Y [R_S]') return Temp + def PlotEqBr( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, MJDc=None, MJD_plot=None, hgsplot=False @@ -1339,99 +1340,448 @@ def PlotjBr(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): Ax.yaxis.set_label_position('right') return Br -#Plor Br in equatorial plane -def PlotEqBx(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): + +def PlotEqBx( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot solar wind x-magnetic field in the solar equatorial plane. + + Plot solar wind x-magnetic field in the solar equatorial plane. By default, + the plot is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Bx : np.array of float + Data for solar wind x-magnetic field in equatorial plane, same shape as + the equatorial grid in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,r'MF B$_x$(r/r$_0)^2$ [nT]',cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, r'MF $B_x$(r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Fetch the data at the specified step. Bx = gsph.eqBx(nStp) - Ax.pcolormesh(gsph.xxi,gsph.yyi,Bx,cmap=BCM,norm=vB) - kv.SetAx(xyBds,Ax) + # Plot the data in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, Bx, cmap=BCM, norm=vB) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, Bx, cmap=BCM, norm=vB) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel('$X [R_S]$') + Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return Bx -#Plor Br in equatorial plane -def PlotEqBy(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): + +def PlotEqBy( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot solar wind y-magnetic field in the solar equatorial plane. + + Plot solar wind y-magnetic field in the solar equatorial plane. By default, + the plot is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + By : np.array of float + Data for solar wind y-magnetic field in equatorial plane, same shape as + the equatorial grid in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,r'MF B$_y$(r/r$_0)^2$ [nT]',cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, r'MF $B_y(r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() + # Fetch the data at the specified step. By = gsph.eqBy(nStp) - Ax.pcolormesh(gsph.xxi,gsph.yyi,By,cmap=BCM,norm=vB) - kv.SetAx(xyBds,Ax) + # Plot the data in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, By, cmap=BCM, norm=vB) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, By, cmap=BCM, norm=vB) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel('$X [R_S]$') + Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return By -#Plor Br in equatorial plane -def PlotEqBz(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True): - - Bz = gsph.eqBz(nStp) - maxBz = np.max(np.abs(Bz)) - vB = kv.genNorm(-maxBz, maxBz, doLog=False, midP=None) - if (AxCB is not None): +def PlotEqBz( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot solar wind z-magnetic field in the solar equatorial plane. + + Plot solar wind z-magnetic field in the solar equatorial plane. By default, + the plot is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Bz : np.array of float + Data for solar wind z-magnetic field in equatorial plane, same shape as + the equatorial grid in the gamhelio results. + + Raises + ------ + None + """ + # Fetch the data. + Bz = gsph.eqBz(nStp) + + # Create a normalizer object for the colorbar. + maxBz = np.max(np.abs(Bz)) + print(f"maxBz = {maxBz}") + # vB = kv.genNorm(-maxBz, maxBz, doLog=False, midP=None) + vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) + + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,r'MF B$_z$ [nT]',cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, r'MF $B_z$ [nT]', cM=BCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() - Ax.pcolormesh(gsph.xxi,gsph.yyi,Bz,cmap=BCM,norm=vB) + # Plot the data in the solar equatorial plane. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: - kv.SetAx(xyBds,Ax) + # Load the equatorial grid cell vertex coordinates (originially in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values + # to 0 since we are using the solar equatorial plane. + zzi = np.zeros_like(gsph.xxi) + c = SkyCoord( + -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(x, y, Bz, cmap=BCM, norm=vB) + + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, Bz, cmap=BCM, norm=vB) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel('$X [R_S]$') + Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return Bz -#Plot Speed at 1 AU -def PlotiSlMagV(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,idx=-1): +def PlotiSlMagV( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 +): + """Plot solar wind speed at a specified radial slice. + + Plot solar wind speed at a specified radial slice. The plot is created in + a GH frame rotating with the sidereal solar rotation period. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + idx : int + Index of radial slice to plot. + + Returns + ------- + V : np.array of float + Data for speed in radial slice. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) - if (AxCB is not None): - #Add the colorbar to AxCB - AxCB.clear() - kv.genCB(AxCB,vMagV,"Speed [km/s]",cM=MagVCM,Ntk=7) + # Create the color bar. + if AxCB: + AxCB.clear() + kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) - #Now do main plotting - if (doClear): + # Clear the plot Axes if needed. + if doClear: Ax.clear() - V = gsph.iSliceMagV(nStp,idx=idx) + # Fetch the data at the specified step and radial slice. + V = gsph.iSliceMagV(nStp, idx=idx) + + # Get the latitude and longitude of the grid cells in the radial slice. + # This is in the GH(MJDc) frame. lat, lon = gsph.iSliceGrid(idx=idx) - Ax.pcolormesh(lon,lat,V,cmap=MagVCM,norm=vMagV) - kv.SetAx(xyBds,Ax) + # Plot the data. + Ax.pcolormesh(lon, lat, V, cmap=MagVCM, norm=vMagV) - if (doDeco): + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + if doDeco: Ax.set_xlabel('Longitude') Ax.set_ylabel('Latitude') + + # Return the data. return V + #Plot Density at 1 AU def PlotiSlD(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,idx=-1): vD = kv.genNorm(D0Min, D0Max, doLog=False, midP=None) diff --git a/quickstart/geo_mpi/geo_mpi_template.pbs b/quickstart/geo_mpi/geo_mpi_template.pbs index ac03becc..c214ab95 100644 --- a/quickstart/geo_mpi/geo_mpi_template.pbs +++ b/quickstart/geo_mpi/geo_mpi_template.pbs @@ -110,6 +110,10 @@ ##PBS -A UJHB0019 ##PBS -q main ##PBS -l select=2:ncpus=128:mpiprocs=2:ompthreads=64+1:ncpus=128:mpiprocs=1:ompthreads=128 +# Use 8 ranks/node for gamera on derecho +# Use 2 ranks for helpers +# 1 node separate for voltron +# Additional PBS chunk #------------------------------------------------------------------------------ # END OF PBS DIRECTIVES diff --git a/quickstart/helio_mpi/helio_mpi_template.ini b/quickstart/helio_mpi/helio_mpi_template.ini index 2081c064..df8b2de9 100644 --- a/quickstart/helio_mpi/helio_mpi_template.ini +++ b/quickstart/helio_mpi/helio_mpi_template.ini @@ -11,7 +11,7 @@ tFin = 200.0 [output] dtOut = 10 tsOut = 50.0 -tier = F +timer = F [physics] doMHD = T diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index e9209e80..d9a05faf 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -451,21 +451,30 @@ def main(): else: fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic3": - hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, idx=0) - hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, idx=0) - hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1, idx=0) - hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, idx=0) + # Plot at 1 AU. iSl -> "i slice" + hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0) + hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0) + hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1) + hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1) elif pic == "pic4": + # Plot at 1 AU in frame rotating with Sun. hviz.PlotiSlBrRotatingFrame(gsph, nStp, xyBds, Ax, AxC) elif pic == "pic5": hviz.PlotDensityProf(gsph, nStp, xyBds, Ax) hviz.PlotSpeedProf(gsph, nStp, xyBds, AxC) hviz.PlotFluxProf(gsph, nStp, xyBds, AxC1) elif pic == "pic6": - hviz.PlotEqBr(gsph, nStp, xyBds, AxL0, AxC1_0) - hviz.PlotEqBx(gsph, nStp, xyBds, AxR0, AxC2_0) - hviz.PlotEqBy(gsph, nStp, xyBds, AxL1, AxC1_1) + hviz.PlotEqBr(gsph, nStp, xyBds, AxL0, AxC1_0, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqBx(gsph, nStp, xyBds, AxR0, AxC2_0, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqBy(gsph, nStp, xyBds, AxL1, AxC1_1, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) hviz.PlotEqBz(gsph, nStp, xyBds, AxR1, AxC2_1) + if hgsplot: + fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + else: + fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic7": hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0, jidx=448) hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=448) From 96242d202dc4b7ecfa6d413c7c85c80d4a36a880 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 25 Sep 2023 11:41:15 -0400 Subject: [PATCH 061/365] Added pic7 support. No HGS frame or spacecraft for now. Added islice argument. --- kaipy/gamhelio/helioViz.py | 405 +++++++++++++++++++++++++++++----- scripts/quicklook/heliopic.py | 28 ++- 2 files changed, 373 insertions(+), 60 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index f6f903d4..57228b49 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -212,27 +212,99 @@ def PlotEqMagV( return MagV -#Plot speed in j plane -def PlotjMagV(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): - vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) - - if (AxCB is not None): - #Add the colorbar to AxCB - AxCB.clear() - kv.genCB(AxCB,vMagV,"Speed [km/s]",cM=MagVCM,Ntk=7) +def PlotjMagV( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, jidx=-1, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot solar wind speed in a specific j plane. - #Now do main plotting - if (doClear): + Plot solar wind speed in the a specific j plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + jidx : int + Index of j-plane to plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + MagV : np.array of float + Data for solar wind speed in selected j-plane, same shape as the + j-plane in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. + vMagV = kv.genNorm(VMin, VMax, doLog=False, midP=None) + + # Create the color bar. + if AxCB: + AxCB.clear() + kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() - MagV = gsph.jMagV(nStp,jidx=jidx) - Ax.pcolormesh(gsph.xxi,gsph.yyi,MagV,cmap=MagVCM,norm=vMagV) + # Fetch the data at the specified step. + MagV = gsph.jMagV(nStp, jidx=jidx) - kv.SetAx(xyBds,Ax) + # Plot the data. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + raise TypeError("HGS frame not supported for pic7!") + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, MagV, cmap=MagVCM, norm=vMagV) - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel(r"$X [R_S]$") + Ax.set_ylabel(r"$Y [R_S]$") + + # Return the data. return MagV @@ -1024,29 +1096,102 @@ def PlotEqD( # Return the data. return NormD -#Plot normalized density in equatorial plane n(r/r0)^2 -def PlotjD(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): - vD = kv.genNorm(DMin, DMax, doLog=False, midP=None) - - if (AxCB is not None): - #Add the colorbar to AxCB - AxCB.clear() - kv.genCB(AxCB,vD,r"Density n(r/r$_0)^2$ [cm$^{-3}$]",cM=DCM,Ntk=7) - #Now do main plotting - if (doClear): +def PlotjD( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, jidx=-1, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized density in a specific j plane. + + Plot normalized density in the a specific j plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + jidx : int + Index of j-plane to plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + NormD : np.array of float + Normalized number density in selected j-plane, same shape as the + j-plane in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. + vD = kv.genNorm(DMin, DMax, doLog=False, midP=None) + + # Create the color bar. + if AxCB: + AxCB.clear() + kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2 [cm^{-3}]$", cM=DCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() - NormD = gsph.jNormD(nStp,jidx=jidx) - Ax.pcolormesh(gsph.xxi,gsph.yyi,NormD,cmap=DCM,norm=vD) + # Fetch the data. + NormD = gsph.jNormD(nStp, jidx=jidx) - kv.SetAx(xyBds,Ax) + # Plot the data. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + raise TypeError("HGS frame not supported for pic7!") + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, NormD, cmap=DCM, norm=vD) - if (doDeco): - Ax.set_xlabel('R [R_S]') - Ax.set_ylabel('Y [R_S]') + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel('$R [R_S]$') + Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return NormD def PlotEqTemp( @@ -1172,24 +1317,99 @@ def PlotEqTemp( return Temp -#Plot normalized Temperature in equatorial plane T(r/r0) -def PlotjTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): +def PlotjTemp( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, jidx=-1, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized temperature in a specific j plane. + + Plot normalized temperature in the a specific j plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + jidx : int + Index of j-plane to plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Temp : np.array of float + Normalized temperature in selected j-plane, same shape as the + j-plane in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vT = kv.genNorm(TMin, TMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vT,r"Temperature T(r/r$_0$) [MK]",cM=TCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() - Temp = gsph.jTemp(nStp,jidx=jidx) - Ax.pcolormesh(gsph.xxi,gsph.yyi,Temp,cmap=TCM,norm=vT) - - kv.SetAx(xyBds,Ax) + # Fetch the data. + Temp = gsph.jTemp(nStp, jidx=jidx) + + # Plot the data. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + raise TypeError("HGS frame not supported for pic7!") + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, Temp, cmap=TCM, norm=vT) - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel('$X [R_S]$') + Ax.set_ylabel('$Y [R_S]$') + + # Return the data. return Temp @@ -1318,26 +1538,101 @@ def PlotEqBr( return Br -#Plor Br in equatorial plane -def PlotjBr(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,jidx=-1): +def PlotjBr( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, jidx=-1, + MJDc=None, MJD_plot=None, hgsplot=False +): + """Plot normalized radial magnetic field in a specific j plane. + + Plot normalized radial magnetic field in the a specific j plane. By default, the plot + is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If MJD_plot is specified, MJDc must also be specified. + In that case, the coordinates are mapped from the GH(MJDc) frame to the + HGS(MJD_plot) frame. + + The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) + frame. The difference is that: + + x (GH) = -x (HGS) + y (GH) = -y (HGS) + z (GH) = z (HGS) + + The GH frame is defined at MJDc, meaning it is fixed in spatial orientation + at that time. The HGS frame is defined at MJD_plot, also producing a + (different) fixed spatial orientation. The conversion maps points in the + GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about + the z-axis, but also accounting for the Earth's orbit and other + astronommical and geodetic parameters. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If true, clear the plot Axes before further plotting. + doDeco : bool + If true, add axis labels to the plot. + jidx : int + Index of j-plane to plot. + MJDc : float + MJD used for the coordinate GH frame of the simulation. + MJD_plot : float + MJD to use for the HGS frame of the plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. + + Returns + ------- + Br : np.array of float + Normalized radial magnetic field in selected j-plane, same shape as the + j-plane in the gamhelio results. + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vB = kv.genNorm(BMin, BMax, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,r'Radial MF B$_r$(r/r$_0)^2$ [nT]',cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, r'Radial MF $B_r (r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + + # Clear the plot Axes if needed. + if doClear: Ax.clear() - Br = gsph.jNormBr(nStp,jidx=jidx) - Ax.pcolormesh(gsph.xxi,gsph.yyi,Br,cmap=BCM,norm=vB) + # Fetch the data. + Br = gsph.jNormBr(nStp, jidx=jidx) - kv.SetAx(xyBds,Ax) + # Plot the data. + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + raise TypeError("HGS frame not supported for pic7!") + else: + Ax.pcolormesh(gsph.xxi, gsph.yyi, Br, cmap=BCM, norm=vB) - if (doDeco): - Ax.set_xlabel('X [R_S]') - Ax.set_ylabel('Y [R_S]') + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: + Ax.set_xlabel('$X [R_S]$') + Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return Br diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index d9a05faf..ce4ac8e6 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -147,6 +147,10 @@ def create_command_line_parser(): "-id", type=str, metavar="runid", default=DEFAULT_RUNID, help="Run ID of data (default: %(default)s)" ) + parser.add_argument( + "-jslice", type=int, metavar="jSlice", default=None, + help="Index of j-slice for pic7 (default: Nj/2-1)" + ) parser.add_argument( "-lon", type=float, metavar="lon", default=0.0, help="Longitude of meridian slice (pic2) (default: %(default)s)" @@ -330,6 +334,7 @@ def main(): fdir = args.directory hgsplot = args.hgsplot ftag = args.id + jslice = args.jslice pic2lon = args.lon steps = args.nlist slices = args.nslice @@ -476,10 +481,21 @@ def main(): else: fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic7": - hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0, jidx=448) - hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=448) - hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=448) - hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1, jidx=448) + if jslice is None: + jidx = gsph.Nj//2 - 1 + else: + jidx = jslice + hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0, jidx=jidx, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=jidx, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=jidx, + MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1, jidx=jidx) + if hgsplot: + fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + else: + fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") else: raise TypeError(f"Invalid figure type: {pic}!") @@ -591,8 +607,10 @@ def main(): ax.plot(x_sc, y_sc, 'o', c="black", fillstyle="none") ax.text(x_sc + x_nudge, y_sc + y_nudge, sc_id, c="black", horizontalalignment="center") + elif pic == "pic7": + raise TypeError("Spacecraft not supported for pic7!") else: - raise TypeError(f"Invalid figure type: {pic}!") + raise TypeError(f"Invalid plot code: {pic}!") # Save the figure to a file. path = os.path.join(fdir, fOut(ftag, pic, nStp, hgsplot)) From ba8c2868ea0a4b1f83651fc99bfec06cb6a300af Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 25 Sep 2023 13:02:08 -0400 Subject: [PATCH 062/365] Added pic7 description. --- scripts/quicklook/heliopic.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index ce4ac8e6..2d12fb77 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -53,7 +53,15 @@ in the solar equatorial plane (z=0), for -200 Rsun <= X, Y <= +200 Rsun. Lower left: y-component of magnetic field (nT) Lower right: z-component of magnetic field (nT) -pic7: ??? +pic7: A 4-panel display showing pcolormesh plots in a j-slice. A j-slice is +a slice through the gamhelio data cube at a fixed colatitude. j = 0 corresponds +to the YZ plane of the gamhelio frame used in the simulation. The j = Nj/2-1 +slice corresponds to the equatorial plane. The plots are: + + Upper left: Solar wind speed (km/s) + Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) + Lower left: Solar wind temperature scaled by r/r0 (MK) + Lower right: Solar wind radial magnetic field scaled by r/r0 (nT) Authors ------- From 673c2158061824ae731f5f1f5bb4b0f6132667fb Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 25 Sep 2023 14:55:55 -0400 Subject: [PATCH 063/365] Added skeletons for pic6 and pic7. --- scripts/quicklook/heliomovie.py | 73 +++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/scripts/quicklook/heliomovie.py b/scripts/quicklook/heliomovie.py index c175dd65..8d3788b4 100755 --- a/scripts/quicklook/heliomovie.py +++ b/scripts/quicklook/heliomovie.py @@ -93,7 +93,7 @@ default_last_step = -1 default_pictype = "pic1" # Valid plot type strings. -valid_pictypes = ("pic1", "pic2", "pic3", "pic4", "pic5") +valid_pictypes = ("pic1", "pic2", "pic3", "pic4", "pic5", "pic6", "pic7") # Default movie format. default_movie_format = "mp4" @@ -113,6 +113,8 @@ figure_sizes = { "pic3": (10, 6.5), "pic4": (10, 6), "pic5": (12, 12), + "pic6": (12.5, 12.5), + "pic7": (10, 12.5), } # List of colors to use for spacecraft position dots. @@ -163,6 +165,10 @@ def create_command_line_parser(): "-id", "--runid", type=str, metavar="runid", default=default_runid, help="Run ID of data (default: %(default)s)" ) + parser.add_argument( + "-jslice", type=int, metavar="jSlice", default=None, + help="Index of j-slice for pic7 (default: Nj/2-1)" + ) parser.add_argument( "-n0", "--first_step", type=int, metavar="n0", default=default_first_step, @@ -230,7 +236,7 @@ def fetch_spacecraft_trajectories(spacecraft_names, gsph): MJDc = scutils.read_MJDc(fname) # Fetch the trajectory of each spacecraft from CDAWeb. - for (i_sc, sc_id) in enumerate(spacecraft_names): + for sc_id in spacecraft_names: # Fetch the spacecraft trajectory in whatever frame is available # from CDAWeb. @@ -1240,6 +1246,54 @@ def create_pic5_movie(args): return movie_file +def create_pic6_movie(args): + """Create a pic6-style gamhelio movie. + + Create a pic6-style gamhelio movie. + + Parameters + ---------- + args : dict + Dictionary of command-line arguments. + + Returns + ------- + movie_file : str + Path to movie file. + + Raises + ------ + None + """ + # Return the path to the movie file. + movie_file = None + return movie_file + + +def create_pic7_movie(args): + """Create a pic7-style gamhelio movie. + + Create a pic7-style gamhelio movie. + + Parameters + ---------- + args : dict + Dictionary of command-line arguments. + + Returns + ------- + movie_file : str + Path to movie file. + + Raises + ------ + None + """ + # Return the path to the movie file. + movie_file = None + return movie_file + + def create_gamhelio_movie(args): """Create a gamhelio movie. @@ -1248,7 +1302,7 @@ def create_gamhelio_movie(args): Parameters ---------- args : dict - Dictionary of command-line options. + Dictionary of command-line arguments. Returns ------- @@ -1263,7 +1317,7 @@ def create_gamhelio_movie(args): debug = args.debug pictype = args.pictype - # Check that a valid plot code was provided. + # Check that a valid picture type code was provided. if pictype not in valid_pictypes: raise TypeError(f"Invalid plot type ({pictype})!") @@ -1278,6 +1332,10 @@ def create_gamhelio_movie(args): movie_file = create_pic4_movie(args) elif pictype == "pic5": movie_file = create_pic5_movie(args) + elif pictype == "pic6": + movie_file = create_pic6_movie(args) + elif pictype == "pic7": + movie_file = create_pic7_movie(args) else: raise TypeError(f"Invalid plot type ({pictype})!") if debug: @@ -1295,17 +1353,14 @@ def main(): # Parse the command-line arguments. args = parser.parse_args() - debug = args.debug - verbose = args.verbose - if debug: + if args.debug: print(f"args = {args}") # Create the movie based on the selected picture type. movie_file = create_gamhelio_movie(args) - if verbose: + if args.verbose: print(f"The movie is available in {movie_file}.") if __name__ == "__main__": - """Begin main program.""" main() From a9ad5cd07892072764a52c1b9121a5d05a9e6e85 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Mon, 25 Sep 2023 14:03:57 -0600 Subject: [PATCH 064/365] Use do loops in Fedder and Zhang precipitation subroutines. Fedder code needs further optimization. --- src/remix/mixconductance.F90 | 214 ++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 105 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index e34b9b4f..afa0ccf4 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -292,13 +292,12 @@ module mixconductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - real(rp) :: Redge, Rmin, Rmin2, Rmax, rfac real(rp) :: signOfY, signOfJ real(rp) :: Rout = 6.D0, Rin = 1.2D0 real(rp) :: rhoFactor = 3.3D-24*0.5D0 - - tmpC = 0.D0 - tmpD = 0.D0 + real(rp) :: rPolarBound = 20.0D0*pi/180.D0, rEquatBound = 30.0D0*pi/180.0D0, rLowLimit = 0.02D0 + real(rp) :: D,Cs,Pe,Ne,dE,phi0,E0,aRes,dEc + integer :: i,j if (St%hemisphere==NORTH) then signOfY = -1 @@ -312,71 +311,68 @@ module mixconductance ! fills in rampFactor if (conductance%doRamp) then - call conductance_ramp(conductance,G,20.0D0*pi/180.D0,30.0D0*pi/180.0D0,0.02D0) + call conductance_ramp(conductance,G,rPolarBound,rEquatBound,rLowLimit) else conductance%rampFactor = 1.0D0 - end if - - if (conductance%doChill) then - ! MHD density replaced with gallagher where it's lower - ! and temperature changed correspondingly - tmpD = max(G%D0*Mp_cgs,St%Vars(:,:,DENSITY)) - tmpC = St%Vars(:,:,SOUND_SPEED)*sqrt(St%Vars(:,:,DENSITY)/tmpD) - else - tmpD = St%Vars(:,:,DENSITY) - tmpC = St%Vars(:,:,SOUND_SPEED) - end if + endif - conductance%E0 = conductance%alpha*Mp_cgs*heFrac*erg2kev*tmpC**2*conductance%RampFactor - conductance%phi0 = sqrt(kev2erg)/(heFrac*Mp_cgs)**1.5D0*conductance%beta*tmpD*sqrt(conductance%E0)*conductance%RampFactor - ! resistence out of the ionosphere is 2*rout resistence into the - ! ionosphere is 2*rin outward current is positive - where ( signOfJ*St%Vars(:,:,FAC) >=0. ) - conductance%aRes = 2.D0*Rout - elsewhere - conductance%aRes = 2.D0*Rin - end where - - ! Density floor to limit characteristic energy. See Wiltberger et al. 2009 for details. - where (tmpD < rhoFactor*conductance%euvSigmaP) - tmpD = rhoFactor*conductance%euvSigmaP - end where - conductance%deltaE = (heFrac*Mp_cgs)**1.5D0/eCharge*1.D-4*sqrt(erg2kev)*conductance%R*conductance%aRes*signOfJ*(St%Vars(:,:,FAC)*1.e-6)*sqrt(conductance%E0)/tmpD + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,D,Cs,dE,phi0,E0,aRes,dEc) + do j=1,G%Nt + do i=1,G%Np + if (conductance%doChill) then + ! MHD density replaced with gallagher where it's lower + ! and temperature changed correspondingly + D = max(G%D0(i,j)*Mp_cgs, St%Vars(i,j,DENSITY)) ! [g/cm^3] + Cs = St%Vars(i,j,SOUND_SPEED)*sqrt(St%Vars(i,j,DENSITY)/D) ! [cm/s] + else + D = St%Vars(i,j,DENSITY) + Cs = St%Vars(i,j,SOUND_SPEED) + endif + ! Mean energy from MHD electron fluxes in [keV]. E_avg = 2*kT for Maxwellian. + E0 = conductance%alpha*Mp_cgs*heFrac*erg2kev*Cs**2*conductance%rampFactor(i,j) + conductance%E0 (i,j) = E0 + ! Thermal number flux from MHD electron fluxes in [#/cm^2/s]. + phi0 = sqrt(kev2erg)/(heFrac*Mp_cgs)**1.5D0*conductance%beta*D*sqrt(E0)*conductance%rampFactor(i,j) + conductance%phi0(i,j) = phi0 + + ! resistence out of the ionosphere is 2*rout resistence into the + ! ionosphere is 2*rin outward current is positive + if( signOfJ*St%Vars(i,j,FAC) >=0. ) then + aRes = 2.D0*Rout + else + aRes = 2.D0*Rin + endif - ! limit the max potential energy drop to 20 [keV] - conductance%deltaE = min(maxDrop,conductance%deltaE) + ! Density floor to limit characteristic energy. See Wiltberger et al. 2009 for details. + D = min(D,rhoFactor*conductance%euvSigmaP(i,j)) + ! Original form for ref: + ! (heFrac*Mp_cgs)**1.5D0/eCharge*1.D-4*sqrt(erg2kev)*conductance%R*aRes*signOfJ*(St%Vars(:,:,FAC)*1.D-6)*sqrt(E0)/D + dE = (heFrac*Mp_cgs)**1.5D0/eCharge*1.D-4*sqrt(erg2kev)*conductance%R*aRes*signOfJ*(St%Vars(i,j,FAC)*1.D-6)*sqrt(E0)/D + conductance%deltaE(i,j) = dE + dEc = min(dE,maxDrop) - ! floor on total energy - St%Vars(:,:,AVG_ENG) = max(conductance%E0 + conductance%deltaE,1.D-8) - - where ( conductance%deltaE > 0. ) - St%Vars(:,:,NUM_FLUX) = conductance%phi0*(8.D0-7.D0*exp(-conductance%deltaE/7.D0/conductance%E0)) - elsewhere - St%Vars(:,:,NUM_FLUX) = conductance%phi0*exp(conductance%deltaE/conductance%E0) - end where + St%Vars(i,j,AVG_ENG) = max( E0+dEc, eTINY) + if(dEc>0.) then + St%Vars(i,j,NUM_FLUX) = phi0*(8.D0-7.D0*exp(-dEc/7.D0/E0)) + else + St%Vars(i,j,NUM_FLUX) = phi0*exp(dEc/E0) + endif + St%Vars(i,j,DELTAE) = conductance%deltaE(i,j) ! [kV] + enddo ! i + enddo ! j end subroutine conductance_fedder95 - subroutine conductance_zhang15(conductance,G,St,dorcmO) + subroutine conductance_zhang15(conductance,G,St) ! Derive electron precipitation energy flux and avg energy using the nonlinear Fridman-Lemaire relation [Zhang et al., 2014JA020615]. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - logical, optional, intent(in) :: dorcmO real(rp) :: signOfY, signOfJ - logical :: dorcm - real(rp), dimension(G%Np,G%Nt) :: Pe_MHD, Ne_MHD, Pe_RMD, Ne_RMD, ceV - - if(present(dorcmO)) then - dorcm = dorcmO - else ! default is NOT use RCM thermal flux for mono derivation. - dorcm = .false. - endif - - tmpC = 0.D0 - tmpD = 0.D0 - JF0 = 0.D0 + real(rp) :: D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT + integer :: i,j if (St%hemisphere==NORTH) then signOfY = -1 @@ -387,62 +383,70 @@ module mixconductance else stop 'Wrong hemisphere label. Stopping...' endif - - if (conductance%doChill) then - ! MHD density replaced with gallagher where it's lower - ! and temperature changed correspondingly - tmpD = max(G%D0*Mp_cgs,St%Vars(:,:,DENSITY)) ! [g/cm^3] - tmpC = St%Vars(:,:,SOUND_SPEED)*sqrt(St%Vars(:,:,DENSITY)/tmpD) ! [cm/s] - else - tmpD = St%Vars(:,:,DENSITY) - tmpC = St%Vars(:,:,SOUND_SPEED) - end if - - call conductance_auroralmask(conductance,G,signOfY) - - Pe_MHD = 0.1/MIXgamma*alpha_RCM*tmpD*tmpC**2 ! electron pressure from MHD side in [Pa]. 0.1 is to convert [g/cm^3]*[cm/s]^2=[g/cm/s^2] to [Pa]. - Ne_MHD = tmpD/(Mp_cgs*heFrac)*1.0D6 ! electron number density from MHD side in [/m^3]. - if(.not.dorcm) then ! default Zhang15 using MHD thermal flux only. - conductance%E0 = 2.0/kev2J*Pe_MHD/Ne_MHD ! Mean energy from MHD electron fluxes in [keV]. E_avg = 2*kT for Maxwellian. - conductance%phi0 = beta_RCM*sqrt(Pe_MHD*Ne_MHD/(2.0D-3*pi*Me_cgs))*1.0D-4 ! Thermal number flux from MHD electron fluxes in [#/cm^2/s]. - else ! Trigger this part by including dorcm=.true. when calling zhang15. - ! similarly, E0 is a ratio and should NOT be merged. - ! Derive it from merged pressure and density instead. - ! See kaiju wiki for derivations of the coefficients. - ! https://bitbucket.org/aplkaiju/kaiju/wiki/userGuide/derivation_of_precipitation - Pe_RMD = gtype_RCM*St%Vars(:,:,IM_EPRE) + (1.0-gtype_RCM)*Pe_MHD ! Merged electron pressure in [Pa]. - Ne_RMD = gtype_RCM*St%Vars(:,:,IM_EDEN) + (1.0-gtype_RCM)*Ne_MHD ! Merged electron number density in [/m^3]. - conductance%E0 = 2.0/kev2J*Pe_RMD/Ne_RMD ! Mean energy from merged electron fluxes in [keV]. - conductance%phi0 = beta_RCM*sqrt(Pe_RMD*Ne_RMD/(2.0D-3*pi*Me_cgs))*1.0D-4 ! Thermal number flux from merged electron fluxes in [#/cm^2/s]. + + if (doDrift) then + ! Use artificial drift to get dawn-preferred diffuse electron precipitation + ! when only using MHD information to derive it. + call conductance_auroralmask(conductance,G,signOfY) + else + ! conductance%drift should be turned off when using RCM for diffuse + conductance%drift = 1.0 endif - JF0 = min( 1.D-4*signOfJ*(St%Vars(:,:,FAC)*1.e-6)/eCharge/(conductance%phi0), RM*0.99 ) + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT) + do j=1,G%Nt + do i=1,G%Np + if (conductance%doChill) then + ! MHD density replaced with gallagher where it's lower + ! and temperature changed correspondingly + D = max(G%D0(i,j)*Mp_cgs, St%Vars(i,j,DENSITY)) ! [g/cm^3] + Cs = St%Vars(i,j,SOUND_SPEED)*sqrt(St%Vars(i,j,DENSITY)/D) ! [cm/s] + else + D = St%Vars(i,j,DENSITY) + Cs = St%Vars(i,j,SOUND_SPEED) + endif + ! electron pressure from MHD side in [Pa] + ! 0.1 is to convert [g/cm^3]*[cm/s]^2=[g/cm/s^2] to [Pa]. + Pe = 0.1/MIXgamma*alpha_RCM(i,j)*D*(Cs**2) + ! electron number density from MHD side in [/m^3]. + Ne = D/(Mp_cgs*heFrac)*1.0D6 + ! Mean energy from MHD electron fluxes in [keV]. E_avg = 2*kT for Maxwellian. + kT = Pe/Ne/kev2J + conductance%E0 (i,j) = 2.0*kT + ! Thermal number flux from MHD electron fluxes in [#/cm^2/s]. + phi0 = beta_RCM(i,j)* sqrt(Pe*Ne/(2.0D-3*PI*Me_cgs))*1.0D-4 + conductance%phi0(i,j) = phi0 - !NOTE: conductance%drift should be turned off when using RCM for diffuse - if (.not. doDrift) then - conductance%drift = 1.0 !Remove dep. - endif + ! Note JF0 may go inf where phi0/Pe_MHD/Ne_MHD is zero. + J2eF0 = min( 1.D-4*signOfJ*(St%Vars(i,j,FAC)*1.D-6)/(eCharge*phi0), RM(i,j)*0.99 ) + ! NonLinear Fridman-Lemaire relation: + ! eV = 2*kB*Te + eV*(1-exp(-eV/(RM-1)/kB/Te))/(1-(1-1/RM)*exp(-eV/(RM-1)/kB/Te)), when + ! 1<=J/e/F0<=RM. + if (J2eF0>1.0) then + dE = kT*(RM(i,j)-1.D0)*dlog((RM(i,j)-1.D0)/(RM(i,j)-J2eF0)) + conductance%deltaE(i,j) = dE + St%Vars(i,j,Z_NFLUX) = J2eF0*phi0 + else + conductance%deltaE(i,j) = 0.0 + St%Vars(i,j,Z_NFLUX) = phi0*conductance%drift(i,j) + endif - where ( JF0 > 1. ) - ! limit the max potential energy drop to 20 [keV] - ! deltaE, or eV=kB*Te*(RM-1)*ln((RM-1)/(RM-J/e/F0)) when 1<=J/e/F0<=RM. - conductance%deltaE = min( 0.5*conductance%E0*(RM - 1.D0)*dlog((RM-1.D0)/(RM-JF0)),maxDrop) - St%Vars(:,:,Z_NFLUX) = JF0*conductance%phi0 - elsewhere - conductance%deltaE = 0. - St%Vars(:,:,Z_NFLUX) = conductance%phi0*conductance%drift - end where + ! limit the max potential energy drop to maxDrop (20 [keV]) + ! Use capped dE to calculate the mean energy. + ! eV2kT = exp(-eV/(RM-1)/kB/Te) + eV2kT = exp( -min(dE,maxDrop)/(kT*(RM(i,j)-1.D0)) ) + ! Floor the mean energy. + ! Note in the original code of Zhang, EAVG is simply 2*kT+dE, a good enough approximation. + St%Vars(i,j,Z_EAVG) = max( 2.0*kT+min(dE,maxDrop)*(1.D0-eV2kT)/(1.D0-(1.D0-1.D0/RM(i,j))*eV2kT), eTINY) - ! floor on total energy - ! Eavg=2*kB*Te + eV*(1-exp(-eV/(RM-1)/kB/Te))/(1-(1-1/RM)*exp(-eV/(RM-1)/kB/Te)) - ceV = exp(-conductance%deltaE/(0.5*conductance%E0*(RM-1))) - St%Vars(:,:,Z_EAVG) = max(conductance%E0 + conductance%deltaE*(1-ceV)/(1-(1-1/RM)*ceV),1.D-8) + ! Apply Zhang15 precipitation to main arrays + St%Vars(i,j,DELTAE) = conductance%deltaE(i,j) ! [kV] + St%Vars(i,j,AVG_ENG) = St%Vars(i,j,Z_EAVG) ! [keV] + St%Vars(i,j,NUM_FLUX) = St%Vars(i,j,Z_NFLUX) ! [#/cm^2/s] + enddo ! i + enddo ! j - ! Apply Zhang15 precipitation to main arrays - St%Vars(:,:,AVG_ENG) = St%Vars(:,:,Z_EAVG) ! [keV] - St%Vars(:,:,NUM_FLUX) = St%Vars(:,:,Z_NFLUX)! [#/cm^2/s] - St%Vars(:,:,DELTAE) = conductance%deltaE ! [kV] - end subroutine conductance_zhang15 subroutine conductance_linmrg(conductance,G,St) From f54299929e5ac00b72d0ad78ad0fe08eff34b5bf Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Mon, 25 Sep 2023 15:00:33 -0600 Subject: [PATCH 065/365] Add missing calculation of spatially varying beta before calling linmono. --- src/remix/mixconductance.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index afa0ccf4..98e7e599 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -471,6 +471,9 @@ module mixconductance !Get RCM grid weighting: 1=RCM and 0=MHD call conductance_IM_GTYPE(G,St) + ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need IM_GTYPE. + call conductance_alpha_beta(conductance,G,St) + ! Derive mono using the linearized FL relation. call conductance_linmono(conductance,G,St) From 51bac3f50d1e098ad3a71ad6589e501990c24009 Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Mon, 25 Sep 2023 16:36:28 -0600 Subject: [PATCH 066/365] Move wave model functions/parameters to module lossutils. --- src/rcm/lossutils.F90 | 85 ++++++++++++++++++++++++++++++++++++- src/rcm/rcm_subs.F90 | 97 ++++--------------------------------------- 2 files changed, 92 insertions(+), 90 deletions(-) diff --git a/src/rcm/lossutils.F90 b/src/rcm/lossutils.F90 index 912eb561..829ce7fe 100644 --- a/src/rcm/lossutils.F90 +++ b/src/rcm/lossutils.F90 @@ -7,6 +7,15 @@ MODULE lossutils USE math, ONLY : SmoothOpTSC,SmoothOperator33,ClampValue,LinRampUp implicit none + !Parameters for electron wave models + real(rprec), parameter :: Lo = 8.0 !Outer L, L > 8Re, strong scattering in charge + real(rprec), parameter :: Li = 7.0 !Middle L, L < 7Re, wave models in charge + real(rprec), parameter, private :: kev0 = 1.1 !Min value to allow [keV] + real(rprec), parameter, private :: nhigh = 100.D0 ! [/cc] ne>nhigh indicates inside plasmasphere. + real(rprec), parameter, private :: nlow = 10.D0 ! [/cc] ne= maxval(Eki)) then eL = Ne !use Ek maximum eU = Ne @@ -380,4 +390,75 @@ MODULE lossutils tau = tau_av/g_MLT/h_KP lambda = 1.D0/tau ! 1/s END FUNCTION RatefnC_tau_h16 + + FUNCTION LossR8_IMAG(xx,yy,alamx,vmx,nex,kpx) + REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,nex,kpx + REAL (rprec), dimension(2) :: LossR8_IMAG + + REAL (rprec) :: MLT,K,L,E,tau_c,tau_h + REAL (rprec) :: E0,tScl !Energy min + + K = abs(alamx*vmx*1.0e-3) !Energy [keV] + L = sqrt(xx**2+yy**2) + + !Calculate E [MeV] to evaluate wave model + if (K <= kev0) then + E = kev0*(1.0e-3) !Energy [MeV] + !Define a scaling factor to multiply tau (lifetime) + !Lower energy particles are slower which increases lifetime + tScl = kev2V(kev0)/kev2V(K) + else + E = K*(1.0e-3) !Energy [MeV] + tScl = 1.0 !No change needed + endif + + MLT = atan2(yy,xx)/pi*12.D0+12.D0 + if (nexnhigh) then + ! Region inside the plasmasphere, wave candidates: Hiss + tau_h = tScl*RatefnC_tau_h16(MLT,E,L,kpx) + LossR8_IMAG(1) = 1.0/tau_h + LossR8_IMAG(2) = HISS !wave type number for hiss + else + ! nlow <= nex <= nhigh, at the plume region, wave candidates: + ! Chorus and Hiss + tau_c = tScl*RatefnDW_tau_c(kpx,MLT,L,E) + tau_h = tScl*RatefnC_tau_h16(MLT,E,L,kpx) + + LossR8_IMAG(1) = (dlog(nhigh/nex)/tau_c + dlog(nex/nlow)/tau_h)/dlog(nhigh/nlow) ! use density-weighted loss rate + LossR8_IMAG(2) = (dlog(nhigh/nex)*CHORUS + dlog(nex/nlow)*HISS)/dlog(nhigh/nlow) + endif + + END FUNCTION LossR8_IMAG + + FUNCTION LossR8_PSHEET(alamx,vmx,beqx,losscx) + REAL (rprec), INTENT (IN) :: alamx,vmx,beqx,losscx + REAL (rprec) :: LossR8_PSHEET + REAL (rprec) :: TauSS + + TauSS = RatefnC_tau_s(alamx,vmx,beqx,losscx) + LossR8_PSHEET = 1.0/TauSS + LossR8_PSHEET = LossR8_PSHEET !SS rate + END FUNCTION LossR8_PSHEET + + !Calculate speed (Re/s) from energy [keV] for ELECTRONS + FUNCTION kev2V(keV) result(V) + use kdefs, only : Re_cgs,mec2 + REAL (rprec), INTENT (IN) :: keV + REAL (rprec) :: V + + REAL (rprec) :: E,gammar + + E = keV*1.0e-3 !Energy [MeV] + gammar = 1.0 + E/mec2 + + V = vc_cgs*sqrt(1.0-1.0/gammar**2)/Re_cgs ! Re/s + + END FUNCTION kev2V + END MODULE lossutils diff --git a/src/rcm/rcm_subs.F90 b/src/rcm/rcm_subs.F90 index 730effdd..87f75081 100644 --- a/src/rcm/rcm_subs.F90 +++ b/src/rcm/rcm_subs.F90 @@ -3135,115 +3135,36 @@ FUNCTION Ratefn (xx,yy,alamx,vmx,beqx,losscx,nex,kpx,fudgxO,sinixO,birxO,xmfactO END FUNCTION Ratefn FUNCTION RatefnWM(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) - use lossutils, ONLY: RatefnC_tau_s, RatefnDW_tau_c,RatefnC_tau_h16 + use lossutils, ONLY : LossR8_PSHEET, LossR8_IMAG, Lo, Li, SSCATTER IMPLICIT NONE - !Wave type: Hiss: 1.0; Chorus: 2.0 (Inner Mag,L<7); strong scattering: 3.0 (Plasmasheet,L>8); Blending zone, 78); Blending zone, 7 Lo) then !Only plasma sheet - rPS = LossR8_PSHEET(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + rPS = LossR8_PSHEET(alamx,vmx,beqx,losscx) RatefnWM(1) = rPS - RatefnWM(2) = 3.0 + RatefnWM(2) = SSCATTER !wave type number for strong scattering else if (L < Li) then !IMAG only - rIMAG = LossR8_IMAG(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + rIMAG = LossR8_IMAG(xx,yy,alamx,vmx,nex,kpx) RatefnWM(1) = rIMAG(1) RatefnWM(2) = rIMAG(2) else !Middle - rIMAG = LossR8_IMAG (xx,yy,alamx,vmx,nex,kpx,beqx,losscx) - rPS = LossR8_PSHEET(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) + rIMAG = LossR8_IMAG (xx,yy,alamx,vmx,nex,kpx) + rPS = LossR8_PSHEET(alamx,vmx,beqx,losscx) wIMAG = RampDown(L,Li,Lo-Li) !Ramp from 1 to 0 RatefnWM(1) = wIMAG*rIMAG(1) + (1-wIMAG)*rPS - RatefnWM(2) = 3.0-wIMAG + RatefnWM(2) = SSCATTER-wIMAG endif - contains - - FUNCTION LossR8_IMAG(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) - REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,nex,kpx,beqx,losscx - REAL (rprec), dimension(2) :: LossR8_IMAG - - REAL (rprec) :: nhigh,nlow,MLT,K,E,tau_c,tau_h - REAL (rprec) :: kev0,E0,tScl !Energy min - - !NOTE: These values should be moved somewhere more appropriate - kev0 = 1.1 !Min value to allow [keV] - nhigh = 100.D0 ! [/cc] ne>nhigh indicates inside plasmasphere. - nlow = 10.D0 ! [/cc] nenhigh) then - ! Region inside the plasmasphere, wave candidates: Hiss - tau_h = tScl*RatefnC_tau_h16(MLT,E,L,kpx) - LossR8_IMAG(1) = 1.0/tau_h - LossR8_IMAG(2) = 1.0 !wave type number for hiss - else - ! nlow <= nex <= nhigh, at the plume region, wave candidates: - ! Chorus and Hiss - tau_c = tScl*RatefnDW_tau_c(kpx,MLT,L,E) - tau_h = tScl*RatefnC_tau_h16(MLT,E,L,kpx) - - LossR8_IMAG(1) = (dlog(nhigh/nex)/tau_c + dlog(nex/nlow)/tau_h)/dlog(nhigh/nlow) ! use density-weighted loss rate - LossR8_IMAG(2) = (dlog(nhigh/nex)*2.0 + dlog(nex/nlow)*1.0)/dlog(nhigh/nlow) - endif - - END FUNCTION LossR8_IMAG - - FUNCTION LossR8_PSHEET(xx,yy,alamx,vmx,nex,kpx,beqx,losscx) - REAL (rprec), INTENT (IN) :: xx,yy,alamx,vmx,nex,kpx,beqx,losscx - REAL (rprec) :: LossR8_PSHEET - REAL (rprec) :: TauSS - - TauSS = RatefnC_tau_s(alamx,vmx,beqx,losscx) - LossR8_PSHEET = 1.0/TauSS - LossR8_PSHEET = LossR8_PSHEET !SS rate - END FUNCTION LossR8_PSHEET - - !Calculate speed (Re/s) from energy [keV] for ELECTRONS - FUNCTION kev2V(keV) result(V) - REAL (rprec), INTENT (IN) :: keV - REAL (rprec) :: V - - REAL (rprec) :: E,gammar - - E = keV*1.0e-3 !Energy [MeV] - gammar = 1.0 + E/mec2 - - V = vc_cgs*sqrt(1.0-1.0/gammar**2)/Re_cgs ! Re/s - - END FUNCTION kev2V - END FUNCTION RatefnWM !------------------------------------- From d2ccffca6fc632da342ccc2390b62745583f8db2 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Wed, 27 Sep 2023 16:39:42 -0400 Subject: [PATCH 067/365] Finalized updated pic1, 2, 3 for CCMC. --- kaipy/gamhelio/helioViz.py | 695 ++++++++++++++++++++-------------- scripts/quicklook/heliopic.py | 68 ++-- 2 files changed, 450 insertions(+), 313 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 57228b49..59a7fcd6 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -93,29 +93,30 @@ def GetSizeBds(pic): def PlotEqMagV( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, - MJDc=None, MJD_plot=None, hgsplot=False + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind speed in the solar equatorial plane. Plot solar wind speed in the solar equatorial plane. By default, the plot is produced in the GH(MJDc) frame (the gamhelio frame used for the - simulation results). If MJD_plot is specified, MJDc must also be specified. - In that case, the coordinates are mapped from the GH(MJDc) frame to the - HGS(MJD_plot) frame. + simulation results). If hgsplot is True, MJDc and MJD_plot must be + specified. In that case, the coordinates are mapped from the GH(MJDc) frame + to the HGS(MJD_plot) frame. The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -130,21 +131,20 @@ def PlotEqMagV( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting doDeco : bool - If true, add axis labels to the plot. - MJDc : float - MJD used for the coordinate GH frame of the simulation. - MJD_plot : float - MJD to use for the HGS frame of the plot. + If True, add axis labels and other decorations to the plot hgsplot : bool - If true, plot in HGS(MJD_plot) frame. + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- MagV : np.array of float - Data for solar wind speed in equatorial plane, same shape as the - equatorial grid in the gamhelio results. + Data values used in plot Raises ------ @@ -158,21 +158,21 @@ def PlotEqMagV( AxCB.clear() kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. MagV = gsph.eqMagV(nStp) - # Plot the data in the solar equatorial plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Load the equatorial grid cell vertex coordinates (originially in the - # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values - # to 0 since we are using the solar equatorial plane. + # Load the equatorial grid cell vertex coordinates (originally in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. All z values are + # set to 0 since we are plotting in the equatorial (XY) plane. zzi = np.zeros_like(gsph.xxi) c = SkyCoord( -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, @@ -214,7 +214,7 @@ def PlotEqMagV( def PlotjMagV( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, jidx=-1, - MJDc=None, MJD_plot=None, hgsplot=False + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind speed in a specific j plane. @@ -256,12 +256,12 @@ def PlotjMagV( If true, add axis labels to the plot. jidx : int Index of j-plane to plot. + hgsplot : bool + If true, plot in HGS(MJD_plot) frame. MJDc : float MJD used for the coordinate GH frame of the simulation. MJD_plot : float MJD to use for the HGS frame of the plot. - hgsplot : bool - If true, plot in HGS(MJD_plot) frame. Returns ------- @@ -281,11 +281,11 @@ def PlotjMagV( AxCB.clear() kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. MagV = gsph.jMagV(nStp, jidx=jidx) # Plot the data. @@ -311,29 +311,30 @@ def PlotjMagV( def PlotMerMagV( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, indx=(None, None), - MJDc=None, MJD_plot=None, hgsplot=False + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind speed in a meridional plane. Plot solar wind speed in a meridional plane. By default, the plot is produced in the GH(MJDc) frame (the gamhelio frame used for the - simulation results). If MJD_plot is specified, MJDc must also be specified. - In that case, the coordinates are mapped from the GH(MJDc) frame to the - HGS(MJD_plot) frame. + simulation results). If hgsplot is True, MJDc and MJD_plot must be + specified. In that case, the coordinates are mapped from the GH(MJDc) frame + to the HGS(MJD_plot) frame. The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -348,23 +349,22 @@ def PlotMerMagV( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting doDeco : bool - If true, add axis labels to the plot. + If True, add axis labels and other decorations to the plot indx : tuple of 2 int or float Index or angle of meridional slice to plot - MJDc : float - MJD used for the coordinate GH frame of the simulation. - MJD_plot : float - MJD to use for the HGS frame of the plot. hgsplot : bool - If true, plot in HGS(MJD_plot) frame. + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- Vr, Vl : np.array of float - Data for solar wind speed in meridional plane plane, for right and - left parts of plot. + Data values used in plot Raises ------ @@ -378,7 +378,7 @@ def PlotMerMagV( AxCB.clear() kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -393,23 +393,21 @@ def PlotMerMagV( else: phi = "" - # Fetch the data at the specified step. - # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) - # frame. + # Fetch the data. + # r(ight) and l(eft) data arrays Vr, Vl = gsph.MerMagV(nStp, indx=indx) # Fetch the coordinates of the grid cell corners in the specified - # meridional plane, in the GH(mjd_gh) frame. + # meridional plane, in the GH(MJDc) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - # Plot the data in the meridional plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: # Load the meridional grid cell vertex coordinates (originially in the - # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values - # to 0 since we are using the solar equatorial plane. + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. cr = SkyCoord( -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, frame=frames.HeliographicStonyhurst, @@ -463,31 +461,33 @@ def PlotMerMagV( def PlotMerDNorm( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, indx=(None, None), - MJDc=None, MJD_plot=None, hgsplot=False + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot normalized solar wind number density in a meridional plane. Plot normalized solar wind number density in a meridional plane. By default, the plot is produced in the GH(MJDc) frame (the gamhelio frame - used for the simulation results). If MJD_plot is specified, MJDc must also - be specified. In that case, the coordinates are mapped from the GH(MJDc) - frame to the HGS(MJD_plot) frame. + used for the simulation results). If hgsplot is True, MJDc and MJD_plot + must be specified. In that case, the coordinates are mapped from the + GH(MJDc) frame to the HGS(MJD_plot) frame. - The density is normalized with the factor (r/r0)**2. + The density is normalized with the factor (r/r0)**2, where r0 is 21.5 Rsun + (the inner edge of the gamhelio grid). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -502,23 +502,22 @@ def PlotMerDNorm( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting. doDeco : bool - If true, add axis labels to the plot. + If True, add axis labels to the plot. indx : tuple of 2 int or float Index or angle of meridional slice to plot + hgsplot : bool + If True, plot in HGS(MJD_plot) frame. MJDc : float MJD used for the coordinate GH frame of the simulation. MJD_plot : float MJD to use for the HGS frame of the plot. - hgsplot : bool - If true, plot in HGS(MJD_plot) frame. Returns ------- Dr, Dl : np.array of float - Data for solar wind normalized number density in meridional plane, for - right and left parts of plot. + Data values used in plot Raises ------ @@ -532,7 +531,7 @@ def PlotMerDNorm( AxCB.clear() kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [$cm^{-3}$]", cM=DCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -547,23 +546,21 @@ def PlotMerDNorm( else: phi = "" - # Fetch the data at the specified step. - # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) - # frame. + # Fetch the data. + # r(ight) and l(eft) data arrays Dr, Dl = gsph.MerDNrm(nStp, indx=indx) # Fetch the coordinates of the grid cell corners in the specified - # meridional plane, in the GH(mjd_gh) frame. + # meridional plane, in the GH(MJDc) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - # Plot the data in the meridional plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: # Load the equatorial grid cell vertex coordinates (originially in the - # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values - # to 0 since we are using the solar equatorial plane. + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. cr = SkyCoord( -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, frame=frames.HeliographicStonyhurst, @@ -625,29 +622,31 @@ def PlotMerBrNorm( indx=(None, None), MJDc=None, MJD_plot=None, hgsplot=False ): - """Plot normalized solar wind temperature in a meridional plane. + """Plot normalized solar wind radial magnetic field in a meridional plane. - Plot normalized solar wind temperature in a meridional plane. By + Plot normalized solar wind radial magnetic field in a meridional plane. By default, the plot is produced in the GH(MJDc) frame (the gamhelio frame - used for the simulation results). If MJD_plot is specified, MJDc must also - be specified. In that case, the coordinates are mapped from the GH(MJDc) - frame to the HGS(MJD_plot) frame. + used for the simulation results). If hgsplot is True, MJDc and MJD_plot + must be specified. In that case, the coordinates are mapped from the + GH(MJDc) frame to the HGS(MJD_plot) frame. - The temperature is normalized with the factor (r/r0)**2. + The temperature is normalized with the factor (r/r0)**2, where r0 is + 21.5 Rsun (the inner edge of the gamhelio grid). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -662,23 +661,22 @@ def PlotMerBrNorm( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting. doDeco : bool - If true, add axis labels to the plot. + If True, add axis labels to the plot. indx : tuple of 2 int or float Index or angle of meridional slice to plot + hgsplot : bool + If True, plot in HGS(MJD_plot) frame. MJDc : float MJD used for the coordinate GH frame of the simulation. MJD_plot : float MJD to use for the HGS frame of the plot. - hgsplot : bool - If true, plot in HGS(MJD_plot) frame. Returns ------- Br_r, Br_l : np.array of float - Data for solar wind normalized radial magnetic field in meridional - plane, for right and left parts of plot. + Data values used in plot Raises ------ @@ -692,7 +690,7 @@ def PlotMerBrNorm( AxCB.clear() kv.genCB(AxCB, vB, r'Radial MF $B_r (r/r_0)^2$ [nT]', cM=BCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -707,13 +705,12 @@ def PlotMerBrNorm( else: phi = "" - # Fetch the data at the specified step. - # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) - # frame. + # Fetch the data. + # r(ight) and l(eft) data arrays Br_r, Br_l = gsph.MerBrNrm(nStp, indx=indx) # Fetch the coordinates of the grid cell corners in the specified - # meridional plane, in the GH(mjd_gh) frame. + # meridional plane, in the GH(MJDc) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) # Compute the cell center coordinates (used to plot current sheet). @@ -724,14 +721,15 @@ def PlotMerBrNorm( yl_c = 0.25*(yl[:-1, :-1] + yl[:-1, 1:] + yl[1:, :-1] + yl[1:, 1:]) zl_c = 0.25*(zl[:-1, :-1] + zl[:-1, 1:] + zl[1:, :-1] + zl[1:, 1:]) - # Plot the data in the meridional plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Load the equatorial grid cell vertex coordinates (originially in the - # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values - # to 0 since we are using the solar equatorial plane. + # Load the equatorial grid cell vertex and center coordinates + # (originially in the GH(MJDc) frame) in the equivalent HGS(MJDc) + # frame. Set all z values to 0 since we are using the solar equatorial + # plane. cr = SkyCoord( -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, frame=frames.HeliographicStonyhurst, @@ -825,27 +823,29 @@ def PlotMerTemp( ): """Plot normalized solar wind temperature in a meridional plane. - Plot normalized solar wind temperature in a meridional plane. By - default, the plot is produced in the GH(MJDc) frame (the gamhelio frame - used for the simulation results). If MJD_plot is specified, MJDc must also - be specified. In that case, the coordinates are mapped from the GH(MJDc) - frame to the HGS(MJD_plot) frame. + Plot normalized solar wind temperature in a meridional plane. By default, + the plot is produced in the GH(MJDc) frame (the gamhelio frame used for the + simulation results). If hgsplot is True, MJDc and MJD_plot must be + specified. In that case, the coordinates are mapped from the GH(MJDc) frame + to the HGS(MJD_plot) frame. - The temperature is normalized with the factor (r/r0). + The temperature is normalized with the factor (r/r0), where r0 is 21.5 Rsun + (the inner edge of the gamhelio grid). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -860,23 +860,22 @@ def PlotMerTemp( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting. doDeco : bool - If true, add axis labels to the plot. + If True, add axis labels to the plot. indx : tuple of 2 int or float Index or angle of meridional slice to plot + hgsplot : bool + If True, plot in HGS(MJD_plot) frame. MJDc : float MJD used for the coordinate GH frame of the simulation. MJD_plot : float MJD to use for the HGS frame of the plot. - hgsplot : bool - If true, plot in HGS(MJD_plot) frame. Returns ------- Tempr, Templ : np.array of float - Data for solar wind normalized temperature in meridional plane, for - right and left parts of plot. + Data values used in plot Raises ------ @@ -890,7 +889,7 @@ def PlotMerTemp( AxCB.clear() kv.genCB(AxCB, vT, r'Temperature $T(r/r_0)$ [MK]', cM=TCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -905,23 +904,21 @@ def PlotMerTemp( else: phi = "" - # Fetch the data at the specified step. - # r(ight) is for +X plane and l(eft) is for -X plane in the GH(mjd_gh) - # frame. + # Fetch the data. + # r(ight) and l(eft) data arrays Tempr, Templ = gsph.MerTemp(nStp, indx=indx) # Fetch the coordinates of the grid cell corners in the specified - # meridional plane, in the GH(mjd_gh) frame. + # meridional plane, in the GH(MJDc) frame. xr, yr, zr, xl, yl, zl, r = gsph.MeridGridHalfs(*indx) - # Plot the data in the meridional plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: # Load the equatorial grid cell vertex coordinates (originially in the - # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values - # to 0 since we are using the solar equatorial plane. + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. cr = SkyCoord( -xr*u.Rsun, -yr*u.Rsun, zr*u.Rsun, frame=frames.HeliographicStonyhurst, @@ -974,31 +971,33 @@ def PlotMerTemp( def PlotEqD( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, - MJDc=None, MJD_plot=None, hgsplot=False + hgsplot=False, MJDc=None, MJD_plot=None ): - """Plot normalized density in the solar equatorial plane. + """Plot normalized solar wind number density in the solar equatorial plane. - Plot normalized density in the solar equatorial plane. By default, the plot - is produced in the GH(MJDc) frame (the gamhelio frame used for the - simulation results). If MJD_plot is specified, MJDc must also be specified. - In that case, the coordinates are mapped from the GH(MJDc) frame to the - HGS(MJD_plot) frame. + Plot normalized solar wind number density in the solar equatorial plane. By + default, the plot is produced in the GH(MJDc) frame (the gamhelio frame + used for the simulation results). If hgsplot is True, MJDc and MJD_plot + must be specified. In that case, the coordinates are mapped from the + GH(MJDc) frame to the HGS(MJD_plot) frame. - The density is normalized with the factor (r/r0)**2. + The density is normalized with the factor (r/r0)**2, where r0 is 21.5 Rsun + (the inner edge of the gamhelio grid). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -1013,21 +1012,20 @@ def PlotEqD( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting doDeco : bool - If true, add axis labels to the plot. - MJDc : float - MJD used for the coordinate GH frame of the simulation. - MJD_plot : float - MJD to use for the HGS frame of the plot. + If True, add axis labels and other decorations to the plot hgsplot : bool - If true, plot in HGS(MJD_plot) frame. + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- MagV : np.array of float - Data for number density in equatorial plane, same shape as the - equatorial grid in the gamhelio results. + Data values used in plot Raises ------ @@ -1041,21 +1039,21 @@ def PlotEqD( AxCB.clear() kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [cm$^{-3}$]", cM=DCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. NormD = gsph.eqNormD(nStp) - # Plot the data in the solar equatorial plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Load the equatorial grid cell vertex coordinates (originially in the - # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. Set all z values - # to 0 since we are using the solar equatorial plane. + # Load the equatorial grid cell vertex coordinates (originally in the + # GH(MJDc) frame) in the equivalent HGS(MJDc) frame. All z values are + # set to 0 since we are plotting in the equatorial (XY) plane. zzi = np.zeros_like(gsph.xxi) c = SkyCoord( -gsph.xxi*u.Rsun, -gsph.yyi*u.Rsun, zzi*u.Rsun, @@ -1166,7 +1164,7 @@ def PlotjD( AxCB.clear() kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2 [cm^{-3}]$", cM=DCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -1198,29 +1196,31 @@ def PlotEqTemp( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, MJDc=None, MJD_plot=None, hgsplot=False ): - """Plot normalized temperature in the solar equatorial plane. + """Plot normalized solar wind temperature in the solar equatorial plane. - Plot normalized temperature in the solar equatorial plane. By default, the - plot is produced in the GH(MJDc) frame (the gamhelio frame used for the - simulation results). If MJD_plot is specified, MJDc must also be specified. - In that case, the coordinates are mapped from the GH(MJDc) frame to the - HGS(MJD_plot) frame. + Plot normalized solar wind temperature in the solar equatorial plane. By + default, the plot is produced in the GH(MJDc) frame (the gamhelio frame + used for the simulation results). If hgsplot is True, MJDc and MJD_plot + must be specified. In that case, the coordinates are mapped from the + GH(MJDc) frame to the HGS(MJD_plot) frame. - The temperature is normalized with the factor (r/r0). + The temperature is normalized with the factor (r/r0), where r0 is 21.5 Rsun + (the inner edge of the gamhelio grid). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -1235,21 +1235,20 @@ def PlotEqTemp( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting doDeco : bool - If true, add axis labels to the plot. - MJDc : float - MJD used for the coordinate GH frame of the simulation. - MJD_plot : float - MJD to use for the HGS frame of the plot. + If True, add axis labels and other decorations to the plot hgsplot : bool - If true, plot in HGS(MJD_plot) frame. + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- Temp : np.array of float - Data for temperature in equatorial plane, same shape as the - equatorial grid in the gamhelio results. + Data values used in plot Raises ------ @@ -1263,14 +1262,14 @@ def PlotEqTemp( AxCB.clear() kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. Temp = gsph.eqTemp(nStp) - # Plot the data in the solar equatorial plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -1386,7 +1385,7 @@ def PlotjTemp( AxCB.clear() kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -1417,29 +1416,31 @@ def PlotEqBr( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, MJDc=None, MJD_plot=None, hgsplot=False ): - """Plot normalized radial magnetic field in the solar equatorial plane. + """Plot normalized solar wind radial magnetic field in the solar equatorial plane. - Plot normalized radial magnetic field in the solar equatorial plane. By - default, the plot is produced in the GH(MJDc) frame (the gamhelio frame - used for the simulation results). If MJD_plot is specified, MJDc must also - be specified. In that case, the coordinates are mapped from the GH(MJDc) - frame to the HGS(MJD_plot) frame. + Plot normalized solar wind radial magnetic field in the solar equatorial + plane. By default, the plot is produced in the GH(MJDc) frame (the gamhelio + frame used for the simulation results). If hgsplot is True, MJDc and + MJD_plot must be specified. In that case, the coordinates are mapped from + the GH(MJDc) frame to the HGS(MJD_plot) frame. - The radial magnetic field is normalized with the factor (r/r0)**2. + The radial magnetic field is normalized with the factor (r/r0)**2, where r0 + is 21.5 Rsun (the inner edge of the gamhelio grid). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) - frame. The difference is that: + frame. The difference is that, at any particular MJD: x (GH) = -x (HGS) y (GH) = -y (HGS) z (GH) = z (HGS) - The GH frame is defined at MJDc, meaning it is fixed in spatial orientation - at that time. The HGS frame is defined at MJD_plot, also producing a - (different) fixed spatial orientation. The conversion maps points in the - GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a rotation about - the z-axis, but also accounting for the Earth's orbit and other - astronommical and geodetic parameters. + The GH frame is defined at MJDc (the MJD of the central meridian in the WSA + file used for initial conditions), meaning it is fixed in spatial + orientation at that time. The HGS frame is defined at MJD_plot, also + producing a (different) fixed spatial orientation. The conversion maps + points in the GH(MJDc) frame to the HGS(MJD_plot) frame, which is almost a + rotation about the z-axis, but also accounting for the eccentricity of + Earth's orbit and other astronomical parameters. Parameters ---------- @@ -1454,21 +1455,19 @@ def PlotEqBr( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting doDeco : bool - If true, add axis labels to the plot. - MJDc : float - MJD used for the coordinate GH frame of the simulation. - MJD_plot : float - MJD to use for the HGS frame of the plot. + If True, add axis labels and other decorations to the plot hgsplot : bool - If true, plot in HGS(MJD_plot) frame. - + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- Br : np.array of float - Data for radial magnetic field in equatorial plane, same shape as the - equatorial grid in the gamhelio results. + Data values used in plot Raises ------ @@ -1482,14 +1481,14 @@ def PlotEqBr( AxCB.clear() kv.genCB(AxCB, vB, r"Radial MF $B_r (r/r_0)^2$ [nT]", cM=BCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. Br = gsph.eqNormBr(nStp) - # Plot the data in the solar equatorial plane. + # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: @@ -1607,7 +1606,7 @@ def PlotjBr( AxCB.clear() kv.genCB(AxCB, vB, r'Radial MF $B_r (r/r_0)^2$ [nT]', cM=BCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -1703,11 +1702,11 @@ def PlotEqBx( AxCB.clear() kv.genCB(AxCB, vB, r'MF $B_x$(r/r_0)^2$ [nT]', cM=BCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. Bx = gsph.eqBx(nStp) # Plot the data in the solar equatorial plane. @@ -1826,11 +1825,11 @@ def PlotEqBy( AxCB.clear() kv.genCB(AxCB, vB, r'MF $B_y(r/r_0)^2$ [nT]', cM=BCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step. + # Fetch the data. By = gsph.eqBy(nStp) # Plot the data in the solar equatorial plane. @@ -1955,7 +1954,7 @@ def PlotEqBz( AxCB.clear() kv.genCB(AxCB, vB, r'MF $B_z$ [nT]', cM=BCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() @@ -2013,8 +2012,7 @@ def PlotiSlMagV( ): """Plot solar wind speed at a specified radial slice. - Plot solar wind speed at a specified radial slice. The plot is created in - a GH frame rotating with the sidereal solar rotation period. + Plot solar wind speed at a specified radial slice. Parameters ---------- @@ -2029,16 +2027,16 @@ def PlotiSlMagV( AxCB : matplotlib.axes.Axes Axes object to use for color bar doClear : bool - If true, clear the plot Axes before further plotting. + If True, clear the plot Axes before further plotting. doDeco : bool - If true, add axis labels to the plot. + If True, add axis labels to the plot. idx : int Index of radial slice to plot. Returns ------- V : np.array of float - Data for speed in radial slice. + Data values used in plot Raises ------ @@ -2052,14 +2050,14 @@ def PlotiSlMagV( AxCB.clear() kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) - # Clear the plot Axes if needed. + # Clear the plot Axes. if doClear: Ax.clear() - # Fetch the data at the specified step and radial slice. + # Fetch the data. V = gsph.iSliceMagV(nStp, idx=idx) - # Get the latitude and longitude of the grid cells in the radial slice. + # Fetch the latitude and longitude of the grid cells in the radial slice. # This is in the GH(MJDc) frame. lat, lon = gsph.iSliceGrid(idx=idx) @@ -2069,6 +2067,7 @@ def PlotiSlMagV( # Set the plot boundaries. kv.SetAx(xyBds, Ax) + # Decorate the plots. if doDeco: Ax.set_xlabel('Longitude') Ax.set_ylabel('Latitude') @@ -2077,54 +2076,153 @@ def PlotiSlMagV( return V -#Plot Density at 1 AU -def PlotiSlD(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,idx=-1): - vD = kv.genNorm(D0Min, D0Max, doLog=False, midP=None) - if (AxCB is not None): - AxCB.clear() - kv.genCB(AxCB,vD,"Number density [cm-3]",cM=D0CM,Ntk=7) +def PlotiSlD( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 +): + """Plot solar wind number density at a specified radial slice. - if (doClear): + Plot solar wind number density at a specified radial slice. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If True, clear the plot Axes before further plotting. + doDeco : bool + If True, add axis labels to the plot. + idx : int + Index of radial slice to plot. + + Returns + ------- + D : np.array of float + Data values used in plot + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. + vD = kv.genNorm(D0Min, D0Max, doLog=False, midP=None) + + # Create the color bar. + if AxCB: + AxCB.clear() + kv.genCB(AxCB, vD, "Density [$cm^{-3}$]", cM=D0CM, Ntk=7) + + # Clear the plot Axes. + if doClear: Ax.clear() - D = gsph.iSliceD(nStp,idx=idx) - lat, lon = gsph.iSliceGrid(idx=idx) - Ax.pcolormesh(lon,lat,D,cmap=D0CM,norm=vD) - kv.SetAx(xyBds,Ax) + # Fetch the data. + D = gsph.iSliceD(nStp, idx=idx) - if (doDeco): + # Fetch the latitude and longitude of the grid cells in the radial slice. + # This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=idx) + + # Plot the data. + Ax.pcolormesh(lon, lat, D, cmap=D0CM, norm=vD) + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: Ax.set_xlabel('Longitude') Ax.set_ylabel('Latitude') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') + + # Return the data. return D -#Plot Br and current sheet (Br=0) at 1 AU -def PlotiSlBr(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,idx=-1): + +def PlotiSlBr( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 +): + """Plot solar wind radial magnetic field and current sheet at a specified radial slice. + + Plot solar wind radial magnetic field and current sheet at a specified + radial slice. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If True, clear the plot Axes before further plotting. + doDeco : bool + If True, add axis labels to the plot. + idx : int + Index of radial slice to plot. + + Returns + ------- + Br : np.array of float + Data values used in plot + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vB = kv.genNorm(B0Min, B0Max, doLog=False, midP=None) - if (AxCB is not None): + + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vB,"Radial magnetic field [nT]",cM=BCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vB, "Radial magnetic field [nT]", cM=BCM, Ntk=7) + + # Clear the plot Axes. + if doClear: Ax.clear() - Br = gsph.iSliceBr(nStp,idx=idx) + # Fetch the data. + Br = gsph.iSliceBr(nStp, idx=idx) + + # Fetch the latitude and longitude of the grid cells in the radial slice. + # This is in the GH(MJDc) frame. lat, lon = gsph.iSliceGrid(idx=idx) - #for contour cell-centered lon lat coordinates - lon_c = 0.25*( lon[:-1,:-1]+lon[:-1,1:]+lon[1:,:-1]+lon[1:,1:] ) - lat_c = 0.25*( lat[:-1,:-1]+lat[:-1,1:]+lat[1:,:-1]+lat[1:,1:] ) - Ax.pcolormesh(lon,lat,Br,cmap=BCM,norm=vB) - Ax.contour(lon_c, lat_c,Br,[0.],colors='black') - kv.SetAx(xyBds,Ax) + # Compute cell-centered lon lat coordinates for contour plot. + lon_c = 0.25*(lon[:-1, :-1] + lon[:-1, 1:] + lon[1:, :-1] + lon[1:, 1:]) + lat_c = 0.25*(lat[:-1, :-1] + lat[:-1, 1:] + lat[1:, :-1] + lat[1:, 1:]) - if (doDeco): + # Plot the data. + Ax.pcolormesh(lon, lat, Br, cmap=BCM, norm=vB) + + # Draw the Br=0 contour line representing the current sheet. + Ax.contour(lon_c, lat_c, Br, [0.], colors='black') + + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: Ax.set_xlabel('Longitude') Ax.set_ylabel('Latitude') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') - #for pic4 - Ax.set_aspect('equal') + + # Return the data. return Br #Plot Br and current sheet (Br=0) at certain distance set in iSliceBr @@ -2182,25 +2280,72 @@ def PlotiSlBrRotatingFrame(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True return Br -#Plot Temperature at 1 AU -def PlotiSlTemp(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True,idx=-1): +def PlotiSlTemp( + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 +): + """Plot solar wind temperature at a specified radial slice. + + Plot solar wind temperature at a specified radial slice. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + nStp : int + Index of simulation step to use in plot + xyBds : list of 4 float + Minimum and maximum values to plot for x- and y-axes + Ax : matplotlib.axes.Axes + Axes object to use for plot + AxCB : matplotlib.axes.Axes + Axes object to use for color bar + doClear : bool + If True, clear the plot Axes before further plotting. + doDeco : bool + If True, add axis labels to the plot. + idx : int + Index of radial slice to plot. + + Returns + ------- + Temp : np.array of float + Data values used in plot + + Raises + ------ + None + """ + # Create a normalizer object for the colorbar. vT = kv.genNorm(T0Min, T0Max, doLog=False, midP=None) - if (AxCB is not None): + # Create the color bar. + if AxCB: AxCB.clear() - kv.genCB(AxCB,vT,"Temperature [MK]",cM=TCM,Ntk=7) - if (doClear): + kv.genCB(AxCB, vT, "Temperature [MK]", cM=TCM, Ntk=7) + + # Clear the plot Axes. + if doClear: Ax.clear() - Temp = gsph.iSliceT(nStp,idx=idx) + # Fetch the data. + Temp = gsph.iSliceT(nStp, idx=idx) + + # Fetch the latitude and longitude of the grid cells in the radial slice. + # This is in the GH(MJDc) frame. lat, lon = gsph.iSliceGrid(idx=idx) - Ax.pcolormesh(lon,lat,Temp,cmap=TCM,norm=vT) - kv.SetAx(xyBds,Ax) + # Plot the data. + Ax.pcolormesh(lon, lat, Temp, cmap=TCM, norm=vT) - if (doDeco): + # Set the plot boundaries. + kv.SetAx(xyBds, Ax) + + # Decorate the plots. + if doDeco: Ax.set_xlabel('Longitude') Ax.set_ylabel('Latitude') + + # Return the data. return Temp #Plot Density as a function of distance diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 2d12fb77..ee6cc40a 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -430,45 +430,49 @@ def main(): # Now create the actual plots. if pic == "pic1": - # These are all equatorial plots in the XY plane of the modified - # HGS frame used by gamhelio. + # Equatorial plots in the XY plane of the modified HGS frame used + # by gamhelio. If hgsplot is True, then the plot frame is the true + # HGS frame at the time of the plot. hviz.PlotEqMagV(gsph, nStp, xyBds, AxL0, AxC1_0, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotEqD(gsph, nStp, xyBds, AxR0, AxC2_0, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotEqTemp(gsph, nStp, xyBds, AxL1, AxC1_1, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") else: fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic2": - # Meridional plots in the XZ plane of the modified HGS frame used - # by gamhelio. + # Meridional plots in the XZ plane of the modified HGS frame used + # by gamhelio. If hgsplot is True, then the plot frame is the true + # HGS frame at the time of the plot. hviz.PlotMerMagV(gsph, nStp, xyBds, AxL0, AxC1_0, indx=(None, pic2lon), - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotMerDNorm(gsph, nStp, xyBds, AxR0, AxC2_0, indx=(None, pic2lon), - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotMerTemp(gsph, nStp, xyBds, AxL1, AxC1_1, indx=(None, pic2lon), - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotMerBrNorm(gsph, nStp, xyBds, AxR1, AxC2_1, indx=(None, pic2lon), - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") else: fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic3": - # Plot at 1 AU. iSl -> "i slice" + # Lat/lon plot at 1 AU (the outer edge of the gamhelio grid), in + # the modified HGS frame rotating with the Sun. hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0) hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0) hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1) + fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic4": # Plot at 1 AU in frame rotating with Sun. hviz.PlotiSlBrRotatingFrame(gsph, nStp, xyBds, Ax, AxC) @@ -477,43 +481,29 @@ def main(): hviz.PlotSpeedProf(gsph, nStp, xyBds, AxC) hviz.PlotFluxProf(gsph, nStp, xyBds, AxC1) elif pic == "pic6": - hviz.PlotEqBr(gsph, nStp, xyBds, AxL0, AxC1_0, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqBx(gsph, nStp, xyBds, AxR0, AxC2_0, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotEqBy(gsph, nStp, xyBds, AxL1, AxC1_1, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotEqBr(gsph, nStp, xyBds, AxL0, AxC1_0) + hviz.PlotEqBx(gsph, nStp, xyBds, AxR0, AxC2_0) + hviz.PlotEqBy(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBz(gsph, nStp, xyBds, AxR1, AxC2_1) - if hgsplot: - fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") - else: - fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic7": if jslice is None: jidx = gsph.Nj//2 - 1 else: jidx = jslice - hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0, jidx=jidx, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=jidx, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) - hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=jidx, - MJDc=MJDc, MJD_plot=mjd, hgsplot=hgsplot) + hviz.PlotjMagV(gsph, nStp, xyBds, AxL0, AxC1_0, jidx=jidx) + hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=jidx) + hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=jidx) hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1, jidx=jidx) - if hgsplot: - fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") - else: - fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") else: raise TypeError(f"Invalid figure type: {pic}!") - # Add time in the upper left. - if pic == "pic1" or pic == "pic2" or pic == "pic6" or pic == "pic7": - gsph.AddTime(nStp, AxL0, xy=[0.025, 0.875], fs="x-large") - elif pic == "pic3": - gsph.AddTime(nStp, AxL0, xy=[0.015, 0.82], fs="small") + # Add time in the upper left (if not in figure title). + if pic == "pic1" or pic == "pic2" or pic == "pic3": + pass elif pic == "pic4" or pic == "pic5": gsph.AddTime(nStp, Ax, xy=[0.015, 0.92], fs="small") + elif pic == "pic6" or pic == "pic7": + gsph.AddTime(nStp, AxL0, xy=[0.025, 0.875], fs="x-large") else: raise TypeError(f"Invalid figure type: {pic}!") @@ -574,6 +564,8 @@ def main(): phi = np.arctan2(y_sc, x_sc) lat = np.degrees(np.pi/2 - theta) lon = np.degrees(phi) + if lon < 0: + lon += 180.0 lat_sc = lat lon_sc = lon From 13a6c8a61b2d55960f8feda8ff3a5737714b36e9 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Thu, 28 Sep 2023 15:37:16 -0400 Subject: [PATCH 068/365] Fixed bug in Cartesian-to-spherical. --- kaipy/gamhelio/heliosphere.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kaipy/gamhelio/heliosphere.py b/kaipy/gamhelio/heliosphere.py index a768845a..e1e94b20 100644 --- a/kaipy/gamhelio/heliosphere.py +++ b/kaipy/gamhelio/heliosphere.py @@ -222,7 +222,7 @@ class GamsphPipe(GameraPipe): phi = np.arctan2(self.Y,self.X) #theta [theta < 0] += np.pi/2. - theta += -np.pi/2. + theta = np.pi/2 - theta theta = theta*180./np.pi phi [phi < 0] += 2*np.pi phi = phi*180./np.pi From dc8a1f37e78fdc009cb8ca0b223dc671f07f8627 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Thu, 28 Sep 2023 15:37:42 -0400 Subject: [PATCH 069/365] Added proper "rolling" of arrays of hgsframe pic3 1st plot. --- kaipy/gamhelio/helioViz.py | 79 +++++++++++++++++++++++++++++++++-- scripts/quicklook/heliopic.py | 6 ++- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 59a7fcd6..23847659 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2008,11 +2008,18 @@ def PlotEqBz( def PlotiSlMagV( - gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind speed at a specified radial slice. - Plot solar wind speed at a specified radial slice. + Plot solar wind speed at a specified radial slice. The plot uses a + latitude/longitude coordinate system in the GH(MJDc) frame. The resulting + plot is, in effect, a view *toward* the origin (the Sun) along the X-axis + in the GH(MJDc) frame. That is, the X-axis is positive *out* of the page, + and longitude increases to the right. Think of it as the +X axis out of the + figure at the middle of the left edge of the image, and the Sun is + "unrolled" to the right. Parameters ---------- @@ -2032,6 +2039,12 @@ def PlotiSlMagV( If True, add axis labels to the plot. idx : int Index of radial slice to plot. + hgsplot : bool + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- @@ -2062,7 +2075,67 @@ def PlotiSlMagV( lat, lon = gsph.iSliceGrid(idx=idx) # Plot the data. - Ax.pcolormesh(lon, lat, V, cmap=MagVCM, norm=vMagV) + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Compute the radius of this i-slice from the coordinates of the first + # point in the slice. + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) + + # Convert the lat/lon at the radial distance to Cartesian coordinates. + lat_rad = np.radians(lat) + lon_rad = np.radians(lon) + xg = rg*np.cos(lat_rad)*np.cos(lon_rad) + yg = rg*np.cos(lat_rad)*np.sin(lon_rad) + zg = rg*np.sin(lat_rad) + + # Load the grid cell vertex coordinates (originally in the GH(MJDc) + # frame) in the equivalent HGS(MJDc) frame. + c = SkyCoord( + -xg*u.Rsun, -yg*u.Rsun, zg*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Convert back to lat/lon coordinates. + rxy = np.sqrt(x**2 + y**2) + lat_rad = np.arctan2(z, rxy) + lon_rad = np.arctan2(y, x) + lat = np.degrees(lat_rad) + lon = np.degrees(lon_rad) + lon[lon < 0] += 360 + + # The arrays of lon and lat and data must now be rotated about axis 1 + # so that lon values increase monotonically to the right. Find the + # index of the first *drop* in lon, and shift that index back to index + # 0 for all 3 arrays. + dlon = lon[0, 1:] - lon[0, :-1] + i_shift = np.where(dlon < 0)[0][0] + lon = np.roll(lon, -i_shift, axis=1) + lat = np.roll(lat, -i_shift, axis=1) + V = np.roll(V, -i_shift, axis=1) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(lon, lat, V, cmap=MagVCM, norm=vMagV) + + else: + Ax.pcolormesh(lon, lat, V, cmap=MagVCM, norm=vMagV) # Set the plot boundaries. kv.SetAx(xyBds, Ax) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index ee6cc40a..2d064589 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -468,7 +468,9 @@ def main(): elif pic == "pic3": # Lat/lon plot at 1 AU (the outer edge of the gamhelio grid), in # the modified HGS frame rotating with the Sun. - hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0) + hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd + ) hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0) hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1) @@ -565,7 +567,7 @@ def main(): lat = np.degrees(np.pi/2 - theta) lon = np.degrees(phi) if lon < 0: - lon += 180.0 + lon += 360.0 lat_sc = lat lon_sc = lon From 631c91e65afebfc2b5d913267228e4ce65378d77 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 29 Sep 2023 09:42:48 -0400 Subject: [PATCH 070/365] Finished hgsplot changes for pic3. --- kaipy/gamhelio/helioViz.py | 262 ++++++++++++++++++++++++++++++++-- scripts/quicklook/heliopic.py | 17 ++- 2 files changed, 263 insertions(+), 16 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 23847659..2c6e6955 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2127,6 +2127,7 @@ def PlotiSlMagV( # 0 for all 3 arrays. dlon = lon[0, 1:] - lon[0, :-1] i_shift = np.where(dlon < 0)[0][0] + i_shift += 1 lon = np.roll(lon, -i_shift, axis=1) lat = np.roll(lat, -i_shift, axis=1) V = np.roll(V, -i_shift, axis=1) @@ -2150,11 +2151,18 @@ def PlotiSlMagV( def PlotiSlD( - gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind number density at a specified radial slice. - Plot solar wind number density at a specified radial slice. + Plot solar wind number density at a specified radial slice. The plot uses a + latitude/longitude coordinate system in the GH(MJDc) frame. The resulting + plot is, in effect, a view *toward* the origin (the Sun) along the X-axis + in the GH(MJDc) frame. That is, the X-axis is positive *out* of the page, + and longitude increases to the right. Think of it as the +X axis out of the + figure at the middle of the left edge of the image, and the Sun is + "unrolled" to the right. Parameters ---------- @@ -2174,6 +2182,12 @@ def PlotiSlD( If True, add axis labels to the plot. idx : int Index of radial slice to plot. + hgsplot : bool + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- @@ -2204,7 +2218,68 @@ def PlotiSlD( lat, lon = gsph.iSliceGrid(idx=idx) # Plot the data. - Ax.pcolormesh(lon, lat, D, cmap=D0CM, norm=vD) + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Compute the radius of this i-slice from the coordinates of the first + # point in the slice. + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) + + # Convert the lat/lon at the radial distance to Cartesian coordinates. + lat_rad = np.radians(lat) + lon_rad = np.radians(lon) + xg = rg*np.cos(lat_rad)*np.cos(lon_rad) + yg = rg*np.cos(lat_rad)*np.sin(lon_rad) + zg = rg*np.sin(lat_rad) + + # Load the grid cell vertex coordinates (originally in the GH(MJDc) + # frame) in the equivalent HGS(MJDc) frame. + c = SkyCoord( + -xg*u.Rsun, -yg*u.Rsun, zg*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Convert back to lat/lon coordinates. + rxy = np.sqrt(x**2 + y**2) + lat_rad = np.arctan2(z, rxy) + lon_rad = np.arctan2(y, x) + lat = np.degrees(lat_rad) + lon = np.degrees(lon_rad) + lon[lon < 0] += 360 + + # The arrays of lon and lat and data must now be rotated about axis 1 + # so that lon values increase monotonically to the right. Find the + # index of the first *drop* in lon, and shift that index back to index + # 0 for all 3 arrays. + dlon = lon[0, 1:] - lon[0, :-1] + i_shift = np.where(dlon < 0)[0][0] + i_shift += 1 + lon = np.roll(lon, -i_shift, axis=1) + lat = np.roll(lat, -i_shift, axis=1) + D = np.roll(D, -i_shift, axis=1) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(lon, lat, D, cmap=D0CM, norm=vD) + + else: + Ax.pcolormesh(lon, lat, D, cmap=D0CM, norm=vD) # Set the plot boundaries. kv.SetAx(xyBds, Ax) @@ -2221,7 +2296,8 @@ def PlotiSlD( def PlotiSlBr( - gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind radial magnetic field and current sheet at a specified radial slice. @@ -2246,6 +2322,12 @@ def PlotiSlBr( If True, add axis labels to the plot. idx : int Index of radial slice to plot. + hgsplot : bool + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- @@ -2276,14 +2358,105 @@ def PlotiSlBr( lat, lon = gsph.iSliceGrid(idx=idx) # Compute cell-centered lon lat coordinates for contour plot. - lon_c = 0.25*(lon[:-1, :-1] + lon[:-1, 1:] + lon[1:, :-1] + lon[1:, 1:]) - lat_c = 0.25*(lat[:-1, :-1] + lat[:-1, 1:] + lat[1:, :-1] + lat[1:, 1:]) + lonc = 0.25*(lon[:-1, :-1] + lon[:-1, 1:] + lon[1:, :-1] + lon[1:, 1:]) + latc = 0.25*(lat[:-1, :-1] + lat[:-1, 1:] + lat[1:, :-1] + lat[1:, 1:]) # Plot the data. - Ax.pcolormesh(lon, lat, Br, cmap=BCM, norm=vB) + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Compute the radius of this i-slice from the coordinates of the first + # point in the slice. + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) + + # Convert the lat/lon at the radial distance to Cartesian coordinates. + lat_rad = np.radians(lat) + lon_rad = np.radians(lon) + xg = rg*np.cos(lat_rad)*np.cos(lon_rad) + yg = rg*np.cos(lat_rad)*np.sin(lon_rad) + zg = rg*np.sin(lat_rad) + + # Now convert the cell centers to Cartesian coordinates. + latc_rad = np.radians(latc) + lonc_rad = np.radians(lonc) + xc = rg*np.cos(latc_rad)*np.cos(lonc_rad) + yc = rg*np.cos(latc_rad)*np.sin(lonc_rad) + zc = rg*np.sin(latc_rad) + + # Load the grid cell vertex coordinates (originally in the GH(MJDc) + # frame) in the equivalent HGS(MJDc) frame. + c = SkyCoord( + -xg*u.Rsun, -yg*u.Rsun, zg*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Load the grid cell center coordinates (originally in the GH(MJDc) + # frame) in the equivalent HGS(MJDc) frame. + cc = SkyCoord( + -xc*u.Rsun, -yc*u.Rsun, zc*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + cc = cc.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + xc = dm.dmarray(cc.cartesian.x) + yc = dm.dmarray(cc.cartesian.y) + zc = dm.dmarray(cc.cartesian.z) + + # Convert back to lat/lon coordinates. + rxy = np.sqrt(x**2 + y**2) + lat_rad = np.arctan2(z, rxy) + lon_rad = np.arctan2(y, x) + lat = np.degrees(lat_rad) + lon = np.degrees(lon_rad) + lon[lon < 0] += 360 + + # Do the same for cell centers. + rxyc = np.sqrt(xc**2 + yc**2) + latc_rad = np.arctan2(zc, rxyc) + lonc_rad = np.arctan2(yc, xc) + latc = np.degrees(latc_rad) + lonc = np.degrees(lonc_rad) + lonc[lonc < 0] += 360 + + # The arrays of lon and lat and data must now be rotated about axis 1 + # so that lon values increase monotonically to the right. Find the + # index of the first *drop* in lon, and shift that index back to index + # 0 for all 3 arrays. + dlon = lon[0, 1:] - lon[0, :-1] + i_shift = np.where(dlon < 0)[0][0] + i_shift += 1 + lon = np.roll(lon, -i_shift, axis=1) + lat = np.roll(lat, -i_shift, axis=1) + lonc = np.roll(lonc, -i_shift, axis=1) + latc = np.roll(latc, -i_shift, axis=1) + Br = np.roll(Br, -i_shift, axis=1) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(lon, lat, Br, cmap=BCM, norm=vB) + + else: + Ax.pcolormesh(lon, lat, Br, cmap=BCM, norm=vB) # Draw the Br=0 contour line representing the current sheet. - Ax.contour(lon_c, lat_c, Br, [0.], colors='black') + Ax.contour(lonc, latc, Br, [0.], colors='black') # Set the plot boundaries. kv.SetAx(xyBds, Ax) @@ -2354,7 +2527,8 @@ def PlotiSlBrRotatingFrame(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True def PlotiSlTemp( - gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1 + gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, + hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind temperature at a specified radial slice. @@ -2378,6 +2552,12 @@ def PlotiSlTemp( If True, add axis labels to the plot. idx : int Index of radial slice to plot. + hgsplot : bool + If True, plot in HGS(MJD_plot) frame + MJDc : float + MJD used for the GH frame of the simulation + MJD_plot : float + MJD to use for the HGS frame of the plot Returns ------- @@ -2407,8 +2587,70 @@ def PlotiSlTemp( # This is in the GH(MJDc) frame. lat, lon = gsph.iSliceGrid(idx=idx) + # Plot the data. - Ax.pcolormesh(lon, lat, Temp, cmap=TCM, norm=vT) + # If the HGS frame was requested, map the grid corner coordinates from the + # GH(MJDc) frame to the HGS(MJD_plot) frame. + if hgsplot: + + # Compute the radius of this i-slice from the coordinates of the first + # point in the slice. + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) + + # Convert the lat/lon at the radial distance to Cartesian coordinates. + lat_rad = np.radians(lat) + lon_rad = np.radians(lon) + xg = rg*np.cos(lat_rad)*np.cos(lon_rad) + yg = rg*np.cos(lat_rad)*np.sin(lon_rad) + zg = rg*np.sin(lat_rad) + + # Load the grid cell vertex coordinates (originally in the GH(MJDc) + # frame) in the equivalent HGS(MJDc) frame. + c = SkyCoord( + -xg*u.Rsun, -yg*u.Rsun, zg*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(MJDc), + representation_type="cartesian" + ) + + # Create a HGS frame for the plot time. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(MJD_plot) + ) + + # Convert the coordinates from HGS(MJDc) to HGS(MJD_plot). + c = c.transform_to(hgs_frame) + + # Extract the converted coordinates. + x = dm.dmarray(c.cartesian.x) + y = dm.dmarray(c.cartesian.y) + z = dm.dmarray(c.cartesian.z) + + # Convert back to lat/lon coordinates. + rxy = np.sqrt(x**2 + y**2) + lat_rad = np.arctan2(z, rxy) + lon_rad = np.arctan2(y, x) + lat = np.degrees(lat_rad) + lon = np.degrees(lon_rad) + lon[lon < 0] += 360 + + # The arrays of lon and lat and data must now be rotated about axis 1 + # so that lon values increase monotonically to the right. Find the + # index of the first *drop* in lon, and shift that index back to index + # 0 for all 3 arrays. + dlon = lon[0, 1:] - lon[0, :-1] + i_shift = np.where(dlon < 0)[0][0] + i_shift += 1 + lon = np.roll(lon, -i_shift, axis=1) + lat = np.roll(lat, -i_shift, axis=1) + Temp = np.roll(Temp, -i_shift, axis=1) + + # Plot the data in the HGS(MJD_plot) frame. + Ax.pcolormesh(lon, lat, Temp, cmap=TCM, norm=vT) + + else: + Ax.pcolormesh(lon, lat, Temp, cmap=TCM, norm=vT) # Set the plot boundaries. kv.SetAx(xyBds, Ax) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 2d064589..3098e7b7 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -469,12 +469,17 @@ def main(): # Lat/lon plot at 1 AU (the outer edge of the gamhelio grid), in # the modified HGS frame rotating with the Sun. hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, - hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd - ) - hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0) - hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1) - hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1) - fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + if hgsplot: + fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + else: + fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic4": # Plot at 1 AU in frame rotating with Sun. hviz.PlotiSlBrRotatingFrame(gsph, nStp, xyBds, Ax, AxC) From ecaa556be8f4e3f900570732dec7071e6b54a4c1 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 29 Sep 2023 11:10:40 -0400 Subject: [PATCH 071/365] Updated instructions for higher-resolution runs on pleiades --- quickstart/geo_mpi/geo_mpi_template.pbs | 20 ++++++++++++-------- quickstart/helio_mpi/helio_mpi_template.pbs | 21 +++++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/quickstart/geo_mpi/geo_mpi_template.pbs b/quickstart/geo_mpi/geo_mpi_template.pbs index ac03becc..933cd234 100644 --- a/quickstart/geo_mpi/geo_mpi_template.pbs +++ b/quickstart/geo_mpi/geo_mpi_template.pbs @@ -14,8 +14,8 @@ # qsub geo_mpi.pbs # IMPORTANT: You *must* do the following in the code below: -# 1. Uncomment the #PBS lines for your system, comment out the #PBS lines -# specific to other systems. Keep the common #PBS lines. +# 1. Uncomment the #PBS lines for your system and model resolution, comment out +# the #PBS lines specific to other cases. Keep the common #PBS lines. # 2. (cheyenne and derecho only) Set the #PBS -A directive to your own account. # 3. Uncomment the modules lines for your system, comment out the others. # 4. Set kaiju_install_dir to your local kaiju installation. @@ -74,16 +74,20 @@ #PBS -q normal # This is the line where you request specific resources from the PBS system. -# system. +# system. Uncomment the line for your model resolution. # select=2 -> Request 2 compute nodes. # ncpus=28 -> Each compute node must have at least 28 cores. This requirement # implies the use of the 2-socket, 14 cores/socket Broadwell nodes. -# mpiprocs=2 -> Each node will run 2 MPI ranks of the kaiju code. NOTE: This -# value should always be 2 for the kaiju software. +# mpiprocs=2 -> Each node will run 2 MPI ranks of the kaiju code. # ompthreads=14 -> Each MPI rank will run 14 OMP threads. # model=bro -> Each compute node must contain Broadwell chips. Specifying # "model" is a HECC-specific PBS requirement. +# For a "D"-resolution model run: #PBS -l select=2:ncpus=28:mpiprocs=2:ompthreads=14:model=bro+1:ncpus=28:mpiprocs=1:ompthreads=28:model=bro +# For a "Q"-resolution model run: +##PBS -l select=8:ncpus=28:mpiprocs=2:ompthreads=14:model=bro+9:ncpus=28:mpiprocs=1:ompthreads=28:model=bro +# For a "O"-resolution model run: +##PBS -l select=48:ncpus=28:mpiprocs=2:ompthreads=14:model=bro+9:ncpus=28:mpiprocs=1:ompthreads=28:model=bro # NOTE: Everything after the "+" is an additional request for resources for # "helper" applications in the MPI kaiju code. @@ -91,9 +95,9 @@ # IMPORTANT NOTE: In the XML file for your run, the "decomposition" of the # problem into MPI ranks is specified by the XML elements , , and # . This breakdown is implemented on the resource request line by using -# select=N/2, where N=iPdir*jPdir*kPdir. This tells PBS to give you N/2 nodes, -# each of which will run 2 MPI ranks. More simply: -# iPdir*jPdir*kPdir = select*mpiprocs +# select=N/mpiprocs, where N=iPdir*jPdir*kPdir. This tells PBS to give you +# N/mpiprocs nodes, each of which will run mpiprocs MPI ranks. More simply: +# iPdir*jPdir*kPdir = select*mpiprocs. #----------------------------------------------------------------------------- diff --git a/quickstart/helio_mpi/helio_mpi_template.pbs b/quickstart/helio_mpi/helio_mpi_template.pbs index 34d5cf3c..8daaee77 100644 --- a/quickstart/helio_mpi/helio_mpi_template.pbs +++ b/quickstart/helio_mpi/helio_mpi_template.pbs @@ -14,8 +14,8 @@ # qsub helio_mpi.pbs # IMPORTANT: You *must* do the following in the code below: -# 1. Uncomment the #PBS lines for your system, comment out the #PBS lines -# specific to other systems. Keep the common #PBS lines. +# 1. Uncomment the #PBS lines for your system and model resolution, comment out +# the #PBS lines specific to other cases. Keep the common #PBS lines. # 2. (cheyenne and derecho only) Set the #PBS -A directive to your own account. # 3. Uncomment the modules lines for your system, comment out the others. # 4. Set kaiju_install_dir to your local kaiju installation. @@ -71,23 +71,28 @@ #PBS -q normal # This is the line where you request specific resources from the PBS -# system. +# system. Uncomment the line for your model resolution. # select=4 -> Request 4 compute nodes. # ncpus=28 -> Each compute node must have at least 28 cores. This requirement # implies the use of the 2-socket, 14 cores/socket Broadwell nodes. -# mpiprocs=2 -> Each node will run 2 MPI ranks of the kaiju code. NOTE: This -# value should always be 2 for the kaiju software. +# mpiprocs=2 -> Each node will run 2 MPI ranks of the kaiju code. # ompthreads=14 -> Each MPI rank will run 14 OMP threads. # model=bro -> Each compute node must contain Broadwell chips. Specifying # "model" is a HECC-specific PBS requirement. +# For a 128x64x128-resolution model run: #PBS -l select=4:ncpus=28:mpiprocs=2:ompthreads=14:model=bro +# For a "256x128x256"-resolution model run: +##PBS -l select=16:ncpus=28:mpiprocs=2:ompthreads=14:model=bro + +# NOTE: Everything after the "+" is an additional request for resources for +# "helper" applications in the MPI kaiju code. # IMPORTANT NOTE: In the XML file for your run, the "decomposition" of the # problem into MPI ranks is specified by the XML elements , , and # . This breakdown is implemented on the resource request line by using -# select=N/2, where N=iPdir*jPdir*kPdir. This tells PBS to give you N/2 nodes, -# each of which will run 2 MPI ranks. More simply: -# iPdir*jPdir*kPdir = select*mpiprocs +# select=N/mpiprocs, where N=iPdir*jPdir*kPdir. This tells PBS to give you +# N/mpiprocs nodes, each of which will run mpiprocs MPI ranks. More simply: +# iPdir*jPdir*kPdir = select*mpiprocs. #----------------------------------------------------------------------------- From 1df163f7bbebeb4acb6c49b4dff94e9665247904 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 29 Sep 2023 13:47:53 -0400 Subject: [PATCH 072/365] Added support for hgsplot option to pic1. --- scripts/quicklook/heliomovie.py | 132 ++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 24 deletions(-) diff --git a/scripts/quicklook/heliomovie.py b/scripts/quicklook/heliomovie.py index 8d3788b4..9cb596c3 100755 --- a/scripts/quicklook/heliomovie.py +++ b/scripts/quicklook/heliomovie.py @@ -63,9 +63,13 @@ import subprocess # Import supplemental modules. import astropy.time +from astropy.coordinates import SkyCoord +import astropy.units as u import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np +import spacepy.datamodel as dm +from sunpy.coordinates import frames # Import project-specific modules. from kaipy import cdaweb_utils @@ -144,53 +148,58 @@ def create_command_line_parser(): formatter_class=argparse.RawTextHelpFormatter ) parser.add_argument( - "--clobber", action="store_true", default=False, + "--clobber", action="store_true", help="Overwrite existing frame and movie files (default: %(default)s)." ) parser.add_argument( - "--debug", action="store_true", default=False, + "--debug", action="store_true", help="Print debugging output (default: %(default)s)." ) parser.add_argument( - "-d", "--directory", type=str, metavar="directory", + "--directory", "-d", type=str, metavar="directory", default=os.getcwd(), help="Directory containing data to read (default: %(default)s)" ) parser.add_argument( - "-f", "--movie_format", type=str, metavar="movie_format", - default=default_movie_format, - help="Output movie format (default: %(default)s)" - ) - parser.add_argument( - "-id", "--runid", type=str, metavar="runid", default=default_runid, - help="Run ID of data (default: %(default)s)" - ) - parser.add_argument( - "-jslice", type=int, metavar="jSlice", default=None, - help="Index of j-slice for pic7 (default: Nj/2-1)" - ) - parser.add_argument( - "-n0", "--first_step", type=int, metavar="n0", + "--first_step", "-n0", type=int, metavar="n0", default=default_first_step, help="First time step to plot (default: %(default)s)" ) parser.add_argument( - "-n1", "--last_step", type=int, metavar="n1", + "--hgsplot", action="store_true", + help="Plot in the Heliographic Stonyhurst frame corresponding to the " + "date of the plot (default: %(default)s)." + ) + parser.add_argument( + "--jslice", "-jslice", type=int, metavar="jSlice", default=None, + help="Index of j-slice for pic7 (default: Nj/2-1)" + ) + parser.add_argument( + "--last_step", "-n1", type=int, metavar="n1", default=default_last_step, help="Last time step to plot (default: %(default)s)" ) parser.add_argument( - "-p", "--pictype", type=str, metavar="pictype", + "--movie_format", type=str, metavar="movie_format", + default=default_movie_format, + help="Output movie format (default: %(default)s)" + ) + parser.add_argument( + "--pictype", "-p", type=str, metavar="pictype", default=default_pictype, help="Code for plot type (default: %(default)s)" ) + parser.add_argument( + "--runid", "-id", type=str, metavar="runid", default=default_runid, + help="Run ID of data (default: %(default)s)" + ) parser.add_argument( "--spacecraft", type=str, metavar="spacecraft", default=None, help="Names of spacecraft to plot positions, separated by commas" " (default: %(default)s)" ) parser.add_argument( - "-v", "--verbose", action="store_true", default=False, + "--verbose", "-v", action="store_true", default=False, help="Print verbose output (default: %(default)s)." ) return parser @@ -294,7 +303,10 @@ def assemble_frames_into_gif(frame_files, args): # Create the movie. cmd = ["convert", "-delay", "10", "-loop", "0"] cmd += frame_files - movie_file = os.path.join(movie_directory, f"{pictype}.gif") + if args.hgsplot: + movie_file = os.path.join(movie_directory, f"{pictype}-HGS.gif") + else: + movie_file = os.path.join(movie_directory, f"{pictype}.gif") cmd.append(movie_file) # NOTE: movie_file is overwritten by default. subprocess.run(cmd, check=True) @@ -331,7 +343,10 @@ def assemble_frames_into_mp4(frame_files, args): # Create the movie. frame_directory = os.path.split(frame_files[0])[0] frame_pattern = os.path.join(frame_directory, f"{pictype}-%06d.png") - movie_file = os.path.join(movie_directory, f"{pictype}.mp4") + if args.hgsplot: + movie_file = os.path.join(movie_directory, f"{pictype}-HGS.mp4") + else: + movie_file = os.path.join(movie_directory, f"{pictype}.mp4") cmd = [ "ffmpeg", "-r", "4", "-s", "1920x1080", "-i", frame_pattern, @@ -382,6 +397,65 @@ def assemble_frames_into_movie(frame_files, args): return movie_file +def GHtoHGS(mjd_gh, x_gh, y_gh, z_gh, mjd_hgs): + """Convert Cartesin GH coordinates to HGS. + + Convert Cartesian coordinates in the gamhelio frame at time mjdc to + the Heliographic Sonyhurst frame at time mjd. + + NOTE: The gamhelio frame at time t is related to the Heliographic + Stonyhurst frame at time t by the reflection of the x- and y-axes: + + x_gh(t) = -x_hgs(t) + y_gh(t) = -y_hgs(t) + z_gh(t) = z_hgs(t) + + Since HGS is a time-dependent frame, a time must be provided for each set + of coordinates. + + Parameters + ---------- + mjd_gh : float + MJD of source gamhelio frame + x_gh, y_gh, z_gh : np.array of float (any shape) or scalar float + Cartesian coordinates in GH(mjdc) frame. All three arrays x, y, z must + have identical shapes. + mjd_hgs : float + MJD of target HGS frame + + Returns + ------- + x_hgs, y_hgs, z_hgs : np.array of float (same shape as x_gh, y_gh, z_gh) + Cartesian coordinates converted to HGS(mjd) frame. + + Raises + ------ + None + """ + # Load the source coordinates (originially in the GH(mjd_gh) frame) into + # the equivalent HGS(mjd_gh) frame. + c_gh = SkyCoord( + -x_gh*u.Rsun, -y_gh*u.Rsun, z_gh*u.Rsun, + frame=frames.HeliographicStonyhurst, + obstime=ktools.MJD2UT(mjd_gh), + representation_type="cartesian" + ) + + # Create the target Heliographic Stonyhurst frame. + hgs_frame = frames.HeliographicStonyhurst( + obstime=ktools.MJD2UT(mjd_hgs) + ) + + # Convert the coordinates from GH(mjd_gh) to HGS(mjd_hgs). + c_hgs = c_gh.transform_to(hgs_frame) + + # Extract and return the converted coordinates. + x_hgs = dm.dmarray(c_hgs.cartesian.x) + y_hgs = dm.dmarray(c_hgs.cartesian.y) + z_hgs = dm.dmarray(c_hgs.cartesian.z) + return x_hgs, y_hgs, z_hgs + + def create_pic1_movie(args): """Create a pic1-style gamhelio movie. @@ -403,6 +477,7 @@ def create_pic1_movie(args): """ # Extract arguments. debug = args.debug + hgsplot=args.hgsplot pictype = args.pictype spacecraft = args.spacecraft verbose = args.verbose @@ -492,6 +567,10 @@ def create_pic1_movie(args): else: raise e + # Get the MJDc value for use in computing the gamhelio frame. + fname = gsph.f0 + MJDc = scutils.read_MJDc(fname) + # Create and save frame images for each step. first_step = args.first_step last_step = args.last_step @@ -512,7 +591,8 @@ def create_pic1_movie(args): print(f"mjd = {mjd}") # Create the individual plots for this frame. - hviz.PlotEqMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v) + hviz.PlotEqMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotEqD(gsph, i_step, plot_limits, ax_n, ax_cb_n) hviz.PlotEqTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T) hviz.PlotEqBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br) @@ -532,7 +612,11 @@ def create_pic1_movie(args): t_sc = mjd x_sc = np.interp(t_sc, sc_t[sc_id], sc_x[sc_id]) y_sc = np.interp(t_sc, sc_t[sc_id], sc_y[sc_id]) - # z_sc = np.interp(t_sc, sc_t[sc_id], sc_z[sc_id]) + z_sc = np.interp(t_sc, sc_t[sc_id], sc_z[sc_id]) + + # If needed, convert the position to HGS(mjd). + if hgsplot: + x_sc, y_sc, z_sc = GHtoHGS(MJDc, x_sc, y_sc, z_sc, mjd) # Plot the spacecraft position as a colored circle with black # outline and a label. From dc223461ae04a56dbfd4d9ea6061d417ac7a3dd0 Mon Sep 17 00:00:00 2001 From: "Andrew J. McCubbin" Date: Fri, 29 Sep 2023 12:35:04 -0700 Subject: [PATCH 073/365] make compression optional and off by default --- CMakeLists.txt | 2 +- kaipy/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59da7bc7..3c1b31c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ option(ENABLE_MPI "Enable MPI parallelization" OFF) option(ENABLE_MKL "Enable Intel MKL usage" OFF) option(ENABLE_CODECOV "Enable Code Coverage tracking" OFF) option(ALLOW_INVALID_COMPILERS "Allow Invalid Compiler Versions" OFF) -option(ENABLE_COMPRESS "Enable Dataset Compression" ON) +option(ENABLE_COMPRESS "Enable Dataset Compression" OFF) #Set Compression Type set(COMPRESS_METHOD "SZIP" CACHE STRING "Set Compression Method (default SZIP) diff --git a/kaipy/requirements.txt b/kaipy/requirements.txt index 39124c7b..b2305f1f 100644 --- a/kaipy/requirements.txt +++ b/kaipy/requirements.txt @@ -6,7 +6,7 @@ cdflib configparser dataclasses_json h5py -hdf5plugin +hdf5plugin[hdf5plugin] jinja2 matplotlib netCDF4 From e4391e54ee2854a590cca5dc3f36edf00cf80e7d Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 29 Sep 2023 16:26:47 -0400 Subject: [PATCH 074/365] Added hgsplot support for pic1, pic2, pic3. pic3 doesn't work yet. --- scripts/quicklook/heliomovie.py | 98 +++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 28 deletions(-) diff --git a/scripts/quicklook/heliomovie.py b/scripts/quicklook/heliomovie.py index 9cb596c3..2931e99e 100755 --- a/scripts/quicklook/heliomovie.py +++ b/scripts/quicklook/heliomovie.py @@ -342,11 +342,12 @@ def assemble_frames_into_mp4(frame_files, args): # Create the movie. frame_directory = os.path.split(frame_files[0])[0] - frame_pattern = os.path.join(frame_directory, f"{pictype}-%06d.png") if args.hgsplot: movie_file = os.path.join(movie_directory, f"{pictype}-HGS.mp4") + frame_pattern = os.path.join(frame_directory, f"{pictype}-HGS-%06d.png") else: movie_file = os.path.join(movie_directory, f"{pictype}.mp4") + frame_pattern = os.path.join(frame_directory, f"{pictype}-%06d.png") cmd = [ "ffmpeg", "-r", "4", "-s", "1920x1080", "-i", frame_pattern, @@ -495,9 +496,6 @@ def create_pic1_movie(args): if debug: print(f"figsize = {figsize}") - # Create figures in a memory buffer. - mpl.use("Agg") - # Create the figure. fig = plt.figure(figsize=figsize) if debug: @@ -558,7 +556,10 @@ def create_pic1_movie(args): ) # Compute the path to the frame directory. - frame_directory = os.path.join(fdir, f"frames-{pictype}") + if hgsplot: + frame_directory = os.path.join(fdir, f"frames-{pictype}-HGS") + else: + frame_directory = os.path.join(fdir, f"frames-{pictype}") try: os.mkdir(frame_directory) except FileExistsError as e: @@ -593,9 +594,12 @@ def create_pic1_movie(args): # Create the individual plots for this frame. hviz.PlotEqMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - hviz.PlotEqD(gsph, i_step, plot_limits, ax_n, ax_cb_n) - hviz.PlotEqTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T) - hviz.PlotEqBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br) + hviz.PlotEqD(gsph, i_step, plot_limits, ax_n, ax_cb_n, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotEqTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotEqBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) # Add time in the upper left. gsph.AddTime(i_step, ax_v, xy=[0.025, 0.875], fs="x-large") @@ -631,7 +635,10 @@ def create_pic1_movie(args): c="black", horizontalalignment="center") # Save the figure to a file. - path = os.path.join(frame_directory, f"{pictype}-{i_step:06d}.png") + if hgsplot: + path = os.path.join(frame_directory, f"{pictype}-HGS-{i_step:06d}.png") + else: + path = os.path.join(frame_directory, f"{pictype}-{i_step:06d}.png") if debug: print(f"path = {path}") kv.savePic(path, bLenX=45) @@ -672,6 +679,7 @@ def create_pic2_movie(args): """ # Extract arguments. debug = args.debug + hgsplot=args.hgsplot pictype = args.pictype spacecraft = args.spacecraft verbose = args.verbose @@ -681,9 +689,6 @@ def create_pic2_movie(args): if debug: print(f"plot_limits = {plot_limits}") - # Create all plot images in a memory buffer. - mpl.use("Agg") - # Fetch the figure size. figsize = figure_sizes[pictype] if debug: @@ -752,7 +757,10 @@ def create_pic2_movie(args): ) # Compute the path to the frame directory. - frame_directory = os.path.join(fdir, f"frames-{pictype}") + if hgsplot: + frame_directory = os.path.join(fdir, f"frames-HGS-{pictype}-HGS") + else: + frame_directory = os.path.join(fdir, f"frames-{pictype}") try: os.mkdir(frame_directory) except FileExistsError as e: @@ -761,6 +769,10 @@ def create_pic2_movie(args): else: raise e + # Get the MJDc value for use in computing the gamhelio frame. + fname = gsph.f0 + MJDc = scutils.read_MJDc(fname) + # Create and save frame images for each step. first_step = args.first_step last_step = args.last_step @@ -781,10 +793,14 @@ def create_pic2_movie(args): print(f"mjd = {mjd}") # Create the individual plots for this frame. - hviz.PlotMerMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v) - hviz.PlotMerDNorm(gsph, i_step, plot_limits, ax_n, ax_cb_n) - hviz.PlotMerTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T) - hviz.PlotMerBrNorm(gsph, i_step, plot_limits, ax_Br, ax_cb_Br) + hviz.PlotMerMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotMerDNorm(gsph, i_step, plot_limits, ax_n, ax_cb_n, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotMerTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotMerBrNorm(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) # Add time in the upper left. gsph.AddTime(i_step, ax_v, xy=[0.025, 0.875], fs="x-large") @@ -800,9 +816,13 @@ def create_pic2_movie(args): # Interpolate the spacecraft position at the time for the plot. t_sc = mjd x_sc = np.interp(t_sc, sc_t[sc_id], sc_x[sc_id]) - # y_sc = np.interp(t_sc, sc_t[sc_id], sc_y[sc_id]) + y_sc = np.interp(t_sc, sc_t[sc_id], sc_y[sc_id]) z_sc = np.interp(t_sc, sc_t[sc_id], sc_z[sc_id]) + # If needed, convert the position to HGS(mjd). + if hgsplot: + x_sc, y_sc, z_sc = GHtoHGS(MJDc, x_sc, y_sc, z_sc, mjd) + # Plot the spacecraft position as a colored circle with black # outline and a label. x_nudge = 0.0 @@ -816,7 +836,10 @@ def create_pic2_movie(args): c="black", horizontalalignment="center") # Save the figure to a file. - path = os.path.join(frame_directory, f"{pictype}-{i_step:06d}.png") + if hgsplot: + path = os.path.join(frame_directory, f"{pictype}-HGS-{i_step:06d}.png") + else: + path = os.path.join(frame_directory, f"{pictype}-{i_step:06d}.png") if debug: print(f"path = {path}") kv.savePic(path, bLenX=45) @@ -857,6 +880,7 @@ def create_pic3_movie(args): """ # Extract arguments. debug = args.debug + hgsplot=args.hgsplot pictype = args.pictype spacecraft = args.spacecraft verbose = args.verbose @@ -866,9 +890,6 @@ def create_pic3_movie(args): if debug: print(f"plot_limits = {plot_limits}") - # Create all plot images in a memory buffer. - mpl.use("Agg") - # Fetch the figure size. figsize = figure_sizes[pictype] if debug: @@ -937,7 +958,10 @@ def create_pic3_movie(args): ) # Compute the path to the frame directory. - frame_directory = os.path.join(fdir, f"frames-{pictype}") + if hgsplot: + frame_directory = os.path.join(fdir, f"frames-HGS-{pictype}-HGS") + else: + frame_directory = os.path.join(fdir, f"frames-{pictype}") try: os.mkdir(frame_directory) except FileExistsError as e: @@ -946,6 +970,10 @@ def create_pic3_movie(args): else: raise e + # Get the MJDc value for use in computing the gamhelio frame. + fname = gsph.f0 + MJDc = scutils.read_MJDc(fname) + # Create and save frame images for each step. first_step = args.first_step last_step = args.last_step @@ -966,10 +994,14 @@ def create_pic3_movie(args): print(f"mjd = {mjd}") # Create the individual plots for this frame. - hviz.PlotiSlMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v) - hviz.PlotiSlD(gsph, i_step, plot_limits, ax_n, ax_cb_n) - hviz.PlotiSlTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T) - hviz.PlotiSlBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br) + hviz.PlotiSlMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotiSlD(gsph, i_step, plot_limits, ax_n, ax_cb_n, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotiSlTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hviz.PlotiSlBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) # Add time in the upper left. gsph.AddTime(i_step, ax_v, xy=[0.015, 0.82], fs="small") @@ -985,6 +1017,13 @@ def create_pic3_movie(args): # Interpolate the spacecraft position at the time for the plot. t_sc = mjd + # If needed, convert the position to HGS(mjd). + if hgsplot: + sc_x[sc_id], sc_y[sc_id], sc_z[sc_id] = ( + GHtoHGS(MJDc, sc_x[sc_id], sc_y[sc_id], sc_z[sc_id], + mjd) + ) + # Convert Cartesian location to heliocentric lon/lat. rxy = np.sqrt(sc_x[sc_id]**2 + sc_y[sc_id]**2) theta = np.arctan2(rxy, sc_z[sc_id]) @@ -1007,7 +1046,10 @@ def create_pic3_movie(args): c="black", horizontalalignment="center") # Save the figure to a file. - path = os.path.join(frame_directory, f"{pictype}-{i_step:06d}.png") + if hgsplot: + path = os.path.join(frame_directory, f"{pictype}-HGS-{i_step:06d}.png") + else: + path = os.path.join(frame_directory, f"{pictype}-{i_step:06d}.png") if debug: print(f"path = {path}") kv.savePic(path, bLenX=45) From 84783ef5b0b2c79a0ac02e6c4707199f1847e4c6 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Fri, 29 Sep 2023 15:19:02 -0600 Subject: [PATCH 075/365] Fix RCM beta calculation by including the trapped flux. Some minor fixes. --- src/remix/mixconductance.F90 | 42 +++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 98e7e599..5a8dc817 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -472,7 +472,7 @@ module mixconductance call conductance_IM_GTYPE(G,St) ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need IM_GTYPE. - call conductance_alpha_beta(conductance,G,St) + call conductance_beta(conductance,G,St) ! Derive mono using the linearized FL relation. call conductance_linmono(conductance,G,St) @@ -583,7 +583,7 @@ module mixconductance conductance%phi0(i,j) = phi0 ! Note JF0 may go inf where phi0/Pe_MHD/Ne_MHD is zero. - J2eF0 = 1.D-4*signOfJ*(St%Vars(i,j,FAC)*1.D-6)/(eCharge*phi0) + J2eF0 = signOfJ*(St%Vars(i,j,FAC)*1.0D-6)/(eCharge*phi0*1.0D4) ! Linearize the Fridman-Lemaire relation: ! eV = kB*Te*(RM-1)*ln((RM-1)/(RM-J/e/F0)) when 1<=J/e/F0<=RM. ! eV ~ kB*Te*(J/e/F0-1) when RM>>J/e/F0 @@ -594,11 +594,12 @@ module mixconductance St%Vars(i,j,Z_NFLUX) = phi0*eV2kT St%Vars(i,j,Z_EAVG) = kT*(eV2kT+1.0/eV2kT) else + ! When there is no acceleration, MHD flux represents the diffuse precipitation. conductance%deltaE(i,j) = 0.0 St%Vars(i,j,Z_NFLUX) = phi0 St%Vars(i,j,Z_EAVG) = kT endif - St%Vars(i,j,DELTAE) = conductance%deltaE(i,j) + St%Vars(i,j,DELTAE) = conductance%deltaE(i,j) St%Vars(i,j,AVG_ENG) = max(St%Vars(i,j,Z_EAVG),eTINY) ! [keV] St%Vars(i,j,NUM_FLUX) = St%Vars(i,j,Z_NFLUX) ! [#/cm^2/s] enddo ! i @@ -828,35 +829,36 @@ module mixconductance end subroutine conductance_IM_GTYPE - subroutine conductance_alpha_beta(conductance,G,St) + subroutine conductance_beta(conductance,G,St) + ! Use RCM precipitation and source population to derive the loss cone rate beta. + ! Assume beta is one in the polar cap and smooth across RCM boundary. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - real(rp), dimension(G%Np,G%Nt) :: phi0_rcmz + real(rp) :: phi0_rcm integer :: i,j - ! In MHD, use the same alpha/beta with RCM. - ! Calculate beta from RCM fluxes. - ! Default values are from xml in conductance_init. - ! It's still necessary to initialize alpha/beta_RCM here because they may be using an old value at some points - ! if IM_EAVG or IM_EPRE or EM_EDEN no longer satisfies the if criteria. Better to use default background beta. - alpha_RCM = 1.0/(tiote_RCM+1.0) - beta_RCM = conductance%beta - phi0_rcmz = sqrt(St%Vars(:,:,IM_EPRE)*St%Vars(:,:,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 ! sqrt([Pa]*[#/m^3]/[kg]) = sqrt([#/m^4/s^2]) = 1e-4*[#/cm^2/s] + St%Vars(:,:,IM_BETA) = 1.0 !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j) + !$OMP private(i,j,phi0_rcm) do j=1,G%Nt do i=1,G%Np - if(phi0_rcmz(i,j)>TINY) then - beta_RCM(i,j) = St%Vars(i,j,IM_ENFLX)/phi0_rcmz(i,j) + ! Total RCM thermal flux includes the trapped and precipitated. + ! 1.0e-4 is to convert to [#/cm^2/s] + ! sqrt([Pa]*[#/m^3]/[kg]) = sqrt([#/m^4/s^2]) = 1e-4*[#/cm^2/s] + phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 + St%Vars(i,j,IM_ENFLX) + if(phi0_rcm>TINY) then + St%Vars(i,j,IM_BETA) = St%Vars(i,j,IM_ENFLX)/phi0_rcm elseif(St%Vars(i,j,IM_GTYPE) > 0.5) then - beta_RCM(i,j) = 0.0 + ! In the low lat, if there is no meaningful RCM precipitation, + ! set beta=0 to freeze other precipitation mechanism. + St%Vars(i,j,IM_BETA) = 0.0 endif enddo enddo - St%Vars(:,:,IM_BETA) = min(beta_RCM,1.0) - - end subroutine conductance_alpha_beta + ! IM_BETA is for output. beta_RCM is used in calculation. + beta_RCM = min(St%Vars(:,:,IM_BETA), 1.0) + end subroutine conductance_beta !Enforce pole condition that all values at the same point are equal subroutine FixPole(Gr,Q) From cee41ff99af52aec6d20f0653f1761396e927960 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Fri, 29 Sep 2023 21:27:55 -0600 Subject: [PATCH 076/365] No need to provide beta for linmono. It assumes unity in the polar cap and transit smoothly from RCM. --- src/remix/mixconductance.F90 | 128 +++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 51 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 5a8dc817..390cedd6 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -472,7 +472,7 @@ module mixconductance call conductance_IM_GTYPE(G,St) ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need IM_GTYPE. - call conductance_beta(conductance,G,St) + call conductance_beta(G,St) ! Derive mono using the linearized FL relation. call conductance_linmono(conductance,G,St) @@ -774,71 +774,42 @@ module mixconductance subroutine conductance_IM_GTYPE(G,St) type(mixGrid_T), intent(in) :: G type(mixState_T), intent(in) :: St - real(rp), dimension(3,3) :: A33 - logical, dimension(3,3) :: isG33 !isG33 = (A3>0.0 .and. A3<1.0) - integer :: i,j,it,MaxIter,im1,ip1,jm1,jp1 - real(rp) :: mad,Ttmp - real(rp) :: temp(G%Np,G%Nt) + logical :: isAnc(G%Np,G%Nt) + integer :: i,j - MaxIter = 15 - - ! Iterative diffusion algorithm to smooth out IM_GTYPE: 0/1 are boundary cells. Evolve with nine-cell mean. - ! Otherwise it only has three values, 0, 0.5, and 1.0, - ! which causes discontinuities in the merged precipitation. + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j) + do j=1,G%Nt ! use open BC for lat. + do i=1,G%Np ! use periodic BC for lon. + if(St%Vars(i,j,IM_GTYPE)>0.01 .and. St%Vars(i,j,IM_GTYPE)<0.99) then + isAnc(i,j) = .false. + else + isAnc(i,j) = .true. + endif + enddo ! i + enddo ! j gtype_RCM = St%Vars(:,:,IM_GTYPE) ! supposed to be between 0 and 1. - - call FixPole(G,gtype_RCM) - - do it=1,MaxIter - mad = 0.D0 ! max abs difference from last iteration. - temp = gtype_RCM - - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,jm1,jp1,im1,ip1,Ttmp) & - !$OMP reduction(max:mad) - do j=1,G%Nt ! use open BC for lat. - do i=1,G%Np ! use periodic BC for lon. - - jm1 = j-1 - jp1 = j+1 - if (j == 1) jm1 = 1 - if (j == G%Nt) jp1 = G%Nt - - im1 = i-1 - ip1 = i+1 - if (i == 1) im1 = G%Np - if (i == G%Np) ip1 = 1 - - if(St%Vars(i,j,IM_GTYPE)>0.01 .and. St%Vars(i,j,IM_GTYPE)<0.99) then - ! Use 0.01/0.99 as the boundary for this numerical diffusion because - ! the interpolation from RCM to REMIX would slightly modify the 0/1 boundary. - Ttmp =(temp(im1,jm1)+temp(im1,j)+temp(im1,jp1) & - + temp(i ,jm1)+temp(i ,j)+temp(i ,jp1) & - + temp(ip1,jm1)+temp(ip1,j)+temp(ip1,jp1))/9.D0 - mad = max(abs(gtype_RCM(i,j)-Ttmp),mad) - gtype_RCM(i,j) = Ttmp - endif - enddo - enddo - call FixPole(G,gtype_RCM) - if(mad<0.025) exit - enddo + call conductance_smooth(G,gtype_RCM,isAnc) gtype_RCM = min(gtype_RCM,1.0) gtype_RCM = max(gtype_RCM,0.0) - end subroutine conductance_IM_GTYPE - subroutine conductance_beta(conductance,G,St) + subroutine conductance_beta(G,St) ! Use RCM precipitation and source population to derive the loss cone rate beta. ! Assume beta is one in the polar cap and smooth across RCM boundary. - type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St + logical :: isAnc(G%Np,G%Nt) + real(rp) :: temp(G%Np,G%Nt) real(rp) :: phi0_rcm integer :: i,j St%Vars(:,:,IM_BETA) = 1.0 + isAnc = .false. + ! set the first circle around pole as anchore. + isAnc(:,1) = .true. + !$OMP PARALLEL DO default(shared) & !$OMP private(i,j,phi0_rcm) do j=1,G%Nt @@ -849,17 +820,72 @@ module mixconductance phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 + St%Vars(i,j,IM_ENFLX) if(phi0_rcm>TINY) then St%Vars(i,j,IM_BETA) = St%Vars(i,j,IM_ENFLX)/phi0_rcm + isAnc(i,j) = .true. ! set points with valid rcm beta as anchore. elseif(St%Vars(i,j,IM_GTYPE) > 0.5) then ! In the low lat, if there is no meaningful RCM precipitation, ! set beta=0 to freeze other precipitation mechanism. + ! also make it anchor points to avoid smoothing. St%Vars(i,j,IM_BETA) = 0.0 + isAnc(i,j) = .true. endif enddo enddo + temp = St%Vars(:,:,IM_BETA) ! supposed to be between 0 and 1. + call conductance_smooth(G,temp,isAnc) + St%Vars(:,:,IM_BETA) = temp + ! IM_BETA is for output. beta_RCM is used in calculation. beta_RCM = min(St%Vars(:,:,IM_BETA), 1.0) end subroutine conductance_beta + subroutine conductance_smooth(Gr,Q,isAnchor) + ! Do smoothing window on ReMIX grid quantity + ! Skip certain points + type(mixGrid_T), intent(in) :: Gr + real(rp), intent(inout) :: Q(Gr%Np,Gr%Nt) + logical, intent(in) :: isAnchor(Gr%Np,Gr%Nt) + real(rp) :: temp(Gr%Np,Gr%Nt) + real(rp) :: thres,mad,Ttmp + integer :: i,j,it,im1,ip1,jm1,jp1,MaxIter + + thres = 0.025 + MaxIter = 15 + call FixPole(Gr,Q) + + do it=1,MaxIter + mad = 0.D0 ! max abs difference from last iteration. + temp = Q + + !$OMP PARALLEL DO default(shared) & + !$OMP private(i,j,jm1,jp1,im1,ip1,Ttmp) & + !$OMP reduction(max:mad) + do j=1,Gr%Nt ! use open BC for lat. + do i=1,Gr%Np ! use periodic BC for lon. + ! Do not smooth anchor points. + if(.not. isAnchor(i,j)) then + jm1 = j-1 + jp1 = j+1 + if (j == 1) jm1 = 1 + if (j == Gr%Nt) jp1 = Gr%Nt + + im1 = i-1 + ip1 = i+1 + if (i == 1) im1 = Gr%Np + if (i == Gr%Np) ip1 = 1 + + Ttmp =(temp(im1,jm1)+temp(im1,j)+temp(im1,jp1) & + + temp(i ,jm1)+temp(i ,j)+temp(i ,jp1) & + + temp(ip1,jm1)+temp(ip1,j)+temp(ip1,jp1))/9.D0 + mad = max(abs(Q(i,j)-Ttmp),mad) + Q(i,j) = Ttmp + endif + enddo + enddo + call FixPole(Gr,Q) + if(mad Date: Mon, 2 Oct 2023 08:19:14 -0700 Subject: [PATCH 077/365] clean-up after merge --- src/base/ioH5.F90 | 782 +++++++++++++++++++++++++++++------------ src/base/ioH5Types.F90 | 23 +- 2 files changed, 578 insertions(+), 227 deletions(-) diff --git a/src/base/ioH5.F90 b/src/base/ioH5.F90 index ab1c2c92..005c4a48 100644 --- a/src/base/ioH5.F90 +++ b/src/base/ioH5.F90 @@ -1,5 +1,8 @@ -!Various routines to read/write HDF5 files - +!> Various routines to read/write HDF5 files +!> @notes +!> Useful user routines +!> AddOutVar, AddInVar, WriteVars,ReadVars +!> FindIO: Example, call FindIO(IOVars,"Toy",n), IOVars(n) = Toy data/metadata module ioH5 use kdefs use ISO_C_BINDING @@ -10,12 +13,58 @@ module ioH5 use ioH5Overload use files use dates - - !Useful user routines - !AddOutVar, AddInVar, WriteVars,ReadVars - !FindIO: Example, call FindIO(IOVars,"Toy",n), IOVars(n) = Toy data/metadata +#ifdef __ENABLE_ZFP + use h5zzfp_props_f +#endif implicit none +#ifdef __ENABLE_COMPRESS + !> Compression Mode variables + enum, bind(C) + enumerator :: ZFP,ZSTD,ZLIB,SZIP,BLOSC + end enum + logical, parameter, private :: ENABLE_COMPRESS = .TRUE. + integer, parameter, private :: COMPRESS_ZSTD = 32015 + integer, parameter, private :: COMPRESS_ZFP = 32013 + integer, parameter, private :: COMPRESS_BLOSC = 32026 + integer, parameter, private :: H5Z_FLAG_MANDATORY = INT(Z'00000000') + ! Compression Algorithm Selection +#ifdef __ENABLE_SZIP + integer, parameter, private :: Z_ALG = SZIP +#endif +#ifdef __ENABLE_ZLIB + integer, parameter, private :: Z_ALG = ZLIB +#endif +#ifdef __ENABLE_ZFP + integer, parameter, private :: Z_ALG = ZFP + ! ZFP compression parameters (defaults taken from ZFP header) + integer(C_INT) :: zfpmode = 5 !1=rate, 2=prec, 3=acc, 4=expert, 5=lossless + real(dp) :: rate = 4.0_dp + real(dp) :: acc = 0.00001_dp + integer(C_INT) :: prec = 11 + integer(C_INT) :: dim = 0 + ! Used for Expert Mode + integer(C_INT) :: minbits = 1 + integer(C_INT) :: maxbits = 32768 + integer(C_INT) :: maxprec = 64 + integer(C_INT) :: minexp = -1074 +#endif +#ifdef __ENABLE_ZSTD + integer, parameter, private :: Z_ALG = ZSTD +#endif +#ifdef __ENABLE_BLOCSC + integer, parameter, private :: Z_ALG = BLOCSC +#endif + +#else + logical, parameter, private :: ENABLE_COMPRESS = .FALSE. +#endif + ! timeAttributeCache options + integer(HSIZE_T), parameter, private :: CACHE_CHUNK_SIZE = 256 + character(len=strLen), parameter :: attrGrpName = "timeAttributeCache" + integer, private :: cacheSize = 0, stepOffset = 0 + logical, private :: createdThisFile = .false., isFirstStep = .true. + !Overloader to add data (array or scalar/string) to output chain interface AddOutVar module procedure AddOut_5D,AddOut_4D,AddOut_3D,AddOut_2D,AddOut_1D,AddOut_Int,AddOut_DP,AddOut_SP,AddOut_Str @@ -29,15 +78,16 @@ module ioH5 end interface IOArrayFill contains -!Routine to stamp output file with various information + + !> Routine to stamp output file with various information subroutine StampIO(fIn) character(len=*), intent(in) :: fIn character(len=strLen) :: gStr,dtStr,bStr type(IOVAR_T), dimension(10) :: IOVars - !Check if this file has already been stamped (has githash) - !NOTE: This creates ambiguity when doing restarts using binaries w/ different hashes - !But what're you gonna do? + !> Check if this file has already been stamped (has githash) + !> @NOTE: This creates ambiguity when doing restarts using binaries w/ different hashes + !> But what're you gonna do? if ( ioExist(fIn,"GITHASH") ) then !Already stamped, let's get outta here return @@ -59,10 +109,12 @@ contains #endif call AddOutVar(IOVars,"DATETIME",dtStr) call WriteVars(IOVars,.true.,fIn,doStampCheckO=.false.) - end subroutine StampIO -!------------------------------------------- -!Various routines to quickly pull scalars from IOVar_T + + ! ------------------------------------------- + ! Various routines to quickly pull scalars from IOVar_T + ! + !> Helper funciton to pull INT from an IOVar data function GetIOInt(IOVars,vID) result(vOut) type(IOVAR_T), dimension(:), intent(in) :: IOVars character(len=*), intent(in) :: vID @@ -72,6 +124,7 @@ contains vOut = IOVars(nvar)%data(1) end function GetIOInt + !> Helper funciton to pull REAL from an IOVar data function GetIOReal(IOVars,vID) result(vOut) type(IOVAR_T), dimension(:), intent(in) :: IOVars character(len=*), intent(in) :: vID @@ -81,10 +134,42 @@ contains nvar = FindIO(IOVars,vID,.true.) vOut = IOVars(nvar)%data(1) end function GetIOReal -!------------------------------------------- -!Various routines to get information about step structure of H5 file + !> Calculate cdims for a given dataset size + subroutine CalcChunkSize(elsize, dims, cdims) + !> Size of elements in bytes + integer, intent(in) :: elsize + !> Dimensions of dataset + integer(HSIZE_T), dimension(*), intent(in) :: dims + !> Maximum allowed chunk size/dims + integer(HSIZE_T), dimension(:), intent(out) :: cdims + + real, parameter :: max_bytes = 1048576 + !1048576 bytes {64,64,32} double + !2097152 bytes {64,64,64} double + !1048576 bytes {64,64,64} float + !2097152 bytes {64,64,128} float + + integer :: d + real(dp) :: nbytes + + d = size(cdims) + cdims(1:d) = dims(1:d) + nbytes = elsize*product(1.d0*cdims) + do while ((nbytes.gt.max_bytes).and.(d.gt.0)) + cdims(d) = max(floor(cdims(d)*max_bytes/nbytes,Int32),1) + nbytes = elsize*product(1.d0*cdims) + d = d - 1 + enddo + + end subroutine CalcChunkSize + + !------------------------------------------- + !Various routines to get information about step structure of H5 file + + !> Determine grid size from HDF5 file function GridSizeH5(fIn) result(Nijk) + !> File Name character(len=*), intent(in) :: fIn integer, dimension(NDIM) :: Nijk @@ -124,10 +209,16 @@ contains endif end function GridSizeH5 - !Get number of groups of form "Step#XXX" and start/end + !> Get number of groups of form "Step#XXX" and start/end subroutine StepInfo(fStr,s0,sE,Nstp) + !> File Name character(len=*), intent(in) :: fStr - integer, intent(out) :: s0,sE,Nstp + !> Step Number + integer, intent(out) :: s0 + !> Step End + integer, intent(out) :: sE + !> Number of Steps + integer, intent(out) :: Nstp integer :: herr,i logical :: gExist,fExist,isEnd,idFirst integer(HID_T) :: h5fId @@ -180,7 +271,7 @@ contains end subroutine StepInfo - !Find step times between s0,sE and store in pre-allocated array + !> Find step times between s0,sE and store in pre-allocated array subroutine StepTimes(fStr,s0,sE,Ts) character(len=*), intent(in) :: fStr integer, intent(in) :: s0,sE @@ -209,7 +300,7 @@ contains end subroutine StepTimes - !Same as StepTimes but for MJDs + !> Same as StepTimes but for MJDs subroutine StepMJDs(fStr,s0,sE,MJDs) character(len=*), intent(in) :: fStr integer, intent(in) :: s0,sE @@ -249,7 +340,7 @@ contains end subroutine StepMJDs - !Count number of groups of form "Step#XXX" + !> Count number of groups of form "Step#XXX" function NumSteps(fStr) result(Nstp) character(len=*), intent(in) :: fStr integer :: Nstp @@ -259,9 +350,9 @@ contains call StepInfo(fStr,s0,sE,Nstp) end function NumSteps - !Checks if object exists in file fStr - !fStr:/vStr (if no gStrO), otherwise - !fStr:/gStrO/vStr + !> Checks if object exists in file fStr + !> fStr:/vStr (if no gStrO), otherwise + !> fStr:/gStrO/vStr function ioExist(fStr,vStr,gStrO) character(len=*), intent(in) :: fStr,vStr character(len=*), intent(in), optional :: gStrO @@ -311,7 +402,7 @@ contains ioExist = dsExist .or. atExist end function ioExist - !Read into array of IOVar from file fOut/optional group ID gStr + !> Read into array of IOVar from file fOut/optional group ID gStr subroutine ReadVars(IOVars,doIOp,baseStr,gStrO) type(IOVAR_T), dimension(:), intent(inout) :: IOVars logical, intent(in) :: doIOp @@ -321,7 +412,7 @@ contains integer :: n, Nv ! integer(HID_T) :: Nr - logical :: fExist,gExist,dsExist,aExist + logical :: fExist, gExist, dsExist = .false., aExist = .false. integer :: herr, dsTest integer(HID_T) :: h5fId, gId, inId character(len=strLen) :: h5File @@ -403,148 +494,11 @@ contains call h5close_f(herr) !Close intereface end subroutine ReadVars + !------------------------------------------- + !Routines to read/write individual variables or attributes - !Write array of IOVar to file fOut (from baseStr), under (optional) group gStrO - !If gStrO unspecified, written to root of HDF5 - !doIOp is whether to use IOP (ie output slice) or rp (ie restart) - subroutine WriteVars(IOVars,doIOp,baseStr,gStrO,gStrOO,doStampCheckO) - type(IOVAR_T), dimension(:), intent(inout) :: IOVars - logical, intent(in) :: doIOp - character(len=*), intent(in) :: baseStr - character(len=*), intent(in), optional :: gStrO,gStrOO - logical , intent(in), optional :: doStampCheckO - - logical :: fExist,gExist,doStampCheck - integer :: herr - integer(HID_T) :: h5fId, gId, ggId,outId - character(len=strLen) :: h5File - - !Set filename to baseStr - !FIXME: Correct to add .h5 to baseStr - h5File = baseStr - - if (present(doStampCheckO)) then - doStampCheck = doStampCheckO - else - doStampCheck = .true. - endif - - !If we're writing to root of this file, then stamp (will ignore if already stamped) - if (.not. present(gStrO) .and. doStampCheck) then - call StampIO(h5File) - endif - - call h5open_f(herr) !Setup Fortran interface - - !Start by opening file, create if necessary - inquire(file=h5File,exist=fExist) - if (fExist) then - !Open file - call h5fopen_f(trim(h5File), H5F_ACC_RDWR_F, h5fId, herr) - else - !Create file - call h5fcreate_f(trim(h5File),H5F_ACC_TRUNC_F, h5fId, herr) - endif - - !Figure out output location (outId) and create groups as necessary - if (present(gStrO)) then - !Write to group - !Check if group already exists - call h5lexists_f(h5fId,trim(gStrO),gExist,herr) - if (gExist .and. .not. present(gStrOO)) then - !Group exists (and not writing to subgroup) - !Can either skip it, or kill and replace - if (doSkipG) then - !Group exists, close up and skip it - write(*,*) 'Skipping due to pre-existing group ', trim(gStrO) - - !Close up - call h5fclose_f(h5fId, herr) - call h5close_f(herr) - return - else - !Kill group - write(*,*) 'Overwriting group ', trim(h5File), '/', trim(gStrO) - call h5ldelete_f(h5fId,trim(gStrO),herr) - !Reset gExist and let next block of code recreate it - gExist = .false. - endif - endif - if (.not. gExist) then - !Create group - call h5gcreate_f(h5fId,trim(gStrO),gId,herr) - else - !Open group - call h5gopen_f(h5fId,trim(gStrO),gId,herr) - endif - - if (present(gStrOO)) then - !Create subgroup - call h5gcreate_f(gId,trim(gStrOO),ggId,herr) - outId = ggId - else - outId = gId - endif - else - !Write to root - outId = h5fId - - endif !gStrO - - !Do writing - call WriteVars2ID(IOVars,outId,doIOp) - - !Now done, close up shop - if (present(gStrOO)) call h5gclose_f(ggId,herr) - if (present(gStrO )) call h5gclose_f( gId,herr) - - call h5fclose_f(h5fId,herr) !Close file - call h5close_f(herr) !Close intereface - - end subroutine WriteVars - - subroutine WriteVars2ID(IOVars,outId,doIOp) - type(IOVAR_T), dimension(:), intent(inout) :: IOVars - logical, intent(in) :: doIOp - integer(HID_T), intent(in) :: outId - - integer :: n, Nv - integer(HID_T) :: Nr - - Nv = size(IOVars) - - do n=1,Nv - if (IOVars(n)%toWrite) then - !Variable is ready, write it - - !Treat as scalar attribute if Nr = 0 - Nr = IOVars(n)%Nr - - if (Nr == 0) then - !Scalar attribute - if(IOVars(n)%useHyperslab) then - write(*,*) 'Unable to write attribute "',trim(IOVars(n)%idStr),'" as a hyperslab' - stop - else - call WriteHDFAtt(IOVars(n),outId) - endif - else - !N-rank array - !Create data space, use rank/dim info from IOVar - if(IOVars(n)%useHyperslab) then - write(*,*) 'Writing dataset "',trim(IOVars(n)%idStr),'" as a hyperslab not yet supported' - stop - else - call WriteHDFVar(IOVars(n),outId,doIOP) - endif - endif !Nr=0 - endif !isSet - enddo - - end subroutine WriteVars2ID -!------------------------------------------- -!Routines to read/write individual variables or attributes !FIXME: Assuming double precision for input binary data + !> Read HDF dataset from group subroutine ReadHDFVar(IOVar,gId) type(IOVAR_T), intent(inout) :: IOVar integer(HID_T), intent(in) :: gId @@ -554,8 +508,13 @@ contains integer :: typeClass integer(SIZE_T) :: typeSize character(len=strLen) :: inStr - logical :: aExists + logical :: aExists, dExists + dExists = h5ltfind_dataset_f(gId, trim(IOVar%idStr)) + if (.not. dExists) then + write(*,"(A,A,A)") "Info: Dataset with name '", trim(IOVar%idStr), "' does not exist, skipping read." + return + endif !Start by getting rank, dimensions and total size call h5ltget_dataset_ndims_f(gId,trim(IOVar%idStr),Nr,herr) allocate(dims(Nr)) @@ -567,21 +526,26 @@ contains IOVar%N = N IOVar%dims(1:Nr) = dims - if (allocated(IOVar%data)) then - deallocate(IOVar%data) - endif - allocate(IOVar%data(N)) - !Read based on data type select case(IOVar%vType) - case(IONULL,IOREAL) - call h5ltread_dataset_f(gId,trim(IOVar%idStr),H5T_NATIVE_DOUBLE,IOVar%data,dims,herr) - case default - write(*,*) 'Unknown HDF data type, bailing ...' - stop + case(IONULL,IOREAL) + if (allocated(IOVar%data)) then + deallocate(IOVar%data) + endif + allocate(IOVar%data(N)) + call h5ltread_dataset_f(gId,trim(IOVar%idStr),H5T_NATIVE_DOUBLE,IOVar%data,dims,herr) + case(IOINT) + if (allocated(IOVar%data_int)) then + deallocate(IOVar%data_int) + endif + allocate(IOVar%data_int(N)) + call h5ltread_dataset_f(gId,trim(IOVar%idStr),H5T_NATIVE_INTEGER,IOVar%data_int,dims,herr) + case default + write(*,*) 'Unknown HDF data type, bailing ...' + stop end select - !Check for attribute strings + !Check for attribute strings !Unit call h5aexists_by_name_f(gID,trim(IOVar%idStr),"Units",aExists,herr) if (aExists) then @@ -599,11 +563,12 @@ contains inStr = "NULL" endif IOVar%descStr = inStr + !write(*,"(A,A,A,I)") 'Read dataset ', trim(IOVar%idStr), ' from ', gId IOVar%isDone = .true. end subroutine ReadHDFVar - ! Read a dataset specified as a hyperslab. This assumes a stride of 1 in all dimensions + !> Read a dataset specified as a hyperslab. This assumes a stride of 1 in all dimensions subroutine ReadHDFVarHyper(IOVar,gId) type(IOVAR_T), intent(inout) :: IOVar integer(HID_T), intent(in) :: gId @@ -648,6 +613,7 @@ contains end subroutine ReadHDFVarHyper !FIXME: Add scaling to attributes + !> Read an HDF attribute from a group subroutine ReadHDFAtt(IOVar,gId) type(IOVAR_T), intent(inout) :: IOVar integer(HID_T), intent(in) :: gId @@ -674,7 +640,7 @@ contains write(*,*) 'Unknown HDF data type, bailing ...' stop end select - !write(*,*) 'Read attribute ', IOVar%data(1) + write(*,"(A,A,F,A,I)") 'Read attribute ', trim(IOVar%idStr), IOVar%data(1), " from ", gId IOVar%isDone = .true. end subroutine ReadHDFAtt @@ -814,8 +780,12 @@ contains end subroutine WriteCacheAtt !FIXME: Add scaling to attributes + !> Write a variable as an attribue for the + !> specified HDF group subroutine WriteHDFAtt(IOVar,gId) + !> Variable to write to group type(IOVAR_T), intent(inout) :: IOVar + !> HDF ID for Group integer(HID_T), intent(in) :: gId integer :: herr @@ -823,36 +793,58 @@ contains !Write based on data type select case(IOVar%vType) - case(IONULL,IOREAL) - X = IOVar%data(1) - call writeReal2HDF(gId,trim(IOVar%idStr),X) - case(IOINT) - X = IOVar%data(1) - call writeInt2HDF(gId,trim(IOVar%idStr),int(X)) - case(IOSTR) - call writeString2HDF(gId,trim(IOVar%idStr),trim(IOVar%dStr)) - !call h5ltmake_dataset_string_f(gID,trim(IOVar%idStr),trim(IOVar%dStr),herr) - case default - write(*,*) 'Unknown HDF data type, bailing ...' - stop + case(IONULL,IOREAL) + X = IOVar%data(1) + call writeReal2HDF(gId,trim(IOVar%idStr),X) + case(IOINT) + X = IOVar%data(1) + call writeInt2HDF(gId,trim(IOVar%idStr),int(X)) + case(IOSTR) + call writeString2HDF(gId,trim(IOVar%idStr),trim(IOVar%dStr)) + !call h5ltmake_dataset_string_f(gID,trim(IOVar%idStr),trim(IOVar%dStr),herr) + case default + write(*,*) 'Unknown HDF data type, bailing ...' + stop end select IOVar%isDone = .true. end subroutine WriteHDFAtt !FIXME: Assuming IOP is single and double otherwise !FIXME: Add variable attributes (units, scaling, etc) - subroutine WriteHDFVar(IOVar,gId,doIOPO) - type(IOVAR_T), intent(inout) :: IOVar - integer(HID_T), intent(in) :: gId - logical, intent(in), optional :: doIOPO + !> Write a variable to an HDF dataset and add + !> attributes to the dataset + subroutine WriteHDFVar(IOVar,gId,doIOPO,doCompress) + !> IO Var to write + type(IOVAR_T), intent(inout) :: IOVar + !> Group ID + integer(HID_T), intent(in) :: gId + !> Flag to do IO Precision + logical, intent(in), optional :: doIOPO + !> Flag to compress + logical, intent(in), optional :: doCompress logical :: doIOP !Do IO precision for reals integer :: Nr integer :: herr integer(HSIZE_T) :: h5dims(MAXIODIM) + integer(HID_T) :: dId, sId, pId real(rp) :: vScl - + integer(HSIZE_T), dimension(:), allocatable :: cdims + !real(dp), dimension(:), allocatable, target:: data +#ifdef __ENABLE_COMPRESS + logical :: avail + !type(C_PTR) :: f_ptr + integer :: status + ! Used for ZFP HDF5 plugin + integer(C_INT), dimension(:), allocatable :: cd_values + integer(C_SIZE_T) :: cd_nelmts = 1 + integer :: szip_options_mask + integer :: szip_pixels_per_block +#endif Nr = IOVar%Nr + allocate(cdims(Nr)) + !allocate(data(size(IOVar%data))) + !data = 0.0_dp h5dims(1:Nr) = IOVar%dims(1:Nr) vScl = IOVar%scale @@ -865,14 +857,163 @@ contains !Write based on data type select case(IOVar%vType) case(IONULL,IOREAL) - !Assume real by default - if (doIOP) then - call h5ltmake_dataset_float_f (gId,trim(IOVar%idStr),Nr,h5dims(1:Nr),real(vScl*IOVar%data,sp),herr) + if(.not. doCompress) then + !Assume real by default + if (doIOP) then + call h5ltmake_dataset_float_f(gId,trim(IOVar%idStr), Nr, h5dims(1:Nr), & + real(vScl*IOVar%data,sp),herr) + else + call h5ltmake_dataset_double_f(gId,trim(IOVar%idStr), Nr, h5dims(1:Nr), & + real(vScl*IOVar%data,dp),herr) + endif +#ifdef __ENABLE_COMPRESS else - call h5ltmake_dataset_double_f(gId,trim(IOVar%idStr),Nr,h5dims(1:Nr),real(vScl*IOVar%data,dp),herr) + call h5screate_simple_f(Nr, h5dims(1:Nr), sid, herr) + call h5pcreate_f(H5P_DATASET_CREATE_F, pId, herr) + if (doIOP) then + call CalcChunkSize(sp, h5dims(1:Nr), cdims) + else + call CalcChunkSize(dp, h5dims(1:Nr), cdims) + endif + call h5pset_chunk_f(pId, Nr, cdims, herr) + + if(Z_ALG == ZLIB) then + call H5zfilter_avail_f(H5Z_FILTER_DEFLATE_F, avail, status) + if(avail) then + call h5pset_shuffle(pId, herr) + call h5pset_deflate_f(pId, 6, herr) + else + write(*,*) 'ZLIB filter not available, please ensure HDF5 was built with ZLIB enabled \n' + stop + endif + elseif(Z_ALG == SZIP) then + call H5zfilter_avail_f(H5Z_FILTER_SZIP_F, avail, status) + if(avail) then + szip_options_mask = H5_SZIP_NN_OM_F + if (doIOP) then + szip_pixels_per_block = 16 + else + szip_pixels_per_block = 8 + endif + call H5pset_szip_f(pId, szip_options_mask, szip_pixels_per_block, herr) + else + write(*,*) 'SZIP filter not available, please ensure HDF5 was built with SZIP enabled \n' + stop + endif + elseif(Z_ALG == ZSTD) then + call H5zfilter_avail_f(COMPRESS_ZSTD, avail, status) + if(avail) then + allocate(cd_values(1)) + cd_nelmts = 1 + cd_values(1) = 20 + call h5pset_filter_f(pId, COMPRESS_ZSTD, H5Z_FLAG_MANDATORY, & + cd_nelmts, cd_values, herr) + else + write(*,*) 'ZSTD filter not available, please ensure the ZSTD HDF5 plugin is loaded. \n' + write(*,*) 'You may also use the default compression SZIP without needing plugins.' + stop + endif +#ifdef __ENABLE_ZFP + elseif(Z_ALG == ZFP) then + ! Only necessary for using H5Z_zfp properties calls + ! status = H5Z_zfp_initialize() + + call H5zfilter_avail_f(COMPRESS_ZFP, avail, status) + + if (avail) then + ! Setup ZFP + !------------ + ! Use Plug-in + !------------ + allocate(cd_values(1:H5Z_ZFP_CD_NELMTS_MEM)) + cd_values = 0 + cd_nelmts = H5Z_ZFP_CD_NELMTS_MEM + + if (zfpmode .EQ. H5Z_ZFP_MODE_RATE) then + call H5pset_zfp_rate_cdata(rate, cd_nelmts, cd_values) + if(cd_values(1).NE.1 .OR. cd_nelmts.NE.4) then + print*,'H5Pset_zfp_rate_cdata failed' + stop 1 + endif + else if (zfpmode .EQ. H5Z_ZFP_MODE_PRECISION) then + call H5pset_zfp_precision_cdata(prec, cd_nelmts, cd_values) + if(cd_values(1).NE.2 .OR. cd_nelmts.NE.3) then + print*,'H5Pset_zfp_precision_cdata failed' + stop 1 + endif + else if (zfpmode .EQ. H5Z_ZFP_MODE_ACCURACY) then + call H5pset_zfp_accuracy_cdata(0._dp, cd_nelmts, cd_values) + if(cd_values(1).NE.3 .OR. cd_nelmts.NE.4) then + print*,'H5Pset_zfp_accuracy_cdata failed' + stop 1 + endif + else if (zfpmode .EQ. H5Z_ZFP_MODE_EXPERT) then + call H5pset_zfp_expert_cdata(minbits, maxbits, maxprec, minexp, cd_nelmts, cd_values) + if(cd_values(1).NE.4 .OR. cd_nelmts.NE.6) then + print*,'H5Pset_zfp_expert_cdata failed' + stop 1 + endif + else if (zfpmode .EQ. H5Z_ZFP_MODE_REVERSIBLE) then + call H5pset_zfp_reversible_cdata(cd_nelmts, cd_values) + if(cd_values(1).NE.5 .OR. cd_nelmts.NE.1) then + print*,'H5Pset_zfp_reversible_cdata failed' + stop 1 + endif + endif + + call h5pset_filter_f(pId, COMPRESS_ZFP, H5Z_FLAG_MANDATORY, & + cd_nelmts, cd_values, herr) + + !--------------- + ! Use Properties (this sets the filter) + !--------------- + ! if (zfpmode == H5Z_ZFP_MODE_RATE) then + ! status = H5Pset_zfp_rate(pId, rate) + ! !call check("H5Pset_zfp_rate", status, nerr) + ! else if (zfpmode == H5Z_ZFP_MODE_PRECISION) then + ! status = H5Pset_zfp_precision(pId, prec) + ! !call check("H5Pset_zfp_precision", status, nerr) + ! else if (zfpmode == H5Z_ZFP_MODE_ACCURACY) then + ! status = H5Pset_zfp_accuracy(pId, acc) + ! !call check("H5Pset_zfp_accuracy", status, nerr) + ! else if (zfpmode == H5Z_ZFP_MODE_EXPERT) then + ! status = H5Pset_zfp_expert(pId, minbits, maxbits, maxprec, minexp) + ! !call check("H5Pset_zfp_expert", status, nerr) + ! else if (zfpmode == H5Z_ZFP_MODE_REVERSIBLE) then + ! status = H5Pset_zfp_reversible(pId) + ! !call check("H5Pset_zfp_reversible", status, nerr) + ! endif + ! status = H5Z_zfp_finalize() + else + write(*,*) 'ZFP filter not available, please ensure the ZFP HDF5 plugin is loaded.' + write(*,*) 'You may also use the default compression SZIP without needing plugins.' + stop + endif +#endif + endif + + if (doIOP) then + call h5dcreate_f(gId, trim(IOVar%idStr), H5T_NATIVE_REAL, sId, & + dId, herr, dcpl_id=pId) + !data = vScl*IOVar%data + !f_ptr = C_LOC(data(1)) + call h5dwrite_f(dId, H5T_NATIVE_REAL, real(vScl*IOVar%data,sp), h5dims(1:Nr), herr) + else + call h5dcreate_f(gId, trim(IOVar%idStr), H5T_NATIVE_DOUBLE, sId, & + dId, herr, dcpl_id=pId) + !data = vScl*IOVar%data + !f_ptr = C_LOC(data(1)) + call h5dwrite_f(dId, H5T_NATIVE_DOUBLE, real(vScl*IOVar%data,dp), h5dims(1:Nr), herr) + endif + + call h5pclose_f(pId, herr) + call h5dclose_f(dId, herr) + call h5sclose_f(sId, herr) +#endif endif case(IOINT) - call h5ltmake_dataset_int_f(gId,trim(IOVar%idStr),Nr,h5dims(1:Nr),int(vScl*IOVar%data),herr) + call h5ltmake_dataset_int_f(gId,trim(IOVar%idStr), Nr, h5dims(1:Nr), & + int(vScl*IOVar%data),herr) case default write(*,*) 'Unknown HDF data type, bailing ...' stop @@ -885,8 +1026,210 @@ contains IOVar%isDone = .true. end subroutine WriteHDFVar -!------------------------------------------- -!These routines add data for input to IO chain + !> Write array of IOVar to file fOut (from baseStr), under (optional) group gStrO + !> @note If gStrO unspecified, written to root of HDF5 + !> doIOp is whether to use IOP (ie output slice) or rp (ie restart) + subroutine WriteVars(IOVars,doIOp,baseStr,gStrO,gStrOO,doStampCheckO) + !> Array of IOVars + type(IOVAR_T), dimension(:), intent(inout) :: IOVars + !> Do IO Precision writes + logical, intent(in) :: doIOp + !> Base filename + character(len=*), intent(in) :: baseStr + !> Group Name + character(len=*), intent(in), optional :: gStrO + !> Subgroup Name + character(len=*), intent(in), optional :: gStrOO + !> Check if output file has been stamped + logical , intent(in), optional :: doStampCheckO + + logical :: fExist, gExist, doStampCheck + logical :: writeCache, cacheExist, cacheCreate + logical :: doCompress + integer :: herr + integer(HID_T) :: h5fId, gId, ggId, outId, cacheId + character(len=strLen) :: h5File + type(IOVAR_T) :: stepVar + !Set filename to baseStr + !FIXME: Correct to add .h5 to baseStr + h5File = baseStr + writeCache = .false. + cacheCreate = .false. + doCompress = .false. + + if (present(doStampCheckO)) then + doStampCheck = doStampCheckO + else + doStampCheck = .true. + endif + + !If we're writing to root of this file, then stamp (will ignore if already stamped) + if (.not. present(gStrO) .and. doStampCheck) then + call StampIO(h5File) + endif + + call h5open_f(herr) !Setup Fortran interface + + !Start by opening file, create if necessary + inquire(file=h5File,exist=fExist) + if (fExist) then + !Open file + call h5fopen_f(trim(h5File), H5F_ACC_RDWR_F, h5fId, herr) + else + !Create file + createdThisFile = .true. + call h5fcreate_f(trim(h5File),H5F_ACC_TRUNC_F, h5fId, herr) + endif + + !Figure out output location (outId) and create groups as necessary + if (present(gStrO)) then + !Write to group + !Check if group already exists + call h5lexists_f(h5fId,trim(gStrO),gExist,herr) + if (gExist .and. .not. present(gStrOO)) then + !Group exists (and not writing to subgroup) + !Can either skip it, or kill and replace + if (doSkipG) then + !Group exists, close up and skip it + write(*,*) 'Skipping due to pre-existing group ', trim(gStrO) + + !Close up + call h5fclose_f(h5fId, herr) + call h5close_f(herr) + return + else + !Kill group + write(*,*) 'Overwriting group ', trim(h5File), '/', trim(gStrO) + call h5ldelete_f(h5fId,trim(gStrO),herr) + !Reset gExist and let next block of code recreate it + gExist = .false. + endif + endif + + if (.not. gExist) then + !Create group + call h5gcreate_f(h5fId,trim(gStrO),gId,herr) + else + !Open group + call h5gopen_f(h5fId,trim(gStrO),gId,herr) + endif + + if(trim(toUpper(gStrO(1:5))) == "STEP#") then + writeCache = .true. + !Check if cache group exists + call h5lexists_f(h5fId,trim(attrGrpName),cacheExist,herr) + if (.not. cacheExist) then + if(.not. createdThisFile) then + write(*,*) "Attempt to create the timeAttributeCache in an existing h5 file", & + " that does not have the cache group." + write(*,*) "Perform restart in a different directory, or create the timeAttributeCache", & + " and populate it in the exisitng h5 file." + stop + endif + !Create cache group + call h5gcreate_f(h5fId,trim(attrGrpName),cacheId,herr) + cacheCreate = .true. + endif + ! Open attribute cache group + call h5gopen_f(h5fId,trim(attrGrpName),cacheId,herr) + + ! Check attribute cache size and resize + call CheckAttCacheSize(trim(gStrO), cacheId, cacheExist, cacheCreate) + ! Write Step# to cache + stepVar%Nr = 0 + stepVar%idStr = "step" + stepVar%vType = IOINT + stepVar%data = [GetStepInt(trim(gStrO))] + call WriteCacheAtt(stepVar,cacheId) + endif + + if (present(gStrOO)) then + !Create subgroup + call h5gcreate_f(gId,trim(gStrOO),ggId,herr) + outId = ggId + else + outId = gId + endif + doCompress = ENABLE_COMPRESS + else + !Write to root + outId = h5fId + doCompress = .False. + endif !gStrO + + !Do writing + if(present(doStampCheckO)) then + call WriteVars2ID(IOVars,outId,doIOp, & + doCompress=doCompress,isRoot=.true.) + else + ! Write step vars and attributes to group outId + if(writeCache) then + call WriteVars2ID(IOVars,outId,doIOp, & + cacheId=cacheId,doCompress=doCompress) + else + call WriteVars2ID(IOVars,outId,doIOp,doCompress=doCompress) + end if + end if + + !Now done, close up shop + if (present(gStrOO)) call h5gclose_f(ggId,herr) + if (present(gStrO )) call h5gclose_f( gId,herr) + if (writeCache) call h5gclose_f(cacheId,herr) !Close cache group + + call h5fclose_f(h5fId,herr) !Close file + call h5close_f(herr) !Close intereface + + end subroutine WriteVars + + !> Write out all IOVars to their respective IDs + subroutine WriteVars2ID(IOVars,outId,doIOp,cacheId,doCompress,isRoot) + type(IOVAR_T), dimension(:), intent(inout) :: IOVars + logical, intent(in) :: doIOp + integer(HID_T), intent(in) :: outId + integer(HID_T), intent(in), optional :: cacheId + logical, intent(in), optional :: doCompress + logical, intent(in), optional :: isRoot + integer :: n, Nv + integer(HID_T) :: Nr + Nv = size(IOVars) + + do n=1,Nv + if (IOVars(n)%toWrite) then + !Variable is ready, write it + + !Treat as scalar attribute if Nr = 0 + Nr = IOVars(n)%Nr + + if (Nr == 0) then + !Scalar attribute + if(IOVars(n)%useHyperslab) then + write(*,*) 'Unable to write attribute "',trim(IOVars(n)%idStr),'" as a hyperslab' + stop + else + call WriteHDFAtt(IOVars(n),outId) + if(present(cacheId)) then + call WriteCacheAtt(IOVars(n),cacheId) + endif + endif + else + !N-rank array + !Create data space, use rank/dim info from IOVar + if(IOVars(n)%useHyperslab) then + write(*,*) 'Writing dataset "',trim(IOVars(n)%idStr),'" as a hyperslab not yet supported' + stop + else + call WriteHDFVar(IOVars(n),outId,doIOP,doCompress) + endif + endif !Nr=0 + endif !isSet + enddo + + end subroutine WriteVars2ID + + !------------------------------------------- + !These routines add data for input to IO chain + + !> Add Input Variable to IOVars pool to read subroutine AddInVar(IOVars,idStr,vTypeO,vSclO) type(IOVAR_T), dimension(:), intent(inout) :: IOVars character(len=*), intent(in) :: idStr @@ -913,7 +1256,7 @@ contains end subroutine AddInVar - ! Alternate subroutine to read in a hyperslab from a dataset + !> Alternate subroutine to read in a hyperslab from a dataset subroutine AddInVarHyper(IOVars,idStr,offsets,counts,vTypeO,vSclO) type(IOVAR_T), dimension(:), intent(inout) :: IOVars integer, dimension(:), intent(in) :: offsets @@ -950,7 +1293,7 @@ contains end subroutine AddInVarHyper -!Clears info/memory from an IO chain + !> Clears info/memory from an IO chain subroutine ClearIO(IOVars) type(IOVAR_T), dimension(:), intent(inout) :: IOVars @@ -981,10 +1324,9 @@ contains enddo end subroutine ClearIO - -!----------------------------- -!HDF 5 helper routines - !Converts Fortran real kind to HDF5 precision + !----------------------------- + !HDF 5 helper routines + !> Converts Fortran real kind to HDF5 precision function H5Precision(h5p) result(h5gReal) integer, intent(in) :: h5p integer(HID_T):: h5gReal @@ -998,10 +1340,10 @@ contains end function H5Precision -!----------------------------- -!Write attributes - - !Write integer scalar to attribute + !----------------------------- + !Write attributes + + !> Write integer scalar to attribute subroutine writeInt2HDF(gId,vId,datIn) integer(HID_T), intent(in) :: gId character(len=*),intent(in) :: vId @@ -1026,8 +1368,8 @@ contains end subroutine writeInt2HDF - !Writes a single scalar as attribute to a group/root - !Always uses RP precision + !> Writes a single scalar as attribute to a group/root + !> Always uses RP precision subroutine writeReal2HDF(gId,vId,datIn) integer(HID_T), intent(in) :: gId @@ -1056,6 +1398,8 @@ contains end subroutine writeReal2HDF + !> Write a string to HDF group + !> as an attribute subroutine writeString2HDF(gId,vId,data) integer(HID_T), intent(in) :: gId character(len=*),intent(in) :: vId @@ -1082,10 +1426,10 @@ contains end subroutine writeString2HDF -!----------------------------- -!Read attributes + !----------------------------- + !Read attributes - !Read integer from HDF5 attribute + !> Read integer from HDF5 attribute function readIntHDF(gId,vId,vDefOpt) result(vOut) integer(HID_T), intent(in) :: gId character(len=*),intent(in) :: vId @@ -1113,7 +1457,7 @@ contains call h5aclose_f(attrId,herror) end function readIntHDF - !Read real (rp) from HDF5 attribute + !> Read real (rp) from HDF5 attribute function readRealHDF(gId,vId,vDefOpt) result(vOut) integer(HID_T), intent(in) :: gId character(len=*),intent(in) :: vId diff --git a/src/base/ioH5Types.F90 b/src/base/ioH5Types.F90 index 2a86569f..6c96cff0 100644 --- a/src/base/ioH5Types.F90 +++ b/src/base/ioH5Types.F90 @@ -24,19 +24,26 @@ module ioH5types !vType: holds info about variable type, only partially implemented type IOVAR_T - character(len=strLen) :: idStr="NONE",unitStr="CODE",descStr="DESCRIPTION" - integer(HSIZE_T) :: dims (MAXIODIM) = 0 !Dimension information - integer(HSIZE_T) :: offsets(MAXIODIM) = 0 !Offset for reading/writing a hyperslab, optional + character(len=strLen) :: idStr="NONE" + character(len=strLen) :: unitStr="CODE" + character(len=strLen) :: descStr="DESCRIPTION" + !> Dimension information + integer(HSIZE_T) :: dims (MAXIODIM) = 0 + !> Offset for reading/writing a hyperslab, optional + integer(HSIZE_T) :: offsets(MAXIODIM) = 0 logical :: useHyperslab=.false. - integer :: Nr = 0 !Number of ranks - integer :: N = 0 !Total number of elements - real(rp), dimension(:), allocatable :: data !1D holder for data + !> Number of ranks of dataset + integer :: Nr = 0 + !> Total number of elements + integer :: N = 0 + !> 1D holder for float data + real(rp), dimension(:), allocatable :: data + !> 1D holder for integer data + integer, dimension(:), allocatable :: data_int real(rp) :: scale=1.0, renorm=0.0 logical :: toWrite=.false.,toRead=.false. !Read or write this variable logical :: isDone =.false. !Whether or not variable has been successfully read/written integer :: vType=IONULL character(len=strLen) :: dStr !Optional string data end type IOVAR_T - - end module ioH5types From ef12a8c43b9b5370298ce15de8c168d5cc84dfdc Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Mon, 2 Oct 2023 14:36:54 -0600 Subject: [PATCH 078/365] Include both NH and SH precipitation for beta calculation. --- src/remix/mixconductance.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 390cedd6..a9eb23a7 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -814,10 +814,10 @@ module mixconductance !$OMP private(i,j,phi0_rcm) do j=1,G%Nt do i=1,G%Np - ! Total RCM thermal flux includes the trapped and precipitated. + ! Total RCM thermal flux includes the trapped and precipitated in both NH and SH. ! 1.0e-4 is to convert to [#/cm^2/s] ! sqrt([Pa]*[#/m^3]/[kg]) = sqrt([#/m^4/s^2]) = 1e-4*[#/cm^2/s] - phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 + St%Vars(i,j,IM_ENFLX) + phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 + St%Vars(i,j,IM_ENFLX)*2.0 if(phi0_rcm>TINY) then St%Vars(i,j,IM_BETA) = St%Vars(i,j,IM_ENFLX)/phi0_rcm isAnc(i,j) = .true. ! set points with valid rcm beta as anchore. From 83bf28033761b7466d5cee72e6921fe302dee6ee Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 2 Oct 2023 19:53:35 -0400 Subject: [PATCH 079/365] Added code to make pic3 plots at 1 AU. --- kaipy/gamhelio/helioViz.py | 272 ++++++++++++++++++++++++++++------ scripts/quicklook/heliopic.py | 15 +- 2 files changed, 238 insertions(+), 49 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 2c6e6955..94dacd2e 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2007,9 +2007,50 @@ def PlotEqBz( return Bz +def find_radial_slice(gsph, radius): + """Find the index of the radial slice containing a radius. + + Find the index of the radial slice containing a radius. This is the index + of the radial grid cell edge just less than the given radius. + + This routine should work for LFM grids of any resolution. + + Parameters + ---------- + gsph : kaipy.gamhelio.heliosphere.GamsphPipe + Pipe to simulation results + radius : float + Radius in Rsun + + Returns + ------- + idx : int + Index of radial slice containing radius. + + Raises + ------ + None + """ + # Starting with the last radial layer, work inward until the first layer + # is found with grid radius less than the specified radius. + idx = -1 + r = np.sqrt(gsph.X[idx][0][0]**2 + gsph.Y[idx][0][0]**2 + + gsph.Z[idx][0][0]**2) + while r > radius: + idx -= 1 + r = np.sqrt(gsph.X[idx][0][0]**2 + gsph.Y[idx][0][0]**2 + + gsph.Z[idx][0][0]**2) + + # Convert the index-from-end to an index-from-start. + idx += gsph.Ni + 1 + + # Return the index of the slice containing the radius. + return idx + + def PlotiSlMagV( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind speed at a specified radial slice. @@ -2037,8 +2078,10 @@ def PlotiSlMagV( If True, clear the plot Axes before further plotting. doDeco : bool If True, add axis labels to the plot. - idx : int - Index of radial slice to plot. + idx : int OR float + Index of radial slice to plot, OR radius in Rsun. + idx_is_radius : bool + If True, interpret idx as a radius in Rsun. hgsplot : bool If True, plot in HGS(MJD_plot) frame MJDc : float @@ -2068,21 +2111,55 @@ def PlotiSlMagV( Ax.clear() # Fetch the data. - V = gsph.iSliceMagV(nStp, idx=idx) + if idx_is_radius: + radius = idx - # Fetch the latitude and longitude of the grid cells in the radial slice. - # This is in the GH(MJDc) frame. - lat, lon = gsph.iSliceGrid(idx=idx) + # Determine the index of the radial slice which is just less than the + # specified radius, and compute the next higher, to bracket the + # radius. + i1 = find_radial_slice(gsph, radius) + i2 = i1 + 1 + + # Compute the bracketing radii. + r1 = np.sqrt(gsph.X[i1][0][0]**2 + gsph.Y[i1][0][0]**2 + + gsph.Z[i1][0][0]**2) + r2 = np.sqrt(gsph.X[i2][0][0]**2 + gsph.Y[i2][0][0]**2 + + gsph.Z[i2][0][0]**2) + + # Compute the interpolation slope for each grid point in the layer. + m = (radius - r1)/(r2 - r1) + + # Fetch the values from the bracketing slices, and interpolate to the + # specified radius. + V1 = gsph.iSliceMagV(nStp, idx=i1) + V2 = gsph.iSliceMagV(nStp, idx=i2) + V = (1 - m)*V1 + m*V2 + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=i1) + + else: + + # Fetch the values from the specified layer. + V = gsph.iSliceMagV(nStp, idx=idx) + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=idx) # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Compute the radius of this i-slice from the coordinates of the first - # point in the slice. - rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + - gsph.Z[idx, 0, 0]**2) + # Use the specified radius, or compute the radius of this i-slice from + # the coordinates of the first point in the slice. + if idx_is_radius: + rg = idx + else: + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) # Convert the lat/lon at the radial distance to Cartesian coordinates. lat_rad = np.radians(lat) @@ -2152,7 +2229,7 @@ def PlotiSlMagV( def PlotiSlD( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind number density at a specified radial slice. @@ -2180,8 +2257,10 @@ def PlotiSlD( If True, clear the plot Axes before further plotting. doDeco : bool If True, add axis labels to the plot. - idx : int - Index of radial slice to plot. + idx : int OR float + Index of radial slice to plot, OR radius in Rsun. + idx_is_radius : bool + If True, interpret idx as a radius in Rsun. hgsplot : bool If True, plot in HGS(MJD_plot) frame MJDc : float @@ -2211,21 +2290,55 @@ def PlotiSlD( Ax.clear() # Fetch the data. - D = gsph.iSliceD(nStp, idx=idx) + if idx_is_radius: + radius = idx - # Fetch the latitude and longitude of the grid cells in the radial slice. - # This is in the GH(MJDc) frame. - lat, lon = gsph.iSliceGrid(idx=idx) + # Determine the index of the radial slice which is just less than the + # specified radius, and compute the next higher, to bracket the + # radius. + i1 = find_radial_slice(gsph, radius) + i2 = i1 + 1 + + # Compute the bracketing radii. + r1 = np.sqrt(gsph.X[i1][0][0]**2 + gsph.Y[i1][0][0]**2 + + gsph.Z[i1][0][0]**2) + r2 = np.sqrt(gsph.X[i2][0][0]**2 + gsph.Y[i2][0][0]**2 + + gsph.Z[i2][0][0]**2) + + # Compute the interpolation slope for each grid point in the layer. + m = (radius - r1)/(r2 - r1) + + # Fetch the values from the bracketing slices, and interpolate to the + # specified radius. + D1 = gsph.iSliceD(nStp, idx=i1) + D2 = gsph.iSliceD(nStp, idx=i2) + D = (1 - m)*D1 + m*D2 + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=i1) + + else: + + # Fetch the values from the specified layer. + D = gsph.iSliceD(nStp, idx=idx) + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=idx) # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Compute the radius of this i-slice from the coordinates of the first - # point in the slice. - rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + - gsph.Z[idx, 0, 0]**2) + # Use the specified radius, or compute the radius of this i-slice from + # the coordinates of the first point in the slice. + if idx_is_radius: + rg = idx + else: + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) # Convert the lat/lon at the radial distance to Cartesian coordinates. lat_rad = np.radians(lat) @@ -2297,7 +2410,7 @@ def PlotiSlD( def PlotiSlBr( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind radial magnetic field and current sheet at a specified radial slice. @@ -2320,8 +2433,10 @@ def PlotiSlBr( If True, clear the plot Axes before further plotting. doDeco : bool If True, add axis labels to the plot. - idx : int - Index of radial slice to plot. + idx : int OR float + Index of radial slice to plot, OR radius in Rsun. + idx_is_radius : bool + If True, interpret idx as a radius in Rsun. hgsplot : bool If True, plot in HGS(MJD_plot) frame MJDc : float @@ -2351,11 +2466,42 @@ def PlotiSlBr( Ax.clear() # Fetch the data. - Br = gsph.iSliceBr(nStp, idx=idx) + if idx_is_radius: + radius = idx - # Fetch the latitude and longitude of the grid cells in the radial slice. - # This is in the GH(MJDc) frame. - lat, lon = gsph.iSliceGrid(idx=idx) + # Determine the index of the radial slice which is just less than the + # specified radius, and compute the next higher, to bracket the + # radius. + i1 = find_radial_slice(gsph, radius) + i2 = i1 + 1 + + # Compute the bracketing radii. + r1 = np.sqrt(gsph.X[i1][0][0]**2 + gsph.Y[i1][0][0]**2 + + gsph.Z[i1][0][0]**2) + r2 = np.sqrt(gsph.X[i2][0][0]**2 + gsph.Y[i2][0][0]**2 + + gsph.Z[i2][0][0]**2) + + # Compute the interpolation slope for each grid point in the layer. + m = (radius - r1)/(r2 - r1) + + # Fetch the values from the bracketing slices, and interpolate to the + # specified radius. + Br1 = gsph.iSliceBr(nStp, idx=i1) + Br2 = gsph.iSliceBr(nStp, idx=i2) + Br = (1 - m)*Br1 + m*Br2 + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=i1) + + else: + + # Fetch the values from the specified layer. + Br = gsph.iSliceBr(nStp, idx=idx) + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=idx) # Compute cell-centered lon lat coordinates for contour plot. lonc = 0.25*(lon[:-1, :-1] + lon[:-1, 1:] + lon[1:, :-1] + lon[1:, 1:]) @@ -2366,10 +2512,13 @@ def PlotiSlBr( # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Compute the radius of this i-slice from the coordinates of the first - # point in the slice. - rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + - gsph.Z[idx, 0, 0]**2) + # Use the specified radius, or compute the radius of this i-slice from + # the coordinates of the first point in the slice. + if idx_is_radius: + rg = idx + else: + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) # Convert the lat/lon at the radial distance to Cartesian coordinates. lat_rad = np.radians(lat) @@ -2528,7 +2677,7 @@ def PlotiSlBrRotatingFrame(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True def PlotiSlTemp( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None ): """Plot solar wind temperature at a specified radial slice. @@ -2550,8 +2699,10 @@ def PlotiSlTemp( If True, clear the plot Axes before further plotting. doDeco : bool If True, add axis labels to the plot. - idx : int - Index of radial slice to plot. + idx : int OR float + Index of radial slice to plot, OR radius in Rsun. + idx_is_radius : bool + If True, interpret idx as a radius in Rsun. hgsplot : bool If True, plot in HGS(MJD_plot) frame MJDc : float @@ -2581,22 +2732,55 @@ def PlotiSlTemp( Ax.clear() # Fetch the data. - Temp = gsph.iSliceT(nStp, idx=idx) + if idx_is_radius: + radius = idx - # Fetch the latitude and longitude of the grid cells in the radial slice. - # This is in the GH(MJDc) frame. - lat, lon = gsph.iSliceGrid(idx=idx) + # Determine the index of the radial slice which is just less than the + # specified radius, and compute the next higher, to bracket the + # radius. + i1 = find_radial_slice(gsph, radius) + i2 = i1 + 1 + # Compute the bracketing radii. + r1 = np.sqrt(gsph.X[i1][0][0]**2 + gsph.Y[i1][0][0]**2 + + gsph.Z[i1][0][0]**2) + r2 = np.sqrt(gsph.X[i2][0][0]**2 + gsph.Y[i2][0][0]**2 + + gsph.Z[i2][0][0]**2) + + # Compute the interpolation slope for each grid point in the layer. + m = (radius - r1)/(r2 - r1) + + # Fetch the values from the bracketing slices, and interpolate to the + # specified radius. + Temp1 = gsph.iSliceT(nStp, idx=i1) + Temp2 = gsph.iSliceT(nStp, idx=i2) + Temp = (1 - m)*Temp1 + m*Temp2 + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=i1) + + else: + + # Fetch the values from the specified layer. + Temp = gsph.iSliceT(nStp, idx=idx) + + # Fetch the latitude and longitude of the grid cells in the radial + # slice. This is in the GH(MJDc) frame. + lat, lon = gsph.iSliceGrid(idx=idx) # Plot the data. # If the HGS frame was requested, map the grid corner coordinates from the # GH(MJDc) frame to the HGS(MJD_plot) frame. if hgsplot: - # Compute the radius of this i-slice from the coordinates of the first - # point in the slice. - rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + - gsph.Z[idx, 0, 0]**2) + # Use the specified radius, or compute the radius of this i-slice from + # the coordinates of the first point in the slice. + if idx_is_radius: + rg = idx + else: + rg = np.sqrt(gsph.X[idx, 0, 0]**2 + gsph.Y[idx, 0, 0]**2 + + gsph.Z[idx, 0, 0]**2) # Convert the lat/lon at the radial distance to Cartesian coordinates. lat_rad = np.radians(lat) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 3098e7b7..89513eea 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -468,18 +468,23 @@ def main(): elif pic == "pic3": # Lat/lon plot at 1 AU (the outer edge of the gamhelio grid), in # the modified HGS frame rotating with the Sun. - hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, + AU_RSUN = 215.0 + radius = AU_RSUN + hviz.PlotiSlMagV(gsph, nStp, xyBds, AxL0, AxC1_0, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, + hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1, + hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"1 AU HGS frame at MJD = {ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"1 AU GH frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic4": # Plot at 1 AU in frame rotating with Sun. hviz.PlotiSlBrRotatingFrame(gsph, nStp, xyBds, Ax, AxC) From 14038a44b5ed720d5d855621216742bb35e3c944 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 12:05:36 -0400 Subject: [PATCH 080/365] Converted tabs to spaces. --- kaipy/gamhelio/heliosphere.py | 1094 ++++++++++++++++----------------- 1 file changed, 547 insertions(+), 547 deletions(-) diff --git a/kaipy/gamhelio/heliosphere.py b/kaipy/gamhelio/heliosphere.py index e1e94b20..0e1e146b 100644 --- a/kaipy/gamhelio/heliosphere.py +++ b/kaipy/gamhelio/heliosphere.py @@ -18,652 +18,652 @@ MK = 1.e6 #MegaKelvin #Adapted to helio grid class GamsphPipe(GameraPipe): - #Initialize object, rely on base class, take optional unit identifier - def __init__(self,fdir,ftag,doFast=False,uID="Inner",doParallel=False,nWorkers=4): + #Initialize object, rely on base class, take optional unit identifier + def __init__(self,fdir,ftag,doFast=False,uID="Inner",doParallel=False,nWorkers=4): - print("Initializing %s heliosphere"%(uID)) - - #units for inner helio - self.bScl = 100. #->nT - self.vScl = 150. #-> km/s - self.tScl = 4637. #->seconds - self.dScl = 200. #cm-3 - self.TScl = 1.e-6/4/np.pi/200./kbltz/MK #in MK + print("Initializing %s heliosphere"%(uID)) + + #units for inner helio + self.bScl = 100. #->nT + self.vScl = 150. #-> km/s + self.tScl = 4637. #->seconds + self.dScl = 200. #cm-3 + self.TScl = 1.e-6/4/np.pi/200./kbltz/MK #in MK # units for OHelio - #self.bScl = 5. #->nT - #self.vScl = 34.5 #-> km/s - #self.tScl = 1.4e8/34.5 - #self.dScl = 10. #cm-3 - #self.TScl = 0.144 #in MK + #self.bScl = 5. #->nT + #self.vScl = 34.5 #-> km/s + #self.tScl = 1.4e8/34.5 + #self.dScl = 10. #cm-3 + #self.TScl = 0.144 #in MK - #2D equatorial grid - self.xxi = [] ; self.yyi = [] #corners - self.xxc = [] ; self.yyc = [] #centers + #2D equatorial grid + self.xxi = [] ; self.yyi = [] #corners + self.xxc = [] ; self.yyc = [] #centers - #base class, will use OpenPipe below - GameraPipe.__init__(self,fdir,ftag,doFast=doFast,doParallel=doParallel,nWorkers=nWorkers) + #base class, will use OpenPipe below + GameraPipe.__init__(self,fdir,ftag,doFast=doFast,doParallel=doParallel,nWorkers=nWorkers) - #inner boundary distance - self.R0 = self.xxc[0,0] + #inner boundary distance + self.R0 = self.xxc[0,0] - #j and k for radial profile - self.jRad = self.Nj//2 - self.kRad = self.Nk//4 + #j and k for radial profile + self.jRad = self.Nj//2 + self.kRad = self.Nk//4 - def OpenPipe(self,doVerbose=True): - GameraPipe.OpenPipe(self,doVerbose) - - if (self.UnitsID != "CODE"): - self.bScl = 1.0 #->nT - self.vScl = 1.0 #-> km/s - self.tScl = 1.0 #-> Seconds - self.dScl = 1.0 #-> cm-3 - self.TScl = 1.0/kbltz/MK #-> MKelvin + def OpenPipe(self,doVerbose=True): + GameraPipe.OpenPipe(self,doVerbose) + + if (self.UnitsID != "CODE"): + self.bScl = 1.0 #->nT + self.vScl = 1.0 #-> km/s + self.tScl = 1.0 #-> Seconds + self.dScl = 1.0 #-> cm-3 + self.TScl = 1.0/kbltz/MK #-> MKelvin - #Rescale time - self.T = self.tScl*self.T + #Rescale time + self.T = self.tScl*self.T - Neq_a = self.Nj//2 #cell above eq plane + Neq_a = self.Nj//2 #cell above eq plane - Nr = self.Ni - Np = self.Nk + Nr = self.Ni + Np = self.Nk - #corners in eq XY plane - self.xxi = np.zeros((Nr+1,Np+1)) - self.yyi = np.zeros((Nr+1,Np+1)) - #centers - self.xxc = np.zeros((Nr ,Np )) - self.yyc = np.zeros((Nr ,Np )) - - #Grid for equatorial plane. Should probably be done as a separate function - #equatorial plane - #corners i,k in eq plane, j index is Neq_a - self.xxi[:,:] = self.X[:,Neq_a,:] - self.yyi[:,:] = self.Y[:,Neq_a,:] + #corners in eq XY plane + self.xxi = np.zeros((Nr+1,Np+1)) + self.yyi = np.zeros((Nr+1,Np+1)) + #centers + self.xxc = np.zeros((Nr ,Np )) + self.yyc = np.zeros((Nr ,Np )) + + #Grid for equatorial plane. Should probably be done as a separate function + #equatorial plane + #corners i,k in eq plane, j index is Neq_a + self.xxi[:,:] = self.X[:,Neq_a,:] + self.yyi[:,:] = self.Y[:,Neq_a,:] - #centers i,k - self.xxc = 0.25*(self.xxi[:-1,:-1] + self.xxi[1:,:-1] + self.xxi[:-1,1:] + self.xxi[1:,1:]) - self.yyc = 0.25*(self.yyi[:-1,:-1] + self.yyi[1:,:-1] + self.yyi[:-1,1:] + self.yyi[1:,1:]) - r = np.sqrt(self.xxc**2.0 + self.yyc**2.0) + #centers i,k + self.xxc = 0.25*(self.xxi[:-1,:-1] + self.xxi[1:,:-1] + self.xxi[:-1,1:] + self.xxi[1:,1:]) + self.yyc = 0.25*(self.yyi[:-1,:-1] + self.yyi[1:,:-1] + self.yyi[:-1,1:] + self.yyi[1:,1:]) + r = np.sqrt(self.xxc**2.0 + self.yyc**2.0) - if (self.hasMJD): - print("Found MJD data") - print("\tTime (Min/Max) = %f/%f"%(self.MJDs.min(),self.MJDs.max())) + if (self.hasMJD): + print("Found MJD data") + print("\tTime (Min/Max) = %f/%f"%(self.MJDs.min(),self.MJDs.max())) - #Var eq slice - def EqSlice(self,vID,sID=None,vScl=None,doEq=True,doVerb=True): - #Get full 3D variable first - Q = self.GetVar(vID,sID,vScl,doVerb) + #Var eq slice + def EqSlice(self,vID,sID=None,vScl=None,doEq=True,doVerb=True): + #Get full 3D variable first + Q = self.GetVar(vID,sID,vScl,doVerb) - Nj2 = self.Nj//2 + Nj2 = self.Nj//2 - #above and below the eq plane - ja = Nj2 - 1 - jb = ja + 1 + #above and below the eq plane + ja = Nj2 - 1 + jb = ja + 1 - Nr = self.Ni - Np = self.Nk + Nr = self.Ni + Np = self.Nk - #equatorial j-slice of var - Qj = np.zeros((Nr,Np)) - #taking average above/below eq plane - Qj[:,:] = 0.5*( Q[:,ja,:] + Q[:,jb,:] ) - return Qj - - #Var theta slice - def jSlice(self,vID,sID=None,vScl=None,doEq=True,doVerb=True,jidx=-1): - #Get full 3D variable first - Q = self.GetVar(vID,sID,vScl,doVerb) + #equatorial j-slice of var + Qj = np.zeros((Nr,Np)) + #taking average above/below eq plane + Qj[:,:] = 0.5*( Q[:,ja,:] + Q[:,jb,:] ) + return Qj + + #Var theta slice + def jSlice(self,vID,sID=None,vScl=None,doEq=True,doVerb=True,jidx=-1): + #Get full 3D variable first + Q = self.GetVar(vID,sID,vScl,doVerb) - if(jidx == -1): - Nj2 = self.Nj//2 - else: - Nj2 = jidx - #above and below the j plane - ja = Nj2 - 1 - jb = ja + 1 + if(jidx == -1): + Nj2 = self.Nj//2 + else: + Nj2 = jidx + #above and below the j plane + ja = Nj2 - 1 + jb = ja + 1 - Nr = self.Ni - Np = self.Nk + Nr = self.Ni + Np = self.Nk - #equatorial j-slice of var - Qj = np.zeros((Nr,Np)) - #taking average above/below eq plane - Qj[:,:] = 0.5*( Q[:,ja,:] + Q[:,jb,:] ) - return Qj + #equatorial j-slice of var + Qj = np.zeros((Nr,Np)) + #taking average above/below eq plane + Qj[:,:] = 0.5*( Q[:,ja,:] + Q[:,jb,:] ) + return Qj - #Radial profile thru cell centers - def RadialProfileGrid(self): - self.GetGrid(doVerbose=True) - #cell corners - x = self.X [:,:,:] - y = self.Y [:,:,:] - z = self.Z [:,:,:] - #cell centers - x_c = 0.125*(x[:-1,:-1,:-1]+x[:-1,:-1,1:]+x[:-1,1:,:-1]+x[:-1,1:,1:]+ - x[1:,:-1,:-1]+x[1:,:-1,1:]+x[1:,1:,:-1]+x[1:,1:,1:]) - y_c = 0.125*(y[:-1,:-1,:-1]+y[:-1,:-1,1:]+y[:-1,1:,:-1]+y[:-1,1:,1:]+ - y[1:,:-1,:-1]+y[1:,:-1,1:]+y[1:,1:,:-1]+y[1:,1:,1:]) - z_c = 0.125*(z[:-1,:-1,:-1]+z[:-1,:-1,1:]+z[:-1,1:,:-1]+z[:-1,1:,1:]+ - z[1:,:-1,:-1]+z[1:,:-1,1:]+z[1:,1:,:-1]+z[1:,1:,1:]) - #radius of cell centers - jR = self.jRad - kR = self.kRad - r = np.sqrt(x_c[:,jR,kR]**2.0 + y_c[:,jR,kR]**2.0 + z_c[:,jR,kR]**2.) + #Radial profile thru cell centers + def RadialProfileGrid(self): + self.GetGrid(doVerbose=True) + #cell corners + x = self.X [:,:,:] + y = self.Y [:,:,:] + z = self.Z [:,:,:] + #cell centers + x_c = 0.125*(x[:-1,:-1,:-1]+x[:-1,:-1,1:]+x[:-1,1:,:-1]+x[:-1,1:,1:]+ + x[1:,:-1,:-1]+x[1:,:-1,1:]+x[1:,1:,:-1]+x[1:,1:,1:]) + y_c = 0.125*(y[:-1,:-1,:-1]+y[:-1,:-1,1:]+y[:-1,1:,:-1]+y[:-1,1:,1:]+ + y[1:,:-1,:-1]+y[1:,:-1,1:]+y[1:,1:,:-1]+y[1:,1:,1:]) + z_c = 0.125*(z[:-1,:-1,:-1]+z[:-1,:-1,1:]+z[:-1,1:,:-1]+z[:-1,1:,1:]+ + z[1:,:-1,:-1]+z[1:,:-1,1:]+z[1:,1:,:-1]+z[1:,1:,1:]) + #radius of cell centers + jR = self.jRad + kR = self.kRad + r = np.sqrt(x_c[:,jR,kR]**2.0 + y_c[:,jR,kR]**2.0 + z_c[:,jR,kR]**2.) - return r + return r - #NOT USED merid plane Y=0 - def MeridGrid(self): - #Get Grid - self.GetGrid(doVerbose=True) + #NOT USED merid plane Y=0 + def MeridGrid(self): + #Get Grid + self.GetGrid(doVerbose=True) - Nk2 = self.Nk//2 - Nt = self.Nj - - #kooking from -Y to XZ plane - xright = self.X[:,:,0] #corners - xleft = self.X [:,:,Nk2] + Nk2 = self.Nk//2 + Nt = self.Nj + + #kooking from -Y to XZ plane + xright = self.X[:,:,0] #corners + xleft = self.X [:,:,Nk2] - zright = self.Z[:,:,0] #corners - zleft = self.Z[:,:,Nk2] + zright = self.Z[:,:,0] #corners + zleft = self.Z[:,:,Nk2] - #stack right and left together - xmer = np.hstack( (xright, xleft[:,::-1]) ) #reverse j - zmer = np.hstack( (zright, zleft[:,::-1]) ) #reverse j + #stack right and left together + xmer = np.hstack( (xright, xleft[:,::-1]) ) #reverse j + zmer = np.hstack( (zright, zleft[:,::-1]) ) #reverse j - #cell centers - xmer_c = 0.25*( xmer[:-1,:-1]+xmer[:-1,1:]+xmer[1:,:-1]+xmer[1:,1:] ) - xmer_c = np.delete(xmer_c, Nt, axis = 1) - zmer_c = 0.25*( zmer[:-1,:-1]+zmer[:-1,1:]+zmer[1:,:-1]+zmer[1:,1:] ) - zmer_c = np.delete(zmer_c, Nt, axis = 1) - return xmer_c, zmer_c + #cell centers + xmer_c = 0.25*( xmer[:-1,:-1]+xmer[:-1,1:]+xmer[1:,:-1]+xmer[1:,1:] ) + xmer_c = np.delete(xmer_c, Nt, axis = 1) + zmer_c = 0.25*( zmer[:-1,:-1]+zmer[:-1,1:]+zmer[1:,:-1]+zmer[1:,1:] ) + zmer_c = np.delete(zmer_c, Nt, axis = 1) + return xmer_c, zmer_c - #merid plane from kidx from two halfs - def MeridGridHalfs(self,kidx=None,phi=None): - self.GetGrid(doVerbose=True) + #merid plane from kidx from two halfs + def MeridGridHalfs(self,kidx=None,phi=None): + self.GetGrid(doVerbose=True) - if(kidx): - Nk1 = kidx - Nk2 = self.Nk//2 + Nk1 - elif(phi): - Nk1 = int(phi/(2*np.pi)*self.Nk) - Nk2 = self.Nk//2 + Nk1 - else: - Nk1 = 0 - Nk2 = self.Nk//2 + if(kidx): + Nk1 = kidx + Nk2 = self.Nk//2 + Nk1 + elif(phi): + Nk1 = int(phi/(2*np.pi)*self.Nk) + Nk2 = self.Nk//2 + Nk1 + else: + Nk1 = 0 + Nk2 = self.Nk//2 - #looking from -Y to XZ plane - xright = self.X[:,:,Nk1] #corners - yright = self.Y[:,:,Nk1] #corners - zright = self.Z[:,:,Nk1] #corners + #looking from -Y to XZ plane + xright = self.X[:,:,Nk1] #corners + yright = self.Y[:,:,Nk1] #corners + zright = self.Z[:,:,Nk1] #corners - xleft = self.X[:,:,Nk2] - yleft = self.Y[:,:,Nk2] - zleft = self.Z[:,:,Nk2] - rleft = -np.sqrt(xleft**2. + yleft**2) + xleft = self.X[:,:,Nk2] + yleft = self.Y[:,:,Nk2] + zleft = self.Z[:,:,Nk2] + rleft = -np.sqrt(xleft**2. + yleft**2) - xright_c = 0.25*( xright[:-1,:-1]+xright[:-1,1:]+xright[1:,:-1]+xright[1:,1:] ) - yright_c = 0.25*( yright[:-1,:-1]+yright[:-1,1:]+yright[1:,:-1]+yright[1:,1:] ) - zright_c = 0.25*( zright[:-1,:-1]+zright[:-1,1:]+zright[1:,:-1]+zright[1:,1:] ) - r = np.sqrt(xright_c**2 + zright_c**2 + yright_c**2) + xright_c = 0.25*( xright[:-1,:-1]+xright[:-1,1:]+xright[1:,:-1]+xright[1:,1:] ) + yright_c = 0.25*( yright[:-1,:-1]+yright[:-1,1:]+yright[1:,:-1]+yright[1:,1:] ) + zright_c = 0.25*( zright[:-1,:-1]+zright[:-1,1:]+zright[1:,:-1]+zright[1:,1:] ) + r = np.sqrt(xright_c**2 + zright_c**2 + yright_c**2) - #centers: right plane, left plane, radius - return xright, yright, zright, xleft, yleft, zleft, r + #centers: right plane, left plane, radius + return xright, yright, zright, xleft, yleft, zleft, r - #Grid at 1 AU lat lon - def iSliceGrid(self,idx=-1): - #Get Grid - self.GetGrid(doVerbose=True) + #Grid at 1 AU lat lon + def iSliceGrid(self,idx=-1): + #Get Grid + self.GetGrid(doVerbose=True) - rxy = np.sqrt(self.X**2 + self.Y**2) - theta = np.arctan2(rxy,self.Z) - phi = np.arctan2(self.Y,self.X) + rxy = np.sqrt(self.X**2 + self.Y**2) + theta = np.arctan2(rxy,self.Z) + phi = np.arctan2(self.Y,self.X) - #theta [theta < 0] += np.pi/2. - theta = np.pi/2 - theta - theta = theta*180./np.pi - phi [phi < 0] += 2*np.pi - phi = phi*180./np.pi + #theta [theta < 0] += np.pi/2. + theta = np.pi/2 - theta + theta = theta*180./np.pi + phi [phi < 0] += 2*np.pi + phi = phi*180./np.pi - #last i-index == face of the last cell - lat = theta[idx,::-1,:] - lon = phi[idx,:,:] - #these are corners + #last i-index == face of the last cell + lat = theta[idx,::-1,:] + lon = phi[idx,:,:] + #these are corners - return lat, lon + return lat, lon - #Vars at Y=0 - def MeridSlice(self,vID,sID=None,vScl=None,doVerb=True,indx=(None,None)): - #Get full 3D variable first - Q = self.GetVar(vID,sID,vScl,doVerb) - - Nk2 = self.Nk//2 - kidx, phi = indx - - if(kidx): - Nk1 = kidx - Nk2 = self.Nk//2 + Nk1 - Np = Nk1 - 1 - elif(phi): - Nk1 = int(phi/(2*np.pi)*self.Nk) - Nk2 = self.Nk//2 + Nk1 - Np = Nk1 - 1 - else: - Nk1 = 0 - Nk2 = self.Nk//2 - Np = self.Nk - - #Nr = self.Ni - #Nt = 2*self.Nj - #XZ meridional slice (k=0) of var - #Qj = np.zeros((Nr,Nt)) - - Qright = 0.5*( Q[:,:,Nk1] + Q[:,:,Np-1] ) - Qleft = 0.5*( Q[:,:,Nk2-1] + Q[:,:,Nk2] ) - #print (Qright.shape, Qleft.shape) - #Qj = np.hstack( (Qright, Qleft[:,::-1]) ) #reverse in j - #print (Qj.shape) - return Qright, Qleft + #Vars at Y=0 + def MeridSlice(self,vID,sID=None,vScl=None,doVerb=True,indx=(None,None)): + #Get full 3D variable first + Q = self.GetVar(vID,sID,vScl,doVerb) + + Nk2 = self.Nk//2 + kidx, phi = indx + + if(kidx): + Nk1 = kidx + Nk2 = self.Nk//2 + Nk1 + Np = Nk1 - 1 + elif(phi): + Nk1 = int(phi/(2*np.pi)*self.Nk) + Nk2 = self.Nk//2 + Nk1 + Np = Nk1 - 1 + else: + Nk1 = 0 + Nk2 = self.Nk//2 + Np = self.Nk + + #Nr = self.Ni + #Nt = 2*self.Nj + #XZ meridional slice (k=0) of var + #Qj = np.zeros((Nr,Nt)) + + Qright = 0.5*( Q[:,:,Nk1] + Q[:,:,Np-1] ) + Qleft = 0.5*( Q[:,:,Nk2-1] + Q[:,:,Nk2] ) + #print (Qright.shape, Qleft.shape) + #Qj = np.hstack( (Qright, Qleft[:,::-1]) ) #reverse in j + #print (Qj.shape) + return Qright, Qleft - #Var at 1 AU - def iSliceVar(self,vID,sID=None,vScl=None,doVerb=True,idx=-1): - #Get full 3D variable first - Q = self.GetVar(vID,sID,vScl,doVerb) + #Var at 1 AU + def iSliceVar(self,vID,sID=None,vScl=None,doVerb=True,idx=-1): + #Get full 3D variable first + Q = self.GetVar(vID,sID,vScl,doVerb) - #cell centered values from the last cell - Qi = Q[idx,:,:] + #cell centered values from the last cell + Qi = Q[idx,:,:] #cell centered values from the first cell - #Qi = Q[0,:,:] - #jd_c = self.MJDs[sID] - #print ('jd_c = ', jd_c) - return Qi + #Qi = Q[0,:,:] + #jd_c = self.MJDs[sID] + #print ('jd_c = ', jd_c) + return Qi - #Var along 1D radial line - def RadialProfileVar(self,vID,sID=None,vScl=None,doVerb=True): - #Get full 3D variable first - Q = self.GetVar(vID,sID,vScl,doVerb) + #Var along 1D radial line + def RadialProfileVar(self,vID,sID=None,vScl=None,doVerb=True): + #Get full 3D variable first + Q = self.GetVar(vID,sID,vScl,doVerb) - #set j and k for a radial profile - jR = self.jRad - kR = self.kRad - Nr = self.Ni + #set j and k for a radial profile + jR = self.jRad + kR = self.kRad + Nr = self.Ni - Qi = np.zeros(Nr) + Qi = np.zeros(Nr) #variable in a cell center - Qi = Q[:,jR,kR] - - return Qi + Qi = Q[:,jR,kR] + + return Qi - #Radial Profile: Normalized Density - def RadProfDen(self,s0=0): - D = self.RadialProfileVar("D", s0) - r = self.RadialProfileGrid() - Norm = r**2./r[0]/r[0] - - D = D*Norm*self.dScl - return D + #Radial Profile: Normalized Density + def RadProfDen(self,s0=0): + D = self.RadialProfileVar("D", s0) + r = self.RadialProfileGrid() + Norm = r**2./r[0]/r[0] + + D = D*Norm*self.dScl + return D - #Radial Profile: Speed - def RadProfSpeed(self,s0=0): - Vx = self.RadialProfileVar("Vx", s0) - Vy = self.RadialProfileVar("Vy", s0) - Vz = self.RadialProfileVar("Vz", s0) + #Radial Profile: Speed + def RadProfSpeed(self,s0=0): + Vx = self.RadialProfileVar("Vx", s0) + Vy = self.RadialProfileVar("Vy", s0) + Vz = self.RadialProfileVar("Vz", s0) - MagV = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) - return MagV + MagV = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) + return MagV - #Radial Profile: Normalized Flux rho*V*r^2 - def RadProfFlux(self,s0=0): - D = self.RadialProfileVar("D", s0) - Vx = self.RadialProfileVar("Vx", s0) - Vy = self.RadialProfileVar("Vy", s0) - Vz = self.RadialProfileVar("Vz", s0) - r = self.RadialProfileGrid() - - Norm = r[:]**2./r[0]/r[0] + #Radial Profile: Normalized Flux rho*V*r^2 + def RadProfFlux(self,s0=0): + D = self.RadialProfileVar("D", s0) + Vx = self.RadialProfileVar("Vx", s0) + Vy = self.RadialProfileVar("Vy", s0) + Vz = self.RadialProfileVar("Vz", s0) + r = self.RadialProfileGrid() + + Norm = r[:]**2./r[0]/r[0] - Flux = D*Norm*self.dScl*self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) - return Flux + Flux = D*Norm*self.dScl*self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) + return Flux - #Speed at 1 AU - def iSliceMagV(self,s0=0,idx=-1): - Vx = self.iSliceVar("Vx",s0,idx=idx) #Unscaled - Vy = self.iSliceVar("Vy",s0,idx=idx) #Unscaled - Vz = self.iSliceVar("Vz",s0,idx=idx) #Unscaled - Vi = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) - return Vi + #Speed at 1 AU + def iSliceMagV(self,s0=0,idx=-1): + Vx = self.iSliceVar("Vx",s0,idx=idx) #Unscaled + Vy = self.iSliceVar("Vy",s0,idx=idx) #Unscaled + Vz = self.iSliceVar("Vz",s0,idx=idx) #Unscaled + Vi = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) + return Vi - #Density at 1 AU - def iSliceD(self,s0=0,idx=-1): - Di = self.iSliceVar("D",s0,idx=idx) #Unscaled - Di = Di*self.dScl - return Di + #Density at 1 AU + def iSliceD(self,s0=0,idx=-1): + Di = self.iSliceVar("D",s0,idx=idx) #Unscaled + Di = Di*self.dScl + return Di - #Br at 1 AU - def iSliceBr(self,s0=0,idx=-1): - Bx = self.iSliceVar("Bx",s0,idx=idx) #Unscaled - By = self.iSliceVar("By",s0,idx=idx) #Unscaled - Bz = self.iSliceVar("Bz",s0,idx=idx) #Unscaled + #Br at 1 AU + def iSliceBr(self,s0=0,idx=-1): + Bx = self.iSliceVar("Bx",s0,idx=idx) #Unscaled + By = self.iSliceVar("By",s0,idx=idx) #Unscaled + Bz = self.iSliceVar("Bz",s0,idx=idx) #Unscaled - self.GetGrid(doVerbose=True) - x = self.X[-1,:,:] - y = self.Y[-1,:,:] - z = self.Z[-1,:,:] - #centers - x_c = 0.25*( x[:-1,:-1]+x[:-1,1:]+x[1:,:-1]+x[1:,1:] ) - y_c = 0.25*( y[:-1,:-1]+y[:-1,1:]+y[1:,:-1]+y[1:,1:] ) - z_c = 0.25*( z[:-1,:-1]+z[:-1,1:]+z[1:,:-1]+z[1:,1:] ) - Br = self.bScl*(Bx*x_c + By*y_c + Bz*z_c)/np.sqrt(x_c**2.+y_c**2.+z_c**2.) - return Br + self.GetGrid(doVerbose=True) + x = self.X[-1,:,:] + y = self.Y[-1,:,:] + z = self.Z[-1,:,:] + #centers + x_c = 0.25*( x[:-1,:-1]+x[:-1,1:]+x[1:,:-1]+x[1:,1:] ) + y_c = 0.25*( y[:-1,:-1]+y[:-1,1:]+y[1:,:-1]+y[1:,1:] ) + z_c = 0.25*( z[:-1,:-1]+z[:-1,1:]+z[1:,:-1]+z[1:,1:] ) + Br = self.bScl*(Bx*x_c + By*y_c + Bz*z_c)/np.sqrt(x_c**2.+y_c**2.+z_c**2.) + return Br - #Bx at 1AU - def iSliceBx(self,s0=0,idx=-1): - Bx = self.iSliceVar("Bx",s0,idx=idx) #Unscaled + #Bx at 1AU + def iSliceBx(self,s0=0,idx=-1): + Bx = self.iSliceVar("Bx",s0,idx=idx) #Unscaled - self.GetGrid(doVerbose=True) - x = self.X[-1,:,:] + self.GetGrid(doVerbose=True) + x = self.X[-1,:,:] - #centers - x_c = 0.25*( x[:-1,:-1]+x[:-1,1:]+x[1:,:-1]+x[1:,1:] ) + #centers + x_c = 0.25*( x[:-1,:-1]+x[:-1,1:]+x[1:,:-1]+x[1:,1:] ) - BxScl = self.bScl*Bx*x_c - return BxScl + BxScl = self.bScl*Bx*x_c + return BxScl - #By at 1AU - def iSliceBy(self,s0=0,idx=-1): - By = self.iSliceVar("By",s0,idx=idx) #Unscaled + #By at 1AU + def iSliceBy(self,s0=0,idx=-1): + By = self.iSliceVar("By",s0,idx=idx) #Unscaled - self.GetGrid(doVerbose=True) - y = self.Y[-1,:,:] - #centers - y_c = 0.25*( y[:-1,:-1]+y[:-1,1:]+y[1:,:-1]+y[1:,1:] ) - ByScl = self.bScl*By*y_c - return ByScl - - #Bz at 1AU - def iSliceBz(self,s0=0,idx=-1): - Bz = self.iSliceVar("Bz",s0,idx=idx) #Unscaled + self.GetGrid(doVerbose=True) + y = self.Y[-1,:,:] + #centers + y_c = 0.25*( y[:-1,:-1]+y[:-1,1:]+y[1:,:-1]+y[1:,1:] ) + ByScl = self.bScl*By*y_c + return ByScl + + #Bz at 1AU + def iSliceBz(self,s0=0,idx=-1): + Bz = self.iSliceVar("Bz",s0,idx=idx) #Unscaled - self.GetGrid(doVerbose=True) - z = self.Z[-1,:,:] - #centers + self.GetGrid(doVerbose=True) + z = self.Z[-1,:,:] + #centers - z_c = 0.25*( z[:-1,:-1]+z[:-1,1:]+z[1:,:-1]+z[1:,1:] ) - BzScl = self.bScl*Bz*z_c - return BzScl - - #Br at first cell - def iSliceBrBound(self,s0=0,idx=-1): - Bx = self.iSliceVar("Bx",s0,idx=idx) #Unscaled - By = self.iSliceVar("By",s0,idx=idx) #Unscaled - Bz = self.iSliceVar("Bz",s0,idx=idx) #Unscaled + z_c = 0.25*( z[:-1,:-1]+z[:-1,1:]+z[1:,:-1]+z[1:,1:] ) + BzScl = self.bScl*Bz*z_c + return BzScl + + #Br at first cell + def iSliceBrBound(self,s0=0,idx=-1): + Bx = self.iSliceVar("Bx",s0,idx=idx) #Unscaled + By = self.iSliceVar("By",s0,idx=idx) #Unscaled + Bz = self.iSliceVar("Bz",s0,idx=idx) #Unscaled - self.GetGrid(doVerbose=True) - x = self.X[0,:,:] - y = self.Y[0,:,:] - z = self.Z[0,:,:] - #centers - x_c = 0.25*( x[:-1,:-1]+x[:-1,1:]+x[1:,:-1]+x[1:,1:] ) - y_c = 0.25*( y[:-1,:-1]+y[:-1,1:]+y[1:,:-1]+y[1:,1:] ) - z_c = 0.25*( z[:-1,:-1]+z[:-1,1:]+z[1:,:-1]+z[1:,1:] ) - Br = self.bScl*(Bx*x_c + By*y_c + Bz*z_c)/np.sqrt(x_c**2.+y_c**2.+z_c**2.) + self.GetGrid(doVerbose=True) + x = self.X[0,:,:] + y = self.Y[0,:,:] + z = self.Z[0,:,:] + #centers + x_c = 0.25*( x[:-1,:-1]+x[:-1,1:]+x[1:,:-1]+x[1:,1:] ) + y_c = 0.25*( y[:-1,:-1]+y[:-1,1:]+y[1:,:-1]+y[1:,1:] ) + z_c = 0.25*( z[:-1,:-1]+z[:-1,1:]+z[1:,:-1]+z[1:,1:] ) + Br = self.bScl*(Bx*x_c + By*y_c + Bz*z_c)/np.sqrt(x_c**2.+y_c**2.+z_c**2.) - return Br + return Br - #temperature at 1 AU - def iSliceT(self,s0=0,idx=-1): - Pi = self.iSliceVar("P",s0,idx=idx) #Unscaled - Di = self.iSliceVar("D",s0,idx=idx) #Unscaled + #temperature at 1 AU + def iSliceT(self,s0=0,idx=-1): + Pi = self.iSliceVar("P",s0,idx=idx) #Unscaled + Di = self.iSliceVar("D",s0,idx=idx) #Unscaled - Temp = Pi/Di*self.TScl - return Temp - - #Equatorial speed (in km/s) in eq plane - def eqMagV(self,s0=0): - Vx = self.EqSlice("Vx",s0) #Unscaled - Vy = self.EqSlice("Vy",s0) #Unscaled - Vz = self.EqSlice("Vz",s0) #Unscaled - Veq = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) - return Veq - - #Equatorial speed (in km/s) in eq plane - def jMagV(self,s0=0,jidx=-1): - Vx = self.jSlice("Vx",s0,jidx=jidx) #Unscaled - Vy = self.jSlice("Vy",s0,jidx=jidx) #Unscaled - Vz = self.jSlice("Vz",s0,jidx=jidx) #Unscaled - Veq = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) - return Veq + Temp = Pi/Di*self.TScl + return Temp + + #Equatorial speed (in km/s) in eq plane + def eqMagV(self,s0=0): + Vx = self.EqSlice("Vx",s0) #Unscaled + Vy = self.EqSlice("Vy",s0) #Unscaled + Vz = self.EqSlice("Vz",s0) #Unscaled + Veq = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) + return Veq + + #Equatorial speed (in km/s) in eq plane + def jMagV(self,s0=0,jidx=-1): + Vx = self.jSlice("Vx",s0,jidx=jidx) #Unscaled + Vy = self.jSlice("Vy",s0,jidx=jidx) #Unscaled + Vz = self.jSlice("Vz",s0,jidx=jidx) #Unscaled + Veq = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) + return Veq - #Normalized density (D*r*r/21.5/21.5 in cm-3) in eq plane - def eqNormD (self,s0=0): + #Normalized density (D*r*r/21.5/21.5 in cm-3) in eq plane + def eqNormD (self,s0=0): - D = self.EqSlice("D",s0) #Unscaled + D = self.EqSlice("D",s0) #Unscaled - Norm = (self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 - NormDeq = self.dScl*D*Norm - return NormDeq - - #Normalized density (D*r*r/21.5/21.5 in cm-3) in eq plane - def jNormD (self,s0=0,jidx=-1): + Norm = (self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + NormDeq = self.dScl*D*Norm + return NormDeq + + #Normalized density (D*r*r/21.5/21.5 in cm-3) in eq plane + def jNormD (self,s0=0,jidx=-1): - D = self.jSlice("D",s0,jidx=jidx) #Unscaled + D = self.jSlice("D",s0,jidx=jidx) #Unscaled - Norm = (self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 - NormDeq = self.dScl*D*Norm - return NormDeq + Norm = (self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + NormDeq = self.dScl*D*Norm + return NormDeq - #Normalized Br (Br*r*r/21.5/21.5) in eq plane - def eqNormBr (self,s0=0): - Bx = self.EqSlice("Bx",s0) #Unscaled - By = self.EqSlice("By",s0) #Unscaled - Bz = self.EqSlice("Bz",s0) #Unscaled + #Normalized Br (Br*r*r/21.5/21.5) in eq plane + def eqNormBr (self,s0=0): + Bx = self.EqSlice("Bx",s0) #Unscaled + By = self.EqSlice("By",s0) #Unscaled + Bz = self.EqSlice("Bz",s0) #Unscaled - Br = (Bx*self.xxc + By*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 - - NormBreq = self.bScl*Br - return NormBreq - - #Normalized Br (Br*r*r/21.5/21.5) in eq plane - def jNormBr (self,s0=0,jidx=-1): - Bx = self.jSlice("Bx",s0,jidx=jidx) #Unscaled - By = self.jSlice("By",s0,jidx=jidx) #Unscaled - Bz = self.jSlice("Bz",s0,jidx=jidx) #Unscaled + Br = (Bx*self.xxc + By*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + + NormBreq = self.bScl*Br + return NormBreq + + #Normalized Br (Br*r*r/21.5/21.5) in eq plane + def jNormBr (self,s0=0,jidx=-1): + Bx = self.jSlice("Bx",s0,jidx=jidx) #Unscaled + By = self.jSlice("By",s0,jidx=jidx) #Unscaled + Bz = self.jSlice("Bz",s0,jidx=jidx) #Unscaled - Br = (Bx*self.xxc + By*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 - - NormBreq = self.bScl*Br - return NormBreq - - #Normalized Br (Br*r*r/21.5/21.5) in eq plane - def eqBx (self,s0=0): - Bx = self.EqSlice("Bx",s0) #Unscaled + Br = (Bx*self.xxc + By*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + + NormBreq = self.bScl*Br + return NormBreq + + #Normalized Br (Br*r*r/21.5/21.5) in eq plane + def eqBx (self,s0=0): + Bx = self.EqSlice("Bx",s0) #Unscaled - BxScl = (Bx*self.xxc + Bx*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 - - BxScl = self.bScl*BxScl - return BxScl + BxScl = (Bx*self.xxc + Bx*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + + BxScl = self.bScl*BxScl + return BxScl - #Normalized Br (Br*r*r/21.5/21.5) in eq plane - def eqBy (self,s0=0): - By = self.EqSlice("By",s0) #Unscaled + #Normalized Br (Br*r*r/21.5/21.5) in eq plane + def eqBy (self,s0=0): + By = self.EqSlice("By",s0) #Unscaled - ByScl = (By*self.xxc + By*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 - - ByScl= self.bScl*ByScl - return ByScl + ByScl = (By*self.xxc + By*self.yyc)*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + + ByScl= self.bScl*ByScl + return ByScl - #Normalized Br (Br*r*r/21.5/21.5) in eq plane - def eqBz (self,s0=0): - Bz = self.EqSlice("Bz",s0) #Unscaled + #Normalized Br (Br*r*r/21.5/21.5) in eq plane + def eqBz (self,s0=0): + Bz = self.EqSlice("Bz",s0) #Unscaled - BzScl = Bz - - BzScl = self.bScl*BzScl - return BzScl + BzScl = Bz + + BzScl = self.bScl*BzScl + return BzScl - #Temperature T(r/r0) in eq plane - def eqTemp (self,s0=0): - Pres = self.EqSlice("P",s0) - D = self.EqSlice("D",s0) - - #T(r/r0) - Temp = Pres/D*self.TScl*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0 - - return Temp + #Temperature T(r/r0) in eq plane + def eqTemp (self,s0=0): + Pres = self.EqSlice("P",s0) + D = self.EqSlice("D",s0) + + #T(r/r0) + Temp = Pres/D*self.TScl*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0 + + return Temp - #Temperature T(r/r0) in eq plane - def jTemp (self,s0=0,jidx=-1): - Pres = self.jSlice("P",s0,jidx=jidx) - D = self.jSlice("D",s0,jidx=jidx) - - #T(r/r0) - Temp = Pres/D*self.TScl*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0 - - return Temp - - #Meridional speed (in km/s) in Y=indx plane - def MerMagV(self,s0=0,indx=(None,None)): - Vxr, Vxl = self.MeridSlice("Vx",s0,indx=indx) #Unscaled - Vyr, Vyl = self.MeridSlice("Vy",s0,indx=indx) #Unscaled - Vzr, Vzl = self.MeridSlice("Vz",s0,indx=indx) #Unscaled - MagVr = self.vScl*np.sqrt(Vxr**2.0+Vyr**2.0+Vzr**2.0) - MagVl = self.vScl*np.sqrt(Vxl**2.0+Vyl**2.0+Vzl**2.0) - return MagVr, MagVl + #Temperature T(r/r0) in eq plane + def jTemp (self,s0=0,jidx=-1): + Pres = self.jSlice("P",s0,jidx=jidx) + D = self.jSlice("D",s0,jidx=jidx) + + #T(r/r0) + Temp = Pres/D*self.TScl*np.sqrt(self.xxc**2.0 + self.yyc**2.0)/self.R0 + + return Temp + + #Meridional speed (in km/s) in Y=indx plane + def MerMagV(self,s0=0,indx=(None,None)): + Vxr, Vxl = self.MeridSlice("Vx",s0,indx=indx) #Unscaled + Vyr, Vyl = self.MeridSlice("Vy",s0,indx=indx) #Unscaled + Vzr, Vzl = self.MeridSlice("Vz",s0,indx=indx) #Unscaled + MagVr = self.vScl*np.sqrt(Vxr**2.0+Vyr**2.0+Vzr**2.0) + MagVl = self.vScl*np.sqrt(Vxl**2.0+Vyl**2.0+Vzl**2.0) + return MagVr, MagVl - #Normalized D in Y=indx plane - def MerDNrm(self,s0=0,indx=(None,None)): - xr, yr, zr, xl, yl, zl, r = self.MeridGridHalfs(*indx) - Dr, Dl = self.MeridSlice("D",s0,indx=indx) #Unscaled - Drn = Dr*self.dScl*r*r/self.R0/self.R0 - Dln = Dl*self.dScl*r*r/self.R0/self.R0 - return Drn, Dln + #Normalized D in Y=indx plane + def MerDNrm(self,s0=0,indx=(None,None)): + xr, yr, zr, xl, yl, zl, r = self.MeridGridHalfs(*indx) + Dr, Dl = self.MeridSlice("D",s0,indx=indx) #Unscaled + Drn = Dr*self.dScl*r*r/self.R0/self.R0 + Dln = Dl*self.dScl*r*r/self.R0/self.R0 + return Drn, Dln - #Mormalized Br in Y=indx plane - def MerBrNrm(self,s0=0,indx=(None,None)): - xr, yr, zr, xl, yl, zl, r = self.MeridGridHalfs(*indx) - #rxyr = np.sqrt(xr**2. + yr**2) - #rxyl = -np.sqrt(xl**2. + yl**2) - Bxr, Bxl = self.MeridSlice("Bx",s0,indx=indx) #Unscaled - Byr, Byl = self.MeridSlice("By",s0,indx=indx) #Unscaled - Bzr, Bzl = self.MeridSlice("Bz",s0,indx=indx) #Unscaled + #Mormalized Br in Y=indx plane + def MerBrNrm(self,s0=0,indx=(None,None)): + xr, yr, zr, xl, yl, zl, r = self.MeridGridHalfs(*indx) + #rxyr = np.sqrt(xr**2. + yr**2) + #rxyl = -np.sqrt(xl**2. + yl**2) + Bxr, Bxl = self.MeridSlice("Bx",s0,indx=indx) #Unscaled + Byr, Byl = self.MeridSlice("By",s0,indx=indx) #Unscaled + Bzr, Bzl = self.MeridSlice("Bz",s0,indx=indx) #Unscaled - #cell centers to calculate Br - xr_c = 0.25*( xr[:-1,:-1]+xr[:-1,1:]+xr[1:,:-1]+xr[1:,1:] ) - yr_c = 0.25*( yr[:-1,:-1]+yr[:-1,1:]+yr[1:,:-1]+yr[1:,1:] ) - zr_c = 0.25*( zr[:-1,:-1]+zr[:-1,1:]+zr[1:,:-1]+zr[1:,1:] ) - - xl_c = 0.25*( xl[:-1,:-1]+xl[:-1,1:]+xl[1:,:-1]+xl[1:,1:] ) - yl_c = 0.25*( yl[:-1,:-1]+yl[:-1,1:]+yl[1:,:-1]+yl[1:,1:] ) - zl_c = 0.25*( zl[:-1,:-1]+zl[:-1,1:]+zl[1:,:-1]+zl[1:,1:] ) + #cell centers to calculate Br + xr_c = 0.25*( xr[:-1,:-1]+xr[:-1,1:]+xr[1:,:-1]+xr[1:,1:] ) + yr_c = 0.25*( yr[:-1,:-1]+yr[:-1,1:]+yr[1:,:-1]+yr[1:,1:] ) + zr_c = 0.25*( zr[:-1,:-1]+zr[:-1,1:]+zr[1:,:-1]+zr[1:,1:] ) + + xl_c = 0.25*( xl[:-1,:-1]+xl[:-1,1:]+xl[1:,:-1]+xl[1:,1:] ) + yl_c = 0.25*( yl[:-1,:-1]+yl[:-1,1:]+yl[1:,:-1]+yl[1:,1:] ) + zl_c = 0.25*( zl[:-1,:-1]+zl[:-1,1:]+zl[1:,:-1]+zl[1:,1:] ) - #calculating Br - Br_r = (Bxr*xr_c + Byr*yr_c + Bzr*zr_c)*r*self.bScl/self.R0/self.R0 - Br_l = (Bxl*xl_c + Byl*yl_c + Bzl*zl_c)*r*self.bScl/self.R0/self.R0 - return Br_r, Br_l + #calculating Br + Br_r = (Bxr*xr_c + Byr*yr_c + Bzr*zr_c)*r*self.bScl/self.R0/self.R0 + Br_l = (Bxl*xl_c + Byl*yl_c + Bzl*zl_c)*r*self.bScl/self.R0/self.R0 + return Br_r, Br_l - #Normalized Temp in Y=indx plane - def MerTemp(self,s0=0,indx=(None,None)): - xr, yr, zr, xl, yl, zl, r = self.MeridGridHalfs(*indx) + #Normalized Temp in Y=indx plane + def MerTemp(self,s0=0,indx=(None,None)): + xr, yr, zr, xl, yl, zl, r = self.MeridGridHalfs(*indx) - Pr, Pl = self.MeridSlice("P",s0,indx=indx) #Unscaled - Dr, Dl = self.MeridSlice("D",s0,indx=indx) #Unscaled + Pr, Pl = self.MeridSlice("P",s0,indx=indx) #Unscaled + Dr, Dl = self.MeridSlice("D",s0,indx=indx) #Unscaled - Tempr = Pr/Dr*self.TScl*r/self.R0 - Templ = Pl/Dl*self.TScl*r/self.R0 - return Tempr, Templ + Tempr = Pr/Dr*self.TScl*r/self.R0 + Templ = Pl/Dl*self.TScl*r/self.R0 + return Tempr, Templ - #Not used for helio as of now - #Return data for meridional 2D field lines - #Need to use Cartesian grid - def bStream(self,s0=0,xyBds=[-35,25,-25,25],dx=0.05): - - #Get field data - U = self.bScl*self.EggSlice("Bx",s0,doEq=False) - V = self.bScl*self.EggSlice("Bz",s0,doEq=False) - - x1,y1,gu,gv,gM = self.doStream(U,V,xyBds,dx) - return x1,y1,gu,gv,gM + #Not used for helio as of now + #Return data for meridional 2D field lines + #Need to use Cartesian grid + def bStream(self,s0=0,xyBds=[-35,25,-25,25],dx=0.05): + + #Get field data + U = self.bScl*self.EggSlice("Bx",s0,doEq=False) + V = self.bScl*self.EggSlice("Bz",s0,doEq=False) + + x1,y1,gu,gv,gM = self.doStream(U,V,xyBds,dx) + return x1,y1,gu,gv,gM - def vStream(self,s0=0,xyBds=[-35,25,-25,25],dx=0.05): - #Get field data - U = self.vScl*self.EggSlice("Vx",s0,doEq=True) - V = self.vScl*self.EggSlice("Vy",s0,doEq=True) + def vStream(self,s0=0,xyBds=[-35,25,-25,25],dx=0.05): + #Get field data + U = self.vScl*self.EggSlice("Vx",s0,doEq=True) + V = self.vScl*self.EggSlice("Vy",s0,doEq=True) - x1,y1,gu,gv,gM = self.doStream(U,V,xyBds,dx) - return x1,y1,gu,gv,gM + x1,y1,gu,gv,gM = self.doStream(U,V,xyBds,dx) + return x1,y1,gu,gv,gM - #Add time label, xy is position in axis (not data) coords - def AddTime(self,n,Ax,xy=[0.9,0.95],cLab=dLabC,fs=dLabFS,T0=0.0,doBox=True,BoxC=dBoxC): - ffam = "monospace" - HUGE = 1.0e+8 - #Decide whether to do UT or elapsed - if (self.hasMJD): - minMJD = self.MJDs[n-self.s0] - else: - minMJD = -HUGE - if (self.hasMJD and minMJD>TINY): - from astropy.time import Time - dtObj = Time(self.MJDs[n-self.s0],format='mjd').datetime - tStr = " " + dtObj.strftime("%H:%M:%S") + "\n" + dtObj.strftime("%m/%d/%Y") + #Add time label, xy is position in axis (not data) coords + def AddTime(self,n,Ax,xy=[0.9,0.95],cLab=dLabC,fs=dLabFS,T0=0.0,doBox=True,BoxC=dBoxC): + ffam = "monospace" + HUGE = 1.0e+8 + #Decide whether to do UT or elapsed + if (self.hasMJD): + minMJD = self.MJDs[n-self.s0] + else: + minMJD = -HUGE + if (self.hasMJD and minMJD>TINY): + from astropy.time import Time + dtObj = Time(self.MJDs[n-self.s0],format='mjd').datetime + tStr = " " + dtObj.strftime("%H:%M:%S") + "\n" + dtObj.strftime("%m/%d/%Y") - else: - #Get time in seconds - t = self.T[n-self.s0] - T0 - Nm = int( (t-T0)/60.0 ) #Minutes, integer - Hr = Nm/60 - Min = np.mod(Nm,60) - Sec = np.mod(int(t),60) + else: + #Get time in seconds + t = self.T[n-self.s0] - T0 + Nm = int( (t-T0)/60.0 ) #Minutes, integer + Hr = Nm/60 + Min = np.mod(Nm,60) + Sec = np.mod(int(t),60) - tStr = "Elapsed Time\n %02d:%02d:%02d"%(Hr,Min,Sec) - if (doBox): - Ax.text(xy[0],xy[1],tStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) + tStr = "Elapsed Time\n %02d:%02d:%02d"%(Hr,Min,Sec) + if (doBox): + Ax.text(xy[0],xy[1],tStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) - #def AddSW(self,n,Ax,xy=[0.725,0.025],cLab=dLabC,fs=dLabFS,T0=0.0,doBox=True,BoxC=dBoxC,doAll=True): - # import kaipy.kaiH5 as kh5 - # #Start by getting SW data - # vIDs = ["D","P","Vx","Bx","By","Bz"] - # Nv = len(vIDs) - # qSW = np.zeros(Nv) - # if (self.isMPI): - # fSW = self.fdir + "/" + kh5.genName(self.ftag,self.Ri-1,0,0,self.Ri,self.Rj,self.Rk) - # else: - # fSW = self.fdir + "/" + self.ftag + ".h5" - # - # for i in range(Nv): - # Q = kh5.PullVar(fSW,vIDs[i],n) - # qSW[i] = Q[-1,0,0] - # D = qSW[0] ; P = qSW[1] ; Vx = qSW[2] ; Bx = qSW[3] ; By = qSW[4] ; Bz = qSW[5] - # SWStr = "Solar Wind\n" - # MagB = self.bScl*np.sqrt(Bx**2.0+By**2.0+Bz**2.0) - # #Clock = atan(by/bz), cone = acos(Bx/B) - # r2deg = 180.0/np.pi - # if (MagB>TINY): - # clk = r2deg*np.arctan2(By,Bz) - # cone = r2deg*np.arccos(self.bScl*Bx/MagB) - # else: - # clk = 0.0 - # cone = 0.0 - # if (clk<0): - # clk = clk+360.0 - # Deg = r"$\degree$" - # SWStr = "Solar Wind\nIMF: %4.1f [nT], %5.1f"%(MagB,clk) + Deg# + ", %5.2f"%(cone) + Deg - # if (doAll): - # SWStr = SWStr + "\nDensity: %5.1f [#/cc] \nSpeed: %6.1f [km/s] "%(D,self.vScl*np.abs(Vx)) + #def AddSW(self,n,Ax,xy=[0.725,0.025],cLab=dLabC,fs=dLabFS,T0=0.0,doBox=True,BoxC=dBoxC,doAll=True): + # import kaipy.kaiH5 as kh5 + # #Start by getting SW data + # vIDs = ["D","P","Vx","Bx","By","Bz"] + # Nv = len(vIDs) + # qSW = np.zeros(Nv) + # if (self.isMPI): + # fSW = self.fdir + "/" + kh5.genName(self.ftag,self.Ri-1,0,0,self.Ri,self.Rj,self.Rk) + # else: + # fSW = self.fdir + "/" + self.ftag + ".h5" + # + # for i in range(Nv): + # Q = kh5.PullVar(fSW,vIDs[i],n) + # qSW[i] = Q[-1,0,0] + # D = qSW[0] ; P = qSW[1] ; Vx = qSW[2] ; Bx = qSW[3] ; By = qSW[4] ; Bz = qSW[5] + # SWStr = "Solar Wind\n" + # MagB = self.bScl*np.sqrt(Bx**2.0+By**2.0+Bz**2.0) + # #Clock = atan(by/bz), cone = acos(Bx/B) + # r2deg = 180.0/np.pi + # if (MagB>TINY): + # clk = r2deg*np.arctan2(By,Bz) + # cone = r2deg*np.arccos(self.bScl*Bx/MagB) + # else: + # clk = 0.0 + # cone = 0.0 + # if (clk<0): + # clk = clk+360.0 + # Deg = r"$\degree$" + # SWStr = "Solar Wind\nIMF: %4.1f [nT], %5.1f"%(MagB,clk) + Deg# + ", %5.2f"%(cone) + Deg + # if (doAll): + # SWStr = SWStr + "\nDensity: %5.1f [#/cc] \nSpeed: %6.1f [km/s] "%(D,self.vScl*np.abs(Vx)) - # if (doBox): - # Ax.text(xy[0],xy[1],SWStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) - # else: - # Ax.text(xy[0],xy[1],SWStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) - #def AddCPCP(self,n,Ax,xy=[0.9,0.95],cLab=dLabC,fs=dLabFS,doBox=True,BoxC=dBoxC): - # cpcp = self.GetCPCP(n) - # tStr = "CPCP (North/South)\n%6.2f / %6.2f [kV]"%(cpcp[0],cpcp[1]) - # if (doBox): - # Ax.text(xy[0],xy[1],tStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) - # else: - # Ax.text(xy[0],xy[1],tStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam) + # if (doBox): + # Ax.text(xy[0],xy[1],SWStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) + # else: + # Ax.text(xy[0],xy[1],SWStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) + #def AddCPCP(self,n,Ax,xy=[0.9,0.95],cLab=dLabC,fs=dLabFS,doBox=True,BoxC=dBoxC): + # cpcp = self.GetCPCP(n) + # tStr = "CPCP (North/South)\n%6.2f / %6.2f [kV]"%(cpcp[0],cpcp[1]) + # if (doBox): + # Ax.text(xy[0],xy[1],tStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam,bbox=dict(boxstyle="round",fc=dBoxC)) + # else: + # Ax.text(xy[0],xy[1],tStr,color=cLab,fontsize=fs,transform=Ax.transAxes,family=ffam) - + From fa33419df3f6d3e7f8f00ddc4c6be8af302e641e Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 12:31:41 -0400 Subject: [PATCH 081/365] Corrected computation of R0. --- kaipy/gamhelio/heliosphere.py | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/kaipy/gamhelio/heliosphere.py b/kaipy/gamhelio/heliosphere.py index 0e1e146b..cbdbdc7b 100644 --- a/kaipy/gamhelio/heliosphere.py +++ b/kaipy/gamhelio/heliosphere.py @@ -45,7 +45,7 @@ class GamsphPipe(GameraPipe): GameraPipe.__init__(self,fdir,ftag,doFast=doFast,doParallel=doParallel,nWorkers=nWorkers) #inner boundary distance - self.R0 = self.xxc[0,0] + self.R0 = np.sqrt(self.X[0, 0, 0]**2 + self.Y[0, 0, 0]**2 + self.Z[0, 0, 0]**2) #j and k for radial profile self.jRad = self.Nj//2 @@ -436,15 +436,45 @@ class GamsphPipe(GameraPipe): Veq = self.vScl*np.sqrt(Vx**2.0+Vy**2.0+Vz**2.0) return Veq - #Normalized density (D*r*r/21.5/21.5 in cm-3) in eq plane - def eqNormD (self,s0=0): + def eqNormD (self, s0=0): + """Compute the normalized number density in the equatorial plane. - D = self.EqSlice("D",s0) #Unscaled + Compute the normalized number density in the equatorial plane. + + The number density is normalized by the factor (r/r0)**2, where r0 is + the radius of the inner edge of the grid (should be 21.5 Rsun). + Parameters + ---------- + self : GamsphPipe + This object + s0 : int + Simulation step number to fetch + + Returns + ------- + NormDeq : np.ndarray, shape same as self.xxc + Normalized number density in equatorial plane + + Raises + ------ + None + """ + # Fetch the unscaled data. + D = self.EqSlice("D", s0) + + # Compute the normalization factor for each data point. Norm = (self.xxc**2.0 + self.yyc**2.0)/self.R0/self.R0 + + # Convert the number density from code units (dimensionless) to + # physical units (cm**-3), then normalize the data using the scale + # factor. NormDeq = self.dScl*D*Norm + + # Return the data. return NormDeq + #Normalized density (D*r*r/21.5/21.5 in cm-3) in eq plane def jNormD (self,s0=0,jidx=-1): From eb2a3756d8c47de6bb631532ab14817610c9799b Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 12:32:05 -0400 Subject: [PATCH 082/365] Corrected radial scaling, moved color bar titles to plot titles. --- kaipy/gamhelio/helioViz.py | 16 ++++++++++------ scripts/quicklook/heliopic.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 94dacd2e..49168835 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -156,7 +156,7 @@ def PlotEqMagV( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + kv.genCB(AxCB, vMagV, cbT=None, cM=MagVCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -205,6 +205,7 @@ def PlotEqMagV( # Decorate the plots. if doDeco: + Ax.set_title(r"Speed [km/s]") Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") @@ -981,8 +982,8 @@ def PlotEqD( must be specified. In that case, the coordinates are mapped from the GH(MJDc) frame to the HGS(MJD_plot) frame. - The density is normalized with the factor (r/r0)**2, where r0 is 21.5 Rsun - (the inner edge of the gamhelio grid). + The density is normalized with the factor (r/r0)**2, where r0 is radius of + the inner edge of the gamhelio grid (should be 21.5 Rsun). The gamhelio frame GH is based on the Heliographic Stonyhurst frame (HGS) frame. The difference is that, at any particular MJD: @@ -1037,7 +1038,7 @@ def PlotEqD( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [cm$^{-3}$]", cM=DCM, Ntk=7) + kv.genCB(AxCB, vD, cbT=None, cM=DCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1086,6 +1087,7 @@ def PlotEqD( # Decorate the plots. if doDeco: + Ax.set_title(r"Number density $n$ [$(r/r_0)^2 cm^{-3}$]") Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") Ax.yaxis.tick_right() @@ -1260,7 +1262,7 @@ def PlotEqTemp( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) + kv.genCB(AxCB, vT, cbT=None, cM=TCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1309,6 +1311,7 @@ def PlotEqTemp( # Decorate the plots. if doDeco: + Ax.set_title(r"Temperature $T$ [$(r/r_0) MK$]") Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") @@ -1479,7 +1482,7 @@ def PlotEqBr( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, r"Radial MF $B_r (r/r_0)^2$ [nT]", cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1528,6 +1531,7 @@ def PlotEqBr( # Decorate the plots. if doDeco: + Ax.set_title(r"Radial magnetic field $B_r$ [$(r/r_0)^2 nT$]") Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") Ax.yaxis.tick_right() diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 89513eea..0c2a7852 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -15,7 +15,7 @@ are: Upper left: Solar wind speed (km/s) Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) Lower left: Solar wind temperature scaled by r/r0 (MK) - Lower right: Solar wind radial magnetic field scaled by r/r0 (nT) + Lower right: Solar wind radial magnetic field scaled by (r/r0)**2 (nT) pic2: A 4-panel display showing pcolormesh plots in the y = 0 (meridional, containing Earth) plane of the gamhelio frame used in the simulation. The From a90d1a4659be9dd111a3d807fcb899ed2bf4b6b1 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 12:54:19 -0400 Subject: [PATCH 083/365] Added titles to pic2 plots. --- kaipy/gamhelio/helioViz.py | 44 +++++++++++++++++++++-------------- scripts/quicklook/heliopic.py | 11 +++++---- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 49168835..bd0be418 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -205,9 +205,9 @@ def PlotEqMagV( # Decorate the plots. if doDeco: - Ax.set_title(r"Speed [km/s]") - Ax.set_xlabel(r"$X [R_S]$") - Ax.set_ylabel(r"$Y [R_S]$") + Ax.set_title(r"Speed [$km/s$]") + Ax.set_xlabel(r"$X$ [$R_S$]") + Ax.set_ylabel(r"$Y$ [$R_S$]$") # Return the data. return MagV @@ -377,7 +377,7 @@ def PlotMerMagV( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + kv.genCB(AxCB, vMagV, cbT=None, cM=MagVCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -452,8 +452,10 @@ def PlotMerMagV( # Decorate the plots. if doDeco: - Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") - Ax.set_ylabel("Z [$R_S$]") + Ax.set_title(r"Speed [$km/s$]") + Ax.set_xlabel(r"$R_{XY}$ [$R_S$] at $\phi=" + + f"{phi:{2}.{2}}$ [$rad$]") + Ax.set_ylabel("$Z$ [$R_S$]") # Return the data. return Vr, Vl @@ -530,7 +532,7 @@ def PlotMerDNorm( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2$ [$cm^{-3}$]", cM=DCM, Ntk=7) + kv.genCB(AxCB, vD, cbT=None, cM=DCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -609,7 +611,9 @@ def PlotMerDNorm( # Decorate the plots. if doDeco: - Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_title(r"Number density $n$ [$(r/r_0)^2 cm^{-3}$]") + Ax.set_xlabel(r"$R_{XY}$ [$R_S$] at $\phi=" + + f"{phi:{2}.{2}}$ [$rad$]") Ax.set_ylabel("Z [$R_S$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') @@ -689,7 +693,7 @@ def PlotMerBrNorm( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, r'Radial MF $B_r (r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -808,7 +812,9 @@ def PlotMerBrNorm( # Decorate the plots. if doDeco: - Ax.set_xlabel(r"$R_XY [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_title(r'Radial magnetic field $B_r$ [$(r/r_0)^2 nT$]') + Ax.set_xlabel(r"$R_{XY}$ [$R_S$] at $\phi=" + + f"{phi:{2}.{2}}$ [$rad$]") Ax.set_ylabel('Z [$R_S$]') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') @@ -888,7 +894,7 @@ def PlotMerTemp( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vT, r'Temperature $T(r/r_0)$ [MK]', cM=TCM, Ntk=7) + kv.genCB(AxCB, vT, cbT=None, cM=TCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -963,7 +969,9 @@ def PlotMerTemp( # Decorate the plots. if doDeco: - Ax.set_xlabel(r"$R_{XY} [R_S]$" + f" Phi={phi:{2}.{2}} [rad]") + Ax.set_title(r'Temperature $T$ [$(r/r_0) MK]$') + Ax.set_xlabel(r"$R_{XY}$ [$R_S$] at $\phi=" + + f"{phi:{2}.{2}}$ [$rad$]") Ax.set_ylabel("Z [$R_S$]") # Return the data. @@ -1088,8 +1096,8 @@ def PlotEqD( # Decorate the plots. if doDeco: Ax.set_title(r"Number density $n$ [$(r/r_0)^2 cm^{-3}$]") - Ax.set_xlabel(r"$X [R_S]$") - Ax.set_ylabel(r"$Y [R_S]$") + Ax.set_xlabel(r"$X$ [$R_S$]") + Ax.set_ylabel(r"$Y$ [$R_S$]$") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") @@ -1312,8 +1320,8 @@ def PlotEqTemp( # Decorate the plots. if doDeco: Ax.set_title(r"Temperature $T$ [$(r/r_0) MK$]") - Ax.set_xlabel(r"$X [R_S]$") - Ax.set_ylabel(r"$Y [R_S]$") + Ax.set_xlabel(r"$X$ [$R_S$]") + Ax.set_ylabel(r"$Y$ [$R_S$]$") # Return the data. return Temp @@ -1532,8 +1540,8 @@ def PlotEqBr( # Decorate the plots. if doDeco: Ax.set_title(r"Radial magnetic field $B_r$ [$(r/r_0)^2 nT$]") - Ax.set_xlabel(r"$X [R_S]$") - Ax.set_ylabel(r"$Y [R_S]$") + Ax.set_xlabel(r"$X$ [$R_S$]") + Ax.set_ylabel(r"$Y$ [$R_S$]$") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 0c2a7852..27b4ab0b 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -10,7 +10,7 @@ value of the "pic" argument. pic1 (default): A 4-panel display showing pcolormesh plots in the z = 0 (equatorial) plane of the gamhelio frame used in the simulation. The plots -are: +are (r0 is the inner radius of the grid, which should be 21.5 Rsun): Upper left: Solar wind speed (km/s) Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) @@ -19,12 +19,12 @@ are: pic2: A 4-panel display showing pcolormesh plots in the y = 0 (meridional, containing Earth) plane of the gamhelio frame used in the simulation. The -plots are: +plots are (r0 is the inner radius of the grid, which should be 21.5 Rsun): Upper left: Solar wind speed (km/s) Upper right: Solar wind number density scaled by (r/r0)**2 (cm**-3) Lower left: Solar wind temperature scaled by r/r0 (MK) - Lower right: Solar wind radial magnetic field scaled by r/r0 (nT) + Lower right: Solar wind radial magnetic field scaled by (r/r0)**2 (nT) pic3: A 4-panel display showing pcolormesh plots in the r = 1 AU slice of the gamhelio frame used in the simulation. The plots are: @@ -442,9 +442,10 @@ def main(): hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle("Heliographic Stonyhurst frame at MJD = " + f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"gamhelio frame at MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic2": # Meridional plots in the XZ plane of the modified HGS frame used # by gamhelio. If hgsplot is True, then the plot frame is the true From fb5da9f166c22841fb9ad5f9c2b28640e64ae4b4 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Tue, 3 Oct 2023 11:04:06 -0600 Subject: [PATCH 084/365] Set rcm grid within 5 deg of its high lat boundary as precipitation merging zone. --- src/remix/mixconductance.F90 | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index a9eb23a7..6f5ddaa1 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -9,6 +9,8 @@ module mixconductance use auroralhelper use kai2geo use rcmdefs, ONLY : tiote_RCM + use rice_housekeeping_module, ONLY : HighLatBD + use Rcm_mod_subs, ONLY: dtr ! pi/180.0_rprec implicit none @@ -776,15 +778,25 @@ module mixconductance type(mixState_T), intent(in) :: St logical :: isAnc(G%Np,G%Nt) integer :: i,j + real(rp) :: crit + + ! critical colat to setup precipitation merging zone. + crit = (90.D0-HighLatBD+5.D0)*dtr !$OMP PARALLEL DO default(shared) & !$OMP private(i,j) - do j=1,G%Nt ! use open BC for lat. - do i=1,G%Np ! use periodic BC for lon. - if(St%Vars(i,j,IM_GTYPE)>0.01 .and. St%Vars(i,j,IM_GTYPE)<0.99) then - isAnc(i,j) = .false. - else + do j=1,G%Nt + do i=1,G%Np + if(St%Vars(i,j,IM_GTYPE)<=0.01) then + ! Set grids outside RCM as anchors. isAnc(i,j) = .true. + elseif( St%Vars(i,j,IM_GTYPE)>=0.99 .and. G%t(i,j)>crit ) then + ! Set closed field lines that are >5 deg lower than rcm high lat boundary as anchors. + ! G%t is colat in radians. + isAnc(i,j) = .true. + else + ! In between is the buffer zone for precipitation merging. + isAnc(i,j) = .false. endif enddo ! i enddo ! j @@ -840,7 +852,7 @@ module mixconductance subroutine conductance_smooth(Gr,Q,isAnchor) ! Do smoothing window on ReMIX grid quantity - ! Skip certain points + ! Skip anchor points type(mixGrid_T), intent(in) :: Gr real(rp), intent(inout) :: Q(Gr%Np,Gr%Nt) logical, intent(in) :: isAnchor(Gr%Np,Gr%Nt) @@ -865,12 +877,12 @@ module mixconductance if(.not. isAnchor(i,j)) then jm1 = j-1 jp1 = j+1 - if (j == 1) jm1 = 1 + if (j == 1) jm1 = 1 if (j == Gr%Nt) jp1 = Gr%Nt im1 = i-1 ip1 = i+1 - if (i == 1) im1 = Gr%Np + if (i == 1) im1 = Gr%Np if (i == Gr%Np) ip1 = 1 Ttmp =(temp(im1,jm1)+temp(im1,j)+temp(im1,jp1) & From 3c3a5e74f73df89d756b4aae6f71443f52ed39b2 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 13:09:13 -0400 Subject: [PATCH 085/365] Add plot titles for pic3. --- kaipy/gamhelio/helioViz.py | 28 ++++++++++++++++------------ scripts/quicklook/heliopic.py | 21 ++++++++++++--------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index bd0be418..51c851b1 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2116,7 +2116,7 @@ def PlotiSlMagV( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + kv.genCB(AxCB, vMagV, cbT=None, cM=MagVCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -2232,8 +2232,9 @@ def PlotiSlMagV( # Decorate the plots. if doDeco: - Ax.set_xlabel('Longitude') - Ax.set_ylabel('Latitude') + Ax.set_title(r"Speed [$km/s$]") + Ax.set_xlabel(r"Longitude [$deg$]") + Ax.set_ylabel(r"Latitude [$deg$]") # Return the data. return V @@ -2295,7 +2296,7 @@ def PlotiSlD( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vD, "Density [$cm^{-3}$]", cM=D0CM, Ntk=7) + kv.genCB(AxCB, vD, cbT=None, cM=D0CM, Ntk=7) # Clear the plot Axes. if doClear: @@ -2411,8 +2412,9 @@ def PlotiSlD( # Decorate the plots. if doDeco: - Ax.set_xlabel('Longitude') - Ax.set_ylabel('Latitude') + Ax.set_title("Number density $n$ [$cm^{-3}$]") + Ax.set_xlabel(r"Longitude [$deg$]") + Ax.set_ylabel(r"Latitude [$deg$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') @@ -2471,7 +2473,7 @@ def PlotiSlBr( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, "Radial magnetic field [nT]", cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -2624,8 +2626,9 @@ def PlotiSlBr( # Decorate the plots. if doDeco: - Ax.set_xlabel('Longitude') - Ax.set_ylabel('Latitude') + Ax.set_title(r"Radial magnetic field $B_r$ [$nT$]") + Ax.set_xlabel(r"Longitude [$deg$]") + Ax.set_ylabel(r"Latitude [$deg$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') @@ -2737,7 +2740,7 @@ def PlotiSlTemp( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vT, "Temperature [MK]", cM=TCM, Ntk=7) + kv.genCB(AxCB, vT, cbT=None, cM=TCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -2853,8 +2856,9 @@ def PlotiSlTemp( # Decorate the plots. if doDeco: - Ax.set_xlabel('Longitude') - Ax.set_ylabel('Latitude') + Ax.set_title("Temperature $T$ [$MK$]") + Ax.set_xlabel(r"Longitude [$deg$]") + Ax.set_ylabel(r"Latitude [$deg$]") # Return the data. return Temp diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 27b4ab0b..632d8e27 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -30,9 +30,9 @@ pic3: A 4-panel display showing pcolormesh plots in the r = 1 AU slice of the gamhelio frame used in the simulation. The plots are: Upper left: Solar wind speed (km/s) - Upper right: Solar wind number density (cm**-3) - SCALED? - Lower left: Solar wind temperature (MK) - SCALED? - Lower right: Solar wind radial magnetic field (nT) - SCALED? + Upper right: Solar wind number density (cm**-3) + Lower left: Solar wind temperature (MK) + Lower right: Solar wind radial magnetic field (nT) pic4: A pcolormesh plot in the innermost radial slice (r = 22 Rsun) of the gamhelio frame used in the simulation. The plot shows the radial magnetic @@ -442,10 +442,10 @@ def main(): hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle("Heliographic Stonyhurst frame at MJD = " + fig.suptitle("Heliographic Stonyhurst frame for MJD = " f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"gamhelio frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"gamhelio frame for MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic2": # Meridional plots in the XZ plane of the modified HGS frame used # by gamhelio. If hgsplot is True, then the plot frame is the true @@ -463,9 +463,10 @@ def main(): indx=(None, pic2lon), hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle(f"HGS frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle("Heliographic Stonyhurst frame for MJD = " + f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"GH frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"gamhelio frame for MJD = {ktools.MJD2UT(mjd)}") elif pic == "pic3": # Lat/lon plot at 1 AU (the outer edge of the gamhelio grid), in # the modified HGS frame rotating with the Sun. @@ -483,9 +484,11 @@ def main(): hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle(f"1 AU HGS frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle("Heliographic Stonyhurst frame at 1 AU for MJD = " + f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"1 AU GH frame at MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle("gamhelio frame at 1 AU for MJD = " + f"{ktools.MJD2UT(mjd)}") elif pic == "pic4": # Plot at 1 AU in frame rotating with Sun. hviz.PlotiSlBrRotatingFrame(gsph, nStp, xyBds, Ax, AxC) From 23a3aa39c14a1e854dba557e370804aaf1c98244 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 13:43:50 -0400 Subject: [PATCH 086/365] Tweaked titles and labels. --- kaipy/gamhelio/helioViz.py | 8 ++++---- scripts/quicklook/heliopic.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 51c851b1..08cf3256 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -207,7 +207,7 @@ def PlotEqMagV( if doDeco: Ax.set_title(r"Speed [$km/s$]") Ax.set_xlabel(r"$X$ [$R_S$]") - Ax.set_ylabel(r"$Y$ [$R_S$]$") + Ax.set_ylabel(r"$Y$ [$R_S$]") # Return the data. return MagV @@ -1097,7 +1097,7 @@ def PlotEqD( if doDeco: Ax.set_title(r"Number density $n$ [$(r/r_0)^2 cm^{-3}$]") Ax.set_xlabel(r"$X$ [$R_S$]") - Ax.set_ylabel(r"$Y$ [$R_S$]$") + Ax.set_ylabel(r"$Y$ [$R_S$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") @@ -1321,7 +1321,7 @@ def PlotEqTemp( if doDeco: Ax.set_title(r"Temperature $T$ [$(r/r_0) MK$]") Ax.set_xlabel(r"$X$ [$R_S$]") - Ax.set_ylabel(r"$Y$ [$R_S$]$") + Ax.set_ylabel(r"$Y$ [$R_S$]") # Return the data. return Temp @@ -1541,7 +1541,7 @@ def PlotEqBr( if doDeco: Ax.set_title(r"Radial magnetic field $B_r$ [$(r/r_0)^2 nT$]") Ax.set_xlabel(r"$X$ [$R_S$]") - Ax.set_ylabel(r"$Y$ [$R_S$]$") + Ax.set_ylabel(r"$Y$ [$R_S$]") Ax.yaxis.tick_right() Ax.yaxis.set_label_position("right") diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 632d8e27..01739e54 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -442,10 +442,10 @@ def main(): hviz.PlotEqBr(gsph, nStp, xyBds, AxR1, AxC2_1, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle("Heliographic Stonyhurst frame for MJD = " + fig.suptitle("Heliographic Stonyhurst frame for " f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"gamhelio frame for MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"GAMERA-Helio frame for {ktools.MJD2UT(mjd)}") elif pic == "pic2": # Meridional plots in the XZ plane of the modified HGS frame used # by gamhelio. If hgsplot is True, then the plot frame is the true @@ -463,10 +463,10 @@ def main(): indx=(None, pic2lon), hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle("Heliographic Stonyhurst frame for MJD = " + fig.suptitle("Heliographic Stonyhurst frame for " f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle(f"gamhelio frame for MJD = {ktools.MJD2UT(mjd)}") + fig.suptitle(f"GAMERA-Helio frame for {ktools.MJD2UT(mjd)}") elif pic == "pic3": # Lat/lon plot at 1 AU (the outer edge of the gamhelio grid), in # the modified HGS frame rotating with the Sun. @@ -484,10 +484,10 @@ def main(): hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) if hgsplot: - fig.suptitle("Heliographic Stonyhurst frame at 1 AU for MJD = " + fig.suptitle("Heliographic Stonyhurst frame at 1 AU for " f"{ktools.MJD2UT(mjd)}") else: - fig.suptitle("gamhelio frame at 1 AU for MJD = " + fig.suptitle("GAMERA-Helio frame at 1 AU for " f"{ktools.MJD2UT(mjd)}") elif pic == "pic4": # Plot at 1 AU in frame rotating with Sun. From 4c030eb87fcea57f6b50858d3340901c596df7b5 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 14:05:20 -0400 Subject: [PATCH 087/365] Added use_outer_range option for pic3. --- kaipy/gamhelio/helioViz.py | 53 ++++++++++++++++++++++++----------- scripts/quicklook/heliopic.py | 9 ++++-- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 08cf3256..3f43958a 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -31,8 +31,8 @@ DCM = "copper_r" D0Max = 2000. D0Min = 300. #1 au -#D0Max = 1. -#D0Min = 15. +D0Max_outer = 1. +D0Min_outer = 20. D0CM = "copper_r" TMin = 0.2 @@ -43,23 +43,21 @@ TCM = "copper" T0Min = 0.2 T0Max = 2.0 #1AU -#T0Min = 0.1 -#T0Max = 0.5 +T0Min_outer = 0.05 +T0Max_outer = 1.0 #21.5 R_S BMax = 150. BMin = -150. #1AU -#BMax = 5. -#BMin = -5. BCM = "coolwarm" #21.5 R_S B0Min = -150. B0Max = 150. #1 AU -#B0Min = -4. -#B0Max = 4. +B0Min_outer = -5.0 +B0Max_outer = 5.0 colorProf = "tab:orange" @@ -2242,7 +2240,8 @@ def PlotiSlMagV( def PlotiSlD( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None, + use_outer_range=False ): """Plot solar wind number density at a specified radial slice. @@ -2280,6 +2279,8 @@ def PlotiSlD( MJD used for the GH frame of the simulation MJD_plot : float MJD to use for the HGS frame of the plot + use_outer_range : bool + If True, use a colorbar range optimized for use at large radius. Returns ------- @@ -2290,8 +2291,12 @@ def PlotiSlD( ------ None """ - # Create a normalizer object for the colorbar. - vD = kv.genNorm(D0Min, D0Max, doLog=False, midP=None) + # Create a normalizer object for the colorbar. If the radial slice is in + # the outer regions of the grid, switch to a smaller data range. + if use_outer_range: + vD = kv.genNorm(D0Min_outer, D0Max_outer, doLog=False, midP=None) + else: + vD = kv.genNorm(D0Min, D0Max, doLog=False, midP=None) # Create the color bar. if AxCB: @@ -2424,7 +2429,8 @@ def PlotiSlD( def PlotiSlBr( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None, + use_outer_range=False ): """Plot solar wind radial magnetic field and current sheet at a specified radial slice. @@ -2457,6 +2463,8 @@ def PlotiSlBr( MJD used for the GH frame of the simulation MJD_plot : float MJD to use for the HGS frame of the plot + use_outer_range : bool + If True, use a colorbar range optimized for use at large radius. Returns ------- @@ -2467,8 +2475,12 @@ def PlotiSlBr( ------ None """ - # Create a normalizer object for the colorbar. - vB = kv.genNorm(B0Min, B0Max, doLog=False, midP=None) + # Create a normalizer object for the colorbar. If the radial slice is in + # the outer regions of the grid, switch to a smaller data range. + if use_outer_range: + vB = kv.genNorm(B0Min_outer, B0Max_outer, doLog=False, midP=None) + else: + vB = kv.genNorm(B0Min, B0Max, doLog=False, midP=None) # Create the color bar. if AxCB: @@ -2692,7 +2704,8 @@ def PlotiSlBrRotatingFrame(gsph,nStp,xyBds,Ax,AxCB=None,doClear=True,doDeco=True def PlotiSlTemp( gsph, nStp, xyBds, Ax, AxCB=None, doClear=True, doDeco=True, idx=-1, - idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None + idx_is_radius=False, hgsplot=False, MJDc=None, MJD_plot=None, + use_outer_range=False ): """Plot solar wind temperature at a specified radial slice. @@ -2724,6 +2737,8 @@ def PlotiSlTemp( MJD used for the GH frame of the simulation MJD_plot : float MJD to use for the HGS frame of the plot + use_outer_range : bool + If True, use a colorbar range optimized for use at large radius. Returns ------- @@ -2734,8 +2749,12 @@ def PlotiSlTemp( ------ None """ - # Create a normalizer object for the colorbar. - vT = kv.genNorm(T0Min, T0Max, doLog=False, midP=None) + # Create a normalizer object for the colorbar. If the radial slice is in + # the outer regions of the grid, switch to a smaller data range. + if use_outer_range: + vT = kv.genNorm(T0Min_outer, T0Max_outer, doLog=False, midP=None) + else: + vT = kv.genNorm(T0Min, T0Max, doLog=False, midP=None) # Create the color bar. if AxCB: diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 01739e54..8a6d6a0c 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -477,12 +477,15 @@ def main(): hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotiSlD(gsph, nStp, xyBds, AxR0, AxC2_0, idx=radius, idx_is_radius=True, - hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd, + use_outer_range=True) hviz.PlotiSlTemp(gsph, nStp, xyBds, AxL1, AxC1_1, idx=radius, idx_is_radius=True, - hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd, + use_outer_range=True) hviz.PlotiSlBr(gsph, nStp, xyBds, AxR1, AxC2_1, - hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) + hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd, + use_outer_range=True) if hgsplot: fig.suptitle("Heliographic Stonyhurst frame at 1 AU for " f"{ktools.MJD2UT(mjd)}") From 972f1c3b81e1625d6dd8cb43d3a7b1688275244f Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Tue, 3 Oct 2023 14:13:35 -0400 Subject: [PATCH 088/365] Tweaked temperature limits in outer region. --- kaipy/gamhelio/helioViz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 3f43958a..b450e3be 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -43,8 +43,8 @@ TCM = "copper" T0Min = 0.2 T0Max = 2.0 #1AU -T0Min_outer = 0.05 -T0Max_outer = 1.0 +T0Min_outer = 0.0 +T0Max_outer = 0.3 #21.5 R_S BMax = 150. From 7f56f0dd56ae4b590080066d5b8137ea113f3181 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Tue, 3 Oct 2023 15:32:14 -0600 Subject: [PATCH 089/365] Adjust smoothing threshold. Add rcmlib for remix2remix. --- CMakeLists.txt | 2 +- src/remix/mixconductance.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ddf61c8a..b6ed9f7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ add_executable(remix.x src/drivers/remix.F90) add_executable(remix2remix.x src/drivers/remix2remix.F90) add_executable(remix2rcm.x src/drivers/remix2rcm.F90) target_link_libraries(remix.x remixlib baselib) -target_link_libraries(remix2remix.x remixlib baselib) +target_link_libraries(remix2remix.x remixlib baselib rcmlib) target_link_libraries(remix2rcm.x remixlib baselib) add_dependencies(remix remix.x remix2remix.x remix2rcm.x baselib) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 6f5ddaa1..65324392 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -860,7 +860,7 @@ module mixconductance real(rp) :: thres,mad,Ttmp integer :: i,j,it,im1,ip1,jm1,jp1,MaxIter - thres = 0.025 + thres = 0.01 MaxIter = 15 call FixPole(Gr,Q) From deb65332c07e731ca39f9090975b90e56f9da5cd Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Thu, 5 Oct 2023 07:05:58 -0600 Subject: [PATCH 090/365] Improved beta calculation and gtype smoothing. --- CMakeLists.txt | 2 +- src/remix/mixconductance.F90 | 41 ++++++++++++------------------------ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6ed9f7f..ddf61c8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ add_executable(remix.x src/drivers/remix.F90) add_executable(remix2remix.x src/drivers/remix2remix.F90) add_executable(remix2rcm.x src/drivers/remix2rcm.F90) target_link_libraries(remix.x remixlib baselib) -target_link_libraries(remix2remix.x remixlib baselib rcmlib) +target_link_libraries(remix2remix.x remixlib baselib) target_link_libraries(remix2rcm.x remixlib baselib) add_dependencies(remix remix.x remix2remix.x remix2rcm.x baselib) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 65324392..ff9db68c 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -9,8 +9,6 @@ module mixconductance use auroralhelper use kai2geo use rcmdefs, ONLY : tiote_RCM - use rice_housekeeping_module, ONLY : HighLatBD - use Rcm_mod_subs, ONLY: dtr ! pi/180.0_rprec implicit none @@ -473,8 +471,9 @@ module mixconductance !Get RCM grid weighting: 1=RCM and 0=MHD call conductance_IM_GTYPE(G,St) - ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need IM_GTYPE. + ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need gtype_RCM. call conductance_beta(G,St) + St%Vars(:,:,IM_GTYPE) = gtype_RCM ! Derive mono using the linearized FL relation. call conductance_linmono(conductance,G,St) @@ -517,16 +516,15 @@ module mixconductance else !Mixture wRCM = RampUp(gtype,wC1,wC2-wC1) - if ( (rcm_nflx > TINY) ) then + mix_nflx = wRCM*rcm_nflx + (1-wRCM)*mhd_nflx + if ( mix_nflx > TINY ) then !Mix both - mix_nflx = wRCM*rcm_nflx + (1-wRCM)*mhd_nflx mix_eflx = wRCM*rcm_eflx + (1-wRCM)*mhd_eflx + mix_eavg = mix_eflx/(mix_nflx*kev2erg) else - !RCM data wasn't good so just use MHD - mix_nflx = mhd_nflx - mix_eflx = mhd_eflx + ! Both RCM and MHD data weren't good so just use TINY + mix_eavg = eTINY endif - mix_eavg = mix_eflx/(mix_nflx*kev2erg) St%Vars(i,j,AVG_ENG ) = mix_eavg St%Vars(i,j,NUM_FLUX) = mix_nflx @@ -778,25 +776,16 @@ module mixconductance type(mixState_T), intent(in) :: St logical :: isAnc(G%Np,G%Nt) integer :: i,j - real(rp) :: crit - - ! critical colat to setup precipitation merging zone. - crit = (90.D0-HighLatBD+5.D0)*dtr + isAnc = .false. !$OMP PARALLEL DO default(shared) & !$OMP private(i,j) do j=1,G%Nt do i=1,G%Np if(St%Vars(i,j,IM_GTYPE)<=0.01) then ! Set grids outside RCM as anchors. + ! IM_GTYPE on all RCM buffer and closed grids will be smoothed. isAnc(i,j) = .true. - elseif( St%Vars(i,j,IM_GTYPE)>=0.99 .and. G%t(i,j)>crit ) then - ! Set closed field lines that are >5 deg lower than rcm high lat boundary as anchors. - ! G%t is colat in radians. - isAnc(i,j) = .true. - else - ! In between is the buffer zone for precipitation merging. - isAnc(i,j) = .false. endif enddo ! i enddo ! j @@ -831,13 +820,11 @@ module mixconductance ! sqrt([Pa]*[#/m^3]/[kg]) = sqrt([#/m^4/s^2]) = 1e-4*[#/cm^2/s] phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 + St%Vars(i,j,IM_ENFLX)*2.0 if(phi0_rcm>TINY) then + ! This criterion includes all RCM grid. Note beta is 0 where IM_ENFLX is zero. St%Vars(i,j,IM_BETA) = St%Vars(i,j,IM_ENFLX)/phi0_rcm - isAnc(i,j) = .true. ! set points with valid rcm beta as anchore. - elseif(St%Vars(i,j,IM_GTYPE) > 0.5) then - ! In the low lat, if there is no meaningful RCM precipitation, - ! set beta=0 to freeze other precipitation mechanism. - ! also make it anchor points to avoid smoothing. - St%Vars(i,j,IM_BETA) = 0.0 + endif + if(St%Vars(i,j,IM_ENFLX)>TINY) then + ! This criterion includes all meaningful RCM precipitation. Elsewhere will be smoothed. isAnc(i,j) = .true. endif enddo @@ -860,7 +847,7 @@ module mixconductance real(rp) :: thres,mad,Ttmp integer :: i,j,it,im1,ip1,jm1,jp1,MaxIter - thres = 0.01 + thres = 0.025 MaxIter = 15 call FixPole(Gr,Q) From 3853fd4993ff7e6a5aad8efa7031c75de9d2863b Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Thu, 5 Oct 2023 13:34:52 -0400 Subject: [PATCH 091/365] Updated pic1/2/3 to use new code. --- scripts/quicklook/heliomovie.py | 39 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/scripts/quicklook/heliomovie.py b/scripts/quicklook/heliomovie.py index 2931e99e..b5023838 100755 --- a/scripts/quicklook/heliomovie.py +++ b/scripts/quicklook/heliomovie.py @@ -600,9 +600,11 @@ def create_pic1_movie(args): hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotEqBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - - # Add time in the upper left. - gsph.AddTime(i_step, ax_v, xy=[0.025, 0.875], fs="x-large") + if hgsplot: + fig.suptitle("Heliographic Stonyhurst frame for " + f"{ktools.MJD2UT(mjd)}") + else: + fig.suptitle(f"GAMERA-Helio frame for {ktools.MJD2UT(mjd)}") # Overlay spacecraft positions (optional). if spacecraft: @@ -801,9 +803,11 @@ def create_pic2_movie(args): hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) hviz.PlotMerBrNorm(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - - # Add time in the upper left. - gsph.AddTime(i_step, ax_v, xy=[0.025, 0.875], fs="x-large") + if hgsplot: + fig.suptitle("Heliographic Stonyhurst frame for " + f"{ktools.MJD2UT(mjd)}") + else: + fig.suptitle(f"GAMERA-Helio frame for {ktools.MJD2UT(mjd)}") # Overlay spacecraft positions (optional). if spacecraft: @@ -994,17 +998,26 @@ def create_pic3_movie(args): print(f"mjd = {mjd}") # Create the individual plots for this frame. - hviz.PlotiSlMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v, + AU_RSUN = 215.0 + radius = AU_RSUN + hviz.PlotiSlMagV(gsph, i_step, plot_limits, ax_v, ax_cb_v, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - hviz.PlotiSlD(gsph, i_step, plot_limits, ax_n, ax_cb_n, + hviz.PlotiSlD(gsph, i_step, plot_limits, ax_n, ax_cb_n, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - hviz.PlotiSlTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T, + hviz.PlotiSlTemp(gsph, i_step, plot_limits, ax_T, ax_cb_T, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - hviz.PlotiSlBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, + hviz.PlotiSlBr(gsph, i_step, plot_limits, ax_Br, ax_cb_Br, idx=radius, + idx_is_radius=True, hgsplot=hgsplot, MJDc=MJDc, MJD_plot=mjd) - - # Add time in the upper left. - gsph.AddTime(i_step, ax_v, xy=[0.015, 0.82], fs="small") + if hgsplot: + fig.suptitle("Heliographic Stonyhurst frame at 1 AU for " + f"{ktools.MJD2UT(mjd)}") + else: + fig.suptitle("GAMERA-Helio frame at 1 AU for " + f"{ktools.MJD2UT(mjd)}") # Overlay spacecraft positions (optional). if spacecraft: From 15e04ecb3fa632187bf57dfaedbe58558374e47f Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Thu, 5 Oct 2023 15:08:39 -0400 Subject: [PATCH 092/365] Updated lead comment. --- scripts/makeitso/makeitso.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index 92bc53af..14bfc4d0 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -5,7 +5,10 @@ This master script is used to perform all of the steps needed to prepare and run a kaiju job. This script is interactive - the user is prompted for -each decision that must be made to prepare for the run. +each decision that must be made to prepare for the run. All options provided +by the user are saved in a JSON file when this script is complete. This JSON +file can then be edited and submitted using the --options_path argument to +avoid repeating a lengthy interactive session. """ From df55c6508ff28bcef2f4f8194bfa99e4c35c7c0e Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 6 Oct 2023 13:00:26 -0400 Subject: [PATCH 093/365] pic3 plots now use +/- 180 deg lon range. --- kaipy/gamhelio/helioViz.py | 21 ++++++++++++++++----- scripts/quicklook/heliopic.py | 5 ++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index b450e3be..cbd690b3 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2201,12 +2201,12 @@ def PlotiSlMagV( z = dm.dmarray(c.cartesian.z) # Convert back to lat/lon coordinates. + # -180 <= lon <= +180 deg rxy = np.sqrt(x**2 + y**2) lat_rad = np.arctan2(z, rxy) lon_rad = np.arctan2(y, x) lat = np.degrees(lat_rad) lon = np.degrees(lon_rad) - lon[lon < 0] += 360 # The arrays of lon and lat and data must now be rotated about axis 1 # so that lon values increase monotonically to the right. Find the @@ -2226,6 +2226,9 @@ def PlotiSlMagV( Ax.pcolormesh(lon, lat, V, cmap=MagVCM, norm=vMagV) # Set the plot boundaries. + if hgsplot: + xyBds[0] = -180.0 + xyBds[1] = 180.0 kv.SetAx(xyBds, Ax) # Decorate the plots. @@ -2388,12 +2391,12 @@ def PlotiSlD( z = dm.dmarray(c.cartesian.z) # Convert back to lat/lon coordinates. + # -180 <= lon <= +180 deg rxy = np.sqrt(x**2 + y**2) lat_rad = np.arctan2(z, rxy) lon_rad = np.arctan2(y, x) lat = np.degrees(lat_rad) lon = np.degrees(lon_rad) - lon[lon < 0] += 360 # The arrays of lon and lat and data must now be rotated about axis 1 # so that lon values increase monotonically to the right. Find the @@ -2413,6 +2416,9 @@ def PlotiSlD( Ax.pcolormesh(lon, lat, D, cmap=D0CM, norm=vD) # Set the plot boundaries. + if hgsplot: + xyBds[0] = -180.0 + xyBds[1] = 180.0 kv.SetAx(xyBds, Ax) # Decorate the plots. @@ -2596,12 +2602,12 @@ def PlotiSlBr( zc = dm.dmarray(cc.cartesian.z) # Convert back to lat/lon coordinates. + # -180 <= lon <= +180 deg rxy = np.sqrt(x**2 + y**2) lat_rad = np.arctan2(z, rxy) lon_rad = np.arctan2(y, x) lat = np.degrees(lat_rad) lon = np.degrees(lon_rad) - lon[lon < 0] += 360 # Do the same for cell centers. rxyc = np.sqrt(xc**2 + yc**2) @@ -2609,7 +2615,6 @@ def PlotiSlBr( lonc_rad = np.arctan2(yc, xc) latc = np.degrees(latc_rad) lonc = np.degrees(lonc_rad) - lonc[lonc < 0] += 360 # The arrays of lon and lat and data must now be rotated about axis 1 # so that lon values increase monotonically to the right. Find the @@ -2634,6 +2639,9 @@ def PlotiSlBr( Ax.contour(lonc, latc, Br, [0.], colors='black') # Set the plot boundaries. + if hgsplot: + xyBds[0] = -180.0 + xyBds[1] = 180.0 kv.SetAx(xyBds, Ax) # Decorate the plots. @@ -2846,12 +2854,12 @@ def PlotiSlTemp( z = dm.dmarray(c.cartesian.z) # Convert back to lat/lon coordinates. + # -180 <= lon <= +180 deg rxy = np.sqrt(x**2 + y**2) lat_rad = np.arctan2(z, rxy) lon_rad = np.arctan2(y, x) lat = np.degrees(lat_rad) lon = np.degrees(lon_rad) - lon[lon < 0] += 360 # The arrays of lon and lat and data must now be rotated about axis 1 # so that lon values increase monotonically to the right. Find the @@ -2871,6 +2879,9 @@ def PlotiSlTemp( Ax.pcolormesh(lon, lat, Temp, cmap=TCM, norm=vT) # Set the plot boundaries. + if hgsplot: + xyBds[0] = -180.0 + xyBds[1] = 180.0 kv.SetAx(xyBds, Ax) # Decorate the plots. diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 8a6d6a0c..32413dbf 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -576,15 +576,14 @@ def main(): x_sc, y_sc, z_sc = GHtoHGS(MJDc, x_sc, y_sc, z_sc, mjd) # If needed, compute heliocentric spherical coordinates - # for the interpolated spacecraft position. + # for the interpolated spacecraft position. Longitude is in + # the -180 to +180 range. if pic == "pic3" or pic == "pic4": rxy = np.sqrt(x_sc**2 + y_sc**2) theta = np.arctan2(rxy, z_sc) phi = np.arctan2(y_sc, x_sc) lat = np.degrees(np.pi/2 - theta) lon = np.degrees(phi) - if lon < 0: - lon += 360.0 lat_sc = lat lon_sc = lon From 38b28ed40e590e6c1466b1aa66ff195a375ec062 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 6 Oct 2023 13:34:06 -0400 Subject: [PATCH 094/365] Fixed indexing bug for computing step slice. --- scripts/quicklook/heliopic.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 32413dbf..f4172d8f 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -106,10 +106,10 @@ DESCRIPTION = "Make a quicklook plot for a gamhelio run." DEFAULT_RUNID = "wsa" # List of steps -DEFAULT_STEPS = "-1" +DEFAULT_STEPS = "1" # Default slices -DEFAULT_SLICE = "1:2:1" +DEFAULT_SLICE = None # Code for default picture type. DEFAULT_PICTYPE = "pic1" @@ -373,14 +373,18 @@ def main(): print(f"Open pipe took {toc-tic} s") # Compute the range of time steps to use. - if (slices and steps[0] == -1): - steps = range(gsph.sFin)[slice(slices[0], slices[1], slices[2])] + if slices and steps[0] == -1: + steps = range(gsph.sFin + 1)[slice(slices[0], slices[1], slices[2])] print(f"steps = {steps}") # Get the MJDc value for use in computing the gamhelio frame. fname = gsph.f0 MJDc = scutils.read_MJDc(fname) + # Split the list into individual spacecraft names. + if spacecraft: + spacecraft = spacecraft.split(',') + # Create figures in a memory buffer. mpl.use("Agg") @@ -529,9 +533,6 @@ def main(): # Overlay the spacecraft positions. if spacecraft: - # Split the list into individual spacecraft names. - spacecraft = spacecraft.split(',') - # Fetch the MJD at start and end of the model results. MJD_start = kh5.tStep(fname, 0, aID="MJD") MJD_end = kh5.tStep(fname, gsph.sFin, aID="MJD") From 32b8a9efc0a1796791af1d190ad7132dbccb0329 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 6 Oct 2023 13:40:11 -0400 Subject: [PATCH 095/365] Fixed wraparound bug for spacecraft plot. --- scripts/quicklook/heliopic.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index f4172d8f..a773edee 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -578,7 +578,8 @@ def main(): # If needed, compute heliocentric spherical coordinates # for the interpolated spacecraft position. Longitude is in - # the -180 to +180 range. + # the -180 to +180 range. Convert to 0-360 if not using + # hgsplot. if pic == "pic3" or pic == "pic4": rxy = np.sqrt(x_sc**2 + y_sc**2) theta = np.arctan2(rxy, z_sc) @@ -587,6 +588,9 @@ def main(): lon = np.degrees(phi) lat_sc = lat lon_sc = lon + if not hgsplot: + if lon_sc < 0: + lon_sc += 360 # Plot the position of the spacecraft at the plot time. Each # spacecraft is plotted as a colored dot with a black outline. From 14ec1c3ca8d6da667397ed1ae324d2d06931a908 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Fri, 6 Oct 2023 21:33:06 -0600 Subject: [PATCH 096/365] Use beta as the ratio between precipitation and trapped flux. Trust MHD where RCM beta>1 near the high lat boundary. --- src/remix/mixconductance.F90 | 97 ++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 55 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index ff9db68c..c89e8505 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -20,6 +20,7 @@ module mixconductance !Replacing some hard-coded inline values (bad) w/ module private values (slightly less bad) real(rp), parameter, private :: maxDrop = 20.0 !Hard-coded max potential drop [kV] real(rp), parameter, private :: eTINY = 1.D-8 ! Floor of average energy [keV] + real(rp), parameter, private :: Ne_floor = 0.03e6 ! minimum Ne in [/m^3] when evaluating the linearized FL relation. real(rp), private :: RinMHD = 0.0 !Rin of MHD grid (0 if not running w/ MHD) real(rp), private :: MIXgamma logical , private :: doDrift = .false. !Whether to add drift term from Zhang @@ -468,12 +469,9 @@ module mixconductance wC1 = 0.15 wC2 = 1.0-wC1 - !Get RCM grid weighting: 1=RCM and 0=MHD - call conductance_IM_GTYPE(G,St) - - ! derive spatially varying beta using RCM precipitation and thermal fluxes. Need gtype_RCM. - call conductance_beta(G,St) - St%Vars(:,:,IM_GTYPE) = gtype_RCM + ! derive spatially varying beta and gtype + ! using RCM precipitation and thermal fluxes. + call conductance_beta_gtype(G,St) ! Derive mono using the linearized FL relation. call conductance_linmono(conductance,G,St) @@ -486,7 +484,6 @@ module mixconductance do j=1,G%Nt do i=1,G%Np !Start by figuring out where we are - !gtype = St%Vars(i,j,IM_GTYPE) gtype = gtype_RCM(i,j) isRCM = gtype >= wC2 isMHD = gtype <= wC1 @@ -543,7 +540,6 @@ module mixconductance real(rp) :: signOfY, signOfJ integer :: i,j real(rp) :: D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT - real(rp) :: Ne_floor if (St%hemisphere==NORTH) then signOfY = -1 @@ -555,8 +551,6 @@ module mixconductance stop 'Wrong hemisphere label. Stopping...' endif - Ne_floor = 0.03e6 ! minimum Ne in [/m^3] when evaluating the linearized FL relation. - !$OMP PARALLEL DO default(shared) & !$OMP private(i,j,D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT) do j=1,G%Nt @@ -579,6 +573,9 @@ module mixconductance kT = Pe/Ne/kev2J conductance%E0 (i,j) = 2.0*kT ! Thermal number flux from MHD electron fluxes in [#/cm^2/s]. + ! Treat MHD flux on closed field lines as trapped flux. + ! Beta now stands for the ratio between precipitation and trapped. + ! Note phi0 can be zero in the polar cap. phi0 = beta_RCM(i,j)* sqrt(Pe*Ne/(2.0D-3*PI*Me_cgs))*1.0D-4 conductance%phi0(i,j) = phi0 @@ -604,7 +601,6 @@ module mixconductance St%Vars(i,j,NUM_FLUX) = St%Vars(i,j,Z_NFLUX) ! [#/cm^2/s] enddo ! i enddo ! j - end subroutine conductance_linmono subroutine conductance_mr(conductance,G,St) @@ -771,71 +767,62 @@ module mixconductance end where end subroutine conductance_auroralmask - subroutine conductance_IM_GTYPE(G,St) - type(mixGrid_T), intent(in) :: G - type(mixState_T), intent(in) :: St - logical :: isAnc(G%Np,G%Nt) - integer :: i,j - - isAnc = .false. - !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j) - do j=1,G%Nt - do i=1,G%Np - if(St%Vars(i,j,IM_GTYPE)<=0.01) then - ! Set grids outside RCM as anchors. - ! IM_GTYPE on all RCM buffer and closed grids will be smoothed. - isAnc(i,j) = .true. - endif - enddo ! i - enddo ! j - gtype_RCM = St%Vars(:,:,IM_GTYPE) ! supposed to be between 0 and 1. - call conductance_smooth(G,gtype_RCM,isAnc) - - gtype_RCM = min(gtype_RCM,1.0) - gtype_RCM = max(gtype_RCM,0.0) - end subroutine conductance_IM_GTYPE - - subroutine conductance_beta(G,St) + subroutine conductance_beta_gtype(G,St) ! Use RCM precipitation and source population to derive the loss cone rate beta. ! Assume beta is one in the polar cap and smooth across RCM boundary. type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - logical :: isAnc(G%Np,G%Nt) - real(rp) :: temp(G%Np,G%Nt) + logical :: isAncB(G%Np,G%Nt),isAncG(G%Np,G%Nt) real(rp) :: phi0_rcm integer :: i,j + ! Initialize IM_BETA with 1, assuming all flux can be precipitated. + ! IM_GTYPE is interpolated from RCM: 1=RCM and 0=MHD. St%Vars(:,:,IM_BETA) = 1.0 - isAnc = .false. - ! set the first circle around pole as anchore. - isAnc(:,1) = .true. + isAncB = .false. ! beta is smoothed everywhere. + isAncG = .false. !$OMP PARALLEL DO default(shared) & !$OMP private(i,j,phi0_rcm) do j=1,G%Nt do i=1,G%Np - ! Total RCM thermal flux includes the trapped and precipitated in both NH and SH. - ! 1.0e-4 is to convert to [#/cm^2/s] + ! Set grids outside RCM as anchors that won't be smoothed. + if(St%Vars(i,j,IM_GTYPE)<=0.01) then + isAncG(i,j) = .true. + endif + + ! Derive beta as the ratio between RCM precipitation and trapped flux. ! sqrt([Pa]*[#/m^3]/[kg]) = sqrt([#/m^4/s^2]) = 1e-4*[#/cm^2/s] - phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 + St%Vars(i,j,IM_ENFLX)*2.0 + phi0_rcm = sqrt(St%Vars(i,j,IM_EPRE)*St%Vars(i,j,IM_EDEN)/(Me_cgs*1e-3*2*pi))*1.0e-4 if(phi0_rcm>TINY) then ! This criterion includes all RCM grid. Note beta is 0 where IM_ENFLX is zero. St%Vars(i,j,IM_BETA) = St%Vars(i,j,IM_ENFLX)/phi0_rcm - endif - if(St%Vars(i,j,IM_ENFLX)>TINY) then - ! This criterion includes all meaningful RCM precipitation. Elsewhere will be smoothed. - isAnc(i,j) = .true. + if(St%Vars(i,j,IM_BETA)>1.0) then + ! Near RCM high lat boundary, trapped flux is not well constrained. + ! The beta there can be easily >>1 and is thus not as reliable. + ! Reset grid weight to MHD if RCM beta>1. + St%Vars(i,j,IM_GTYPE) = 0.0 !1.0/St%Vars(i,j,IM_BETA)**2 + St%Vars(i,j,IM_BETA) = 1.0 + isAncG(i,j) = .true. + endif endif enddo enddo - temp = St%Vars(:,:,IM_BETA) ! supposed to be between 0 and 1. - call conductance_smooth(G,temp,isAnc) - St%Vars(:,:,IM_BETA) = temp - ! IM_BETA is for output. beta_RCM is used in calculation. - beta_RCM = min(St%Vars(:,:,IM_BETA), 1.0) - end subroutine conductance_beta + ! Smooth IM_BETA and save in beta_RCM + beta_RCM = St%Vars(:,:,IM_BETA) + call conductance_smooth(G,beta_RCM,isAncB) + St%Vars(:,:,IM_BETA) = beta_RCM + beta_RCM = min(beta_RCM,1.0) + beta_RCM = max(beta_RCM,0.0) + + ! Smooth IM_GTYPE and save in gtype_RCM + gtype_RCM = St%Vars(:,:,IM_GTYPE) + call conductance_smooth(G,gtype_RCM,isAncG) + St%Vars(:,:,IM_GTYPE) = gtype_RCM + gtype_RCM = min(gtype_RCM,1.0) + gtype_RCM = max(gtype_RCM,0.0) + end subroutine conductance_beta_gtype subroutine conductance_smooth(Gr,Q,isAnchor) ! Do smoothing window on ReMIX grid quantity From 180a7dbb6db35459c8e9359c010e7f3b87f4f829 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Sat, 7 Oct 2023 13:41:15 -0600 Subject: [PATCH 097/365] Minor fix of tmpC/D/E/F in mixconductance. --- src/remix/mixconductance.F90 | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index c89e8505..2980b46e 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -12,9 +12,7 @@ module mixconductance implicit none - real(rp), dimension(:,:), allocatable, private :: tmpD,tmpC ! used for chilling in Fedder95. Declare it here so we can allocate in init. real(rp), dimension(:,:), allocatable, private :: JF0,RM,RRdi ! used for zhang15 - real(rp), dimension(:,:), allocatable, private :: tmpE,tmpF ! used for smoothing precipitation avg_eng and num_flux real(rp), dimension(:,:), allocatable, private :: beta_RCM,alpha_RCM,gtype_RCM ! two-dimensional beta based on RCM fluxes. !Replacing some hard-coded inline values (bad) w/ module private values (slightly less bad) @@ -74,24 +72,16 @@ module mixconductance if (.not. allocated(conductance%PrecipMask)) allocate(conductance%PrecipMask(G%Np,G%Nt)) ! these arrays are global and should not be! reallocate them - if(allocated(tmpD)) deallocate(tmpD) - if(allocated(tmpC)) deallocate(tmpC) if(allocated(JF0)) deallocate(JF0) if(allocated(RM)) deallocate(RM) if(allocated(RRdi)) deallocate(RRdi) - if(allocated(tmpE)) deallocate(tmpE) - if(allocated(tmpF)) deallocate(tmpF) if(allocated(beta_RCM)) deallocate(beta_RCM) if(allocated(alpha_RCM)) deallocate(alpha_RCM) if(allocated(gtype_RCM)) deallocate(gtype_RCM) - allocate(tmpD(G%Np,G%Nt)) - allocate(tmpC(G%Np,G%Nt)) allocate(JF0(G%Np,G%Nt)) allocate(RM(G%Np,G%Nt)) allocate(RRdi(G%Np,G%Nt)) - allocate(tmpE(G%Np+4,G%Nt+4)) ! for boundary processing. - allocate(tmpF(G%Np+4,G%Nt+4)) allocate(beta_RCM(G%Np,G%Nt)) allocate(alpha_RCM(G%Np,G%Nt)) allocate(gtype_RCM(G%Np,G%Nt)) @@ -660,6 +650,7 @@ module mixconductance integer :: i, j logical :: smthDEPonly = .true. integer :: smthE + real(rp) :: tmpC(G%Np,G%Nt),tmpD(G%Np,G%Nt),tmpE(G%Np+4,G%Nt+4),tmpF(G%Np+4,G%Nt+4) smthE = 1 ! 1. smooth EFLUX; 2. smooth EAVG tmpC = 0.D0 From bbe523067735d622c317d9b50f47bdccef1a3156 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Mon, 9 Oct 2023 11:28:37 -0600 Subject: [PATCH 098/365] Minor clean-up of signOfY. --- src/remix/mixconductance.F90 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 2980b46e..9fa8b0b1 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -283,7 +283,7 @@ module mixconductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - real(rp) :: signOfY, signOfJ + real(rp) :: signOfJ real(rp) :: Rout = 6.D0, Rin = 1.2D0 real(rp) :: rhoFactor = 3.3D-24*0.5D0 real(rp) :: rPolarBound = 20.0D0*pi/180.D0, rEquatBound = 30.0D0*pi/180.0D0, rLowLimit = 0.02D0 @@ -291,10 +291,8 @@ module mixconductance integer :: i,j if (St%hemisphere==NORTH) then - signOfY = -1 signOfJ = -1 elseif (St%hemisphere==SOUTH) then - signOfY = 1 signOfJ = 1 else stop 'Wrong hemisphere label. Stopping...' @@ -453,6 +451,7 @@ module mixconductance real(rp) :: rcm_eavg,rcm_nflx,rcm_eflx real(rp) :: mix_eavg,mix_nflx,mix_eflx logical :: isRCM,isMHD,isMIX + St%Vars(:,:,AUR_TYPE) = 0 ! Two thresholds of rcm grid type between which both MHD and RCM precipitation will be merged. @@ -527,15 +526,13 @@ module mixconductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St - real(rp) :: signOfY, signOfJ + real(rp) :: signOfJ integer :: i,j real(rp) :: D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT if (St%hemisphere==NORTH) then - signOfY = -1 signOfJ = -1 elseif (St%hemisphere==SOUTH) then - signOfY = 1 signOfJ = 1 else stop 'Wrong hemisphere label. Stopping...' From 37e911fbaf245353af98060951ba8efb9235eea0 Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Tue, 10 Oct 2023 11:52:35 -0600 Subject: [PATCH 099/365] Adjust file open function mode. --- kaipy/rcm/wmutils/genWM.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kaipy/rcm/wmutils/genWM.py b/kaipy/rcm/wmutils/genWM.py index c65696bb..266620ac 100644 --- a/kaipy/rcm/wmutils/genWM.py +++ b/kaipy/rcm/wmutils/genWM.py @@ -24,8 +24,8 @@ def genWM(params, useWM=True): def genh5(fIn, fOut, inputParams, useWM=True): if fIn != fOut: - oH5 = h5.File(fOut, 'w') - iH5 = h5.File(fIn,'r') + oH5 = h5.File(fOut, 'a') + iH5 = h5.File(fIn, 'r') for Q in iH5.keys(): sQ = str(Q) oH5.create_dataset(sQ, data=iH5[sQ]) From b6701b8efdb0f2e75790bc153f55351ddc36d1f3 Mon Sep 17 00:00:00 2001 From: vmerkin Date: Tue, 10 Oct 2023 15:02:25 -0600 Subject: [PATCH 100/365] Uncommitted work since last commit many months ago. Work in progress. --- src/base/shellutils/shellGrid.F90 | 7 ++- src/base/shellutils/shellInterp.F90 | 76 +++++++++++++++++++++++++++-- src/drivers/gremix.F90 | 4 +- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/base/shellutils/shellGrid.F90 b/src/base/shellutils/shellGrid.F90 index 938bcdea..b7fe5aa8 100644 --- a/src/base/shellutils/shellGrid.F90 +++ b/src/base/shellutils/shellGrid.F90 @@ -21,7 +21,8 @@ module shellGrid ! Assuming lat \in -pi/2,pi/2 and lon \in [0,2pi] ! note, th/ph are colatitude/longitude; also, defining lat/latc for latitude real(rp), dimension(:), allocatable :: th, ph, thc, phc, lat, latc ! Radians - logical :: doSP = .false., doNP = .false. ! Whether grid contains south/north pole + logical :: doSP = .false., doNP = .false. ! Whether active grid contains south/north pole, no ghosts in this case + logical :: ghostSP = .false., ghostNP = .false. ! Whether the last ghost corner is the south/north pole real(rp) :: minTheta, maxTheta, minPhi, maxPhi real(rp) :: minGTheta, maxGTheta, minGPhi, maxGPhi @@ -195,6 +196,8 @@ module shellGrid do i=1,shGr%Ngn shGr%th(is-i) = shGr%th(is) - delta*i end do + + shGr%ghostNP = .true. end if end if @@ -212,6 +215,8 @@ module shellGrid do i=1,shGr%Ngs shGr%th(ie+1+i) = shGr%th(ie+1) + delta*i end do + + shGr%ghostSP = .true. end if end if diff --git a/src/base/shellutils/shellInterp.F90 b/src/base/shellutils/shellInterp.F90 index 92aa3cd7..f9714e44 100644 --- a/src/base/shellutils/shellInterp.F90 +++ b/src/base/shellutils/shellInterp.F90 @@ -80,16 +80,27 @@ module shellInterp end if ! Trap for near-pole cases + + if (shGr%doNP .and. (i0==1)) then + call interpPole(shGr,Qind,t,pin,Qinterp) + + ! Handle north pole and return + write(*,*) "Not implemented!" + stop + endif + + ! First, if active grid has poles if (shGr%doSP .and. (i0==shGr%Nt)) then ! Handle south pole and return write(*,*) "Not implemented!" stop endif - if (shGr%doNP .and. (i0==1)) then - ! Handle north pole and return - write(*,*) "Not implemented!" - stop + ! Now, if ghost grid has poles + if (shGr%ghostSP .and. (i0==shGr%ieg)) then + endif + + if (shGr%ghostNP .and. (i0==shGr%isg)) then endif ! Note: If still here we know i0 isn't on the boundary @@ -238,4 +249,61 @@ module shellInterp end subroutine GetShellIJ + subroutine interpPole(shGr,Qind,t,pin,Qinterp) + type(ShellGrid_T), intent(in) :: shGr + integer, intent(in) :: Qind + real(rp), intent(out) :: Qinterp + real(rp), intent(in) :: t,pin + real(rp) :: f0,f1,f2,I1,I2 + integer :: j,pole,iind ! the latter is the index of the pole cell (first/last for NORTH/SOUTH) + integer :: jpi2,jpi32,jpi ! which cell do pi/2, 3pi/2 and pi points belong to + + Qinterp = 0.0 + + ! first, find out which pole we're at + ! note, if we're inside this function, we already know we're at one of the poles + if ( (t.ge.0).and.(t.le.shGr%th(shGr%is+1)) ) then + pole = NORTH + iind = shGr%is + else if ( (t.le.PI).and.(t.ge.shGr%th(shGr%ie)) ) then + pole = SOUTH + iind = shGr%ie + else + write(*,*) "Inside interPole. Shouldn't be here. Quitting..." + end if + + write(*,*) 'which pole ',pole,iind + + + ! represent the function near pole to first order in theta as + ! f(t,p) = f0 + f1*cos(p)*t + f2*sin(p)*t + ! (Lewis&Bellan, J. Math. Phys. 31, 2592 (1990); + ! https://doi.org/10.1063/1.529009 + ! + ! then, 2pi*f0 = \int_0^2pi f(t(i=1),p), where t(i=1) is the cell center of first cell in i-direction + ! to calculate f1, define + ! I1 = \int_(-pi/2)^(pi/2) f(t,p)dp = - \int_(pi/2)^(3pi/2) f(t,p)dp = 2*f1*t+f0*pi + ! compute I1 = 0.5*(\int_(-pi/2)^(pi/2) f(t,p)dp - \int_(pi/2)^(3pi/2)) + ! to take all points around the ring into account + ! then f1 = (I1-f0*pi)/(2*t) + ! + ! similarly, + ! f2 = (I2-f0*pi)/(2*t), where + ! I2 = 0.5*(\int_0^pi f(t,p)dp - \int_pi^(2pi) f(t,p)dp) + + f0 = 0. + do j=1,shGr%Np + f0 = f0 + (shGr%ph(j+1)-shGr%ph(j))*shGr%shellVars(Qind)%cellData(iind,j)/(2.*PI) + + ! find which cells do pi/2, 3pi/2 and pi points belong to + ! this can be done a priori but it doesn't add to the computation + if ( (shGr%ph(j).le.0.5*pi).and.(shGr%ph(j+1).gt.0.5*pi) ) jpi2 = j + if ( (shGr%ph(j).le. pi).and.(shGr%ph(j+1).gt. pi) ) jpi = j + if ( (shGr%ph(j).le.1.5*pi).and.(shGr%ph(j+1).gt.1.5*pi) ) jpi32 = j + end do + + write(*,*) 'pi indices ',jpi2,jpi,jpi32 + + end subroutine interpPole + end module shellInterp diff --git a/src/drivers/gremix.F90 b/src/drivers/gremix.F90 index e4aea35f..95f9adf7 100644 --- a/src/drivers/gremix.F90 +++ b/src/drivers/gremix.F90 @@ -36,8 +36,8 @@ program gremixx call GenShellGrid(shGr,t,p,nGhosts=[0,0,4,4]) shGr%shellVars(1)%cellData = Q - do i=2,Nt-2 - do j=2,Np-2 + do i=1,Nt-1 + do j=1,Np-1 call InterpShell(shGr,1,0.5*(t(i)+t(i+1)),0.5*(p(j)+p(j+1)),Qinterp(i-1,j-1)) ! write(*,*) 0.5*(t(i)+t(i+1))*rad2deg,0.5*(p(j)+p(j+1))*rad2deg,Qinterp(i-1,j-1) end do From a601b117271c423c64027c65b8838e6145b662dd Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Tue, 10 Oct 2023 16:04:29 -0600 Subject: [PATCH 101/365] Copy attributes. Remove unwanted function parameters --- kaipy/rcm/wmutils/genWM.py | 14 ++++++-------- scripts/preproc/genRCM.py | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/kaipy/rcm/wmutils/genWM.py b/kaipy/rcm/wmutils/genWM.py index 266620ac..e0ca32dd 100644 --- a/kaipy/rcm/wmutils/genWM.py +++ b/kaipy/rcm/wmutils/genWM.py @@ -3,7 +3,7 @@ import h5py as h5 from scipy.interpolate import RectBivariateSpline from kaipy.rcm.wmutils.wmData import wmParams -def genWM(params, useWM=True): +def genWM(params): import os @@ -14,26 +14,24 @@ def genWM(params, useWM=True): print("Reading %s"%fInChorus) - if useWM: - return genChorus(params,fInChorus) - else: - return toyWM(params) + return genChorus(params,fInChorus) # Add wpi-induced electron lifetime model to input file and create an output file # Writes arrays to file in rcmconfig.h5 format -def genh5(fIn, fOut, inputParams, useWM=True): +def genh5(fIn, fOut, inputParams): if fIn != fOut: - oH5 = h5.File(fOut, 'a') + oH5 = h5.File(fOut, 'w') iH5 = h5.File(fIn, 'r') for Q in iH5.keys(): sQ = str(Q) oH5.create_dataset(sQ, data=iH5[sQ]) + oH5.attrs.update(iH5.attrs) else: oH5 = h5.File(fOut, 'r+') if not ('Taui' in oH5.keys()): - kpi, mlti, li, eki, taui = genWM(inputParams, useWM = useWM) + kpi, mlti, li, eki, taui = genWM(inputParams) attrs = inputParams.getAttrs() oH5.create_dataset('Kpi', data=kpi) diff --git a/scripts/preproc/genRCM.py b/scripts/preproc/genRCM.py index 7cf69869..200cb71d 100755 --- a/scripts/preproc/genRCM.py +++ b/scripts/preproc/genRCM.py @@ -79,7 +79,7 @@ if __name__ == "__main__": if addWM: tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 41, nEk = 155) - genWM.genh5(fIn,fOut,tauParams,useWM = True) + genWM.genh5(fIn,fOut,tauParams) else: # Determine proton channel limits based on resolving a certain (proton) temperature at given L bVol = kT.L_to_bVol(L_kt) @@ -107,7 +107,7 @@ if __name__ == "__main__": # Add data needed for wavemodel if not noWaveModel: tauParams = wmParams(dim = 4, nKp = 7, nMLT = 97, nL = 41, nEk = 155) - genWM.genh5(fOut,fOut,tauParams,useWM = True) + genWM.genh5(fOut,fOut,tauParams) print("Wrote RCM configuration to %s"%(fOut)) From a1efa95ad49ae1e73dfd6d34f00e0a68490e2337 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Thu, 12 Oct 2023 13:16:49 -0400 Subject: [PATCH 102/365] Fixed HGS frame roll. --- kaipy/gamhelio/helioViz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index cbd690b3..1989447e 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2625,8 +2625,8 @@ def PlotiSlBr( i_shift += 1 lon = np.roll(lon, -i_shift, axis=1) lat = np.roll(lat, -i_shift, axis=1) - lonc = np.roll(lonc, -i_shift, axis=1) - latc = np.roll(latc, -i_shift, axis=1) + lonc = np.roll(lonc, -i_shift + 1, axis=1) + latc = np.roll(latc, -i_shift + 1, axis=1) Br = np.roll(Br, -i_shift, axis=1) # Plot the data in the HGS(MJD_plot) frame. From a363fdde5780b991d191f3dd82e8c7d378c7df58 Mon Sep 17 00:00:00 2001 From: Shanshan Bao Date: Thu, 12 Oct 2023 14:45:17 -0600 Subject: [PATCH 103/365] Fix discontinuity in the wm merging zone. Optimize the IO numbering. Comment fix. --- src/rcm/lossutils.F90 | 6 ------ src/rcm/rcm_subs.F90 | 44 +++++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/rcm/lossutils.F90 b/src/rcm/lossutils.F90 index 829ce7fe..a114bdcc 100644 --- a/src/rcm/lossutils.F90 +++ b/src/rcm/lossutils.F90 @@ -183,7 +183,6 @@ MODULE lossutils REAL(rprec) :: tauKMLE(2,2,2,2),tauMLE(2,2,2),tauLE(2,2),tauE(2)! tauKMLE(1,2,2,2) means tauKlMuLuEu, l:lower bound, u: upper bound in the NN method REAL(rprec) :: dK,wK,dM,wM,dL,wL,dE,wE INTEGER :: iK,kL,kU,mL,mU,lL,lU,eL,eU - LOGICAL :: Lflag = .false. associate(Nm=>EWMTauInput%ChorusTauInput%Nm,Nl=>EWMTauInput%ChorusTauInput%Nl,Nk=>EWMTauInput%ChorusTauInput%Nk,Ne=>EWMTauInput%ChorusTauInput%Ne,& @@ -215,7 +214,6 @@ MODULE lossutils if (Lx >= maxval(Li)) then lL = Nl !use L maximum lU = Nl - Lflag = .true. else if (Lx <= minval(Li)) then lL = 1 ! use L minimum lU = 1 @@ -293,10 +291,6 @@ MODULE lossutils if (lL == lU) then tauE(1) = tauLE(2,1) tauE(2) = tauLE(2,2) - if (Lflag) then ! use gaussian decay for L > maxval(Li) (7Re) - tauE(1)=tauE(1)/exp(-(Lx-maxval(Li))**2) - tauE(2)=tauE(2)/exp(-(Lx-maxval(Li))**2) - endif else dL = Li(lU)-Li(lL) wL = (Lx-Li(lL))/dL diff --git a/src/rcm/rcm_subs.F90 b/src/rcm/rcm_subs.F90 index 87f75081..4b7fc752 100644 --- a/src/rcm/rcm_subs.F90 +++ b/src/rcm/rcm_subs.F90 @@ -1220,16 +1220,16 @@ doSP = .false. call ClearIO(IOVars) !Reset IO chain - call AddInVar(IOVars,"alamc") !1 - call AddInVar(IOVars,"ikflavc") !2 - call AddInVar(IOVars,"fudgec") !3 + call AddInVar(IOVars,"alamc") + call AddInVar(IOVars,"ikflavc") + call AddInVar(IOVars,"fudgec") call ReadVars(IOVars,doSP,RCMGAMConfig) !Store data for energy channels - alamc(:) = IOVars(1)%data - ikflavc(:) = IOVars(2)%data - fudgec(:) = IOVars(3)%data - + alamc(:) = IOVars(FindIO(IOVars, "alamc"))%data + ikflavc(:) = IOVars(FindIO(IOVars, "ikflavc"))%data + fudgec(:) = IOVars(FindIO(IOVars, "fudgec"))%data + ! reset to make sure species if ikflav ==1 alamc is set to negative, for electrons where(ikflavc==1)alamc = -abs(alamc) @@ -1244,25 +1244,25 @@ if (ioExist(RCMGAMConfig,"Taui")) then EWMTauInput%useWM = .true. !Chorus wave - call AddInVar(IOVars,"Kpi") !4 - call AddInVar(IOVars,"MLTi") !5 - call AddInVar(IOVars,"Li") !6 - call AddInVar(IOVars,"Eki") !7 - call AddInVar(IOVars,"Taui") !8 + call AddInVar(IOVars,"Kpi") + call AddInVar(IOVars,"MLTi") + call AddInVar(IOVars,"Li") + call AddInVar(IOVars,"Eki") + call AddInVar(IOVars,"Taui") call ReadVars(IOVars,doSP,RCMGAMConfig) - tauDim = IOVars(8)%Nr + tauDim = IOVars(FindIO(IOVars, "Taui"))%Nr if (tauDim /= 4) then write(*,*) "tauDim:",tauDim write(*,*) 'Currently only support tau model files in the form tau(Kp,MLT,L,Ek)' - write(*,*)"tau:",IOVars(8)%dims + write(*,*)"tau:",IOVars(FindIO(IOVars, "Taui"))%dims stop endif - dims = IOVars(8)%dims(1:tauDim) - Nk = IOVars(4)%N - Nm = IOVars(5)%N - Nl = IOVars(6)%N - Ne = IOVars(7)%N + dims = IOVars(FindIO(IOVars, "Taui"))%dims(1:tauDim) + Nk = IOVars(FindIO(IOVars, "Kpi"))%N + Nm = IOVars(FindIO(IOVars, "MLTi"))%N + Nl = IOVars(FindIO(IOVars, "Li"))%N + Ne = IOVars(FindIO(IOVars, "Eki"))%N if (Nk /= dims(1) .or. Nm /= dims(2) .or. Nl /= dims(3) .or. Ne /= dims(4)) then write(*,*) "dims:",dims,"Nk:",Nk,"Nm:",Nm,"Nl:",Nl,"Ne:",Ne write(*,*) 'Dimensions of tau arrays are not compatible' @@ -2503,16 +2503,16 @@ SUBROUTINE Move_plasma_grid_MHD (dt,nstep) ENDIF eeta_avg(:,:,kc) = 0.0 - ! Just set eeta_avg to whatever eta is there, and leave - !! Note: Regions that have ever been outside of active domain will never have psph again, based on current implementation (2023-08-24) if ( (kc==1) .and. dp_on == .false.) then ! We are plasmasphere and we don't want to evolve it + ! Just set eeta_avg to whatever eta is there, and leave + !! Note: Regions that have ever been outside of active domain will never have psph again, based on current implementation (2023-08-24) eeta_avg(:,:,kc) = eeta(:,:,kc) cycle else ! We are hot channel or we are evolving plasmasphere channel ! Add current weighted eeta as first contribution eeta_avg(:,:,kc) = eeta(:,:,kc)/(nstep+1) endif - + !Sub-step nstep times do n=1,nstep !--- From 9e0644a9395e62d036cfe98686da5838c7a18800 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 13 Oct 2023 08:59:34 -0400 Subject: [PATCH 104/365] Fixed labelling issues in pic6. --- kaipy/gamhelio/helioViz.py | 15 +++++++-------- scripts/quicklook/heliopic.py | 6 ++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 1989447e..feb2e35e 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -1540,8 +1540,6 @@ def PlotEqBr( Ax.set_title(r"Radial magnetic field $B_r$ [$(r/r_0)^2 nT$]") Ax.set_xlabel(r"$X$ [$R_S$]") Ax.set_ylabel(r"$Y$ [$R_S$]") - Ax.yaxis.tick_right() - Ax.yaxis.set_label_position("right") # Return the data. return Br @@ -1614,7 +1612,7 @@ def PlotjBr( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, r'Radial MF $B_r (r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1710,7 +1708,7 @@ def PlotEqBx( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, r'MF $B_x$(r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1759,6 +1757,7 @@ def PlotEqBx( # Decorate the plots. if doDeco: + Ax.set_title(r"x-magnetic field $B_x$ [$(r/r_0)^2 nT$]") Ax.set_xlabel('$X [R_S]$') Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() @@ -1833,7 +1832,7 @@ def PlotEqBy( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, r'MF $B_y(r/r_0)^2$ [nT]', cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1882,10 +1881,9 @@ def PlotEqBy( # Decorate the plots. if doDeco: + Ax.set_title(r"y-magnetic field $B_y$ [$(r/r_0)^2 nT$]") Ax.set_xlabel('$X [R_S]$') Ax.set_ylabel('$Y [R_S]$') - Ax.yaxis.tick_right() - Ax.yaxis.set_label_position('right') # Return the data. return By @@ -1962,7 +1960,7 @@ def PlotEqBz( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, r'MF $B_z$ [nT]', cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -2008,6 +2006,7 @@ def PlotEqBz( # Decorate the plots. if doDeco: + Ax.set_title(r"z-magnetic field $B_z$ [$(r/r_0)^2 nT$]") Ax.set_xlabel('$X [R_S]$') Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index a773edee..43109ce1 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -508,6 +508,8 @@ def main(): hviz.PlotEqBx(gsph, nStp, xyBds, AxR0, AxC2_0) hviz.PlotEqBy(gsph, nStp, xyBds, AxL1, AxC1_1) hviz.PlotEqBz(gsph, nStp, xyBds, AxR1, AxC2_1) + fig.suptitle("GAMERA-Helio frame at 1 AU for " + f"{ktools.MJD2UT(mjd)}") elif pic == "pic7": if jslice is None: jidx = gsph.Nj//2 - 1 @@ -521,11 +523,11 @@ def main(): raise TypeError(f"Invalid figure type: {pic}!") # Add time in the upper left (if not in figure title). - if pic == "pic1" or pic == "pic2" or pic == "pic3": + if pic == "pic1" or pic == "pic2" or pic == "pic3" or pic == "pic6": pass elif pic == "pic4" or pic == "pic5": gsph.AddTime(nStp, Ax, xy=[0.015, 0.92], fs="small") - elif pic == "pic6" or pic == "pic7": + elif pic == pic == "pic7": gsph.AddTime(nStp, AxL0, xy=[0.025, 0.875], fs="x-large") else: raise TypeError(f"Invalid figure type: {pic}!") From 76b28675faa86c29b31dfbb0fdc766b037fa8b6b Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 13 Oct 2023 09:15:59 -0400 Subject: [PATCH 105/365] Cleaned up pic7. --- kaipy/gamhelio/helioViz.py | 14 +++++++++----- scripts/quicklook/heliopic.py | 11 ++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index feb2e35e..0e8b63cb 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -278,7 +278,7 @@ def PlotjMagV( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vMagV, "Speed [km/s]", cM=MagVCM, Ntk=7) + kv.genCB(AxCB, vMagV, cbT=None, cM=MagVCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -300,6 +300,7 @@ def PlotjMagV( # Decorate the plots. if doDeco: + Ax.set_title(r"Speed [$km/s$]") Ax.set_xlabel(r"$X [R_S]$") Ax.set_ylabel(r"$Y [R_S]$") @@ -1170,7 +1171,7 @@ def PlotjD( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vD, r"Density $n(r/r_0)^2 [cm^{-3}]$", cM=DCM, Ntk=7) + kv.genCB(AxCB, vD, cbT=None, cM=DCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1192,7 +1193,8 @@ def PlotjD( # Decorate the plots. if doDeco: - Ax.set_xlabel('$R [R_S]$') + Ax.set_title(r"Number density $n$ [$(r/r_0)^2 cm^{-3}$]") + Ax.set_xlabel('$X [R_S]$') Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() Ax.yaxis.set_label_position('right') @@ -1392,7 +1394,7 @@ def PlotjTemp( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vT, r"Temperature $T(r/r_0)$ [MK]", cM=TCM, Ntk=7) + kv.genCB(AxCB, vT, cbT=None, cM=TCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1414,6 +1416,7 @@ def PlotjTemp( # Decorate the plots. if doDeco: + Ax.set_title(r"Temperature $T$ [$(r/r_0) MK$]") Ax.set_xlabel('$X [R_S]$') Ax.set_ylabel('$Y [R_S]$') @@ -1612,7 +1615,7 @@ def PlotjBr( # Create the color bar. if AxCB: AxCB.clear() - kv.genCB(AxCB, vB, cM=BCM, Ntk=7) + kv.genCB(AxCB, vB, cbT=None, cM=BCM, Ntk=7) # Clear the plot Axes. if doClear: @@ -1634,6 +1637,7 @@ def PlotjBr( # Decorate the plots. if doDeco: + Ax.set_title(r"Radial magnetic field $B_r$ [$(r/r_0)^2 nT$]") Ax.set_xlabel('$X [R_S]$') Ax.set_ylabel('$Y [R_S]$') Ax.yaxis.tick_right() diff --git a/scripts/quicklook/heliopic.py b/scripts/quicklook/heliopic.py index 43109ce1..b64da9d4 100755 --- a/scripts/quicklook/heliopic.py +++ b/scripts/quicklook/heliopic.py @@ -519,18 +519,15 @@ def main(): hviz.PlotjD(gsph, nStp, xyBds, AxR0, AxC2_0, jidx=jidx) hviz.PlotjTemp(gsph, nStp, xyBds, AxL1, AxC1_1, jidx=jidx) hviz.PlotjBr(gsph, nStp, xyBds, AxR1, AxC2_1, jidx=jidx) + fig.suptitle("GAMERA-Helio frame at 1 AU for " + f"{ktools.MJD2UT(mjd)}") else: raise TypeError(f"Invalid figure type: {pic}!") # Add time in the upper left (if not in figure title). - if pic == "pic1" or pic == "pic2" or pic == "pic3" or pic == "pic6": - pass - elif pic == "pic4" or pic == "pic5": + # if pic == "pic1" or pic == "pic2" or pic == "pic3" or pic == "pic6": + if pic == "pic4" or pic == "pic5": gsph.AddTime(nStp, Ax, xy=[0.015, 0.92], fs="small") - elif pic == pic == "pic7": - gsph.AddTime(nStp, AxL0, xy=[0.025, 0.875], fs="x-large") - else: - raise TypeError(f"Invalid figure type: {pic}!") # Overlay the spacecraft positions. if spacecraft: From 19d0bf138fa3d406caa299f62c4194d82dc99d1c Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Fri, 13 Oct 2023 09:42:13 -0400 Subject: [PATCH 106/365] Placeholder. --- kaipy/gamhelio/helioViz.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kaipy/gamhelio/helioViz.py b/kaipy/gamhelio/helioViz.py index 0e8b63cb..89f489ec 100644 --- a/kaipy/gamhelio/helioViz.py +++ b/kaipy/gamhelio/helioViz.py @@ -2635,11 +2635,17 @@ def PlotiSlBr( # Plot the data in the HGS(MJD_plot) frame. Ax.pcolormesh(lon, lat, Br, cmap=BCM, norm=vB) + # Draw the Br=0 contour line representing the current sheet. + # + # This call creates a spurious horizontal line. + Ax.contour(lonc, latc, np.roll(Br, 1, axis=1), [0.], colors='black') + # + else: Ax.pcolormesh(lon, lat, Br, cmap=BCM, norm=vB) - # Draw the Br=0 contour line representing the current sheet. - Ax.contour(lonc, latc, Br, [0.], colors='black') + # Draw the Br=0 contour line representing the current sheet. + Ax.contour(lonc, latc, np.roll(Br, [0.], colors='black')) # Set the plot boundaries. if hgsplot: From 4506a9ab8d1e6b7bdae430a706828989eabb3311 Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Fri, 13 Oct 2023 08:50:55 -0600 Subject: [PATCH 107/365] Use RCM flux for mono. Merge mono-diffuse based on acceleration, plasmasphere, and grid type weighting. --- src/remix/mixconductance.F90 | 126 ++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 54 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 9fa8b0b1..46d59b86 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -19,6 +19,7 @@ module mixconductance real(rp), parameter, private :: maxDrop = 20.0 !Hard-coded max potential drop [kV] real(rp), parameter, private :: eTINY = 1.D-8 ! Floor of average energy [keV] real(rp), parameter, private :: Ne_floor = 0.03e6 ! minimum Ne in [/m^3] when evaluating the linearized FL relation. + real(rp), parameter, private :: Ne_psp = 10.0e6 ! Ne threshold for the plasmasphere in [/m^3]. real(rp), private :: RinMHD = 0.0 !Rin of MHD grid (0 if not running w/ MHD) real(rp), private :: MIXgamma logical , private :: doDrift = .false. !Whether to add drift term from Zhang @@ -440,24 +441,18 @@ module mixconductance subroutine conductance_linmrg(conductance,G,St) ! Derive mono-diffuse electron precipitation where mono is based on linearized FL relation, - ! diffuse is derived from RCM. The merging code was duplicated from kmerge. + ! and diffuse is a combination of MHD and RCM precipitation. type(mixConductance_T), intent(inout) :: conductance type(mixGrid_T), intent(in) :: G type(mixState_T), intent(inout) :: St integer :: i,j - real(rp) :: wC1,wC2,wRCM,gtype - real(rp) :: mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE - real(rp) :: rcm_eavg,rcm_nflx,rcm_eflx - real(rp) :: mix_eavg,mix_nflx,mix_eflx - logical :: isRCM,isMHD,isMIX + real(rp) :: wRCM,wMHD + real(rp) :: mhd_nflx,rcm_nflx,mhd_eflx,rcm_eflx,rcm_eavg,mix_nflx,mhd_SigP,rcm_SigP + logical :: isMono,isPSP St%Vars(:,:,AUR_TYPE) = 0 - ! Two thresholds of rcm grid type between which both MHD and RCM precipitation will be merged. - wC1 = 0.15 - wC2 = 1.0-wC1 - ! derive spatially varying beta and gtype ! using RCM precipitation and thermal fluxes. call conductance_beta_gtype(G,St) @@ -466,54 +461,54 @@ module mixconductance call conductance_linmono(conductance,G,St) !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,isRCM,isMHD,isMIX,wRCM,gtype) & - !$OMP private(mhd_eavg,mhd_nflx,mhd_eflx,mhd_delE) & - !$OMP private(rcm_eavg,rcm_nflx,rcm_eflx ) & - !$OMP private(mix_eavg,mix_nflx,mix_eflx ) + !$OMP private(i,j,isMono,isPSP,wRCM,wMHD) & + !$OMP private(mhd_nflx,mhd_eflx,mix_nflx) & + !$OMP private(rcm_nflx,rcm_eflx,rcm_eavg) & + !$OMP private(mhd_SigP,rcm_SigP) do j=1,G%Nt do i=1,G%Np !Start by figuring out where we are - gtype = gtype_RCM(i,j) - isRCM = gtype >= wC2 - isMHD = gtype <= wC1 - if ( (.not. isRCM) .and. (.not. isMHD) ) then - isMIX = .true. - endif - + isPSP = St%Vars(i,j,IM_EDEN)>=Ne_psp .or. beta_RCM(i,j)<=0.01 + isMono = St%Vars(i,j,DELTAE)>eTINY !Grab values - mhd_eavg = St%Vars(i,j,AVG_ENG) mhd_nflx = St%Vars(i,j,NUM_FLUX) - mhd_delE = conductance%deltaE(i,j) - mhd_eflx = St%Vars(i,j,AVG_ENG)*St%Vars(i,j,NUM_FLUX)*kev2erg rcm_nflx = St%Vars(i,j,IM_ENFLX) + mhd_eflx = St%Vars(i,j,AVG_ENG)*St%Vars(i,j,NUM_FLUX)*kev2erg rcm_eflx = St%Vars(i,j,IM_EFLUX) - rcm_eavg = rcm_eflx/(rcm_nflx*kev2erg) !Back to keV - - if (isMHD) then - St%Vars(i,j,AVG_ENG ) = mhd_eavg - St%Vars(i,j,NUM_FLUX) = mhd_nflx - else if (isRCM) then - if (rcm_nflx <= TINY) then - rcm_eavg = eTINY - endif - St%Vars(i,j,AVG_ENG ) = rcm_eavg - St%Vars(i,j,NUM_FLUX) = rcm_nflx - conductance%deltaE(i,j) = 0.0 + if(rcm_nflx>TINY) then + rcm_eavg = rcm_eflx/(rcm_nflx*kev2erg) else - !Mixture - wRCM = RampUp(gtype,wC1,wC2-wC1) - mix_nflx = wRCM*rcm_nflx + (1-wRCM)*mhd_nflx - if ( mix_nflx > TINY ) then - !Mix both - mix_eflx = wRCM*rcm_eflx + (1-wRCM)*mhd_eflx - mix_eavg = mix_eflx/(mix_nflx*kev2erg) + rcm_eavg = eTINY + endif + if(isPSP) then + ! Set auroral type to diffuse. Use RCM values for diffuse nflux and eavg in the plasmasphere. + St%Vars(i,j,NUM_FLUX) = rcm_nflx + St%Vars(i,j,AVG_ENG) = rcm_eavg + St%Vars(i,j,AUR_TYPE) = AT_RMnoE + elseif(isMono .and. .not.isPSP) then + ! Set auroral type to mono. Keep linmono values for mono nflux and eavg. + mhd_SigP = SigmaP_Robinson(St%Vars(i,j,AVG_ENG),mhd_eflx) + rcm_SigP = SigmaP_Robinson(rcm_eavg,rcm_eflx) + if(mhd_SigP>rcm_SigP) then + St%Vars(i,j,AUR_TYPE) = AT_RMono else - ! Both RCM and MHD data weren't good so just use TINY - mix_eavg = eTINY + St%Vars(i,j,NUM_FLUX) = rcm_nflx + St%Vars(i,j,AVG_ENG) = rcm_eavg + St%Vars(i,j,AUR_TYPE) = AT_RMnoE endif - - St%Vars(i,j,AVG_ENG ) = mix_eavg + else + ! Linearly merge MHD and RCM diffuse nflux and eflux. + ! Note where deltaE>eTINY but beta_RCM<=0.01, gtype will be near 1. + wRCM = gtype_RCM(i,j) + wMHD = 1.0-wRCM + mix_nflx = wMHD*mhd_nflx + wRCM*rcm_nflx St%Vars(i,j,NUM_FLUX) = mix_nflx + if(mix_nflx>TINY) then + St%Vars(i,j,AVG_ENG) = (wMHD*mhd_eflx+wRCM*rcm_eflx)/(mix_nflx*kev2erg) + else + St%Vars(i,j,AVG_ENG) = eTINY + endif + St%Vars(i,j,AUR_TYPE) = AT_RMnoE endif enddo enddo @@ -529,6 +524,9 @@ module mixconductance real(rp) :: signOfJ integer :: i,j real(rp) :: D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT + real(rp) :: Pe_mhd,Ne_mhd,kT_mhd,phi0_mhd + real(rp) :: Pe_rcm,Ne_rcm,kT_rcm,phi0_rcm + real(rp) :: wMHD,wRCM if (St%hemisphere==NORTH) then signOfJ = -1 @@ -539,7 +537,9 @@ module mixconductance endif !$OMP PARALLEL DO default(shared) & - !$OMP private(i,j,D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT) + !$OMP private(i,j,D,Cs,Pe,Ne,J2eF0,eV2kT,dE,phi0,kT) & + !$OMP private(Pe_mhd,Ne_mhd,kT_mhd,phi0_mhd) & + !$OMP private(Pe_rcm,Ne_rcm,kT_rcm,phi0_rcm) do j=1,G%Nt do i=1,G%Np if (conductance%doChill) then @@ -553,19 +553,36 @@ module mixconductance endif ! electron pressure from MHD side in [Pa] ! 0.1 is to convert [g/cm^3]*[cm/s]^2=[g/cm/s^2] to [Pa]. - Pe = 0.1/MIXgamma*alpha_RCM(i,j)*D*(Cs**2) + Pe_mhd = 0.1/MIXgamma*alpha_RCM(i,j)*D*(Cs**2) ! electron number density from MHD side in [/m^3]. - Ne = D/(Mp_cgs*heFrac)*1.0D6 + Ne_mhd = D/(Mp_cgs*heFrac)*1.0D6 ! Mean energy from MHD electron fluxes in [keV]. E_avg = 2*kT for Maxwellian. - kT = Pe/Ne/kev2J - conductance%E0 (i,j) = 2.0*kT + kT_mhd = Pe_mhd/(Ne_mhd*kev2J) ! Thermal number flux from MHD electron fluxes in [#/cm^2/s]. ! Treat MHD flux on closed field lines as trapped flux. ! Beta now stands for the ratio between precipitation and trapped. ! Note phi0 can be zero in the polar cap. - phi0 = beta_RCM(i,j)* sqrt(Pe*Ne/(2.0D-3*PI*Me_cgs))*1.0D-4 - conductance%phi0(i,j) = phi0 + phi0_mhd = beta_RCM(i,j)*sqrt(Pe_mhd*Ne_mhd/(2.0D-3*PI*Me_cgs))*1.0D-4 + ! RCM flux + Pe_rcm = St%Vars(i,j,IM_EPRE) + Ne_rcm = St%Vars(i,j,IM_EDEN) + phi0_rcm = St%Vars(i,j,IM_ENFLX) + + ! Merged flux + wMHD = 1.0-gtype_RCM(i,j) + wRCM = gtype_RCM(i,j) + phi0 = wMHD*phi0_mhd + wRCM*phi0_rcm + Ne = wMHD*Ne_mhd + wRCM*Ne_rcm + Pe = wMHD*Pe_mhd + wRCM*Pe_rcm + if(Ne>TINY) then + kT = Pe/(Ne*kev2J) + else + kT = 0.D0 + endif + + conductance%E0 (i,j) = 2.0*kT + conductance%phi0(i,j) = phi0 ! Note JF0 may go inf where phi0/Pe_MHD/Ne_MHD is zero. J2eF0 = signOfJ*(St%Vars(i,j,FAC)*1.0D-6)/(eCharge*phi0*1.0D4) ! Linearize the Fridman-Lemaire relation: @@ -583,6 +600,7 @@ module mixconductance St%Vars(i,j,Z_NFLUX) = phi0 St%Vars(i,j,Z_EAVG) = kT endif + St%Vars(i,j,DELTAE) = conductance%deltaE(i,j) St%Vars(i,j,AVG_ENG) = max(St%Vars(i,j,Z_EAVG),eTINY) ! [keV] St%Vars(i,j,NUM_FLUX) = St%Vars(i,j,Z_NFLUX) ! [#/cm^2/s] From d2a0d22e610cbb91827ac07d9b07b2e67a33f64c Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Fri, 13 Oct 2023 10:42:00 -0600 Subject: [PATCH 108/365] Minor clean-up. --- src/remix/mixconductance.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 46d59b86..97013257 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -12,7 +12,7 @@ module mixconductance implicit none - real(rp), dimension(:,:), allocatable, private :: JF0,RM,RRdi ! used for zhang15 + real(rp), dimension(:,:), allocatable, private :: RM,RRdi ! used for zhang15 real(rp), dimension(:,:), allocatable, private :: beta_RCM,alpha_RCM,gtype_RCM ! two-dimensional beta based on RCM fluxes. !Replacing some hard-coded inline values (bad) w/ module private values (slightly less bad) @@ -73,14 +73,12 @@ module mixconductance if (.not. allocated(conductance%PrecipMask)) allocate(conductance%PrecipMask(G%Np,G%Nt)) ! these arrays are global and should not be! reallocate them - if(allocated(JF0)) deallocate(JF0) if(allocated(RM)) deallocate(RM) if(allocated(RRdi)) deallocate(RRdi) if(allocated(beta_RCM)) deallocate(beta_RCM) if(allocated(alpha_RCM)) deallocate(alpha_RCM) if(allocated(gtype_RCM)) deallocate(gtype_RCM) - allocate(JF0(G%Np,G%Nt)) allocate(RM(G%Np,G%Nt)) allocate(RRdi(G%Np,G%Nt)) allocate(beta_RCM(G%Np,G%Nt)) @@ -480,6 +478,7 @@ module mixconductance else rcm_eavg = eTINY endif + if(isPSP) then ! Set auroral type to diffuse. Use RCM values for diffuse nflux and eavg in the plasmasphere. St%Vars(i,j,NUM_FLUX) = rcm_nflx From 91218d0bd92aee2028073e87dd47037c8bf251d2 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Sun, 15 Oct 2023 16:40:32 -0400 Subject: [PATCH 109/365] Part 1 of overhaul --- scripts/makeitso/makeitso.py | 594 ++++++++++++---------- scripts/makeitso/option_descriptions.json | 119 +++-- scripts/makeitso/template.ini | 2 +- scripts/makeitso/template.pbs | 24 +- 4 files changed, 421 insertions(+), 318 deletions(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index 54c44a1e..fb206e3c 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -13,7 +13,7 @@ each decision that must be made to prepare for the run. import argparse import json import os -import subprocess +# import subprocess # Import 3rd-party modules. from jinja2 import Template @@ -29,41 +29,7 @@ DESCRIPTION = "Interactive script to prepare a MAGE magnetosphere model run." # Indent level for JSON output. JSON_INDENT = 4 - -# Program defaults - -# Module sets by platform and run type. -modules = { - "cheyenne": [ - "cmake/3.22.0", - "git/2.33.1", - "ncarenv/1.3", - "intel/2022.1", - "geos/3.10.1", - "ncarcompilers/0.5.0", - "mpt/2.25", - "hdf5-mpi/1.12.2", - ], - "derecho": [ - "ncarenv/23.06", - "cmake/3.26.3", - "craype/2.7.20", - "intel/2023.0.0", - "geos/3.9.1", - "ncarcompilers/1.0.0", - "cray-mpich/8.1.25", - "hdf5-mpi/1.12.2", - ], - "pleiades": [ - "nas" - "pkgsrc/2022Q1-rome", - "comp-intel/2020.4.304", - "mpi-hpe/mpt.2.23", - "hdf5/1.8.18_mpt", - ], -} - -# Path to directory containing support files. +# Path to directory containing support files for makeitso. SUPPORT_FILES_DIRECTORY = os.path.join( os.environ["KAIJUHOME"], "scripts", "makeitso" ) @@ -79,6 +45,39 @@ INI_TEMPLATE = os.path.join(SUPPORT_FILES_DIRECTORY, "template.ini") # Path to template .pbs file. PBS_TEMPLATE = os.path.join(SUPPORT_FILES_DIRECTORY, "template.pbs") +# # Program defaults + +# # Module sets by platform and run type. +# modules = { +# "cheyenne": [ +# "cmake/3.22.0", +# "git/2.33.1", +# "ncarenv/1.3", +# "intel/2022.1", +# "geos/3.10.1", +# "ncarcompilers/0.5.0", +# "mpt/2.25", +# "hdf5-mpi/1.12.2", +# ], +# "derecho": [ +# "ncarenv/23.06", +# "cmake/3.26.3", +# "craype/2.7.20", +# "intel/2023.0.0", +# "geos/3.9.1", +# "ncarcompilers/1.0.0", +# "cray-mpich/8.1.25", +# "hdf5-mpi/1.12.2", +# ], +# "pleiades": [ +# "nas" +# "pkgsrc/2022Q1-rome", +# "comp-intel/2020.4.304", +# "mpi-hpe/mpt.2.23", +# "hdf5/1.8.18_mpt", +# ], +# } + def create_command_line_parser(): """Create the command-line argument parser. @@ -100,15 +99,19 @@ def create_command_line_parser(): """ parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument( - "--debug", "-d", action="store_true", default=False, + "--clobber", action="store_true", + help="Overwrite existing options file (default: %(default)s)." + ) + parser.add_argument( + "--debug", "-d", action="store_true", help="Print debugging output (default: %(default)s)." ) parser.add_argument( - "--options", "-o", default=None, + "--options_path", "-o", default=None, help="Path to JSON file of options (default: %(default)s)" ) parser.add_argument( - "--verbose", "-v", action="store_true", default=False, + "--verbose", "-v", action="store_true", help="Print verbose output (default: %(default)s)." ) return parser @@ -175,7 +178,7 @@ def get_run_option(name, description): return str(option_value) -def get_run_options(): +def prompt_user_for_run_options(): """Prompt the user for run options. Prompt the user for run options. @@ -200,177 +203,234 @@ def get_run_options(): """ # Read the dictionary of option descriptions. with open(OPTION_DESCRIPTIONS_FILE, "r", encoding="utf-8") as f: - options_yaml = json.load(f) - option_descriptions = options_yaml["option_descriptions"] + option_descriptions = json.load(f) + # Convenience variables to help avoid dict key errors. + option_descriptions_pbs = option_descriptions["pbs"] + option_descriptions_basic = option_descriptions["basic"] + option_descriptions_advanced = option_descriptions["advanced"] # Initialize the dictionary of program options. - options = {} + options = { + "pbs": {}, + "basic": {}, + "advanced": {}, + } + # Convenience variables to help avoid dict key errors. + options_pbs = options["pbs"] + options_basic = options["basic"] + options_advanced = options["advanced"] - # Fetch the HPC system and run type. - for name in ["hpc_system"]: - options[name] = get_run_option(name, option_descriptions[name]) + #------------------------------------------------------------------------- - # Specify the location of the kaiju installation to use. + # Run definition options + + # HPC system run directory. + for name in ["hpc_system", "run_directory"]: + options_basic[name] = get_run_option( + name, option_descriptions_basic[name] + ) + + # Compute the default for the location of the kaiju installation to use. # The installation is assumed to be the installation which contains # this running script. Note that this code requires that the KAIJUHOME # environment variable is set. This environment variable is set # automatically when the user runs the setupEnvironment.[c]sh script # or its equivalent. - options["kaijuhome"] = os.environ["KAIJUHOME"] + option_descriptions_basic["kaiju_install_directory"]["default"] = ( + os.environ["KAIJUHOME"] + ) + + # kaiju installation directory + for name in ["kaiju_install_directory"]: + options_basic[name] = get_run_option( + name, option_descriptions_basic[name] + ) # Compute the default build directory based on KAIJUHOME and the run # type. The build directory is the directory where cmake and make were # run during the build process. Typically, this is done in the kaiju # subdirectory "build_mpi" (for MPI builds). - option_descriptions["kaiju_build_directory"]["default"] = ( - f"{options['kaijuhome']}/build_mpi" + option_descriptions_basic["kaiju_build_directory"]["default"] = ( + os.path.join(options_basic['kaiju_install_directory'], "build_mpi") ) - # Fetch options about the kaiju software to use. - option_names = [ - "kaiju_build_directory" - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) + # kaiju build directory + for name in ["kaiju_build_directory"]: + options_basic[name] = get_run_option( + name, option_descriptions_basic[name] + ) # Fetch high-level options about the run. # NOTE: All files must be in the run directory. - option_names = [ - "run_directory", "runid", "LFM_grid_type", "solar_wind_file" - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) + # for name in ["runid", "LFM_grid_type", "solar_wind_file"]: + for name in ["runid"]: + options_basic[name] = get_run_option( + name, option_descriptions_basic[name] + ) + + #------------------------------------------------------------------------- # PBS job options - # Assumes all nodes have same architecture. - hpc_system = options["hpc_system"] - option_names = [ - "pbs_account_name", "pbs_queue", "pbs_walltime", - "pbs_select", "pbs_ncpus", - "pbs_mpiprocs", "pbs_ompthreads", - "pbs_num_helpers", "pbs_numjobs", - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) - # The helper nodes use one OMP thread per core. - options["pbs_helper_ompthreads"] = options["pbs_ncpus"] + # Compute PBS defaults. + option_descriptions_pbs["account_name" ]["default"] = os.getlogin() + + # Cross-platform PBS options + for name in ["account_name"]: + options_pbs[name] = get_run_option( + name, option_descriptions_pbs[name] + ) + + # HPC system-specific PBS options + option_descriptions_pbs_hpc = ( + option_descriptions_pbs[options_basic["hpc_system"]] + ) + + # Options for main computation nodes + for name in ["queue", "walltime", "select", "ncpus", "mpiprocs", + "ompthreads"]: + options_pbs[name] = get_run_option( + name, option_descriptions_pbs_hpc[name] + ) + + # Options for helper nodes + for name in ["helper_select", "helper_mpiprocs", "helper_ompthreads"]: + options_pbs[name] = get_run_option( + name, option_descriptions_pbs_hpc[name] + ) + + # Options for segmented jobs + for name in ["num_jobs"]: + options_pbs[name] = get_run_option( + name, option_descriptions_pbs_hpc[name] + ) + + # Determine the module set to use. + options_pbs["modules"] = option_descriptions_pbs_hpc["modules"] # Specify the command to launch an MPI program. - options["pbs_mpiexec_command"] = "mpiexec omplace" + options_pbs["mpiexec_command"] = option_descriptions_pbs_hpc["mpiexec_command"] - # Assemble the module load statements into a single string. - hpc_system = options["hpc_system"] - module_names = modules[hpc_system] - pbs_module_load = "" - for module_name in module_names: - pbs_module_load += f"module load {module_name}\n" - pbs_module_load = pbs_module_load.rstrip() - options["pbs_module_load"] = pbs_module_load + # Specify other environment variables to set (this is a placeholder). + options["pbs.additional_environment_variables"] = None - # Specify other environment variables to set. - options["pbs_additional_environment_variables"] = "" + #------------------------------------------------------------------------- - # GAMERA section - option_names = [ - "gamera_sim_doH5g", "gamera_sim_H5Grid", "gamera_sim_icType", - "gamera_sim_pdmb", "gamera_sim_rmeth", - "gamera_floors_dFloor", "gamera_floors_pFloor", - "gamera_timestep_doCPR", "gamera_timestep_limCPR", - "gamera_restart_doRes", "gamera_restart_resID", "gamera_restart_nRes", - "gamera_physics_doMHD", "gamera_physics_doBoris", "gamera_physics_Ca", - "gamera_ring_gid", "gamera_ring_doRing", - "gamera_ringknobs_doVClean", - "gamera_wind_tsfile", - "gamera_source_doSource", "gamera_source_doWolfLim", - "gamera_source_doBounceDT", "gamera_source_nBounce", - "gamera_iPdir_N", "gamera_iPdir_bcPeriodic", - "gamera_jPdir_N", "gamera_jPdir_bcPeriodic", - "gamera_kPdir_N", "gamera_kPdir_bcPeriodic", - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) + # Options for gamera_mpi.x - # VOLTRON section - option_names = [ - "voltron_time_tFin", - "voltron_spinup_doSpin", "voltron_spinup_tSpin", - "voltron_output_dtOut", "voltron_output_tsOut", - "voltron_coupling_dtCouple", "voltron_coupling_rTrc", - "voltron_coupling_imType", "voltron_coupling_doQkSquish", - "voltron_coupling_qkSquishStride", - "voltron_restart_dtRes", - "voltron_imag_doInit", - "voltron_ebsquish_epsSquish", - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) +# option_names = [ +# "gamera_sim_doH5g", "gamera_sim_H5Grid", "gamera_sim_icType", +# "gamera_sim_pdmb", "gamera_sim_rmeth", +# "gamera_floors_dFloor", "gamera_floors_pFloor", +# "gamera_timestep_doCPR", "gamera_timestep_limCPR", +# "gamera_restart_doRes", "gamera_restart_resID", "gamera_restart_nRes", +# "gamera_physics_doMHD", "gamera_physics_doBoris", "gamera_physics_Ca", +# "gamera_ring_gid", "gamera_ring_doRing", +# "gamera_ringknobs_doVClean", +# "gamera_wind_tsfile", +# "gamera_source_doSource", "gamera_source_doWolfLim", +# "gamera_source_doBounceDT", "gamera_source_nBounce", +# "gamera_iPdir_N", "gamera_iPdir_bcPeriodic", +# "gamera_jPdir_N", "gamera_jPdir_bcPeriodic", +# "gamera_kPdir_N", "gamera_kPdir_bcPeriodic", +# ] +# for name in option_names: +# options[name] = get_run_option(name, option_descriptions[name]) - # CHIMP section - option_names = [ - "chimp_units_uid", - "chimp_fields_grType", - "chimp_domain_dtype", - "chimp_tracer_epsds", - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) + #------------------------------------------------------------------------- - # REMIX section - option_names = [ - "remix_conductance_doStarlight", "remix_conductance_doRamp", - "remix_precipitation_aurora_model_type", "remix_precipitation_alpha", - "remix_precipitation_beta", - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) + # Options for voltron_mpi.x - # RCM section - option_names = [ - "rcm_rcmdomain_domType", - "rcm_ellipse_xSun", "rcm_ellipse_yDD", "rcm_ellipse_xTail", - "rcm_ellipse_isDynamic", "rcm_grid_LowLat", "rcm_grid_HiLat", - "rcm_grid_doLatStretch", - "rcm_plasmasphere_isDynamic", "rcm_plasmasphere_initKp", - "rcm_plasmasphere_doRefill", "rcm_plasmasphere_DenPP0", - "rcm_plasmasphere_tAvg", - ] - for name in option_names: - options[name] = get_run_option(name, option_descriptions[name]) +# option_names = [ +# "voltron_time_tFin", +# "voltron_spinup_doSpin", "voltron_spinup_tSpin", +# "voltron_output_dtOut", "voltron_output_tsOut", +# "voltron_coupling_dtCouple", "voltron_coupling_rTrc", +# "voltron_coupling_imType", "voltron_coupling_doQkSquish", +# "voltron_coupling_qkSquishStride", +# "voltron_restart_dtRes", +# "voltron_imag_doInit", +# "voltron_ebsquish_epsSquish", +# ] +# for name in option_names: +# options[name] = get_run_option(name, option_descriptions[name]) + + #------------------------------------------------------------------------- + + # Options for CHIMP + +# option_names = [ +# "chimp_units_uid", +# "chimp_fields_grType", +# "chimp_domain_dtype", +# "chimp_tracer_epsds", +# ] +# for name in option_names: +# options[name] = get_run_option(name, option_descriptions[name]) + + #------------------------------------------------------------------------- + + # Options for REMIX + +# option_names = [ +# "remix_conductance_doStarlight", "remix_conductance_doRamp", +# "remix_precipitation_aurora_model_type", "remix_precipitation_alpha", +# "remix_precipitation_beta", +# ] +# for name in option_names: +# options[name] = get_run_option(name, option_descriptions[name]) + + #------------------------------------------------------------------------- + + # Options for RCM + +# option_names = [ +# "rcm_rcmdomain_domType", +# "rcm_ellipse_xSun", "rcm_ellipse_yDD", "rcm_ellipse_xTail", +# "rcm_ellipse_isDynamic", "rcm_grid_LowLat", "rcm_grid_HiLat", +# "rcm_grid_doLatStretch", +# "rcm_plasmasphere_isDynamic", "rcm_plasmasphere_initKp", +# "rcm_plasmasphere_doRefill", "rcm_plasmasphere_DenPP0", +# "rcm_plasmasphere_tAvg", +# ] +# for name in option_names: +# options[name] = get_run_option(name, option_descriptions[name]) + + #------------------------------------------------------------------------- # Return the options dictionary. return options -def run_preprocessing_steps(options): - """Execute any preprocessing steps required for the run. +# def run_preprocessing_steps(options): +# """Execute any preprocessing steps required for the run. - Execute any preprocessing steps required for the run. +# Execute any preprocessing steps required for the run. - Parameters - ---------- - options : dict - Dictionary of program options, each entry maps str to str. +# Parameters +# ---------- +# options : dict +# Dictionary of program options, each entry maps str to str. - Returns - ------- - None +# Returns +# ------- +# None - Raises - ------ - None - """ - # Create the LFM grid file. - # NOTE: Assumes genLFM.py is in PATH. - cmd = "genLFM.py" - args = [cmd, "-gid", options["LFM_grid_type"]] - subprocess.run(args, check=True) +# Raises +# ------ +# None +# """ +# # Create the LFM grid file. +# # NOTE: Assumes genLFM.py is in PATH. +# cmd = "genLFM.py" +# args = [cmd, "-gid", options["LFM_grid_type"]] +# subprocess.run(args, check=True) - # Create the RCM configuration file. - # NOTE: Assumes genRCM.py is in PATH. - cmd = "genRCM.py" - args = [cmd] - subprocess.run(args, check=True) +# # Create the RCM configuration file. +# # NOTE: Assumes genRCM.py is in PATH. +# cmd = "genRCM.py" +# args = [cmd] +# subprocess.run(args, check=True) def create_ini_files(options): @@ -404,69 +464,69 @@ def create_ini_files(options): # If a single segment was requested, create a single file. # If multiple segments were requested, create an .ini file for each # segment. - if int(options["pbs_numjobs"]) == 1: + if int(options["pbs"]["num_jobs"]) == 1: ini_content = template.render(options) ini_file = os.path.join( - - options["run_directory"], f"{options['runid']}.ini" + options["basic"]["run_directory"], + f"{options['basic']['runid']}.ini" ) with open(ini_file, "w", encoding="utf-8") as f: f.write(ini_content) ini_files.append(ini_file) - else: - for job in range(int(options["pbs_numjobs"])): - opt = options # Need a copy of options - if job > 0: - opt["gamera_restart_doRes"] = "T" - ini_content = template.render(options) - ini_file = os.path.join( - options["run_directory"], f"{options['runid']}-{job:02d}.ini" - ) - ini_files.append(ini_file) - with open(ini_file, "w", encoding="utf-8") as f: - f.write(ini_content) + # else: + # for job in range(int(options["pbs"]["num_jobs"])): + # opt = options # Need a copy of options + # if job > 0: + # opt["gamera_restart_doRes"] = "T" + # ini_content = template.render(options) + # ini_file = os.path.join( + # options["run_directory"], f"{options['runid']}-{job:02d}.ini" + # ) + # ini_files.append(ini_file) + # with open(ini_file, "w", encoding="utf-8") as f: + # f.write(ini_content) # Return the paths to the .ini files. return ini_files -def convert_ini_to_xml(ini_files): - """Convert the .ini files to XML. +# def convert_ini_to_xml(ini_files): +# """Convert the .ini files to XML. - Convert the .ini files describing the run to XML files. +# Convert the .ini files describing the run to XML files. - Parameters - ---------- - ini_files : list of str - Paths to the .ini files to convert. +# Parameters +# ---------- +# ini_files : list of str +# Paths to the .ini files to convert. - Returns - ------- - xml_files : str - Paths to the XML files. +# Returns +# ------- +# xml_files : str +# Paths to the XML files. - Raises - ------ - None - """ - # Convert each .ini file to an .xml file. - xml_files = [] - for ini_file in ini_files: +# Raises +# ------ +# None +# """ +# # Convert each .ini file to an .xml file. +# xml_files = [] +# for ini_file in ini_files: - # Put the XML file in the same directory as the .ini file. - xml_file = ini_file.replace(".ini", ".xml") +# # Put the XML file in the same directory as the .ini file. +# xml_file = ini_file.replace(".ini", ".xml") - # Convert the .ini file to .xml. - # NOTE: assumes XMLGenerator.py is in PATH. - cmd = "XMLGenerator.py" - args = [cmd, ini_file, xml_file] - subprocess.run(args, check=True) +# # Convert the .ini file to .xml. +# # NOTE: assumes XMLGenerator.py is in PATH. +# cmd = "XMLGenerator.py" +# args = [cmd, ini_file, xml_file] +# subprocess.run(args, check=True) - # Add this file to the list of XML files. - xml_files.append(xml_file) +# # Add this file to the list of XML files. +# xml_files.append(xml_file) - # Return the paths to the XML files. - return xml_files +# # Return the paths to the XML files. +# return xml_files def create_pbs_scripts(options): @@ -488,50 +548,56 @@ def create_pbs_scripts(options): ------ None """ - # Read and create the template. + # Read the template. with open(PBS_TEMPLATE, "r", encoding="utf-8") as f: template_content = f.read() template = Template(template_content) # Create a PBS script for each segment. + hpc_system = options["basic"]["hpc_system"] + options_pbs = options["pbs"] pbs_scripts = [] - if int(options["pbs_numjobs"]) == 1: + # + options_pbs["num_jobs"] = 1 + # + if int(options_pbs["num_jobs"]) == 1: pbs_content = template.render(options) pbs_script = os.path.join( - options["run_directory"], f"{options['runid']}.pbs" + options["basic"]["run_directory"], + f"{options['basic']['runid']}.pbs" ) with open(pbs_script, "w", encoding="utf-8") as f: f.write(pbs_content) pbs_scripts.append(pbs_script) - else: - for segment in range(int(options["pbs_numjobs"])): - pbs_content = template.render(options) - pbs_script = os.path.join( - options["run_directory"], - f"{options['runid']}-{segment:02d}.pbs" - ) - pbs_scripts.append(pbs_script) - with open(pbs_script, "w", encoding="utf-8") as f: - f.write(pbs_content) +# else: +# for segment in range(int(options["pbs_num_jobs"])): +# pbs_content = template.render(options) +# pbs_script = os.path.join( +# options["run_directory"], +# f"{options['runid']}-{segment:02d}.pbs" +# ) +# pbs_scripts.append(pbs_script) +# with open(pbs_script, "w", encoding="utf-8") as f: +# f.write(pbs_content) - # Create a single script which will submit all of the PBS jobs in order. - path = "submit_pbs.sh" - with open(path, "w", encoding="utf-8") as f: - s = pbs_scripts[0] - cmd = f"job_id=`qsub {s}`\n" - f.write(cmd) - cmd = f"echo $job_id\n" - f.write(cmd) - for s in pbs_scripts[1:]: - cmd = "old_job_id=$job_id\n" - f.write(cmd) - cmd = f"job_id=`qsub -W depend=afterok:$old_job_id {s}`\n" - f.write(cmd) - cmd = f"echo $job_id\n" - f.write(cmd) +# # Create a single script which will submit all of the PBS jobs in order. +# path = "submit_pbs.sh" +# with open(path, "w", encoding="utf-8") as f: +# s = pbs_scripts[0] +# cmd = f"job_id=`qsub {s}`\n" +# f.write(cmd) +# cmd = f"echo $job_id\n" +# f.write(cmd) +# for s in pbs_scripts[1:]: +# cmd = "old_job_id=$job_id\n" +# f.write(cmd) +# cmd = f"job_id=`qsub -W depend=afterok:$old_job_id {s}`\n" +# f.write(cmd) +# cmd = f"echo $job_id\n" +# f.write(cmd) - # Return the paths to the PBS scripts. - return pbs_scripts +# # Return the paths to the PBS scripts. +# return pbs_scripts def main(): @@ -556,11 +622,12 @@ def main(): # Parse the command-line arguments. args = parser.parse_args() - debug = args.debug - options_path = args.options - verbose = args.verbose - if debug: + if args.debug: print(f"args = {args}") + clobber = args.clobber + debug = args.debug + options_path = args.options_path + verbose = args.verbose # Fetch the run options. if options_path: @@ -569,12 +636,15 @@ def main(): options = json.load(f) else: # Prompt the user for the run options. - options = get_run_options() + options = prompt_user_for_run_options() if debug: print(f"options = {options}") - # Save the options dictionary as a JSON file. - path = os.path.join(options["run_directory"], "options.json") + # Save the options dictionary as a JSON file in the run directory. + path = os.path.join(options["basic"]["run_directory"], "options.json") + if os.path.exists(path): + if not clobber: + raise FileExistsError(f"Options file {path} exists!") with open(path, "w", encoding="utf-8") as f: json.dump(options, f, indent=JSON_INDENT) @@ -582,12 +652,12 @@ def main(): original_directory = os.getcwd() # Move to the output directory. - os.chdir(options["run_directory"]) + os.chdir(options["basic"]["run_directory"]) - # Run the preprocessing steps. - if verbose: - print("Running preprocessing steps.") - run_preprocessing_steps(options) +# # Run the preprocessing steps. +# if verbose: +# print("Running preprocessing steps.") +# run_preprocessing_steps(options) # Create the .ini file(s). if verbose: @@ -596,12 +666,12 @@ def main(): if debug: print(f"ini_files = {ini_files}") - # Convert the .ini file(s) to .xml files(s). - if verbose: - print("Converting .ini file(s) to .xml file(s).") - xml_files = convert_ini_to_xml(ini_files) - if debug: - print(f"xml_files = {xml_files}") +# # Convert the .ini file(s) to .xml files(s). +# if verbose: +# print("Converting .ini file(s) to .xml file(s).") +# xml_files = convert_ini_to_xml(ini_files) +# if debug: +# print(f"xml_files = {xml_files}") # Create the PBS job script(s). if verbose: diff --git a/scripts/makeitso/option_descriptions.json b/scripts/makeitso/option_descriptions.json index 143342b3..dc90dead 100644 --- a/scripts/makeitso/option_descriptions.json +++ b/scripts/makeitso/option_descriptions.json @@ -1,21 +1,84 @@ { "version": "0.0.0", - "option_descriptions": { + + "pbs": { + "account_name": { + "prompt": "PBS account name" + }, + "cheyenne": {}, + "derecho": {}, + "pleiades": { + "queue": { + "prompt": "PBS queue name", + "default": "normal", + "valids": ["low", "normal", "long", "debug", "devel"] + }, + "walltime": { + "prompt": "Requested wall time for PBS job (HH:MM:SS)", + "default": "01:00:00" + }, + "select": { + "prompt": "Number of nodes to request", + "default": "2" + }, + "ncpus": { + "prompt": "Number of cores per node", + "default": "28" + }, + "mpiprocs": { + "prompt": "Number of MPI ranks per node", + "default": "2" + }, + "ompthreads": { + "prompt": "Number of OMP threads per MPI rank", + "default": "14" + }, + "helper_select": { + "prompt": "Number of voltron helper nodes", + "default": "1" + }, + "helper_mpiprocs": { + "prompt": "Number of MPI ranks per helper node", + "default": "1" + }, + "helper_ompthreads": { + "prompt": "Number of OMP threads per helper node", + "default": "28" + }, + "modules": [ + "nas", + "pkgsrc/2022Q1-rome", + "comp-intel/2020.4.304", + "mpi-hpe/mpt.2.23", + "hdf5/1.8.18_mpt" + ], + "num_jobs": { + "prompt": "Number of sequential PBS jobs to submit for the run", + "default": "1" + }, + "mpiexec_command": { + "prompt": "MPI command to launch run", + "default": "mpiexec correctOMPenvironment.sh $nodefile omplace" + } + } + }, + + "basic": { "hpc_system": { "prompt": "Name of HPC system", - "default": "cheyenne", + "default": "pleiades", "valids": ["cheyenne", "derecho", "pleiades"] }, - "kaijuhome": { - "prompt": "Path to kaiju installation" - }, - "kaiju_build_directory": { - "prompt": "Path to kaiju build directory" - }, "run_directory": { "prompt": "Run directory", "default": "." }, + "kaiju_install_directory": { + "prompt": "Path to kaiju installation" + }, + "kaiju_build_directory": { + "prompt": "Path to kaiju build directory" + }, "runid": { "prompt": "ID string for run", "default": "msphere" @@ -28,42 +91,10 @@ "solar_wind_file": { "prompt": "Name of solar wind file", "default": "bcwind.h5" - }, - "pbs_account_name": { - "prompt": "PBS account name" - }, - "pbs_queue": { - "prompt": "PBS queue name", - "default": "regular" - }, - "pbs_walltime": { - "prompt": "Requested time for PBS job (HH:MM:SS)", - "default": "01:00:00" - }, - "pbs_select": { - "prompt": "Number of nodes", - "default": "2" - }, - "pbs_ncpus": { - "prompt": "Number of cores per node", - "default": "36" - }, - "pbs_mpiprocs": { - "prompt": "Number of MPI ranks per node", - "default": "2" - }, - "pbs_ompthreads": { - "prompt": "Number of OMP threads per MPI rank", - "default": "18" - }, - "pbs_num_helpers": { - "prompt": "Number of voltron helper processes", - "default": "1" - }, - "pbs_numjobs": { - "prompt": "Number of sequential PBS jobs to submit", - "default": "1" - }, + } + }, + + "advanced": { "gamera_sim_doH5g": { "prompt": "gamera_sim_doH5g", "default": "T" diff --git a/scripts/makeitso/template.ini b/scripts/makeitso/template.ini index ce98e878..77823116 100644 --- a/scripts/makeitso/template.ini +++ b/scripts/makeitso/template.ini @@ -1,6 +1,6 @@ ## Gamera ## [sim] -runid = {{ runid }} +runid = {{ basic.runid }} doH5g = {{ gamera_sim_doH5g }} H5Grid = {{ gamera_sim_H5Grid }} icType = {{ gamera_sim_icType }} diff --git a/scripts/makeitso/template.pbs b/scripts/makeitso/template.pbs index 8d786057..b64ab2be 100644 --- a/scripts/makeitso/template.pbs +++ b/scripts/makeitso/template.pbs @@ -1,12 +1,12 @@ #!/bin/bash -# This script was generated to run on {{ hpc_system }}. +# This script was generated to run on {{ basic.hpc_system }}. -#PBS -N {{ runid }} -#PBS -A {{ pbs_account_name }} -#PBS -q {{ pbs_queue }} -#PBS -l walltime={{ pbs_walltime }} -#PBS -l select={{ pbs_select }}:ncpus={{ pbs_ncpus }}:mpiprocs={{ pbs_mpiprocs }}:ompthreads={{ pbs_ompthreads }}+{{ pbs_num_helpers }}:ncpus={{ pbs_ncpus }}:mpiprocs=1:ompthreads={{ pbs_helper_ompthreads }} +#PBS -N {{ basic.runid }} +#PBS -A {{ pbs.account_name }} +#PBS -q {{ pbs.queue }} +#PBS -l walltime={{ pbs.walltime }} +#PBS -l select={{ pbs.select }}:ncpus={{ pbs.ncpus }}:mpiprocs={{ pbs.mpiprocs }}:ompthreads={{ pbs.ompthreads }}+{{ pbs.helper_select }}:ncpus={{ pbs.ncpus }}:mpiprocs={{ pbs.helper_mpiprocs }}:ompthreads={{ pbs.helper_ompthreads }} #PBS -m abe #PBS -j oe @@ -17,21 +17,23 @@ export RUNID=$PBS_JOBNAME # Load the required modules for kaiju. module purge -{{ pbs_module_load}} +{%- for module in pbs.modules %} +module load {{ module }} +{%- endfor %} module list # Define the kaiju installation location. -export KAIJU_INSTALL_DIR={{ kaijuhome }} +export KAIJU_INSTALL_DIR={{ basic.kaiju_install_directory }} # Set kaiju-related environment variables. # This script sets KAIJUHOME and other environment variables. source $KAIJU_INSTALL_DIR/scripts/setupEnvironment.sh # Add the kaiju build bin directory to PATH. -export PATH={{ kaiju_build_directory }}/bin:$PATH +export PATH={{ basic.kaiju_build_directory }}/bin:$PATH # Set other environment variables. -{{ pbs_additional_environment_variables }} +{{ pbs.additional_environment_variables -}} export MPI_TYPE_DEPTH=32 export OMP_STACKSIZE=100M @@ -41,6 +43,6 @@ printenv # Run the model. Direct output from the program is saved in a text file. EXE=voltron_mpi.x echo "Running $EXE on model $RUNID." -{{ pbs_mpiexec_command }} $EXE $RUNID.xml >& $RUNID.out +{{ pbs.mpiexec_command }} $EXE $RUNID.xml >& $RUNID.out echo "Job $PBS_JOBID ended at `date` on `hostname`." From 1b5715a1168ae67d847cd4dfc35a4160a6ce8332 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Sun, 15 Oct 2023 16:43:57 -0400 Subject: [PATCH 110/365] Added --advanced command-line option. --- scripts/makeitso/makeitso.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index fb206e3c..fb0b436a 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -98,6 +98,10 @@ def create_command_line_parser(): None """ parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument( + "--advanced", action="store_true", + help="Configure advanced parameters (default: %(default)s)." + ) parser.add_argument( "--clobber", action="store_true", help="Overwrite existing options file (default: %(default)s)." @@ -178,7 +182,7 @@ def get_run_option(name, description): return str(option_value) -def prompt_user_for_run_options(): +def prompt_user_for_run_options(args): """Prompt the user for run options. Prompt the user for run options. @@ -190,7 +194,8 @@ def prompt_user_for_run_options(): Parameters ---------- - None + args : dict + Dictionary of command-line options Returns ------- @@ -337,6 +342,10 @@ def prompt_user_for_run_options(): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) + # Configure advanced options. + if args.advanced: + pass + #------------------------------------------------------------------------- # Options for voltron_mpi.x @@ -355,6 +364,10 @@ def prompt_user_for_run_options(): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) + # Configure advanced options. + if args.advanced: + pass + #------------------------------------------------------------------------- # Options for CHIMP @@ -368,6 +381,10 @@ def prompt_user_for_run_options(): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) + # Configure advanced options. + if args.advanced: + pass + #------------------------------------------------------------------------- # Options for REMIX @@ -380,6 +397,10 @@ def prompt_user_for_run_options(): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) + # Configure advanced options. + if args.advanced: + pass + #------------------------------------------------------------------------- # Options for RCM @@ -396,6 +417,10 @@ def prompt_user_for_run_options(): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) + # Configure advanced options. + if args.advanced: + pass + #------------------------------------------------------------------------- # Return the options dictionary. @@ -636,7 +661,7 @@ def main(): options = json.load(f) else: # Prompt the user for the run options. - options = prompt_user_for_run_options() + options = prompt_user_for_run_options(args) if debug: print(f"options = {options}") From 12cc7f23ecfda9481560d446607c5ad34fdad35e Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Sun, 15 Oct 2023 17:45:56 -0400 Subject: [PATCH 111/365] placeholder --- scripts/makeitso/makeitso.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index fb0b436a..e6e0108b 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -342,7 +342,9 @@ def prompt_user_for_run_options(args): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) - # Configure advanced options. + # These options have defaults based on the LFM grid resolution. + + # Configure or copy advanced options. if args.advanced: pass @@ -364,7 +366,7 @@ def prompt_user_for_run_options(args): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) - # Configure advanced options. + # Configure or copy advanced options. if args.advanced: pass @@ -381,7 +383,7 @@ def prompt_user_for_run_options(args): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) - # Configure advanced options. + # Configure or copy advanced options. if args.advanced: pass @@ -397,7 +399,7 @@ def prompt_user_for_run_options(args): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) - # Configure advanced options. + # Configure or copy advanced options. if args.advanced: pass @@ -417,7 +419,7 @@ def prompt_user_for_run_options(args): # for name in option_names: # options[name] = get_run_option(name, option_descriptions[name]) - # Configure advanced options. + # Configure or copy advanced options. if args.advanced: pass From 8ee642836e0d8148be01feb5c5f182568c87cebb Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Mon, 16 Oct 2023 07:21:41 -0600 Subject: [PATCH 112/365] Never let zero go to a denominator. --- src/remix/mixconductance.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remix/mixconductance.F90 b/src/remix/mixconductance.F90 index 97013257..20e303e5 100644 --- a/src/remix/mixconductance.F90 +++ b/src/remix/mixconductance.F90 @@ -569,15 +569,15 @@ module mixconductance phi0_rcm = St%Vars(i,j,IM_ENFLX) ! Merged flux - wMHD = 1.0-gtype_RCM(i,j) wRCM = gtype_RCM(i,j) + wMHD = 1.0-wRCM phi0 = wMHD*phi0_mhd + wRCM*phi0_rcm Ne = wMHD*Ne_mhd + wRCM*Ne_rcm Pe = wMHD*Pe_mhd + wRCM*Pe_rcm if(Ne>TINY) then kT = Pe/(Ne*kev2J) else - kT = 0.D0 + kT = eTINY endif conductance%E0 (i,j) = 2.0*kT From 267e568e1591909dc93c3824feb58c799770a8be Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 16 Oct 2023 10:05:21 -0400 Subject: [PATCH 113/365] Snapshot with basic/advanced mode. --- scripts/makeitso/makeitso.py | 257 +++++++-------- scripts/makeitso/option_descriptions.json | 377 +++------------------- scripts/makeitso/template.ini | 2 +- scripts/makeitso/template.pbs | 10 +- 4 files changed, 175 insertions(+), 471 deletions(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index e6e0108b..3cfa747e 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -29,10 +29,11 @@ DESCRIPTION = "Interactive script to prepare a MAGE magnetosphere model run." # Indent level for JSON output. JSON_INDENT = 4 +# Path to current kaiju installation +KAIJUHOME = os.environ["KAIJUHOME"] + # Path to directory containing support files for makeitso. -SUPPORT_FILES_DIRECTORY = os.path.join( - os.environ["KAIJUHOME"], "scripts", "makeitso" -) +SUPPORT_FILES_DIRECTORY = os.path.join(KAIJUHOME, "scripts", "makeitso") # Path to option descriptions file. OPTION_DESCRIPTIONS_FILE = os.path.join( @@ -45,7 +46,7 @@ INI_TEMPLATE = os.path.join(SUPPORT_FILES_DIRECTORY, "template.ini") # Path to template .pbs file. PBS_TEMPLATE = os.path.join(SUPPORT_FILES_DIRECTORY, "template.pbs") -# # Program defaults +# Program defaults # # Module sets by platform and run type. # modules = { @@ -121,7 +122,7 @@ def create_command_line_parser(): return parser -def get_run_option(name, description): +def get_run_option(name, description, advanced=False): """Prompt the user for a single run option. Prompt the user for a single run option. If no user input is provided, @@ -135,6 +136,8 @@ def get_run_option(name, description): Name of option description : dict, default None Dictionary of metadata for the option. + advanced : bool + True if parameter is from advanced configuration Returns ------- @@ -150,6 +153,11 @@ def get_run_option(name, description): default = description.get("default", None) valids = description.get("valids", None) + # If not in advanced mode, and this is an advanced option, take the + # default. + if not advanced and "advanced" in description: + return default + # If provided, add the valid values in val1|val2 format to the prompt. if valids is not None: vs = "|".join(valids) @@ -209,144 +217,123 @@ def prompt_user_for_run_options(args): # Read the dictionary of option descriptions. with open(OPTION_DESCRIPTIONS_FILE, "r", encoding="utf-8") as f: option_descriptions = json.load(f) - # Convenience variables to help avoid dict key errors. - option_descriptions_pbs = option_descriptions["pbs"] - option_descriptions_basic = option_descriptions["basic"] - option_descriptions_advanced = option_descriptions["advanced"] # Initialize the dictionary of program options. - options = { - "pbs": {}, - "basic": {}, - "advanced": {}, - } - # Convenience variables to help avoid dict key errors. - options_pbs = options["pbs"] - options_basic = options["basic"] - options_advanced = options["advanced"] + options = {} #------------------------------------------------------------------------- - # Run definition options + # PBS options + options["pbs"] = {} + o = options["pbs"] - # HPC system run directory. - for name in ["hpc_system", "run_directory"]: - options_basic[name] = get_run_option( - name, option_descriptions_basic[name] - ) + # Common (HPC platform-independent) options + od = option_descriptions["pbs"]["_common"] + od["account_name"]["default"] = os.getlogin() + od["kaiju_install_directory"]["default"] = KAIJUHOME + od["kaiju_build_directory"]["default"] = os.path.join(KAIJUHOME, "build_mpi") + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) - # Compute the default for the location of the kaiju installation to use. - # The installation is assumed to be the installation which contains - # this running script. Note that this code requires that the KAIJUHOME - # environment variable is set. This environment variable is set - # automatically when the user runs the setupEnvironment.[c]sh script - # or its equivalent. - option_descriptions_basic["kaiju_install_directory"]["default"] = ( - os.environ["KAIJUHOME"] - ) - - # kaiju installation directory - for name in ["kaiju_install_directory"]: - options_basic[name] = get_run_option( - name, option_descriptions_basic[name] - ) - - # Compute the default build directory based on KAIJUHOME and the run - # type. The build directory is the directory where cmake and make were - # run during the build process. Typically, this is done in the kaiju - # subdirectory "build_mpi" (for MPI builds). - option_descriptions_basic["kaiju_build_directory"]["default"] = ( - os.path.join(options_basic['kaiju_install_directory'], "build_mpi") - ) - - # kaiju build directory - for name in ["kaiju_build_directory"]: - options_basic[name] = get_run_option( - name, option_descriptions_basic[name] - ) + # Platform-specific options + hpc_platform = o["hpc_system"] + od = option_descriptions["pbs"][hpc_platform] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) # Fetch high-level options about the run. # NOTE: All files must be in the run directory. # for name in ["runid", "LFM_grid_type", "solar_wind_file"]: - for name in ["runid"]: - options_basic[name] = get_run_option( - name, option_descriptions_basic[name] - ) + # for name in ["runid"]: + # options_basic[name] = get_run_option( + # name, option_descriptions_basic[name] + # ) #------------------------------------------------------------------------- # PBS job options # Compute PBS defaults. - option_descriptions_pbs["account_name" ]["default"] = os.getlogin() + # option_descriptions_pbs["account_name" ]["default"] = os.getlogin() # Cross-platform PBS options - for name in ["account_name"]: - options_pbs[name] = get_run_option( - name, option_descriptions_pbs[name] - ) + # for name in ["account_name"]: + # options_pbs[name] = get_run_option( + # name, option_descriptions_pbs[name] + # ) # HPC system-specific PBS options - option_descriptions_pbs_hpc = ( - option_descriptions_pbs[options_basic["hpc_system"]] - ) + # option_descriptions_pbs_hpc = ( + # option_descriptions_pbs[options_basic["hpc_system"]] + # ) # Options for main computation nodes - for name in ["queue", "walltime", "select", "ncpus", "mpiprocs", - "ompthreads"]: - options_pbs[name] = get_run_option( - name, option_descriptions_pbs_hpc[name] - ) + # for name in ["queue", "walltime", "select", "ncpus", "mpiprocs", + # "ompthreads"]: + # options_pbs[name] = get_run_option( + # name, option_descriptions_pbs_hpc[name] + # ) # Options for helper nodes - for name in ["helper_select", "helper_mpiprocs", "helper_ompthreads"]: - options_pbs[name] = get_run_option( - name, option_descriptions_pbs_hpc[name] - ) + # for name in ["helper_select", "helper_mpiprocs", "helper_ompthreads"]: + # options_pbs[name] = get_run_option( + # name, option_descriptions_pbs_hpc[name] + # ) # Options for segmented jobs - for name in ["num_jobs"]: - options_pbs[name] = get_run_option( - name, option_descriptions_pbs_hpc[name] - ) + # for name in ["num_jobs"]: + # options_pbs[name] = get_run_option( + # name, option_descriptions_pbs_hpc[name] + # ) # Determine the module set to use. - options_pbs["modules"] = option_descriptions_pbs_hpc["modules"] + # options_pbs["modules"] = option_descriptions_pbs_hpc["modules"] # Specify the command to launch an MPI program. - options_pbs["mpiexec_command"] = option_descriptions_pbs_hpc["mpiexec_command"] + # for name in ["mpiexec_command"]: + # options_pbs[name] = get_run_option( + # name, option_descriptions_pbs_hpc[name] + # ) # Specify other environment variables to set (this is a placeholder). - options["pbs.additional_environment_variables"] = None + # options["pbs"]["additional_environment_variables"] = None #------------------------------------------------------------------------- - # Options for gamera_mpi.x + # Options for gamera -# option_names = [ -# "gamera_sim_doH5g", "gamera_sim_H5Grid", "gamera_sim_icType", -# "gamera_sim_pdmb", "gamera_sim_rmeth", -# "gamera_floors_dFloor", "gamera_floors_pFloor", -# "gamera_timestep_doCPR", "gamera_timestep_limCPR", -# "gamera_restart_doRes", "gamera_restart_resID", "gamera_restart_nRes", -# "gamera_physics_doMHD", "gamera_physics_doBoris", "gamera_physics_Ca", -# "gamera_ring_gid", "gamera_ring_doRing", -# "gamera_ringknobs_doVClean", -# "gamera_wind_tsfile", -# "gamera_source_doSource", "gamera_source_doWolfLim", -# "gamera_source_doBounceDT", "gamera_source_nBounce", -# "gamera_iPdir_N", "gamera_iPdir_bcPeriodic", -# "gamera_jPdir_N", "gamera_jPdir_bcPeriodic", -# "gamera_kPdir_N", "gamera_kPdir_bcPeriodic", -# ] -# for name in option_names: -# options[name] = get_run_option(name, option_descriptions[name]) + # Configure basic options. + # o = options["basic"]["gamera"] = {} + # d = option_descriptions["basic"]["gamera"]["sim"] + # for name in [ + # "doH5g" + # ]: + # o[name] = get_run_option(name, d[name]) # These options have defaults based on the LFM grid resolution. - + # Configure or copy advanced options. - if args.advanced: - pass + # for name in [ + # "gamera_sim_doH5g" + # "gamera_sim_doH5g", "gamera_sim_H5Grid", "gamera_sim_icType", + # "gamera_sim_pdmb", "gamera_sim_rmeth", + # "gamera_floors_dFloor", "gamera_floors_pFloor", + # "gamera_timestep_doCPR", "gamera_timestep_limCPR", + # "gamera_restart_doRes", "gamera_restart_resID", "gamera_restart_nRes", + # "gamera_physics_doMHD", "gamera_physics_doBoris", "gamera_physics_Ca", + # "gamera_ring_gid", "gamera_ring_doRing", + # "gamera_ringknobs_doVClean", + # "gamera_wind_tsfile", + # "gamera_source_doSource", "gamera_source_doWolfLim", + # "gamera_source_doBounceDT", "gamera_source_nBounce", + # "gamera_iPdir_N", "gamera_iPdir_bcPeriodic", + # "gamera_jPdir_N", "gamera_jPdir_bcPeriodic", + # "gamera_kPdir_N", "gamera_kPdir_bcPeriodic", + # ]: + # if args.advanced: + # options_basic[name] = get_run_option(name, option_descriptions_advanced[name]) + # else: + # options_basic[name] = option_descriptions_advanced[name]["default"] #------------------------------------------------------------------------- @@ -491,15 +478,15 @@ def create_ini_files(options): # If a single segment was requested, create a single file. # If multiple segments were requested, create an .ini file for each # segment. - if int(options["pbs"]["num_jobs"]) == 1: - ini_content = template.render(options) - ini_file = os.path.join( - options["basic"]["run_directory"], - f"{options['basic']['runid']}.ini" - ) - with open(ini_file, "w", encoding="utf-8") as f: - f.write(ini_content) - ini_files.append(ini_file) + # if int(options["pbs"]["num_jobs"]) == 1: + ini_content = template.render(options) + ini_file = os.path.join( + options["pbs"]["run_directory"], + f"{options['pbs']['runid']}.ini" + ) + with open(ini_file, "w", encoding="utf-8") as f: + f.write(ini_content) + ini_files.append(ini_file) # else: # for job in range(int(options["pbs"]["num_jobs"])): # opt = options # Need a copy of options @@ -581,22 +568,20 @@ def create_pbs_scripts(options): template = Template(template_content) # Create a PBS script for each segment. - hpc_system = options["basic"]["hpc_system"] options_pbs = options["pbs"] + hpc_system = options_pbs["hpc_system"] pbs_scripts = [] - # - options_pbs["num_jobs"] = 1 - # - if int(options_pbs["num_jobs"]) == 1: - pbs_content = template.render(options) - pbs_script = os.path.join( - options["basic"]["run_directory"], - f"{options['basic']['runid']}.pbs" - ) - with open(pbs_script, "w", encoding="utf-8") as f: - f.write(pbs_content) - pbs_scripts.append(pbs_script) -# else: + # if int(options_pbs["num_jobs"]) == 1: + pbs_content = template.render(options) + pbs_script = os.path.join( + options_pbs["run_directory"], + f"{options['pbs']['runid']}.pbs" + ) + with open(pbs_script, "w", encoding="utf-8") as f: + f.write(pbs_content) + pbs_scripts.append(pbs_script) + # else: + # raise TypeError("No segmented runs yet!") # for segment in range(int(options["pbs_num_jobs"])): # pbs_content = template.render(options) # pbs_script = os.path.join( @@ -622,9 +607,9 @@ def create_pbs_scripts(options): # f.write(cmd) # cmd = f"echo $job_id\n" # f.write(cmd) - -# # Return the paths to the PBS scripts. -# return pbs_scripts + + # Return the paths to the PBS scripts. + return pbs_scripts def main(): @@ -667,8 +652,11 @@ def main(): if debug: print(f"options = {options}") - # Save the options dictionary as a JSON file in the run directory. - path = os.path.join(options["basic"]["run_directory"], "options.json") + # Move to the output directory. + os.chdir(options["pbs"]["run_directory"]) + + # Save the options dictionary as a JSON file in the current directory. + path = f"{options['pbs']['runid']}.json" if os.path.exists(path): if not clobber: raise FileExistsError(f"Options file {path} exists!") @@ -676,10 +664,7 @@ def main(): json.dump(options, f, indent=JSON_INDENT) # Save the current directory. - original_directory = os.getcwd() - - # Move to the output directory. - os.chdir(options["basic"]["run_directory"]) + # original_directory = os.getcwd() # # Run the preprocessing steps. # if verbose: @@ -708,7 +693,7 @@ def main(): print(f"The PBS job scripts {pbs_scripts} are ready.") # Move back to the original directory. - os.chdir(original_directory) + # os.chdir(original_directory) if __name__ == "__main__": diff --git a/scripts/makeitso/option_descriptions.json b/scripts/makeitso/option_descriptions.json index dc90dead..6d8a8b12 100644 --- a/scripts/makeitso/option_descriptions.json +++ b/scripts/makeitso/option_descriptions.json @@ -2,11 +2,36 @@ "version": "0.0.0", "pbs": { - "account_name": { - "prompt": "PBS account name" + "_common": { + "hpc_system": { + "prompt": "Name of HPC system", + "default": "pleiades", + "valids": ["cheyenne", "derecho", "pleiades"] + }, + "account_name": { + "prompt": "PBS account name" + }, + "run_directory": { + "prompt": "Run directory", + "default": "." + }, + "kaiju_install_directory": { + "prompt": "Path to kaiju installation" + }, + "kaiju_build_directory": { + "prompt": "Path to kaiju build directory" + }, + "runid": { + "prompt": "ID string for run", + "default": "msphere" + } + }, + "cheyenne": { + + }, + "derecho": { + }, - "cheyenne": {}, - "derecho": {}, "pleiades": { "queue": { "prompt": "PBS queue name", @@ -45,333 +70,27 @@ "prompt": "Number of OMP threads per helper node", "default": "28" }, - "modules": [ - "nas", - "pkgsrc/2022Q1-rome", - "comp-intel/2020.4.304", - "mpi-hpe/mpt.2.23", - "hdf5/1.8.18_mpt" - ], - "num_jobs": { - "prompt": "Number of sequential PBS jobs to submit for the run", - "default": "1" + "other": { + "advanced": true, + "prompt": "Additional options for PBS -l", + "default": ":model=bro" }, - "mpiexec_command": { - "prompt": "MPI command to launch run", - "default": "mpiexec correctOMPenvironment.sh $nodefile omplace" + "modules": { + "advanced": true, + "prompt": "Modules to load", + "default": [ + "nas", + "pkgsrc/2022Q1-rome", + "comp-intel/2020.4.304", + "mpi-hpe/mpt.2.23", + "hdf5/1.8.18_mpt" + ] + }, + "environment_variables": { + "advanced": true, + "prompt": "Additional environment variable settings", + "default": null } } - }, - - "basic": { - "hpc_system": { - "prompt": "Name of HPC system", - "default": "pleiades", - "valids": ["cheyenne", "derecho", "pleiades"] - }, - "run_directory": { - "prompt": "Run directory", - "default": "." - }, - "kaiju_install_directory": { - "prompt": "Path to kaiju installation" - }, - "kaiju_build_directory": { - "prompt": "Path to kaiju build directory" - }, - "runid": { - "prompt": "ID string for run", - "default": "msphere" - }, - "LFM_grid_type": { - "prompt": "Lyon-Fedder-Mobarry (LFM) grid type", - "default": "D", - "valids": ["D", "Q", "O", "H"] - }, - "solar_wind_file": { - "prompt": "Name of solar wind file", - "default": "bcwind.h5" - } - }, - - "advanced": { - "gamera_sim_doH5g": { - "prompt": "gamera_sim_doH5g", - "default": "T" - }, - "gamera_sim_H5Grid": { - "prompt": "Path to LFM grid file", - "default": "lfmD.h5" - }, - "gamera_sim_icType": { - "prompt": "Initial condition type", - "default": "user", - "valids": ["user"] - }, - "gamera_sim_pdmb": { - "prompt": "gamera_sim_pdmb", - "default": "1.0" - }, - "gamera_sim_rmeth": { - "prompt": "gamera_sim_rmeth", - "default": "7UP" - }, - "gamera_floors_dFloor": { - "prompt": "gamera_floors_dFloor", - "default": "1.0e-4" - }, - "gamera_floors_pFloor": { - "prompt": "gamera_floors_pFloor", - "default": "1.0e-6" - }, - "gamera_timestep_doCPR": { - "prompt": "gamera_timestep_doCPR", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_timestep_limCPR": { - "prompt": "gamera_timestep_limCPR", - "default": "0.25" - }, - "gamera_restart_doRes": { - "prompt": "gamera_restart_doRes", - "default": "F", - "valids": ["T", "F"] - }, - "gamera_restart_resID": { - "prompt": "gamera_restart_resID", - "default": "msphere" - }, - "gamera_restart_nRes": { - "prompt": "gamera_restart_nRes", - "default": "-1" - }, - "gamera_physics_doMHD": { - "prompt": "gamera_physics_doMHD", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_physics_doBoris": { - "prompt": "gamera_physics_doBoris", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_physics_Ca": { - "prompt": "gamera_physics_Ca", - "default": "10.0" - }, - "gamera_ring_gid": { - "prompt": "gamera_ring_gid", - "default": "lfm" - }, - "gamera_ring_doRing": { - "prompt": "gamera_ring_doRing", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_ringknobs_doVClean": { - "prompt": "gamera_ringknobs_doVClean", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_wind_tsfile": { - "prompt": "gamera_wind_tsfile", - "default": "bcwind.h5" - }, - "gamera_source_doSource": { - "prompt": "gamera_source_doSource", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_source_doWolfLim": { - "prompt": "gamera_source_doWolfLim", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_source_doBounceDT": { - "prompt": "gamera_source_doBounceDT", - "default": "T", - "valids": ["T", "F"] - }, - "gamera_source_nBounce": { - "prompt": "gamera_source_nBounce", - "default": "1.0" - }, - "gamera_iPdir_N": { - "prompt": "gamera_iPdir_N", - "default": "2" - }, - "gamera_iPdir_bcPeriodic": { - "prompt": "gamera_iPdir_bcPeriodic", - "default": "F", - "valids": ["T", "F"] - }, - "gamera_jPdir_N": { - "prompt": "gamera_jPdir_N", - "default": "2" - }, - "gamera_jPdir_bcPeriodic": { - "prompt": "gamera_jPdir_bcPeriodic", - "default": "F", - "valids": ["T", "F"] - }, - "gamera_kPdir_N": { - "prompt": "gamera_kPdir_N", - "default": "1" - }, - "gamera_kPdir_bcPeriodic": { - "prompt": "gamera_kPdir_bcPeriodic", - "default": "T", - "valids": ["T", "F"] - }, - "voltron_time_tFin": { - "prompt": "voltron_time_tFin", - "default": "3600.0" - }, - "voltron_spinup_doSpin": { - "prompt": "voltron_spinup_doSpin", - "default": "T", - "valids": ["T", "F"] - }, - "voltron_spinup_tSpin": { - "prompt": "voltron_spinup_tSpin", - "default": "3600.0" - }, - "voltron_output_dtOut": { - "prompt": "voltron_output_dtOut", - "default": "900.0" - }, - "voltron_output_tsOut": { - "prompt": "voltron_output_tsOut", - "default": "100" - }, - "voltron_coupling_dtCouple": { - "prompt": "voltron_coupling_dtCouple", - "default": "5.0" - }, - "voltron_coupling_rTrc": { - "prompt": "voltron_coupling_rTrc", - "default": "4.0" - }, - "voltron_coupling_imType": { - "prompt": "voltron_coupling_imType", - "default": "RCM" - }, - "voltron_coupling_doQkSquish": { - "prompt": "voltron_coupling_doQkSquish", - "default": "T", - "valids": ["T", "F"] - }, - "voltron_coupling_qkSquishStride": { - "prompt": "voltron_coupling_qkSquishStride", - "default": "2" - }, - "voltron_restart_dtRes": { - "prompt": "voltron_restart_dtRes", - "default": "1800.0" - }, - "voltron_imag_doInit": { - "prompt": "voltron_imag_doInit", - "default": "T", - "valids": ["T", "F"] - }, - "voltron_ebsquish_epsSquish": { - "prompt": "voltron_ebsquish_epsSquish", - "default": "0.05" - }, - "chimp_units_uid": { - "prompt": "chimp_units_uid", - "default": "EARTHCODE" - }, - "chimp_fields_grType": { - "prompt": "chimp_fields_grType", - "default": "lfm" - }, - "chimp_domain_dtype": { - "prompt": "chimp_domain_dtype", - "default": "MAGE" - }, - "chimp_tracer_epsds": { - "prompt": "chimp_tracer_epsds", - "default": "0.05" - }, - "remix_conductance_doStarlight": { - "prompt": "remix_conductance_doStarlight", - "default": "T", - "valids": ["T", "F"] - }, - "remix_conductance_doRamp": { - "prompt": "remix_conductance_doRamp", - "default": "F", - "valids": ["T", "F"] - }, - "remix_precipitation_aurora_model_type": { - "prompt": "remix_precipitation_aurora_model_type", - "default": "RCMONO" - }, - "remix_precipitation_alpha": { - "prompt": "remix_precipitation_alpha", - "default": "0.2" - }, - "remix_precipitation_beta": { - "prompt": "remix_precipitation_beta", - "default": "04" - }, - "rcm_rcmdomain_domType": { - "prompt": "rcm_rcmdomain_domType", - "default": "ELLIPSE" - }, - "rcm_ellipse_xSun": { - "prompt": "rcm_ellipse_xSun", - "default": "12.5" - }, - "rcm_ellipse_yDD": { - "prompt": "rcm_ellipse_yDD", - "default": "15.0" - }, - "rcm_ellipse_xTail": { - "prompt": "rcm_ellipse_xTail", - "default": "15.0" - }, - "rcm_ellipse_isDynamic": { - "prompt": "rcm_ellipse_isDynamic", - "default": "T", - "valids": ["T", "F"] - }, - "rcm_grid_LowLat": { - "prompt": "rcm_grid_LowLat", - "default": "30.0" - }, - "rcm_grid_HiLat": { - "prompt": "rcm_grid_HiLat", - "default": "75.0" - }, - "rcm_grid_doLatStretch": { - "prompt": "rcm_grid_doLatStretch", - "default": "F", - "valids": ["T", "F"] - }, - "rcm_plasmasphere_isDynamic": { - "prompt": "rcm_plasmasphere_isDynamic", - "default": "F", - "valids": ["T", "F"] - }, - "rcm_plasmasphere_initKp": { - "prompt": "rcm_plasmasphere_initKp", - "default": "2" - }, - "rcm_plasmasphere_doRefill": { - "prompt": "rcm_plasmasphere_doRefill", - "default": "T", - "valids": ["T", "F"] - }, - "rcm_plasmasphere_DenPP0": { - "prompt": "rcm_plasmasphere_DenPP0", - "default": "1.0e-2" - }, - "rcm_plasmasphere_tAvg": { - "prompt": "rcm_plasmasphere_tAvg", - "default": "600.0" - } } } diff --git a/scripts/makeitso/template.ini b/scripts/makeitso/template.ini index 77823116..ec989a5b 100644 --- a/scripts/makeitso/template.ini +++ b/scripts/makeitso/template.ini @@ -1,6 +1,6 @@ ## Gamera ## [sim] -runid = {{ basic.runid }} +runid = {{ pbs.runid }} doH5g = {{ gamera_sim_doH5g }} H5Grid = {{ gamera_sim_H5Grid }} icType = {{ gamera_sim_icType }} diff --git a/scripts/makeitso/template.pbs b/scripts/makeitso/template.pbs index b64ab2be..00ff89b5 100644 --- a/scripts/makeitso/template.pbs +++ b/scripts/makeitso/template.pbs @@ -1,12 +1,12 @@ #!/bin/bash -# This script was generated to run on {{ basic.hpc_system }}. +# This script was generated to run on {{ pbs.hpc_system }}. -#PBS -N {{ basic.runid }} +#PBS -N {{ pbs.runid }} #PBS -A {{ pbs.account_name }} #PBS -q {{ pbs.queue }} #PBS -l walltime={{ pbs.walltime }} -#PBS -l select={{ pbs.select }}:ncpus={{ pbs.ncpus }}:mpiprocs={{ pbs.mpiprocs }}:ompthreads={{ pbs.ompthreads }}+{{ pbs.helper_select }}:ncpus={{ pbs.ncpus }}:mpiprocs={{ pbs.helper_mpiprocs }}:ompthreads={{ pbs.helper_ompthreads }} +#PBS -l select={{ pbs.select }}:ncpus={{ pbs.ncpus }}:mpiprocs={{ pbs.mpiprocs }}:ompthreads={{ pbs.ompthreads }}{{ pbs.other }}+{{ pbs.helper_select }}:ncpus={{ pbs.ncpus }}:mpiprocs={{ pbs.helper_mpiprocs }}:ompthreads={{ pbs.helper_ompthreads }} #PBS -m abe #PBS -j oe @@ -23,14 +23,14 @@ module load {{ module }} module list # Define the kaiju installation location. -export KAIJU_INSTALL_DIR={{ basic.kaiju_install_directory }} +export KAIJU_INSTALL_DIR={{ pbs.kaiju_install_directory }} # Set kaiju-related environment variables. # This script sets KAIJUHOME and other environment variables. source $KAIJU_INSTALL_DIR/scripts/setupEnvironment.sh # Add the kaiju build bin directory to PATH. -export PATH={{ basic.kaiju_build_directory }}/bin:$PATH +export PATH={{ pbs.kaiju_build_directory }}/bin:$PATH # Set other environment variables. {{ pbs.additional_environment_variables -}} From b0b012ccc6e05c13e80862b13eb9279b3759eeb1 Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 16 Oct 2023 10:24:38 -0400 Subject: [PATCH 114/365] PBS template complete --- scripts/makeitso/option_descriptions.json | 67 ++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/scripts/makeitso/option_descriptions.json b/scripts/makeitso/option_descriptions.json index 6d8a8b12..f7b97c54 100644 --- a/scripts/makeitso/option_descriptions.json +++ b/scripts/makeitso/option_descriptions.json @@ -27,11 +27,74 @@ } }, "cheyenne": { - + "queue": { + "prompt": "PBS queue name", + "default": "regular", + "valids": ["economy", "premium", "regular", "share"] + }, + "walltime": { + "prompt": "Requested wall time for PBS job (HH:MM:SS)", + "default": "01:00:00" + }, + "select": { + "prompt": "Number of nodes to request", + "default": "2" + }, + "ncpus": { + "prompt": "Number of cores per node", + "default": "36" + }, + "mpiprocs": { + "prompt": "Number of MPI ranks per node", + "default": "2" + }, + "ompthreads": { + "prompt": "Number of OMP threads per MPI rank", + "default": "18" + }, + "helper_select": { + "prompt": "Number of voltron helper nodes", + "default": "1" + }, + "helper_mpiprocs": { + "advanced": true, + "prompt": "Number of MPI ranks per helper node", + "default": "1" + }, + "helper_ompthreads": { + "advanced": true, + "prompt": "Number of OMP threads per helper node", + "default": "36" + }, + "other": { + "advanced": true, + "prompt": "Additional options for PBS -l", + "default": "" + }, + "modules": { + "advanced": true, + "prompt": "Modules to load", + "default": [ + "cmake/3.22.0", + "git/2.33.1", + "ncarenv/1.3", + "intel/2022.1", + "geos/3.10.1", + "ncarcompilers/0.5.0", + "mpt/2.25", + "hdf5-mpi/1.12.2" + ] + }, + "mpiexec_command": { + "prompt": "MPI command to launch run", + "default": "mpiexec correctOMPenvironment.sh $nodefile omplace" + } }, + "derecho": { }, + "pleiades": { "queue": { "prompt": "PBS queue name", @@ -63,10 +126,12 @@ "default": "1" }, "helper_mpiprocs": { + "advanced": true, "prompt": "Number of MPI ranks per helper node", "default": "1" }, "helper_ompthreads": { + "advanced": true, "prompt": "Number of OMP threads per helper node", "default": "28" }, From 26c15ca73db83ce815a47b6c3b01466016b03a6c Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 16 Oct 2023 10:37:53 -0400 Subject: [PATCH 115/365] Added derecho descriptions. --- scripts/makeitso/makeitso.py | 59 +--------------- scripts/makeitso/option_descriptions.json | 85 ++++++++++++++++++++++- 2 files changed, 85 insertions(+), 59 deletions(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index 3cfa747e..7e8ddc8b 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -241,66 +241,9 @@ def prompt_user_for_run_options(args): for on in od: o[on] = get_run_option(on, od[on], args.advanced) - # Fetch high-level options about the run. - # NOTE: All files must be in the run directory. - # for name in ["runid", "LFM_grid_type", "solar_wind_file"]: - # for name in ["runid"]: - # options_basic[name] = get_run_option( - # name, option_descriptions_basic[name] - # ) - #------------------------------------------------------------------------- - # PBS job options - - # Compute PBS defaults. - # option_descriptions_pbs["account_name" ]["default"] = os.getlogin() - - # Cross-platform PBS options - # for name in ["account_name"]: - # options_pbs[name] = get_run_option( - # name, option_descriptions_pbs[name] - # ) - - # HPC system-specific PBS options - # option_descriptions_pbs_hpc = ( - # option_descriptions_pbs[options_basic["hpc_system"]] - # ) - - # Options for main computation nodes - # for name in ["queue", "walltime", "select", "ncpus", "mpiprocs", - # "ompthreads"]: - # options_pbs[name] = get_run_option( - # name, option_descriptions_pbs_hpc[name] - # ) - - # Options for helper nodes - # for name in ["helper_select", "helper_mpiprocs", "helper_ompthreads"]: - # options_pbs[name] = get_run_option( - # name, option_descriptions_pbs_hpc[name] - # ) - - # Options for segmented jobs - # for name in ["num_jobs"]: - # options_pbs[name] = get_run_option( - # name, option_descriptions_pbs_hpc[name] - # ) - - # Determine the module set to use. - # options_pbs["modules"] = option_descriptions_pbs_hpc["modules"] - - # Specify the command to launch an MPI program. - # for name in ["mpiexec_command"]: - # options_pbs[name] = get_run_option( - # name, option_descriptions_pbs_hpc[name] - # ) - - # Specify other environment variables to set (this is a placeholder). - # options["pbs"]["additional_environment_variables"] = None - - #------------------------------------------------------------------------- - - # Options for gamera + # gamera options # Configure basic options. # o = options["basic"]["gamera"] = {} diff --git a/scripts/makeitso/option_descriptions.json b/scripts/makeitso/option_descriptions.json index f7b97c54..02655d60 100644 --- a/scripts/makeitso/option_descriptions.json +++ b/scripts/makeitso/option_descriptions.json @@ -86,13 +86,87 @@ ] }, "mpiexec_command": { + "advanced": true, "prompt": "MPI command to launch run", "default": "mpiexec correctOMPenvironment.sh $nodefile omplace" + }, + "num_jobs": { + "advanced": true, + "prompt": "Number of sequential PBS jobs to submit for the run", + "default": "1" } }, "derecho": { - + "queue": { + "prompt": "PBS queue name", + "default": "regular", + "valids": ["economy", "premium", "regular", "share"] + }, + "walltime": { + "prompt": "Requested wall time for PBS job (HH:MM:SS)", + "default": "01:00:00" + }, + "select": { + "prompt": "Number of nodes to request", + "default": "2" + }, + "ncpus": { + "prompt": "Number of cores per node", + "default": "128" + }, + "mpiprocs": { + "prompt": "Number of MPI ranks per node", + "default": "2" + }, + "ompthreads": { + "prompt": "Number of OMP threads per MPI rank", + "default": "64" + }, + "helper_select": { + "prompt": "Number of voltron helper nodes", + "default": "1" + }, + "helper_mpiprocs": { + "advanced": true, + "prompt": "Number of MPI ranks per helper node", + "default": "1" + }, + "helper_ompthreads": { + "advanced": true, + "prompt": "Number of OMP threads per helper node", + "default": "128" + }, + "other": { + "advanced": true, + "prompt": "Additional options for PBS -l", + "default": "" + }, + "modules": { + "advanced": true, + "prompt": "Modules to load", + "default": [ + "ncarenv/23.06", + "cmake/3.26.3", + "craype/2.7.20", + "intel/2023.0.0", + "geos/3.9.1", + "ncarcompilers/1.0.0", + "cray-mpich/8.1.25", + "hdf5/1.12.2", + "mkl/2023.0.0" + ] + }, + "mpiexec_command": { + "advanced": true, + "prompt": "MPI command to launch run", + "default": "mpiexec pinCpuCores.sh" + }, + "num_jobs": { + "advanced": true, + "prompt": "Number of sequential PBS jobs to submit for the run", + "default": "1" + } }, "pleiades": { @@ -155,6 +229,15 @@ "advanced": true, "prompt": "Additional environment variable settings", "default": null + }, + "mpiexec_command": { + "advanced": true, + "prompt": "MPI command to launch run", + "default": "mpiexec correctOMPenvironment.sh $nodefile omplace" + }, + "num_jobs": { + "prompt": "Number of sequential PBS jobs to submit for the run", + "default": "1" } } } From ffc22b45730cc06e2a2844e024541113bd546caf Mon Sep 17 00:00:00 2001 From: Eric Winter Date: Mon, 16 Oct 2023 13:23:22 -0400 Subject: [PATCH 116/365] First complete version with advanced parameters. --- scripts/makeitso/makeitso.py | 295 ++++++++++----- scripts/makeitso/option_descriptions.json | 419 +++++++++++++++++++++- scripts/makeitso/template.ini | 129 +++---- 3 files changed, 692 insertions(+), 151 deletions(-) diff --git a/scripts/makeitso/makeitso.py b/scripts/makeitso/makeitso.py index 7e8ddc8b..12cfb9f0 100755 --- a/scripts/makeitso/makeitso.py +++ b/scripts/makeitso/makeitso.py @@ -243,115 +243,238 @@ def prompt_user_for_run_options(args): #------------------------------------------------------------------------- - # gamera options + # GAMERA options + options["gamera"] = {} - # Configure basic options. - # o = options["basic"]["gamera"] = {} - # d = option_descriptions["basic"]["gamera"]["sim"] - # for name in [ - # "doH5g" - # ]: - # o[name] = get_run_option(name, d[name]) + # Common options + options["gamera"]["_common"] = {} + o = options["gamera"]["_common"] + od = option_descriptions["gamera"]["_common"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) - # These options have defaults based on the LFM grid resolution. + # options + options["gamera"]["sim"] = {} + o = options["gamera"]["sim"] + od = option_descriptions["gamera"]["sim"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) - # Configure or copy advanced options. - # for name in [ - # "gamera_sim_doH5g" - # "gamera_sim_doH5g", "gamera_sim_H5Grid", "gamera_sim_icType", - # "gamera_sim_pdmb", "gamera_sim_rmeth", - # "gamera_floors_dFloor", "gamera_floors_pFloor", - # "gamera_timestep_doCPR", "gamera_timestep_limCPR", - # "gamera_restart_doRes", "gamera_restart_resID", "gamera_restart_nRes", - # "gamera_physics_doMHD", "gamera_physics_doBoris", "gamera_physics_Ca", - # "gamera_ring_gid", "gamera_ring_doRing", - # "gamera_ringknobs_doVClean", - # "gamera_wind_tsfile", - # "gamera_source_doSource", "gamera_source_doWolfLim", - # "gamera_source_doBounceDT", "gamera_source_nBounce", - # "gamera_iPdir_N", "gamera_iPdir_bcPeriodic", - # "gamera_jPdir_N", "gamera_jPdir_bcPeriodic", - # "gamera_kPdir_N", "gamera_kPdir_bcPeriodic", - # ]: - # if args.advanced: - # options_basic[name] = get_run_option(name, option_descriptions_advanced[name]) - # else: - # options_basic[name] = option_descriptions_advanced[name]["default"] + # options + options["gamera"]["floors"] = {} + o = options["gamera"]["floors"] + od = option_descriptions["gamera"]["floors"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["timestep"] = {} + o = options["gamera"]["timestep"] + od = option_descriptions["gamera"]["timestep"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["restart"] = {} + o = options["gamera"]["restart"] + od = option_descriptions["gamera"]["restart"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["physics"] = {} + o = options["gamera"]["physics"] + od = option_descriptions["gamera"]["physics"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["ring"] = {} + o = options["gamera"]["ring"] + od = option_descriptions["gamera"]["ring"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["ringknobs"] = {} + o = options["gamera"]["ringknobs"] + od = option_descriptions["gamera"]["ringknobs"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["wind"] = {} + o = options["gamera"]["wind"] + od = option_descriptions["gamera"]["wind"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["source"] = {} + o = options["gamera"]["source"] + od = option_descriptions["gamera"]["source"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["iPdir"] = {} + o = options["gamera"]["iPdir"] + od = option_descriptions["gamera"]["iPdir"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["jPdir"] = {} + o = options["gamera"]["jPdir"] + od = option_descriptions["gamera"]["jPdir"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) + + # options + options["gamera"]["kPdir"] = {} + o = options["gamera"]["kPdir"] + od = option_descriptions["gamera"]["kPdir"] + for on in od: + o[on] = get_run_option(on, od[on], args.advanced) #------------------------------------------------------------------------- - # Options for voltron_mpi.x + # VOLTRON options + options["voltron"] = {} -# option_names = [ -# "voltron_time_tFin", -# "voltron_spinup_doSpin", "voltron_spinup_tSpin", -# "voltron_output_dtOut", "voltron_output_tsOut", -# "voltron_coupling_dtCouple", "voltron_coupling_rTrc", -# "voltron_coupling_imType", "voltron_coupling_doQkSquish", -# "voltron_coupling_qkSquishStride", -# "voltron_restart_dtRes", -# "voltron_imag_doInit", -# "voltron_ebsquish_epsSquish", -# ] -# for name in option_names: -# options[name] = get_run_option(name, option_descriptions[name]) + #