module photolysis_mod !*********************************************************************** ! ! subject: ! -------- ! ! module for photolysis online ! ! VERSION: Extracted from LMDZ.MARS work of Franck Lefevre (Yassin Jaziri) ! April 2019 - Yassin Jaziri add updates generic input (Yassin Jaziri) ! !*********************************************************************** use types_asis implicit none ! photolysis integer, save :: nabs ! number of absorbing gases ! spectral grid integer, save :: nw ! number of spectral intervals (low-res) integer, save :: mopt ! high-res/low-res switch real, allocatable, save :: wl(:) ! lower wavelength for each interval (nm) real, allocatable, save :: wc(:) ! center wavelength for each interval (nm) real, allocatable, save :: wu(:) ! upper wavelength for each interval (nm) real, save :: photoheat_lmax ! maximum wavelength until photochemical heating is calculated ! solar flux real, allocatable, save :: fstar1AU(:) ! stellar flux (photon.s-1.nm-1.cm-2) at 1 au ! cross-sections and quantum yields real, allocatable, save :: qy(:,:) ! photodissociation yield real, allocatable, save :: xs(:,:,:) ! absorption cross-section (cm2) real, allocatable, save :: sj(:,:,:) ! general cross-section array by photodissociation yield (cm2) real, allocatable, save :: xs_temp(:,:) ! absorption cross-section temperature (K) integer, allocatable, save :: tdim(:) ! absorption cross-section temperature dimension logical, allocatable, save :: jlabelbis(:) ! check in jlabel if the species is included in more than 1 photolysis contains subroutine init_photolysis(nlayer,nreact) use types_asis, only: reactions use tracer_h use chimiedata_h, only: indexchim use ioipsl_getin_p_mod, only: getin_p integer, intent(in) :: nlayer ! number of atmospheric layers integer, intent(in) :: nreact ! number of reactions in reactions files integer :: iphot,tdim_max,i3prod,ij,iw,ilay,ireact integer :: specheck(nesp) ! initialise on-line photolysis ! mopt = 1 high-resolution ! mopt = 2 low-resolution (recommended for gcm use) ! mopt = 3 input file reading grid mopt = 3 ! set wavelength grid call gridw(nw,wl,wc,wu,mopt) allocate(fstar1AU(nw)) ! read and grid solar flux data call rdsolarflux(nw,wl,wc,fstar1AU) ! read maximum wavelength until photochemical heating is calculated write(*,*) "Maximum wavelength until photochemical heating is calculated" photoheat_lmax=wc(nw-1) ! default value last point of wavelength grid call getin_p("photoheat_lmax",photoheat_lmax) write(*,*) "photoheat_lmax = ",photoheat_lmax ! calculate nabs number of absorbing gases allocate(jlabelbis(nb_phot_hv_max)) nabs = 0 specheck(:) = 0 jlabelbis(:) = .true. do iphot=1,nb_phot_hv_max if (specheck(indexchim(trim(jlabel(iphot,2)))) .eq. 0) then specheck(indexchim(trim(jlabel(iphot,2)))) = 1 nabs = nabs + 1 else jlabelbis(iphot) = .false. endif end do ! Get temperature dimension and allocate allocate(tdim(nb_hv_max)) tdim(:) = 1 tdim_max = 1 do iphot=1,nb_hv_max read(jparamline(iphot),*) tdim(iphot) if (tdim(iphot) > tdim_max) tdim_max = tdim(iphot) end do ! allocation allocate(qy(nw,nb_hv_max)) allocate(xs(tdim_max,nw,nb_hv_max)) allocate(sj(nlayer,nw,nb_phot_hv_max)) allocate(xs_temp(tdim_max,nb_hv_max)) xs(:,:,:) = 0. xs_temp(:,:) = 0. print*, 'WARNING: for all branching photolysis of one specie' print*, ' the cross section files should be the same' print*, ' they are used for optical depths calculation' print*, ' only the quantum yield should be different' i3prod = 0 ireact = 0 ! read and grid cross-sections do iphot=1,nb_hv_max ireact = ireact + 1 call rdxsi(nw,wl,xs(:tdim(iphot),:,iphot),jparamline(iphot),xs_temp(:tdim(iphot),iphot),qy(:,iphot),tdim(iphot),jlabel(iphot+i3prod,2)) do while(reactions(ireact)%rtype/=0) ireact = ireact + 1 end do if (reactions(ireact)%three_prod) i3prod = i3prod + 1 end do ! init sj for no temperature dependent cross sections (tdim.eq.1) iphot = 0 ij = 0 ireact = 0 do while(iphot x1 nm and y1 in photon.s-1.nm-1.cm-2 ENDDO CLOSE (kin) ! Interpolation on the grid CALL addpnt(x1,y1,n+naddpnt,n,x1(1)*(1.-deltax),0.) CALL addpnt(x1,y1,n+naddpnt-1,n, 0.,0.) CALL addpnt(x1,y1,n+naddpnt-2,n,x1(n)*(1.+deltax),0.) CALL addpnt(x1,y1,n+naddpnt-3,n, 1.e+38,0.) CALL inter2(nw,wl,yg1,n,x1,y1,ierr) IF (ierr .NE. 0) THEN WRITE(*,*) ierr, fil call abort_physic("photolysis","cannot open stellarflux file",1) ENDIF ! factor to convert to photon.s-1.nm-1.cm-2 : ! 5.039e11 = 1.e-4*1e-9/(hc = 6.62e-34*2.998e8) ! fstar1AU need to be in photon.s-1.nm-1.cm-2 DO iw = 1, nw-1 !f(iw) = yg1(iw)*wc(iw)*5.039e11 ! If yg1 in w.m-2.nm-1 fstar1AU(iw) = yg1(iw) ENDDO end subroutine rdsolarflux !============================================================================== subroutine addpnt ( x, y, ld, n, xnew, ynew ) !-----------------------------------------------------------------------------* != PURPOSE: =* != Add a point to a set of data pairs . x must be in =* != ascending order =* !-----------------------------------------------------------------------------* != PARAMETERS: =* != X - REAL vector of length LD, x-coordinates (IO)=* != Y - REAL vector of length LD, y-values (IO)=* != LD - INTEGER, dimension of X, Y exactly as declared in the calling (I)=* != program =* != N - INTEGER, number of elements in X, Y. On entry, it must be: (IO)=* != N < LD. On exit, N is incremented by 1. =* != XNEW - REAL, x-coordinate at which point is to be added (I)=* != YNEW - REAL, y-value of point to be added (I)=* !-----------------------------------------------------------------------------* IMPLICIT NONE ! calling parameters INTEGER ld, n REAL x(ld), y(ld) REAL xnew, ynew INTEGER ierr ! local variables INTEGER insert INTEGER i !----------------------------------------------------------------------- ! initialize error flag ierr = 0 ! check n>> ERROR (ADDPNT) <<< Cannot expand array ' WRITE(0,*) ' All elements used.' call abort_physic("photolysis","cannot expand array ADDPNT, all elements used",1) ENDIF insert = 1 i = 2 ! check, whether x is already sorted. ! also, use this loop to find the point at which xnew needs to be inserted ! into vector x, if x is sorted. 10 CONTINUE IF (i .LT. n) THEN IF (x(i) .LT. x(i-1)) THEN print*, x(i-1), x(i) WRITE(0,*) '>>> ERROR (ADDPNT) <<< x-data must be in ascending order!' call abort_physic("photolysis", "ADDPNT: x-data must be in ascending order",1) ELSE IF (xnew .GT. x(i)) insert = i + 1 ENDIF i = i+1 GOTO 10 ENDIF ! if needs to be appended at the end, just do so, ! otherwise, insert at position INSERT IF ( xnew .GT. x(n) ) THEN x(n+1) = xnew y(n+1) = ynew ELSE ! shift all existing points one index up DO i = n, insert, -1 x(i+1) = x(i) y(i+1) = y(i) ENDDO ! insert new point x(insert) = xnew y(insert) = ynew ENDIF ! increase total number of elements in x, y n = n+1 end subroutine addpnt !============================================================================== subroutine inter2(ng,xg,yg,n,x,y,ierr) !-----------------------------------------------------------------------------* != PURPOSE: =* != Map input data given on single, discrete points onto a set of target =* != bins. =* != The original input data are given on single, discrete points of an =* != arbitrary grid and are being linearly interpolated onto a specified set =* != of target bins. In general, this is the case for most of the weighting =* != functions (action spectra, molecular cross section, and quantum yield =* != data), which have to be matched onto the specified wavelength intervals. =* != The average value in each target bin is found by averaging the trapezoi- =* != dal area underneath the input data curve (constructed by linearly connec-=* != ting the discrete input values). =* != Some caution should be used near the endpoints of the grids. If the =* != input data set does not span the range of the target grid, an error =* != message is printed and the execution is stopped, as extrapolation of the =* != data is not permitted. =* != If the input data does not encompass the target grid, use ADDPNT to =* != expand the input array. =* !-----------------------------------------------------------------------------* != PARAMETERS: =* != NG - INTEGER, number of bins + 1 in the target grid (I)=* != XG - REAL, target grid (e.g., wavelength grid); bin i is defined (I)=* != as [XG(i),XG(i+1)] (i = 1..NG-1) =* != YG - REAL, y-data re-gridded onto XG, YG(i) specifies the value for (O)=* != bin i (i = 1..NG-1) =* != N - INTEGER, number of points in input grid (I)=* != X - REAL, grid on which input data are defined (I)=* != Y - REAL, input y-data (I)=* !-----------------------------------------------------------------------------* IMPLICIT NONE ! input: INTEGER ng, n REAL x(n), y(n), xg(ng) ! output: REAL yg(ng) ! local: REAL area, xgl, xgu REAL darea, slope REAL a1, a2, b1, b2 INTEGER ngintv INTEGER i, k, jstart INTEGER ierr !_______________________________________________________________________ ierr = 0 ! test for correct ordering of data, by increasing value of x DO 10, i = 2, n IF (x(i) .LE. x(i-1)) THEN ierr = 1 WRITE(*,*)'data not sorted' WRITE(*,*) x(i), x(i-1) RETURN ENDIF 10 CONTINUE DO i = 2, ng IF (xg(i) .LE. xg(i-1)) THEN ierr = 2 WRITE(0,*) '>>> ERROR (inter2) <<< xg-grid not sorted!' RETURN ENDIF ENDDO ! check for xg-values outside the x-range IF ( (x(1) .GT. xg(1)) .OR. (x(n) .LT. xg(ng)) ) THEN WRITE(0,*) '>>> ERROR (inter2) <<< Data do not span grid. ' WRITE(0,*) ' Use ADDPNT to expand data and re-run.' call abort_physic("photolysis","inter2: Data do not span grid",1) ENDIF ! find the integral of each grid interval and use this to ! calculate the average y value for the interval ! xgl and xgu are the lower and upper limits of the grid interval jstart = 1 ngintv = ng - 1 DO 50, i = 1,ngintv ! initialize: area = 0.0 xgl = xg(i) xgu = xg(i+1) ! discard data before the first grid interval and after the ! last grid interval ! for internal grid intervals, start calculating area by interpolating ! between the last point which lies in the previous interval and the ! first point inside the current interval k = jstart IF (k .LE. n-1) THEN ! if both points are before the first grid, go to the next point 30 CONTINUE IF (x(k+1) .LE. xgl) THEN jstart = k - 1 k = k+1 IF (k .LE. n-1) GO TO 30 ENDIF ! if the last point is beyond the end of the grid, complete and go to the next ! grid 40 CONTINUE IF ((k .LE. n-1) .AND. (x(k) .LT. xgu)) THEN jstart = k-1 ! compute x-coordinates of increment a1 = MAX(x(k),xgl) a2 = MIN(x(k+1),xgu) ! if points coincide, contribution is zero IF (x(k+1).EQ.x(k)) THEN darea = 0.e0 ELSE slope = (y(k+1) - y(k))/(x(k+1) - x(k)) b1 = y(k) + slope*(a1 - x(k)) b2 = y(k) + slope*(a2 - x(k)) darea = (a2 - a1)*(b2 + b1)/2. ENDIF ! find the area under the trapezoid from a1 to a2 area = area + darea ! go to next point k = k+1 GO TO 40 ENDIF ENDIF ! calculate the average y after summing the areas in the interval yg(i) = area/(xgu - xgl) 50 CONTINUE end subroutine inter2 !============================================================================== subroutine rdxsi(nw,wl,xsi,jparamlinei,xs_tempi,qyi,tdimi,species) !-----------------------------------------------------------------------------* != PURPOSE: =* != Read molecular absorption cross section. Re-grid data to match =* != specified wavelength working grid. =* !-----------------------------------------------------------------------------* != PARAMETERS: =* != NW - INTEGER, number of specified intervals + 1 in working (I)=* != wavelength grid =* != WL - REAL, vector of lower limits of wavelength intervals in (I)=* != working wavelength grid =* != XS - REAL, molecular absoprtion cross section (cm^2) at each (O)=* != specified wavelength =* !-----------------------------------------------------------------------------* use datafile_mod, only: datadir IMPLICIT NONE ! input integer, intent(in) :: nw ! number of wavelength grid points integer, intent(in) :: tdimi ! temperature dimension real, intent(in) :: wl(nw) ! lower and central wavelength for each interval (nm) character(len = 20) :: species ! species which is photolysed character(len = 300), intent(in) :: jparamlinei ! line of jonline parameters ! output real, intent(inout) :: qyi(nw) ! photodissociation yield real, intent(inout) :: xsi(tdimi,nw) ! absorption cross-section (cm2) real, intent(inout) :: xs_tempi(tdimi) ! absorption cross-section temperature (K) ! local character(len = 50) :: sigfile(tdimi) ! cross section file name character(len = 50) :: qyfile ! quantum yield file name CHARACTER(len = 120) :: fil ! path files integer :: tdimdummy, ifile, itemp, ierr, nheadxs, nheadqy, i, n, naddpnt integer :: kin ! input logical unit real, allocatable :: wlf(:), xsf(:) ! input cross section real, allocatable :: wlqy(:), qyf(:) ! input photodissociation yield real, parameter :: deltax = 1.e-4 kin = 10 ! input logical unit naddpnt = 4 ! number of adding point (careful: same for xs and qy) nheadxs = 1 ! number of skipping line at the beggining of the cross section files nheadqy = 1 ! number of skipping line at the beggining of the quantum yield files sigfile(:) = '' qyfile = '' read(jparamlinei,*) tdimdummy, (xs_tempi(itemp), itemp=1,tdimi), (sigfile(ifile), ifile=1,tdimi), qyfile ! Check if xs_tempi is sorted from low to high values do i = 1,tdimi-1 if (xs_tempi(i)>xs_tempi(i+1)) then print*, 'ERROR: temperature cross section file' print*, ' has to be sorted from low to high values' print*, ' Check reactfile file' call abort_physic("photolysis", "temperature cross section file has to be sorted from low to high values",1) end if end do ! Cross section do itemp=1,tdimi fil = trim(datadir)//'/cross_sections/'//trim(sigfile(itemp)) print*, 'section efficace '//trim(species)//': ', fil OPEN(UNIT=kin,FILE=fil,STATUS='old',iostat=ierr) if (ierr /= 0) THEN write(*,*)'Error : cannot open cross_sections file ', trim(sigfile(itemp)) write(*,*)'It should be in :',trim(datadir),'/cross_sections/' write(*,*)'1) You can change the datadir directory in callphys.def' write(*,*)' with:' write(*,*)' datadir=/path/to/the/directory' write(*,*)'2) You can check if the file is in datadir/cross_sections/' write(*,*)'3) You can change the input cross_sections file name in' write(*,*)' chemestry/reactfile file for the specie:',trim(species) call abort_physic("photolysis", "cannot open cross_sections file",1) end if ! Get number of line in the file DO i = 1, nheadxs READ(kin,*) ENDDO n = 0 do read(kin,*,iostat=ierr) if (ierr<0) exit n = n + 1 end do rewind(kin) DO i = 1, nheadxs READ(kin,*) ENDDO allocate(wlf(n+naddpnt)) allocate(xsf(n+naddpnt)) ! Read cross section DO i = 1, n READ(kin,*) wlf(i), xsf(i) !-> xsf in cm2 and wlf in nm ENDDO CLOSE (kin) CALL addpnt(wlf,xsf,n+naddpnt,n,wlf(1)*(1.-deltax),0.) CALL addpnt(wlf,xsf,n+naddpnt-1,n, 0.,0.) CALL addpnt(wlf,xsf,n+naddpnt-2,n,wlf(n)*(1.+deltax),0.) CALL addpnt(wlf,xsf,n+naddpnt-3,n, 1.e+38,0.) CALL inter2(nw,wl,xsi(itemp,:),n,wlf,xsf,ierr) IF (ierr .NE. 0) THEN WRITE(*,*) ierr, fil call abort_physic("photolysis", "inter2 error",1) ENDIF deallocate(wlf) deallocate(xsf) end do ! Photodissociation yield fil = trim(datadir)//'/cross_sections/'//trim(qyfile) print*, 'photodissociation yield '//trim(species)//': ', fil OPEN(UNIT=kin,FILE=fil,STATUS='old',iostat=ierr) if (ierr /= 0) THEN write(*,*)'Error : cannot open photodissociation yield file ', trim(qyfile) write(*,*)'It should be in :',trim(datadir),'/cross_sections/' write(*,*)'1) You can change the datadir directory in callphys.def' write(*,*)' with:' write(*,*)' datadir=/path/to/the/directory' write(*,*)'2) You can check if the file is in datadir/cross_sections/' write(*,*)'3) You can change the input photodissociation yield file name in' write(*,*)' chemestry/reactfile file for the specie:',trim(species) call abort_physic("photolysis", "cannot open photodissociation yield file",1) end if ! Get number of line in the file DO i = 1, nheadqy READ(kin,*) ENDDO n = 0 do read(kin,*,iostat=ierr) if (ierr<0) exit n = n + 1 end do rewind(kin) DO i = 1, nheadqy READ(kin,*) ENDDO allocate(wlqy(n+naddpnt)) allocate(qyf(n+naddpnt)) ! Read photodissociation yield DO i = 1, n READ(kin,*) wlqy(i), qyf(i) !-> wlqy in nm ENDDO CLOSE (kin) CALL addpnt(wlqy,qyf,n+naddpnt,n,wlqy(1)*(1.-deltax),0.) CALL addpnt(wlqy,qyf,n+naddpnt-1,n, 0.,0.) CALL addpnt(wlqy,qyf,n+naddpnt-2,n,wlqy(n)*(1.+deltax),0.) CALL addpnt(wlqy,qyf,n+naddpnt-3,n, 1.e+38,0.) CALL inter2(nw,wl,qyi,n,wlqy,qyf,ierr) IF (ierr .NE. 0) THEN WRITE(*,*) ierr, fil call abort_physic("photolysis", "inter2 error",1) ENDIF end subroutine rdxsi end module photolysis_mod