calc_pm.F90 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #define TRACEBACK write (gol,'("in ",a," (",a,i6,")")') rname, __FILE__, __LINE__ ; call goErr
  2. #define IF_NOTOK_RETURN(action) if (status/=0) then; TRACEBACK; action; return; end if
  3. #define IF_ERROR_RETURN(action) if (status> 0) then; TRACEBACK; action; return; end if
  4. !
  5. #include "tm5.inc"
  6. !-----------------------------------------------------------------------------
  7. ! TM5 !
  8. !-----------------------------------------------------------------------------
  9. !BOP
  10. !
  11. ! !MODULE: CALC_PM
  12. !
  13. ! !DESCRIPTION: PM module to calculate particulate matter concentrations for
  14. ! user defined aerodynamic diameters. This is used for diagnostics
  15. ! purposes by some user_output_* routines.
  16. !\\
  17. !\\
  18. ! !INTERFACE:
  19. !
  20. MODULE CALC_PM
  21. !
  22. ! !USES:
  23. !
  24. USE GO, only : gol, goErr, goPr
  25. IMPLICIT NONE
  26. PRIVATE
  27. !
  28. ! !PUBLIC MEMBER FUNCTIONS:
  29. !
  30. Public :: PMX_Integrate_3d,PMx_integrate_0d
  31. !
  32. ! !PRIVATE DATA MEMBERS:
  33. !
  34. character(len=*), parameter :: mname = 'calc_pm'
  35. Real, Parameter :: hr2 = 0.5*sqrt(2.0)
  36. !
  37. ! !REVISION HISTORY:
  38. ! 1 Oct 2010 - Achim Strunk - Revised and made suitable for 3D and stations
  39. ! 19 Jun 2012 - P. Le Sager - adapted for lon-lat MPI domain decomposition
  40. !
  41. ! !REMARKS:
  42. ! (1) module used only if with_m7 is used
  43. !
  44. !EOP
  45. !------------------------------------------------------------------------
  46. CONTAINS
  47. !--------------------------------------------------------------------------
  48. ! TM5 !
  49. !--------------------------------------------------------------------------
  50. !BOP
  51. !
  52. ! !IROUTINE: PMX_Integrate_0d
  53. !
  54. ! !DESCRIPTION: This routine generates a pmx value for a given grid cell
  55. ! Arbitrary radii may be specified.
  56. ! NO3 and NH4 come from EQSAM and are considered to be in
  57. ! the accumulation mode.
  58. !\\
  59. !\\
  60. ! !INTERFACE:
  61. !
  62. Subroutine PMX_Integrate_0d(region, is, js, ls, isizelimit, pmx, status)
  63. !
  64. ! !USES:
  65. !
  66. use binas, only : pi
  67. Use tracer_data, only : mass_dat
  68. ! I need to convert (/kg air) to (/m3 air)
  69. use chem_param, only : iso4nus,isoanus
  70. use chem_param, only : iso4ais, ibcais, ipomais, isoaais
  71. use chem_param, only : iso4acs, ibcacs, ipomacs, isoaacs, issacs, iduacs
  72. use chem_param, only : iso4cos, ibccos, ipomcos, isoacos, isscos, iducos
  73. use chem_param, only : ibcaii, ipomaii, isoaaii, iduaci
  74. use chem_param, only : inus_n, iais_n, iacs_n, icos_n, iaii_n, iaci_n, icoi_n
  75. use chem_param, only : iducoi, ino3_a, inh4, imsa
  76. Use mo_aero_m7, only : nmod, cmedr2mmedr, sigma, cmr2ram
  77. Use m7_data, only : h2o_mode, rw_mode
  78. ! To rewrite ug / cell to ug/m3
  79. Use global_data, only : region_dat
  80. Use Meteodata, only : gph_dat
  81. !
  82. ! !INPUT PARAMETERS:
  83. !
  84. Integer, Intent(In) :: region
  85. real, intent(in) :: isizelimit ! aerodynamic diameter in micrometers
  86. integer, Intent(in) :: is, js, ls
  87. !
  88. ! !OUTPUT PARAMETERS:
  89. !
  90. Integer, Intent(out) :: status
  91. real, Intent(out) :: pmx ! units = 'kg/m3'
  92. !
  93. ! !REVISION HISTORY:
  94. ! 7 Oct 2010 - Achim Strunk - v0
  95. ! 19 Jun 2012 - P. Le Sager - adapted for lon-lat MPI domain decomposition
  96. ! 21 Feb 2023 - T. van Noije - changed to cut-off based on aerodynamic diameter
  97. !
  98. !EOP
  99. !------------------------------------------------------------------------
  100. !BOC
  101. character(len=*), parameter :: rname = mname//'/PMX_Integrate_0d'
  102. Integer :: imod
  103. Real :: z, rad, rad_limit
  104. Real :: dry_mass, wet_mass, particle_number, mode_volume, mode_density
  105. Real :: air_volume ! For converting mass to density in kg/m3
  106. Real :: modfrac
  107. Real, Dimension(nmod) :: lnsigma
  108. ! --- begin -------------------------------------
  109. ! PMx is defined as the mass below a threshold diameter x,
  110. ! where x is the aerodynamic diameter in micrometers.
  111. ! The aerodynamic diameter is defined as the diameter of a
  112. ! spherical particle with equal settling velocity but a material density of 1 g/cm^3.
  113. ! For particles larger than the mean free path of air,
  114. ! the aerodynamic diameter is approximated by the wet diameter times
  115. ! the square root of the particle density in g/cm^3.
  116. ! This approximation is often used for particles as small as 0.5 micron diameter
  117. ! (e.g., US EPA 1996, Air Quality Criteria for Particulate Matter, Volume I, 1996)
  118. ! We currently do not correct for non-spherical shapes.
  119. ! That could be an additional correction.
  120. lnsigma = log(sigma)
  121. ! initialise target value
  122. pmx = 0.0
  123. Do imod = 1, nmod
  124. ! Calculate the dry and wet mass per mode.
  125. ! The calculation is kept consistent with the earlier version of the code,
  126. ! where the mass of NO3_a and NH4 are added to the soluble accumulation mode.
  127. ! Consistent with the treatment in other parts of the code,
  128. ! we add the MSA mass also to the soluble accumulation code.
  129. ! Note that it would be possible to make the calculation more consistent with
  130. ! the calculation of the modal mass in M7.
  131. ! However, as I cannot exclude that this subroutine is called at initialization,
  132. ! this would require to add the dry (or wet) modal mass field to the restart file.
  133. ! Because of this extra complication, I haven't done that.
  134. select case (imod)
  135. case (1)
  136. dry_mass = mass_dat(region)%rm(is,js,ls, iso4nus) + mass_dat(region)%rm(is,js,ls, isoanus)
  137. wet_mass = dry_mass + h2o_mode(region,1)%d3(is,js,ls)
  138. particle_number = mass_dat(region)%rm(is,js,ls, inus_n)
  139. case (2)
  140. dry_mass = mass_dat(region)%rm(is,js,ls, iso4ais) + mass_dat(region)%rm(is,js,ls, ibcais ) + &
  141. mass_dat(region)%rm(is,js,ls, ipomais) + mass_dat(region)%rm(is,js,ls, isoaais)
  142. wet_mass = dry_mass + h2o_mode(region,2)%d3(is,js,ls)
  143. particle_number = mass_dat(region)%rm(is,js,ls, iais_n)
  144. case (3)
  145. dry_mass = mass_dat(region)%rm(is,js,ls, iso4acs) + mass_dat(region)%rm(is,js,ls, ibcacs ) + &
  146. mass_dat(region)%rm(is,js,ls, ipomacs) + mass_dat(region)%rm(is,js,ls, isoaacs) + &
  147. mass_dat(region)%rm(is,js,ls, issacs ) + mass_dat(region)%rm(is,js,ls, iduacs ) + &
  148. mass_dat(region)%rm(is,js,ls, ino3_a ) + mass_dat(region)%rm(is,js,ls, inh4 ) + &
  149. mass_dat(region)%rm(is,js,ls, imsa )
  150. wet_mass = dry_mass + h2o_mode(region,3)%d3(is,js,ls)
  151. particle_number = mass_dat(region)%rm(is,js,ls, iacs_n)
  152. case (4)
  153. dry_mass = mass_dat(region)%rm(is,js,ls, iso4cos) + mass_dat(region)%rm(is,js,ls, ibccos ) + &
  154. mass_dat(region)%rm(is,js,ls, ipomcos) + mass_dat(region)%rm(is,js,ls, isoacos) + &
  155. mass_dat(region)%rm(is,js,ls, isscos ) + mass_dat(region)%rm(is,js,ls, iducos )
  156. wet_mass = dry_mass + h2o_mode(region,4)%d3(is,js,ls)
  157. particle_number = mass_dat(region)%rm(is,js,ls, icos_n)
  158. case (5)
  159. dry_mass = mass_dat(region)%rm(is,js,ls, ibcaii ) + &
  160. mass_dat(region)%rm(is,js,ls, ipomaii) + mass_dat(region)%rm(is,js,ls, isoaaii)
  161. wet_mass = dry_mass
  162. particle_number = mass_dat(region)%rm(is,js,ls, iaii_n)
  163. case (6)
  164. dry_mass = mass_dat(region)%rm(is,js,ls, iduaci )
  165. wet_mass = dry_mass
  166. particle_number = mass_dat(region)%rm(is,js,ls, iaci_n)
  167. case (7)
  168. dry_mass = mass_dat(region)%rm(is,js,ls, iducoi )
  169. wet_mass = dry_mass
  170. particle_number = mass_dat(region)%rm(is,js,ls, icoi_n)
  171. end select
  172. if ( dry_mass > 100*TINY(1.0) .and. particle_number > 100*TINY(1.0) ) then
  173. ! Calculate the median radius of the volume weighted distribution.
  174. rad = rw_mode(region,imod)%d3(is,js,ls) * cmedr2mmedr(imod)
  175. ! In principe, as the dry mass in the considered mode is non-zero,
  176. ! the mode radius or volume cannot be zero at this point.
  177. ! However, as the mass calculated here is not fully consistent with that in M7,
  178. ! an additional check on the mode radius is included.
  179. ! The exact same condition is used as in the earlier version of the code,
  180. ! where PMx was calculated using for the threshold diameter x
  181. ! the geometric wet diameter instead of the aerodynamic diameter.
  182. if( rad < 100*TINY(1.0) ) then
  183. ! Particles are so small that all mass should be included.
  184. modfrac = 1.0
  185. else
  186. ! Calculate the mass density of ambient particles for each mode
  187. mode_volume = particle_number * ((rw_mode(region,imod)%d3(is,js,ls)*cmr2ram(imod))**3.0)*pi/0.75
  188. mode_density = wet_mass / mode_volume ! kg/m3
  189. ! Convert the threshold aerodynamic diameter in microns
  190. ! to a geometric radius in meters.
  191. rad_limit = isizelimit * ((1000./mode_density)**0.5) * 5.e-7
  192. z = ( log(rad_limit) - log(rad) ) / lnsigma(imod)
  193. modfrac = 0.5 + 0.5 * ERF(z*hr2)
  194. endif
  195. ! Sum contributions from the different modes to the dry mass below the threshold diameter
  196. pmx = pmx + dry_mass * modfrac
  197. endif
  198. end do
  199. ! Finally divide by the air volume to get PMx in kg/m3
  200. air_volume = ( gph_dat(region)%data(is,js,ls+1) - gph_dat(region)%data(is,js,ls) ) * &
  201. region_dat(region)%dxyp(js)
  202. pmx = pmx / air_volume
  203. status = 0
  204. End Subroutine PMX_INTEGRATE_0D
  205. !EOC
  206. !--------------------------------------------------------------------------
  207. ! TM5 !
  208. !--------------------------------------------------------------------------
  209. !BOP
  210. !
  211. ! !IROUTINE: PMX_Integrate_3d
  212. !
  213. ! !DESCRIPTION: This routine uses PMX_Integrate_0d for generating a 3d
  214. ! array (over im, jm, lm) of pmx.
  215. ! Arbitrary radii may be specified.
  216. !\\
  217. !\\
  218. ! !INTERFACE:
  219. !
  220. SUBROUTINE PMX_Integrate_3d( region, isizelimit, pmx, status )
  221. !
  222. ! !USES:
  223. !
  224. USE DIMS, ONLY : im, jm, lm
  225. USE TM5_DISTGRID, ONLY : dgrid, Get_DistGrid, gather, scatter_i_band
  226. !
  227. ! !INPUT PARAMETERS:
  228. !
  229. INTEGER, INTENT(in) :: region
  230. REAL, INTENT(in) :: isizelimit
  231. !
  232. ! !OUTPUT PARAMETERS:
  233. !
  234. INTEGER, INTENT(out) :: status
  235. REAL, DIMENSION(:,:,:), INTENT(out) :: pmx
  236. !
  237. ! !REVISION HISTORY:
  238. ! 7 Oct 2010 - Achim Strunk - v0
  239. ! 19 Jun 2012 - P. Le Sager - adapted for lon-lat MPI domain decomposition
  240. !
  241. ! !REMARKS:
  242. !
  243. !EOP
  244. !------------------------------------------------------------------------
  245. !BOC
  246. CHARACTER(len=*), PARAMETER :: rname = mname//'/PMX_Integrate_3d'
  247. INTEGER :: is, js, ls, i1, i2, j1, j2
  248. REAL :: pmxl
  249. ! reset value
  250. pmx = 0.0
  251. CALL GET_DISTGRID( dgrid(region), I_STRT=i1, I_STOP=i2, J_STRT=j1, J_STOP=j2 )
  252. DO ls = 1, lm(region)
  253. DO js = j1, j2
  254. DO is = i1, i2
  255. pmxl=0.0
  256. CALL pmx_integrate_0d( region, is, js, ls, isizelimit, pmxl, status )
  257. IF_NOTOK_RETURN( status=1 )
  258. !write(4444,*)pmxl,is,js,ls,ubound(pmx),lbound(pmx)
  259. pmx(is,js,ls) = pmxl
  260. !write(4444,*)pmx(is,js,ls),is,js,ls,ubound(pmx),lbound(pmx)
  261. END DO
  262. END DO
  263. ! write(5555,*)size(pmx),shape(pmx),i2,j2,lm(region),is,js,ls,isizelimit,pmxl
  264. END DO
  265. status = 0
  266. END SUBROUTINE PMX_Integrate_3d
  267. !NOT-USED SUBROUTINE PMX_Integrate_2d( region, isizelimit, pmx, status )
  268. !NOT-USED !
  269. !NOT-USED ! !USES:
  270. !NOT-USED !
  271. !NOT-USED USE DIMS, ONLY : im, jm, lm
  272. !NOT-USED USE TM5_DISTGRID, ONLY : dgrid, Get_DistGrid, gather, scatter_i_band
  273. !NOT-USED !
  274. !NOT-USED ! !INPUT PARAMETERS:
  275. !NOT-USED !
  276. !NOT-USED INTEGER, INTENT(in) :: region
  277. !NOT-USED REAL, INTENT(in) :: isizelimit
  278. !NOT-USED !
  279. !NOT-USED ! !OUTPUT PARAMETERS:
  280. !NOT-USED !
  281. !NOT-USED INTEGER, INTENT(out) :: status
  282. !NOT-USED REAL, DIMENSION(:,:), INTENT(out) :: pmx
  283. !NOT-USED !
  284. !NOT-USED ! !REVISION HISTORY:
  285. !NOT-USED ! 7 Oct 2010 - Achim Strunk - v0
  286. !NOT-USED ! 19 Jun 2012 - P. Le Sager - adapted for lon-lat MPI domain decomposition
  287. !NOT-USED !
  288. !NOT-USED ! !REMARKS:
  289. !NOT-USED !
  290. !NOT-USED !EOP
  291. !NOT-USED !------------------------------------------------------------------------
  292. !NOT-USED !BOC
  293. !NOT-USED
  294. !NOT-USED CHARACTER(len=*), PARAMETER :: rname = mname//'/PMX_Integrate_3d'
  295. !NOT-USED INTEGER :: is, js, ls, i1, i2, j1, j2
  296. !NOT-USED REAL :: pmxl
  297. !NOT-USED
  298. !NOT-USED ! reset value
  299. !NOT-USED pmx = 0.0
  300. !NOT-USED
  301. !NOT-USED CALL GET_DISTGRID( dgrid(region), I_STRT=i1, I_STOP=i2, J_STRT=j1, J_STOP=j2 )
  302. !NOT-USED
  303. !NOT-USED
  304. !NOT-USED DO js = j1, j2
  305. !NOT-USED DO is = i1, i2
  306. !NOT-USED
  307. !NOT-USED CALL pmx_integrate_0d( region, is, js, ls, isizelimit, pmxl, status )
  308. !NOT-USED IF_NOTOK_RETURN( status=1 )
  309. !NOT-USED
  310. !NOT-USED pmx(is,js) = pmxl
  311. !NOT-USED END DO
  312. !NOT-USED END DO
  313. !NOT-USED
  314. !NOT-USED
  315. !NOT-USED status = 0
  316. !NOT-USED
  317. !NOT-USED END SUBROUTINE PMX_Integrate_2d
  318. !NOT-USED !EOC
  319. END MODULE CALC_PM