Line data Source code
1 : module ion_electron_temp
2 :
3 : !---------------------------------------------------------------------------------
4 : ! Purpose:
5 : !
6 : ! Module to compute the ion/electron temperature and dry static heating
7 : !
8 : ! Authors: Joe McInerney/Hanli Liu/Art Richmond
9 : !
10 : !---------------------------------------------------------------------------------
11 : use shr_kind_mod, only : r8 => shr_kind_r8 ! Real kind to declare variables
12 : use ppgrid, only : pcols, pver, pverp ! Dimensions and chunk bounds
13 : use ppgrid, only : begchunk, endchunk
14 : use cam_history, only : outfld, hist_fld_active, write_inithist ! Routine to output fields to history files
15 : use cam_history, only : horiz_only, addfld, add_default ! Routines and variables for adding fields to history output
16 : use physics_types, only : physics_state, & ! Structures containing physics state variables
17 : physics_ptend, & ! Structures containing physics tendency variables
18 : physics_ptend_init ! Routine to initialize physics tendency variables
19 : use physics_buffer, only : pbuf_add_field, pbuf_get_chunk, &
20 : pbuf_get_index,dtype_r8, & !
21 : physics_buffer_desc, & !
22 : pbuf_get_field, & ! Needed to access physics buffer
23 : pbuf_set_field
24 : use mo_jeuv, only : nIonRates ! Number of ionization rates in mo_photo
25 : use shr_const_mod, only : kboltz => shr_const_boltz, &
26 : pi => shr_const_pi ! Boltzmann constant and pi
27 : use chem_mods, only : adv_mass ! Array holding mass values for short lived species
28 : use cam_abortutils, only : endrun
29 : use mo_chem_utls, only : get_spc_ndx ! Routine to get index of adv_mass array for short lived species
30 : use constituents, only : cnst_get_ind, cnst_mw ! Routines to get molecular weights for constituents
31 : use solar_parms_data, only : f107=>solar_parms_f107 ! 10.7 cm solar flux
32 : use steady_state_tei, only : steady_state_tei_init, steady_state_tei_tend
33 : use perf_mod, only : t_startf, t_stopf ! timing utils
34 : use spmd_utils, only : masterproc
35 : use cam_logfile, only : iulog ! Output unit
36 : use ionos_state_mod, only : ionos_state
37 : use air_composition,only : cpairv
38 :
39 : implicit none
40 :
41 : save
42 :
43 : private ! Make default type private to the module
44 :
45 : !------------------------
46 : ! PUBLIC: interfaces
47 : !------------------------
48 : public :: ion_electron_temp_init ! Initialization
49 : public :: ion_electron_temp_timestep_init
50 : public :: ion_electron_temp_register ! Registration of ionosphere variables in pbuf physics buffer
51 : public :: ion_electron_temp_inidat ! Get fields from initial condition file into physics buffer
52 : public :: ion_electron_temp_tend ! Calculate tendencies for extended model ionosphere
53 : public :: ion_electron_temp_readnl
54 :
55 : !------------------------------------------------------------------------
56 : ! PRIVATE: Rest of the data and interfaces are private to this module
57 : !------------------------------------------------------------------------
58 : real(r8), parameter :: kboltz_ev = 8.617E-5_r8 ! Boltzmann constant (eV/K)
59 : real(r8), parameter :: temax = 7.0E3_r8 ! maximum electron temperature (K)
60 : real(r8), parameter :: dayOPFlux = 2.0E8_r8 ! Daytime O+ flux at upper boundary (
61 : real(r8), parameter :: nightOPFlux = -2.0E8_r8 ! Nighttime O+ flux at upper boundary (
62 :
63 : real(r8), parameter :: rads2Degs = 180._r8/pi ! radians to degrees
64 :
65 : ! private data
66 : real(r8) :: rMassOp ! O+ molecular weight kg/kmol
67 :
68 : logical :: steady_state_ion_elec_temp = .true.
69 : logical :: initialized_TiTe = .false.
70 :
71 : integer :: index_te=-1, index_ti=-1 ! Indices to find ion and electron temperature in pbuf
72 :
73 : contains
74 :
75 : !==============================================================================
76 :
77 768 : subroutine ion_electron_temp_readnl(nlfile)
78 :
79 : use namelist_utils, only: find_group_name
80 : use units, only: getunit, freeunit
81 : use spmd_utils, only: mpicom, masterprocid, mpicom, mpi_logical
82 :
83 : character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
84 :
85 : ! Local variables
86 : integer :: unitn, ierr
87 : character(len=*), parameter :: subname = 'ion_electron_temp_readnl'
88 :
89 : namelist /ion_electron_temp_nl/ steady_state_ion_elec_temp
90 :
91 768 : if (masterproc) then
92 2 : unitn = getunit()
93 2 : open( unitn, file=trim(nlfile), status='old' )
94 2 : call find_group_name(unitn, 'ion_electron_temp_nl', status=ierr)
95 2 : if (ierr == 0) then
96 2 : read(unitn, ion_electron_temp_nl, iostat=ierr)
97 2 : if (ierr /= 0) then
98 0 : call endrun(subname // ':: ERROR reading namelist')
99 : end if
100 : end if
101 2 : close(unitn)
102 2 : call freeunit(unitn)
103 :
104 : end if
105 :
106 768 : call mpi_bcast (steady_state_ion_elec_temp, 1, mpi_logical, masterprocid, mpicom, ierr)
107 :
108 768 : if (masterproc) then
109 2 : write(iulog,*) subname//': steady_state_ion_elec_temp = ',steady_state_ion_elec_temp
110 : endif
111 :
112 768 : end subroutine ion_electron_temp_readnl
113 :
114 : !==============================================================================
115 : !==============================================================================
116 :
117 1536 : subroutine ion_electron_temp_init(pbuf2d)
118 :
119 : !-----------------------------------------------------------------------
120 : ! Time independent initialization for ionosphere simulation.
121 : !-----------------------------------------------------------------------
122 :
123 : use phys_control, only: phys_getopts !Method used to get flag for waccmx ionosphere output variables
124 :
125 : type(physics_buffer_desc), pointer :: pbuf2d(:,:)
126 :
127 : logical :: history_waccmx
128 : integer :: indxOp,sIndxOp ! state%q or pbuf index for O+ mixing ratio
129 :
130 768 : if (steady_state_ion_elec_temp) then
131 0 : call steady_state_tei_init(pbuf2d)
132 : end if
133 :
134 768 : call phys_getopts(history_waccmx_out=history_waccmx)
135 :
136 : !-------------------------------------------------------------------------------
137 : ! Add history variables for ionosphere
138 : !-------------------------------------------------------------------------------
139 1536 : call addfld ('QIonElec' ,(/ 'lev' /), 'I', 'K sec-1', 'Electron Ion Thermal Heating Rate')
140 1536 : call addfld ('TElec&IC' ,(/ 'lev' /), 'I', 'K', 'Electron Temperature')
141 1536 : call addfld ('TIon&IC' ,(/ 'lev' /), 'I', 'K', 'Ion Temperature')
142 1536 : call addfld ('TElec' ,(/ 'lev' /), 'I', 'K', 'Electron Temperature')
143 1536 : call addfld ('TIon' ,(/ 'lev' /), 'I', 'K', 'Ion Temperature')
144 768 : call addfld ('ElecColDens' ,horiz_only , 'I', 'TECU', 'Electron Column Density')
145 768 : if (.not.steady_state_ion_elec_temp) then
146 1536 : call addfld ('QIN' ,(/ 'lev' /), 'I', 'K sec-1','Ion-neutral Heating Rate')
147 1536 : call addfld ('QEN' ,(/ 'lev' /), 'I', 'K sec-1','Electron-neutral Heating Rate')
148 1536 : call addfld ('QEI' ,(/ 'lev' /), 'I', 'K sec-1','Electron-ion Heating Rate')
149 1536 : call addfld ('LOSS_g3' ,(/ 'lev' /), 'I', ' ', 'Loss Term g3')
150 1536 : call addfld ('LOSS_EI' ,(/ 'lev' /), 'I', ' ', 'Loss Term EI')
151 1536 : call addfld ('LOSS_IN' ,(/ 'lev' /), 'I', ' ', 'Loss Term IN')
152 1536 : call addfld ('SOURCER' ,(/ 'lev' /), 'I', ' ', 'SOURCER')
153 1536 : call addfld ('SOURCEEff' ,(/ 'lev' /), 'I', ' ', 'SOURCEEff')
154 1536 : call addfld ('AURIPRATESUM' ,(/ 'lev' /), 'I', ' ', 'Auroral ionization')
155 1536 : call addfld ('OpI' ,(/ 'lev' /), 'I', ' ', 'O+ Ionosphere')
156 1536 : call addfld ('eI' ,(/ 'lev' /), 'I', ' ', 'e Ionosphere')
157 : end if
158 :
159 768 : call add_default ('TElec&IC' , 0, ' ')
160 768 : call add_default ('TIon&IC' , 0, ' ')
161 :
162 : !-------------------------------------------------------------------------------
163 : ! Set default values for ionosphere history variables
164 : !-------------------------------------------------------------------------------
165 768 : if (history_waccmx) then
166 768 : call add_default ('TElec' , 1, ' ')
167 768 : call add_default ('TIon' , 1, ' ')
168 768 : if (.not.steady_state_ion_elec_temp) then
169 768 : call add_default ('QIN' , 1, ' ')
170 768 : call add_default ('QEN' , 1, ' ')
171 768 : call add_default ('QEI' , 1, ' ')
172 768 : call add_default ('SOURCER' , 1, ' ')
173 768 : call add_default ('SOURCEEff' , 1, ' ')
174 768 : call add_default ('AURIPRATESUM' , 1, ' ')
175 : end if
176 : end if
177 :
178 768 : if (.not.steady_state_ion_elec_temp) then
179 768 : call cnst_get_ind( 'Op', indxOp, abort=.false. )
180 768 : if (indxOp > 0) then
181 0 : rMassOp = cnst_mw(indxOP)
182 : else
183 768 : sIndxOp = get_spc_ndx( 'Op' )
184 768 : if (sIndxOp > 0) then
185 768 : rMassOp = adv_mass(sIndxOp)
186 : else
187 0 : call endrun('update_teti: Cannot find short-lived index for Op in update_teti')
188 : endif
189 : endif
190 : endif
191 :
192 768 : end subroutine ion_electron_temp_init
193 :
194 : !==============================================================================
195 16128 : subroutine ion_electron_temp_timestep_init(phys_state,pbuf2d)
196 : use time_manager, only: is_first_step
197 :
198 : type(physics_state), intent(in) :: phys_state(begchunk:endchunk)
199 : type(physics_buffer_desc), pointer :: pbuf2d(:,:)
200 :
201 : integer :: ncol
202 : integer :: lchnk
203 8064 : type(physics_buffer_desc), pointer :: phys_buffer_chunk(:)
204 :
205 8064 : if (is_first_step() .and. .not.initialized_TiTe .and. index_te>0 .and. index_ti>0) then
206 0 : do lchnk=begchunk,endchunk
207 0 : ncol=phys_state(lchnk)%ncol
208 0 : phys_buffer_chunk => pbuf_get_chunk(pbuf2d,lchnk)
209 0 : call pbuf_set_field(phys_buffer_chunk,index_te,phys_state(lchnk)%t(:ncol,:),start=(/1,1/), kount=(/ncol,pver/))
210 0 : call pbuf_set_field(phys_buffer_chunk,index_ti,phys_state(lchnk)%t(:ncol,:),start=(/1,1/), kount=(/ncol,pver/))
211 : end do
212 0 : initialized_TiTe=.true.
213 : endif
214 16128 : end subroutine ion_electron_temp_timestep_init
215 :
216 : !==============================================================================
217 :
218 768 : subroutine ion_electron_temp_register
219 :
220 : !-----------------------------------------------------------------------
221 : ! Register ionosphere variables with physics buffer:
222 : !
223 : ! Ion production rates pcols,pver,nIonRates,
224 : ! so firstdim = 1 middledim = pver lastdim = nIonRates.
225 : !
226 : ! pcols dimension and lchnk assumed here
227 : !
228 : !-----------------------------------------------------------------------
229 :
230 : !------------------------------------------------------------------------------
231 : ! Electron temperature in physics buffer (global so can write to history files)
232 : !------------------------------------------------------------------------------
233 768 : call pbuf_add_field('TElec','global',dtype_r8,(/pcols,pver/), index_te)
234 :
235 : !--------------------------------------------------------------------------
236 : ! Ion temperature in physics buffer (global so can write to history files)
237 : !--------------------------------------------------------------------------
238 768 : call pbuf_add_field('TIon', 'global',dtype_r8,(/pcols,pver/), index_ti)
239 :
240 8064 : end subroutine ion_electron_temp_register
241 :
242 : !==============================================================================
243 :
244 384 : subroutine ion_electron_temp_inidat(ncid_ini, pbuf2d)
245 :
246 : !-----------------------------------------------------------------------
247 : ! Grab fields from initial condition file and put in physics buffer
248 : !-----------------------------------------------------------------------
249 :
250 : use pio, only: file_desc_t
251 : use cam_grid_support, only: cam_grid_check, cam_grid_id
252 : use cam_grid_support, only: cam_grid_get_dim_names
253 : use cam_abortutils, only: endrun
254 : use ncdio_atm, only: infld
255 : use ppgrid, only: pcols, pver, begchunk, endchunk
256 : use infnan, only: nan, assignment(=)
257 :
258 : type(file_desc_t), intent(inout) :: ncid_ini ! Initial condition file id
259 : type(physics_buffer_desc), pointer :: pbuf2d(:,:) ! Physics buffer
260 :
261 : integer :: grid_id
262 : character(len=4) :: dim1name, dim2name
263 : logical :: found
264 384 : real(r8),pointer :: tE(:,:,:) ! Electron temperature pointer
265 384 : real(r8),pointer :: tI(:,:,:) ! Ion temperature pointer
266 : integer :: ierr
267 : character(len=*), parameter :: subname='ION_ELECTRON_TEMP_INIDAT'
268 : real(r8) :: nanval
269 :
270 384 : nanval=nan
271 384 : found = .false.
272 :
273 384 : grid_id = cam_grid_id('physgrid')
274 384 : if (.not. cam_grid_check(grid_id)) then
275 0 : call endrun(trim(subname)//': Internal error, no "physgrid" grid')
276 : end if
277 384 : call cam_grid_get_dim_names(grid_id, dim1name, dim2name)
278 :
279 384 : if (index_te>0) then
280 : !---------------------------------------------------------------------------------
281 : ! Electron temperature
282 : !---------------------------------------------------------------------------------
283 1152 : allocate(tE(pcols,pver,begchunk:endchunk))
284 : call infld( 'TElec',ncid_ini,dim1name, 'lev', dim2name, 1, pcols, 1, pver, begchunk, endchunk, &
285 384 : tE, found, gridname='physgrid')
286 :
287 384 : if (.not.found) then
288 0 : if (masterproc) write(iulog,*) 'ion_electron_temp_inidat: Could not find electron temperature in ic file. ' &
289 0 : // 'Try to read neutral temperature.'
290 : call infld( 'T',ncid_ini,dim1name, 'lev', dim2name, 1, pcols, 1, pver, begchunk, endchunk, &
291 0 : tE, found, gridname='physgrid')
292 : endif
293 384 : if (found) then
294 384 : call pbuf_set_field(pbuf2d, index_te, tE)
295 : else
296 0 : call pbuf_set_field(pbuf2d, index_te, nanval)
297 : endif
298 :
299 384 : initialized_TiTe = found
300 384 : deallocate(tE)
301 : endif
302 :
303 384 : if (index_ti>0) then
304 : !----------------------------------------------------------------------------
305 : ! Ion temperature
306 : !----------------------------------------------------------------------------
307 1152 : allocate(tI(pcols,pver,begchunk:endchunk))
308 384 : if (initialized_TiTe) then ! try to initialize ion temp only if electron temp was initialized above
309 : call infld( 'TIon',ncid_ini,dim1name, 'lev', dim2name, 1, pcols, 1, pver, begchunk, endchunk, &
310 384 : tI, found, gridname='physgrid')
311 384 : if (.not.found) then
312 0 : if (masterproc) write(iulog,*) 'ion_electron_temp_inidat: Could not find ion temperature in ic file. ' &
313 0 : // 'Try to read neutral temperature.'
314 : call infld( 'T',ncid_ini,dim1name, 'lev', dim2name, 1, pcols, 1, pver, begchunk, endchunk, &
315 0 : tI, found, gridname='physgrid')
316 : endif
317 : endif
318 384 : if (found) then
319 384 : call pbuf_set_field(pbuf2d, index_ti, tI)
320 : else
321 0 : call pbuf_set_field(pbuf2d, index_ti, nanval)
322 : endif
323 :
324 384 : initialized_TiTe = initialized_TiTe .and. found
325 384 : deallocate(tI)
326 : endif
327 384 : if (index_te>0 .and. index_ti>0 .and. .not.initialized_TiTe) then
328 : write(iulog,*) 'ion_electron_temp_inidat: Not able to read temperatures from IC file.' &
329 0 : //' Will set ion and electron temperatures to neutral temperature (state%t) on initial timestep.'
330 : endif
331 :
332 768 : end subroutine ion_electron_temp_inidat
333 :
334 : !==============================================================================
335 :
336 21888 : subroutine ion_electron_temp_tend(state, ptend, pbuf, ztodt)
337 :
338 : !-------------------------------------------------------------------------------------
339 : ! Calculate dry static energy and O+ tendency for extended ionosphere simulation
340 : !-------------------------------------------------------------------------------------
341 :
342 : !------------------------------Arguments--------------------------------
343 :
344 384 : use physics_types, only: physics_ptend_sum
345 :
346 : type(physics_state), intent(in) :: state ! physics state structure
347 : type(physics_ptend), intent(inout) :: ptend ! parameterization tendency structure
348 : type(physics_buffer_desc),pointer :: pbuf(:) ! physics buffer
349 :
350 : real(r8), intent(in) :: ztodt ! Physics time step
351 :
352 : !---------------------------Local storage-------------------------------
353 :
354 2232576 : type(physics_ptend) :: ptend_loc ! Local parameterization tendencies
355 : type(ionos_state) :: istate ! ionosphere state structure
356 :
357 : integer :: lchnk ! Chunk number
358 : integer :: ncol ! Number of columns in chunk
359 :
360 : integer :: teTiBot ! bottom of ionosphere calculations
361 :
362 21888 : real(r8), dimension(:,:), pointer :: tE ! Pointer to electron temperature in pbuf (K)
363 21888 : real(r8), dimension(:,:), pointer :: tI ! Pointer to ion temperature in pbuf (K)
364 :
365 : logical :: ls
366 : real(r8) :: dse_tend(pcols,pver) ! dry static energy tendency
367 : real(r8) :: qionelec(pcols,pver) ! diagnostic heating rate (neutrals)
368 :
369 21888 : call t_startf ('ion_electron_temp_tend')
370 :
371 : !----------------------------------------------------------------
372 : ! Get number of this chunk
373 : !----------------------------------------------------------------
374 21888 : lchnk = state%lchnk
375 21888 : ncol = state%ncol
376 :
377 21888 : ls = .TRUE.
378 21888 : call physics_ptend_init(ptend_loc, state%psetcols, 'ionosphere', ls=ls)
379 :
380 : !-------------------------------------------------------------------------------------------------------------------
381 : ! Get electron and ion temperatures from physics buffer.
382 : !-------------------------------------------------------------------------------------------------------------------
383 21888 : call pbuf_get_field(pbuf, index_te, tE)
384 21888 : call pbuf_get_field(pbuf, index_ti, tI)
385 :
386 : !------------------------------------------------------------
387 : ! Initialize data needed in the ionosphere calculations
388 : !------------------------------------------------------------
389 21888 : call update_istate(state, pbuf, istate, teTiBot)
390 :
391 21888 : if (steady_state_ion_elec_temp) then
392 : !-----------------------------------------------------------------
393 : ! steady-state solution
394 : !-----------------------------------------------------------------
395 0 : dse_tend(:ncol,:) = ptend%s(:ncol,:)
396 0 : call steady_state_tei_tend(state, istate, dse_tend, pbuf)
397 0 : ptend_loc%s(:ncol,:) = dse_tend(:ncol,:)
398 : else
399 : !-----------------------------------------------------------------
400 : ! Get electron temperature and update dry static energy tendency
401 : !-----------------------------------------------------------------
402 21888 : call update_teti(state, ptend%s, ptend_loc%s, ztodt, istate, tE, tI, teTiBot)
403 : end if
404 :
405 : !--------------------------------------------------------------
406 : ! Make Te and Ti fields available for output to history files
407 : !--------------------------------------------------------------
408 21888 : call outfld ('TElec' , tE, pcols, lchnk)
409 21888 : call outfld ('TIon' , tI, pcols, lchnk)
410 21888 : if (write_inithist()) then
411 0 : call outfld ('TElec&IC', tE, pcols, lchnk)
412 0 : call outfld ('TIon&IC' , tI, pcols, lchnk)
413 : endif
414 :
415 37012608 : qionelec(:ncol,:) = ptend_loc%s(:ncol,:)/cpairv(:ncol,:,lchnk)
416 21888 : call outfld ('QIonElec' , qionelec, pcols, lchnk)
417 :
418 21888 : call physics_ptend_sum(ptend_loc, ptend, ncol)
419 :
420 21888 : call t_stopf ('ion_electron_temp_tend')
421 :
422 43776 : end subroutine ion_electron_temp_tend
423 :
424 : !===============================================================================
425 :
426 21888 : subroutine update_istate(state, pbuf, istate, teTiBot)
427 :
428 : !---------------------------------------------------------------------------------------
429 : ! Time independent initialization for extended ionosphere simulation called in phys_init
430 : ! of physpkg module which is called in cam_comp module
431 : !---------------------------------------------------------------------------------------
432 21888 : use mo_apex, only: bnorth, beast, bdown ! Magnetic field components
433 : use time_manager, only: get_curr_calday ! Routine to get current calendar day
434 : use physconst, only: rearth
435 : use air_composition, only: rairv, mbarv ! Constituent dependent rair and mbar
436 : use ref_pres, only: press_lim_idx
437 : use orbit, only: zenith
438 :
439 : use short_lived_species, only: slvd_index,slvd_pbf_ndx => pbf_idx ! Routines to access short lived species
440 :
441 : type(physics_buffer_desc), pointer :: pbuf(:) ! physics buffer
442 : type(physics_state), intent(in), target :: state ! physics state structure
443 : type(ionos_state), intent(inout), target :: istate ! ionosphere state structure
444 :
445 : integer, intent(out) :: teTiBot ! bottom of ionosphere calculations
446 :
447 : !---------------------------Local storage-------------------------------
448 : integer,parameter :: nCnst = 9 ! Number of species needed from state%q or pbuf
449 :
450 : integer :: lchnk ! Chunk number
451 : integer :: ncol ! Number of columns in current chunk
452 :
453 : integer :: indxIR ! pbuf index for ionization rates
454 : integer :: indxAIPRS ! pbuf index for aurora ion production rate sum
455 :
456 : integer :: indxCnst ! Constituent index used in cslculating densities
457 :
458 : integer :: indxSLvd ! index of pbuf to access short lived species
459 : integer :: sIndx ! index of adv_mass for any short lived species to access constituent mass
460 :
461 : integer :: iVer ! Counter for vertical loops
462 : integer :: iCol ! Counter for column loops
463 : integer :: iIonR ! Counter for ionization rates loops
464 : integer :: iCnst ! Counter for constituent loop
465 :
466 : integer :: indxSP ! pbuf index for Pedersen Conductivity
467 : integer :: indxSH ! pbuf index for Hall Conductivity
468 :
469 : real(r8), parameter :: teTiBotPres = 50._r8 ! Pressure above which electron/ion temperature are calculated
470 : ! in WACCM-X. (Pa)
471 :
472 : character(len = 3), dimension(nCnst) :: cCnst
473 :
474 21888 : real(r8), dimension(:,:), pointer :: sigma_ped ! Pointer to Pedersen Conductivity in pbuf (siemens/m) from module iondrag
475 21888 : real(r8), dimension(:,:), pointer :: sigma_hall ! Pointer to Hall Conductivity in pbuf (siemens/m)
476 :
477 21888 : real(r8), dimension(:,:), pointer :: mmrP ! Pointer to access short lived species in pbuf
478 :
479 21888 : real(r8), dimension(:),pointer :: geoLatR ! Latitude (radians) Make ncol because zenith aurora are ncol
480 21888 : real(r8), dimension(:),pointer :: geoLonR ! Longitude (radians)
481 :
482 21888 : real(r8), dimension(:,:),pointer :: pMid ! Midpoint pressure (Pa)
483 21888 : real(r8), dimension(:,:),pointer :: tN ! Neutral temperature (K)
484 :
485 21888 : real(r8), dimension(:,:),pointer :: tNInt ! Interface Temperture (K)
486 :
487 21888 : real(r8), dimension(:),pointer :: cosZenAngR ! cosine of zenith angle (radians)
488 21888 : real(r8), dimension(:),pointer :: zenAngD ! zenith angle (degrees)
489 :
490 21888 : real(r8), dimension(:,:),pointer :: bNorth3d ! northward component of magnetic field units?
491 21888 : real(r8), dimension(:,:),pointer :: bEast3d ! eastward component of magnetic field
492 21888 : real(r8), dimension(:,:),pointer :: bDown3d ! downward component of magnetic field
493 :
494 : real(r8), dimension(pcols,pver) :: sourceR ! R term of source g4 calculation
495 : real(r8), dimension(pcols,pver) :: sourceEff ! Efficiency term of source g4 calculation
496 :
497 21888 : real(r8), dimension(:,:),pointer :: rairvi ! Constituent dependent gas constant
498 :
499 21888 : real(r8), dimension(:,:),pointer :: dipMag ! dip angle for each column (radians)
500 :
501 : real(r8), parameter :: rMassN2 = 28._r8 ! N2 molecular weight kg/kmol
502 : real(r8) :: rMass ! Constituent molecular weight kg/kmol
503 :
504 21888 : real(r8), dimension(:,:),pointer :: mmrN2 ! N2 mass mixing ratio kg/kg
505 : real(r8), dimension(pcols,pver) :: mmrO2 ! O2 mass mixing ratio kg/kg
506 : real(r8), dimension(pcols,pver) :: mmrO1 ! O mass mixing ratio kg/kg
507 :
508 : real(r8), dimension(pcols,pver) :: mmr ! Constituent mass mixing ratio kg/kg
509 :
510 21888 : real(r8), dimension(:,:),pointer :: ndensN2 ! N2 number density (cm-3)
511 : real(r8), dimension(pcols,pver) :: ndensO2 ! O2 number density (cm-3)
512 : real(r8), dimension(pcols,pver) :: ndensO1 ! O number density (cm-3)
513 : real(r8), dimension(pcols,pver) :: ndensN1 ! N number density (cm-3)
514 : real(r8), dimension(pcols,pver) :: ndensE ! E electron number density (cm-3)
515 :
516 21888 : real(r8), dimension(:,:) ,pointer :: ndens ! Constituent number density (cm-3)
517 :
518 21888 : real(r8), dimension(:,:,:),pointer :: ionRates ! Pointer to ionization rates for O+,O2+,N+,N2+,NO+ in pbuf (s-1)
519 : ! (from modules mo_jeuv and mo_jshort)
520 :
521 21888 : real(r8), dimension(:,:,:),pointer :: ionPRates ! ionization rates temporary array (s-1 cm-3)
522 21888 : real(r8), dimension(:,:) ,pointer :: sumIonPRates ! Sum of ionization rates for O+,O2+,N+,N2+,NO+ (s-1 cm-3)
523 :
524 21888 : real(r8), dimension(:,:) ,pointer :: aurIPRateSum ! Auroral ion production sum for O2+,O+,N2+
525 : ! (s-1 cm-3 from module mo_aurora)
526 :
527 21888 : real(r8), dimension(:,:) ,pointer :: sourceg4 ! g4 source term for electron/ion temperature update
528 :
529 : real(r8) :: calDay ! current calendar day
530 :
531 : !real(r8), dimension(pcols,pver) :: tempout ! temporary scratch output array
532 :
533 : real(r8), dimension(pcols,pver) :: zGeom ! Geometric altitude (cm)
534 : real(r8), dimension(pcols,pver) :: zThickness ! Geometric altitude thickness (cm)
535 : real(r8), dimension(pcols) :: eColDens ! Electron column density (TECU = 1E16 m-2)
536 :
537 : !--------------------------------------------------------------------------------
538 :
539 21888 : sourceR = 0._r8
540 21888 : sourceEff = 0._r8
541 :
542 21888 : mmrN2 => istate%n2_mmr
543 48394368 : mmrN2 = 0._r8
544 21888 : mmrO2 = 0._r8
545 21888 : mmrO1 = 0._r8
546 21888 : mmr = 0._r8
547 :
548 21888 : ndensO2(:,:) = 0._r8
549 21888 : ndensO1(:,:) = 0._r8
550 21888 : ndensN1(:,:) = 0._r8
551 21888 : ndensE(:,:) = 0._r8
552 :
553 21888 : sourceR(:,:) = 0._r8
554 21888 : sourceEff(:,:) = 0._r8
555 :
556 : !tempout(:,:) = 0._r8
557 :
558 : !--------------------------------------------------------------------------------------
559 : ! Get lchnk from state
560 : !--------------------------------------------------------------------------------------
561 21888 : lchnk = state%lchnk
562 21888 : ncol = state%ncol
563 :
564 : !------------------------------------------------------------------------------------------------------
565 : ! Set the bottom of the ionosphere calculations at around 50 Pascals or 0.5 hectopascals(millibars).
566 : ! teTiBotPres is in Pascals.
567 : !------------------------------------------------------------------------------------------------------
568 21888 : teTiBot = press_lim_idx(teTiBotPres, top=.false.)
569 :
570 : !----------------------------------------------------------------
571 : ! Get latitude and longitude of each column in this chunk
572 : !----------------------------------------------------------------
573 21888 : geoLatR => state%lat(1:ncol)
574 21888 : geoLonR => state%lon(1:ncol)
575 :
576 : !-------------------------------------------------------------------------------------------------------
577 : ! Need to get midpoint and interface pressure and neutral temperature from state structure (pcols,pver)
578 : !-------------------------------------------------------------------------------------------------------
579 21888 : pMid => state%pmid(1:ncol,1:pver)
580 21888 : tN => state%t(1:ncol,1:pver)
581 :
582 21888 : tNInt => istate%tNInt(1:ncol,1:pverp)
583 21888 : cosZenAngR => istate%cosZenAngR(1:ncol)
584 21888 : zenAngD => istate%zenAngD(1:ncol)
585 :
586 21888 : bNorth3d => istate%bNorth3d(1:ncol,1:pver)
587 21888 : bEast3d => istate%bEast3d(1:ncol,1:pver)
588 21888 : bDown3d => istate%bDown3d(1:ncol,1:pver)
589 :
590 21888 : rairvi => istate%rairvi(1:ncol,1:pverp)
591 :
592 21888 : dipMag => istate%dipMag(1:ncol,1:pver)
593 :
594 21888 : ndensN2 => istate%ndensN2(1:ncol,1:pver)
595 :
596 21888 : ionPRates => istate%ionPRates(1:ncol,1:pver,1:nIonRates)
597 21888 : sumIonPRates => istate%sumIonPRates(1:ncol,1:pver)
598 :
599 21888 : sourceg4 => istate%sourceg4(1:ncol,1:pver)
600 :
601 : !-------------------------------------------------------------------------------------
602 : ! Calculate neutral temperature on interface levels. tN vertical dimension is pver
603 : !-------------------------------------------------------------------------------------
604 2845440 : do iVer = 2, pver
605 :
606 36728064 : do iCol = 1, ncol
607 :
608 36706176 : tNInt(iCol,iVer) = 0.5_r8 * tN(iCol,iVer) + 0.5_r8 * tN(iCol,iVer-1)
609 :
610 : enddo
611 : enddo
612 :
613 284544 : do iCol = 1, ncol
614 284544 : tNInt(iCol,1) = 1.5_r8 * tNInt(iCol,2) - 0.5_r8 * tNInt(iCol,3)
615 : enddo
616 284544 : do iCol = 1, ncol
617 284544 : tNInt(iCol,pverp) = 1.5_r8 * tNInt(iCol,pver) - 0.5_r8 * tNInt(iCol,pver-1)
618 : enddo
619 :
620 : !--------------------------------------------------------------
621 : ! Get zenith angle
622 : !--------------------------------------------------------------
623 21888 : calDay = get_curr_calday()
624 21888 : call zenith(calDay,geoLatR(1:ncol),geoLonR(1:ncol),cosZenAngR(1:ncol),ncol)
625 :
626 284544 : do iCol = 1, ncol
627 :
628 284544 : zenAngD(iCol) = ACOS(cosZenAngR(iCol)) * rads2Degs
629 :
630 : enddo
631 :
632 : !---------------------------------------------------------------------------------------
633 : ! Expand magnetic field components in vertical to make 3D, pcols,pver,begchunk:endchunk
634 : ! These are used in calculation of magnetic dip angle and magnetic declination angle so
635 : ! store in local ionosphere module structure.
636 : !---------------------------------------------------------------------------------------
637 2867328 : do iVer = 1, pver
638 :
639 37012608 : do iCol = 1, ncol
640 :
641 34145280 : bNorth3d(iCol,iVer) = bnorth(iCol,lchnk)
642 34145280 : bEast3d(iCol,iVer) = beast(iCol,lchnk)
643 36990720 : bDown3d(iCol,iVer) = bdown(iCol,lchnk)
644 :
645 : enddo
646 :
647 : enddo
648 :
649 : !------------------------------------------------------------------------
650 : ! Get constituent dependent gas constant and derive on interface levels
651 : !------------------------------------------------------------------------
652 2845440 : do iVer = 2, pver
653 36728064 : do iCol = 1, ncol
654 36706176 : rairvi(iCol,iVer) = 0.5_r8 * rairv(iCol,iVer-1,lchnk) + 0.5_r8 * rairv(iCol,iVer,lchnk)
655 : enddo
656 : enddo
657 :
658 284544 : do iCol = 1, ncol
659 284544 : rairvi(iCol,1) = 1.5_r8 * rairvi(iCol,2) - 0.5_r8 * rairvi(iCol,3)
660 : enddo
661 284544 : do iCol = 1, ncol
662 284544 : rairvi(iCol,pverp) = 1.5_r8 * rairvi(iCol,pver) - 0.5_r8 * rairvi(iCol,pver-1)
663 : enddo
664 :
665 : !-------------------------------------------------------------------------------
666 : ! Need to get dip angle from magnetic field components
667 : !-------------------------------------------------------------------------------
668 2867328 : do iVer = 1, pver
669 37012608 : do iCol = 1, ncol
670 34145280 : dipMag(iCol,iVer) = ATAN(bDown3d(iCol,iVer) / SQRT(bNorth3d(iCol,iVer)**2 + bEast3d(iCol,iVer)**2))
671 34145280 : if (dipMag(iCol,iVer) < 0.17_r8 .and. dipMag(iCol,iVer) > 0._r8 ) dipMag(iCol,iVer) = 0.17_r8
672 36990720 : if (dipMag(iCol,iVer) > -0.17_r8 .and. dipMag(iCol,iVer) < 0._r8 ) dipMag(iCol,iVer) = 0.17_r8
673 : enddo
674 : enddo
675 :
676 : !-------------------------------------------------------------------------------------------
677 : ! Set up constituents to be accessed here from pbuf or state%q.
678 : !-------------------------------------------------------------------------------------------
679 218880 : cCnst = (/'O ','O2 ','NO ','H ','N ','e ','Op ','O2p','NOp'/)
680 :
681 218880 : do iCnst = 1, nCnst
682 :
683 : !--------------------------------------
684 : ! Assign density to istate array
685 : !--------------------------------------
686 196992 : if (cCnst(iCnst) == 'O ') ndens => istate%ndensO1(1:ncol,1:pver)
687 196992 : if (cCnst(iCnst) == 'O2 ') ndens => istate%ndensO2(1:ncol,1:pver)
688 196992 : if (cCnst(iCnst) == 'NO ') ndens => istate%ndensNO(1:ncol,1:pver)
689 196992 : if (cCnst(iCnst) == 'N ') ndens => istate%ndensN1(1:ncol,1:pver)
690 196992 : if (cCnst(iCnst) == 'e ') ndens => istate%ndensE(1:ncol,1:pver)
691 196992 : if (cCnst(iCnst) == 'Op ') ndens => istate%ndensOp(1:ncol,1:pver)
692 196992 : if (cCnst(iCnst) == 'O2p') ndens => istate%ndensO2p(1:ncol,1:pver)
693 196992 : if (cCnst(iCnst) == 'NOp') ndens => istate%ndensNOp(1:ncol,1:pver)
694 :
695 : !-------------------------------------------------------------------------------------------
696 : ! Set flag and get field mmr whether each constituent is short-lived(pbuf) or not(state%q).
697 : !-------------------------------------------------------------------------------------------
698 196992 : call cnst_get_ind( TRIM(cCnst(iCnst)), indxCnst, abort=.false. )
699 196992 : if (indxCnst < 0) then
700 87552 : indxSlvd = slvd_index( TRIM(cCnst(iCnst)) )
701 87552 : if (indxSLvd > 0) then
702 350208 : call pbuf_get_field(pbuf, slvd_pbf_ndx, mmrP, start=(/1,1,indxSLvd/), kount=(/pcols,pver,1/) )
703 148050432 : mmr(1:ncol,1:pver) = mmrP(1:ncol,1:pver)
704 87552 : sIndx = get_spc_ndx( TRIM(cCnst(iCnst)) )
705 87552 : rMass = adv_mass(sIndx)
706 : endif
707 : else
708 185063040 : mmr(1:ncol,1:pver) = state%q(1:ncol,1:pver,indxCnst)
709 109440 : rMass = cnst_mw(indxCnst)
710 : endif
711 :
712 : !--------------------------------------------------------------------------------------------------------------
713 : ! Need to get number density (cgs units) from mass mixing ratio. mbarv is kg/mole, same as rMass units
714 : ! kg/kg * (kg/mole)/(kg/mole) * (Pa or N/m*m)/((Joules/K or N*m/K) * (K)) = m-3 * 1E-06 = cm-3
715 : !---------------------------------------------------------------------------------------------------------------
716 787968 : ndens(1:ncol,1:pver) = mmr(1:ncol,1:pver) * mbarv(1:ncol,1:pver,lchnk) / rMass * &
717 666817920 : pMid(1:ncol,1:pver) / (kboltz * tN(1:ncol,1:pver)) * 1.E-06_r8
718 :
719 196992 : if (cCnst(iCnst) == 'O ') then
720 37012608 : mmrO1(1:ncol,1:pver) = mmr(1:ncol,1:pver)
721 37012608 : ndensO1(1:ncol,1:pver) = ndens(1:ncol,1:pver)
722 : endif
723 196992 : if (cCnst(iCnst) == 'O2 ') then
724 37012608 : mmrO2(1:ncol,1:pver) = mmr(1:ncol,1:pver)
725 37012608 : ndensO2(1:ncol,1:pver) = ndens(1:ncol,1:pver)
726 : endif
727 37187712 : if (cCnst(iCnst) == 'N ') ndensN1(1:ncol,1:pver) = ndens(1:ncol,1:pver)
728 37187712 : if (cCnst(iCnst) == 'e ') ndensE(1:ncol,1:pver) = ndens(1:ncol,1:pver)
729 :
730 : !----------------------------------------------------------------------------
731 : ! Calculate N2 density from O2 and O and assign to istate array
732 : !----------------------------------------------------------------------------
733 218880 : if (iCnst == nCnst) then
734 :
735 37012608 : mmrN2(1:ncol,1:pver) = 1._r8 - (mmrO2(1:ncol,1:pver) + mmrO1(1:ncol,1:pver))
736 37012608 : mmrN2(1:ncol,1:pver) = MAX(1.e-20_r8,mmrN2(1:ncol,1:pver))
737 43776 : ndensN2(1:ncol,1:pver) = mmrN2(1:ncol,1:pver) * mbarv(1:ncol,1:pver,lchnk) / rMassN2 * &
738 74047104 : pMid(1:ncol,1:pver) / (kboltz * tN(1:ncol,1:pver)) * 1.E-06_r8
739 :
740 : endif
741 :
742 : enddo ! nCnst
743 :
744 21888 : if (hist_fld_active('ElecColDens')) then
745 : !---------------------------------------
746 : ! Calculate electron column density
747 : !---------------------------------------
748 : !------------------------------------------------------------------------------
749 : ! Convert geopotential altitude in meters to geometric altitude in centimeters
750 : !------------------------------------------------------------------------------
751 37012608 : zGeom(1:ncol,1:pver) = state%zm(1:ncol,1:pver) * (1._r8 + state%zm(1:ncol,1:pver) / rearth) * 100._r8
752 :
753 : !------------------------------------------------------------
754 : ! Calculate vertical thickness at each level in centimeters
755 : !------------------------------------------------------------
756 2823552 : do iVer = 2, pver-1
757 :
758 36443520 : zThickness(1:ncol,iVer) = (zGeom(1:ncol,iVer-1) - zGeom(1:ncol,iVer+1)) / 2._r8
759 :
760 : enddo
761 :
762 284544 : zThickness(1:ncol,1) = (1.5_r8 * zThickness(1:ncol,2)) - (0.5_r8 * zThickness(1:ncol,3))
763 284544 : zThickness(1:ncol,pver) = (1.5_r8 * zThickness(1:ncol,pver-1)) - (0.5_r8 * zThickness(1:ncol,pver-2))
764 :
765 : !----------------------------------------------------------------------------------
766 : ! Calculate electron column density converting from cm-2 to TEC units (1E16 m-2)
767 : ! and make available for history output
768 : !----------------------------------------------------------------------------------
769 34429824 : eColDens(1:ncol) = sum(ndensE(1:ncol,:) * zThickness(1:ncol,:), dim=2) / 1.E12_r8
770 :
771 21888 : call outfld('ElecColDens', eColDens, pcols, lchnk)
772 : endif
773 :
774 : !------------------------------------------------------------------------------------
775 : ! Get ionization rates from physics buffer which were calculated in mo_jeuv and
776 : ! mo_jshort modules. Rates array dimensions are pcols, pver, nIonRates. Units s-1
777 : !------------------------------------------------------------------------------------
778 21888 : indxIR = pbuf_get_index( 'IonRates' )
779 21888 : call pbuf_get_field(pbuf, indxIR, ionRates)
780 :
781 : !----------------------------------------------------------------------------------------------
782 : ! Need to convert these ionization rates to ion production rates by multiplying number density
783 : ! of neutral species appropriate from reactions in mo_jeuv(jeuv) and mo_jshort(jshort)(for NO)
784 : !----------------------------------------------------------------------------------------------
785 2867328 : do iVer = 1, pver
786 37012608 : do iCol = 1, ncol
787 :
788 409743360 : do iIonR = 1, nIonRates
789 409743360 : IF (iIonR <= 3) then
790 102435840 : ionPRates(iCol,iVer,iIonR) = ionRates(iCol,iVer,iIonR) * ndensO1(iCol,iVer)
791 273162240 : else IF (iIonR == 4) then
792 34145280 : ionPRates(iCol,iVer,iIonR) = ionRates(iCol,iVer,iIonR) * ndensN1(iCol,iVer)
793 239016960 : else IF ((iIonR == 5) .OR. (iIonR >= 7 .AND. iIonR <= 9)) then
794 :
795 136581120 : ionPRates(iCol,iVer,iIonR) = ionRates(iCol,iVer,iIonR) * ndensO2(iCol,iVer)
796 102435840 : else IF (iIonR == 6 .OR. iIonR == 10 .OR. iIonR == 11) then
797 102435840 : ionPRates(iCol,iVer,iIonR) = ionRates(iCol,iVer,iIonR) * ndensN2(iCol,iVer)
798 : endif
799 : enddo
800 :
801 : !----------------------------------------------
802 : ! Sum ion production rates all reactions
803 : !----------------------------------------------
804 412588800 : sumIonPRates(iCol,iVer) = SUM(ionPRates(iCol,iVer,1:11))
805 :
806 : enddo
807 : enddo
808 21888 : if (.not.steady_state_ion_elec_temp) then
809 : !-------------------------------------------------------------------------------------------
810 : ! Get aurora ion production rate sum from physics buffer which were calculated in mo_aurora
811 : ! module. Rate array dimensions are pcols, pver. Units s-1 cm-3
812 : !-------------------------------------------------------------------------------------------
813 21888 : indxAIPRS = pbuf_get_index( 'AurIPRateSum' )
814 21888 : call pbuf_get_field(pbuf, indxAIPRS, aurIPRateSum)
815 :
816 : !-------------------------------------------------------------------------------------------------
817 : ! Calculate electron heating rate which is a source in electron/ion temperature derivation
818 : !-------------------------------------------------------------------------------------------------
819 1860480 : do iVer = 1, teTiBot
820 23923584 : do iCol = 1, ncol
821 44126208 : sourceR(iCol,iVer) = LOG( ndensE(iCol,iVer) / (ndensO2(iCol,iVer) + ndensN2(iCol,iVer) + &
822 44126208 : 0.1_r8 * ndensO1(iCol,iVer)) )
823 : sourceEff(iCol,iVer) = EXP( -(12.75_r8 + 6.941_r8 * sourceR(iCol,iVer) + 1.166_r8 * sourceR(iCol,iVer)**2 + &
824 22063104 : 0.08043_r8 * sourceR(iCol,iVer)**3 + 0.001996_r8 * sourceR(iCol,iVer)**4) )
825 :
826 : !-------------------------------------------------------------------------------
827 : ! Calculate g4 source term for electron temperature update
828 : !-------------------------------------------------------------------------------
829 23901696 : sourceg4(iCol,iVer) = (sumIonPRates(iCol,iVer) + aurIPRateSum(iCol,iVer)) * sourceEff(iCol,iVer)
830 :
831 : enddo
832 :
833 : enddo
834 :
835 21888 : call outfld ('SOURCER' , sourceR , pcols, lchnk)
836 21888 : call outfld ('SOURCEEff' , sourceEff , pcols, lchnk)
837 21888 : call outfld ('AURIPRATESUM', aurIPRateSum, pcols, lchnk)
838 :
839 : !----------------------------------------------------------------------------------------------
840 : ! Get Pedersen and Hall Conductivities from physics buffer which were calculated in iondrag
841 : ! module. Conductivity array dimensions are pcols, pver
842 : !-------------------------------------------------------------------------------
843 21888 : indxSP = pbuf_get_index( 'PedConduct' )
844 21888 : indxSH = pbuf_get_index( 'HallConduct' )
845 21888 : call pbuf_get_field(pbuf, indxSP, sigma_ped)
846 21888 : call pbuf_get_field(pbuf, indxSH, sigma_hall)
847 :
848 : endif
849 :
850 21888 : return
851 :
852 43776 : end subroutine update_istate
853 : !
854 : !===============================================================================
855 :
856 21888 : subroutine update_teti(state, dSETendIn, dSETendOut, ztodt, istate, tE, tI, teTiBot)
857 :
858 : !-----------------------------------------------------------------------
859 : ! Routine to compute the electron and ion temperature
860 : !-----------------------------------------------------------------------
861 :
862 21888 : use physconst, only: gravit ! Gravity (m/s2)
863 : use air_composition, only: rairv, mbarv ! Constituent dependent rair and mbar
864 : use mo_apex, only: alatm
865 :
866 : !------------------------------Arguments--------------------------------
867 :
868 : type(physics_state), intent(in), target :: state ! physics state structure
869 : type(ionos_state), intent(in), target :: istate ! ionosphere state structure
870 :
871 : real(r8), dimension(pcols,pver), intent(in) :: dSETendIn ! dry static energy tendency
872 : real(r8), dimension(pcols,pver), intent(out) :: dSETendOut ! dry static energy tendency
873 :
874 : real(r8), intent(in) :: ztodt ! physics time step
875 :
876 : real(r8), dimension(:,:), pointer, intent(inout) :: tE ! Pointer to electron temperature in pbuf (K)
877 : real(r8), dimension(:,:), pointer, intent(inout) :: tI ! Pointer to ion temperature in pbuf (K)
878 :
879 : integer, intent(in) :: teTiBot ! bottom of ionosphere calculations
880 :
881 : !---------------------------Local storage-------------------------------
882 : integer, parameter :: maxIter = 6 ! maximum number of iterations to solve for electron/ion temperature
883 :
884 : integer :: lchnk ! Chunk number
885 : integer :: ncol ! Number of atmospheric columns
886 : integer :: teTiBotP ! bottom of ionosphere calculations plus one more level
887 :
888 : integer :: iVer ! Counter for vertical loops
889 : integer :: iCol ! Counter for column loops
890 : integer :: iter ! Counter for iteration loop
891 :
892 : real(r8), parameter :: Kec1 = 7.5E5_r8 ! c1 constant for calculation of electron conductivity(Ke)
893 : real(r8), parameter :: Kec2 = 3.22E4_r8 ! c2 constant for calculation of electron conductivity(Ke)
894 : real(r8), parameter :: stepweight = 1.0_r8 ! weight of previous and current times step for diagonals
895 : real(r8), parameter :: sToQConv = 6.24E15_r8 ! Conversion from J/kg/s to ev/g/s
896 :
897 : real(r8), parameter :: lossc5 = 1.21E-4_r8 ! c5 constant needed for loss term g3 for electron temperature update
898 : real(r8), parameter :: lossc7 = 3.6E-2_r8 ! c7 constant needed for loss term g3 for electron temperature update
899 : real(r8), parameter :: lossc9 = 5.7E-4_r8 ! c9 constant needed for loss term g3 for electron temperature update
900 : real(r8), parameter :: lossc13 = 7.E-5_r8 ! c13 constant needed for loss term g3 for electron temperature update
901 :
902 : real(r8), parameter :: lossc4pCoef = 1.77E-19_r8
903 : real(r8), parameter :: lossc6pCoef = 1.21E-18_r8
904 : real(r8), parameter :: lossc8pCoef = 7.9E-19_r8
905 : real(r8), parameter :: lossc10pCoef = 1.3E-4_r8
906 : real(r8), parameter :: lossc11pCoef = 3.125E-21_r8
907 : real(r8), parameter :: lossc12pCoef = 3.4E-12_r8
908 : real(r8), parameter :: lossc14pCoef = 1.57E-12_r8
909 : real(r8), parameter :: lossc15pCoef = 2.9E-14_r8
910 : real(r8), parameter :: lossc16pCoef = 6.9E-14_r8
911 : real(r8), parameter :: lossc3pC1 = 3.2E-8_r8
912 : real(r8), parameter :: lossc3pC2 = 15._r8
913 : real(r8), parameter :: lossc3pC3 = 0.53_r8
914 :
915 : real(r8), parameter :: losscinCoef1 = 6.6e-14_r8
916 : real(r8), parameter :: losscinCoef2 = 5.8e-14_r8
917 : real(r8), parameter :: losscinCoef3 = 0.21e-14_r8
918 : real(r8), parameter :: losscinCoef4 = 5.9e-14_r8
919 : real(r8), parameter :: losscinCoef5 = 5.45e-14_r8
920 : real(r8), parameter :: losscinCoef6 = 4.5e-14_r8
921 : real(r8), parameter :: losscinCoef7 = 5.8e-14_r8
922 : real(r8), parameter :: losscinCoef8 = 0.14e-14_r8
923 : real(r8), parameter :: losscinCoef9 = 4.4e-14_r8
924 :
925 : real(r8), parameter :: FeDCoef1 = -9.0E+7_r8
926 : real(r8), parameter :: FeDCoef2 = 4.0E+7_r8
927 :
928 : real(r8), parameter :: losscACoef1 = 5.71E-8_r8
929 : real(r8), parameter :: losscACoef2 = -3352.6_r8
930 : real(r8), parameter :: losscACoef3 = 2.0E-7_r8
931 : real(r8), parameter :: losscACoef4 = -4605.2_r8
932 : real(r8), parameter :: losscACoef5 = 2.53E-6_r8
933 : real(r8), parameter :: losscACoef6 = -17620._r8
934 :
935 : real(r8), parameter :: loss10pCoef = 3200._r8
936 : real(r8), parameter :: lossc12pC1 = 0.4_r8
937 : real(r8), parameter :: lossc12pC2 = 150._r8
938 :
939 : real(r8), parameter :: losscf2dC1 = 2.4E+4_r8
940 : real(r8), parameter :: losscf2dC2 = 0.3_r8
941 : real(r8), parameter :: losscf2dC3 = 1500._r8
942 : real(r8), parameter :: losscf2dC4 = 1.947E-5_r8
943 : real(r8), parameter :: losscf2dC5 = 4000._r8
944 :
945 : real(r8), parameter :: losscf2C1 = 3000._r8
946 :
947 : real(r8), parameter :: losscf3c1 = -22713._r8
948 :
949 : real(r8), parameter :: f1Ted1C1 = 2.82E-17_r8
950 : real(r8), parameter :: f1Ted1C2 = 3.41E-21_r8
951 :
952 : real(r8), parameter :: f1Ted2C1 = 2.2E-16_r8
953 : real(r8), parameter :: f1Ted2C2 = 7.92E-18_r8
954 :
955 : real(r8), parameter :: f1Ted3C1 = 1.1E-16_r8
956 : real(r8), parameter :: f1Ted3C2 = 5.7E-4_r8
957 :
958 : real(r8) :: wrk1 ! 2/3/kboltz_ev
959 : real(r8) :: FeDB ! B term of electron heat flux of UB
960 : real(r8) :: FeD ! Day time flux
961 : real(r8) :: FeN ! Night time flux
962 : real(r8) :: f1Ted1 ! d1 of f1(Te) calculation used to get electron conductivity
963 : real(r8) :: f1Ted2 ! d2 of f1(Te) calculation used to get electron conductivity
964 : real(r8) :: f1Ted3 ! d3 of f1(Te) calculation used to get electron conductivity
965 : real(r8) :: f1Te
966 :
967 21888 : real(r8), dimension(:,:), pointer :: pMid ! Midpoint pressure (Pa)
968 21888 : real(r8), dimension(:,:), pointer :: tN ! Neutral temperature (K)
969 : real(r8), dimension(pcols,pver) :: tEPrevI ! Electron temperature from previous iteration (K)
970 :
971 21888 : real(r8), dimension(:,:), pointer :: pInt ! Interface pressure (Pa)
972 21888 : real(r8), dimension(:,:), pointer :: tNInt ! Interface Temperture (K)
973 21888 : real(r8), dimension(:,:), pointer :: rairvi ! Constituent dependent gas constant on interface levels
974 :
975 21888 : real(r8), dimension(:,:), pointer :: ndensN2 ! N2 number density (cm-3)
976 21888 : real(r8), dimension(:,:), pointer :: ndensO2 ! O2 number density (cm-3)
977 21888 : real(r8), dimension(:,:), pointer :: ndensO1 ! O number density (cm-3)
978 21888 : real(r8), dimension(:,:), pointer :: ndensE ! E electron number density (cm-3)
979 21888 : real(r8), dimension(:,:), pointer :: ndensOp ! O plus number density (cm-3)
980 21888 : real(r8), dimension(:,:), pointer :: ndensO2p ! O2 plus ion number density (cm-3)
981 21888 : real(r8), dimension(:,:), pointer :: ndensNOp ! NO plus ion number density (cm-3)
982 :
983 21888 : real(r8), dimension(:,:), pointer :: sourceg4 ! g4 source term for electron/ion temperature update
984 :
985 21888 : real(r8), dimension(:,:), pointer :: dipMag ! dip angle for each column (radians)
986 : real(r8), dimension(pcols) :: dlatm ! magnetic latitude of each phys column (degrees)
987 :
988 21888 : real(r8), dimension(:), pointer :: zenAngD ! zenith angle (degrees)
989 :
990 : real(r8), dimension(pcols) :: FeUB ! electron heat flux at upper boundary
991 :
992 : real(r8), dimension(pver) :: sqrtTE ! Square root of electron temperature
993 :
994 : real(r8), dimension(pver) :: Ke ! electron conductivity
995 :
996 : real(r8), dimension(pverp) :: Kei ! electron conductivity interface levels
997 :
998 : real(r8), dimension(pcols,pver) :: lossc4p ! c4 prime of Lc(eN2) component of loss term
999 : real(r8), dimension(pcols,pver) :: lossceN2 ! Lc(eN2) component of loss term equation
1000 :
1001 : real(r8), dimension(pcols,pver) :: lossc6p ! c6 prime of Lc(eO2) component of loss term equation
1002 : real(r8), dimension(pcols,pver) :: lossceO2 ! Lc(eO2) component of loss term equation
1003 :
1004 : real(r8), dimension(pcols,pver) :: lossc8p ! c8 prime of Lc(eO) component of loss term equation
1005 : real(r8), dimension(pcols,pver) :: lossceO1 ! Lc(eO) component of loss term equation
1006 :
1007 : real(r8), dimension(pcols,pver) :: lossc10p ! c10 prime of Lc(eN2) component of loss term equation
1008 : real(r8), dimension(pcols,pver) :: losscA ! A of Lc(eN2)v component of loss term equation
1009 : real(r8), dimension(pcols,pver) :: tENDiff ! Difference between electron and neutral temperatures
1010 : real(r8), dimension(pcols,pver) :: lossceN2v ! Lc(eN2)v component of loss term equation
1011 :
1012 : real(r8), dimension(pcols,pver) :: lossc11p ! c11 prime of Lc(eO2)v component of loss term equation
1013 : real(r8), dimension(pcols,pver) :: lossceO2v ! Lc(eO2)v component of loss term equation
1014 :
1015 : real(r8), dimension(pcols,pver) :: lossc12p ! c12 prime of Lc(eO)f component of loss term equation
1016 : real(r8), dimension(pcols,pver) :: lossceOf ! Lc(eO)f component of loss term equation
1017 :
1018 : real(r8), dimension(pcols,pver) :: lossc14p ! c14 prime of Lc(eO)1D component of loss term equation
1019 : real(r8), dimension(pcols,pver) :: losscf2d ! d of f2 of Lc(eO)1D component of loss term equation
1020 : real(r8), dimension(pcols,pver) :: losscf2 ! f2 of Lc(eO)1D component of loss term equation
1021 : real(r8), dimension(pcols,pver) :: losscf3 ! f3 of Lc(eO)1D component of loss term equation
1022 : real(r8), dimension(pcols,pver) :: lossceO1D ! Lc(eO)1D component of loss term equation
1023 :
1024 : real(r8), dimension(pcols,pver) :: lossc15p ! c15 prime of Lc(eN2)Rot component of loss term equation
1025 : real(r8), dimension(pcols,pver) :: lossceN2Rot ! Lc(eN2)Rot component of loss term equation
1026 :
1027 : real(r8), dimension(pcols,pver) :: lossc16p ! c16 prime of Lc(eO2)Rot component of loss term equation
1028 : real(r8), dimension(pcols,pver) :: lossceO2Rot ! Lc(eO2)Rot component of loss term equation
1029 :
1030 : real(r8), dimension(pcols,pver) :: lossc3p ! c3 prime of Lc(ei) component of loss term equation
1031 : real(r8), dimension(pcols,pver) :: losscei ! Lc(ei) component of loss term equation
1032 : real(r8), dimension(pcols,pver) :: losscin ! ion-neutral heating coeff.
1033 :
1034 : real(r8), dimension(pcols,pver) :: lossg3 ! g3 loss term for Te tendency
1035 :
1036 : real(r8), dimension(pcols,pverp) :: delZi ! Delta z: interfaces
1037 : real(r8), dimension(pcols,pver) :: delZ ! Delta z: midpoints
1038 :
1039 : real(r8), dimension(pcols,pver) :: qjoule ! joule heating
1040 : real(r8), dimension(pcols,pver) :: qen ! electron-neutral heating (units: ev/g/s)
1041 : real(r8), dimension(pcols,pver) :: qei ! electron-ion Coulomb heating (units: ev/g/s)
1042 : real(r8), dimension(pcols,pver) :: qin ! ion-neutral heating (units: ev/g/s)
1043 : real(r8), dimension(pcols,pver) :: rho ! mass density
1044 :
1045 : real(r8), dimension(pcols,pver) :: wrk2
1046 :
1047 43776 : real(r8), dimension(teTiBot) :: subdiag ! subdiagonal values for Te tendency solving
1048 43776 : real(r8), dimension(teTiBot) :: superdiag ! superdiagonal values for Te tendency solving
1049 43776 : real(r8), dimension(teTiBot) :: diag ! diagonal values for Te tendency solving
1050 43776 : real(r8), dimension(teTiBot) :: rHS ! RHS of electron temperature update
1051 43776 : real(r8), dimension(teTiBot) :: tETemp ! temporary electron temperature array for input to tridag
1052 :
1053 : logical, dimension(pcols) :: colConv ! flag for column converging
1054 : logical :: converged ! Flag for convergence in electron temperature
1055 : ! calculation iteration loop
1056 : real(r8) :: qrate(pcols,pver) ! heating rate diagnostic
1057 :
1058 : !---------------------------------------------------------------------------------------------------------
1059 : ! Initialize arrays to zero and column convergence logical to .false.
1060 : !---------------------------------------------------------------------------------------------------------
1061 :
1062 21888 : sqrtTE(:) = 0._r8
1063 21888 : Ke(:) = 0._r8
1064 21888 : Kei(:) = 0._r8
1065 21888 : lossc4p(:,:) = 0._r8
1066 21888 : lossceN2(:,:) = 0._r8
1067 21888 : lossc6p(:,:) = 0._r8
1068 21888 : lossceO2(:,:) = 0._r8
1069 21888 : lossc8p(:,:) = 0._r8
1070 21888 : lossceO1(:,:) = 0._r8
1071 21888 : lossc10p(:,:) = 0._r8
1072 21888 : losscA(:,:) = 0._r8
1073 21888 : tENDiff(:,:) = 0._r8
1074 21888 : lossceN2v(:,:) = 0._r8
1075 21888 : lossc11p(:,:) = 0._r8
1076 21888 : lossceO2v(:,:) = 0._r8
1077 21888 : lossc12p(:,:) = 0._r8
1078 21888 : lossceOf(:,:) = 0._r8
1079 21888 : lossc14p(:,:) = 0._r8
1080 21888 : losscf2d(:,:) = 0._r8
1081 21888 : losscf2(:,:) = 0._r8
1082 21888 : losscf3(:,:) = 0._r8
1083 21888 : lossceO1D(:,:) = 0._r8
1084 21888 : lossc15p(:,:) = 0._r8
1085 21888 : lossceN2Rot(:,:) = 0._r8
1086 21888 : lossc16p(:,:) = 0._r8
1087 21888 : lossceO2Rot(:,:) = 0._r8
1088 21888 : lossc3p(:,:) = 0._r8
1089 21888 : losscei(:,:) = 0._r8
1090 21888 : losscin(:,:) = 0._r8
1091 21888 : lossg3(:,:) = 0._r8
1092 21888 : delZi(:,:) = 0._r8
1093 21888 : delZ(:,:) = 0._r8
1094 1860480 : subDiag(:) = 0._r8
1095 1860480 : superDiag(:) = 0._r8
1096 1860480 : diag(:) = 0._r8
1097 1860480 : rHS(:) = 0._r8
1098 1860480 : teTemp(:) = 0._r8
1099 21888 : qjoule(:,:) = 0._r8
1100 21888 : qei(:,:) = 0._r8
1101 21888 : qen(:,:) = 0._r8
1102 21888 : qin(:,:) = 0._r8
1103 21888 : rho(:,:) = 0._r8
1104 21888 : dSETendOut = 0._r8
1105 21888 : colConv(:) = .false.
1106 :
1107 : !--------------------------------------------------------------------------------------
1108 : ! Get lchnk and ncol from state
1109 : !--------------------------------------------------------------------------------------
1110 21888 : lchnk = state%lchnk
1111 21888 : ncol = state%ncol
1112 :
1113 : !-------------------------------------------
1114 : ! Calculate some commonly used variables
1115 : !-------------------------------------------
1116 21888 : wrk1 = 2._r8 / 3._r8/ kboltz_ev
1117 21888 : teTiBotP = teTiBot + 1
1118 :
1119 : !-------------------------------------------------------------------------------------------------------
1120 : ! Need to get midpoint and interface pressure and neutral temperature from state structure (ncol,teTiBot)
1121 : !-------------------------------------------------------------------------------------------------------
1122 21888 : pMid => state%pmid(1:ncol,1:pver)
1123 21888 : tN => state%t(1:ncol,1:pver)
1124 37012608 : rho(1:ncol,1:pver) = pMid(1:ncol,1:pver)/rairv(1:ncol,1:pver,lchnk)/tN(1:ncol,1:pver) * 1.E-3_r8 ! convert to g/cm3
1125 :
1126 23923584 : qjoule(1:ncol,1:teTiBot) = dSETendIn(1:ncol,1:teTiBot) * sToQConv ! convert from J/kg/s to ev/g/s
1127 :
1128 21888 : pInt => state%pint(1:ncol,1:pverp)
1129 21888 : tNInt => istate%tNInt(1:ncol,1:pverp)
1130 21888 : rairvi => istate%rairvi(1:ncol,1:pverp)
1131 :
1132 : !----------------------------------------------------------------
1133 : ! Get variables needed from the ionosphere state structure
1134 : !----------------------------------------------------------------
1135 21888 : ndensO2 => istate%ndensO2(1:ncol,1:pver)
1136 21888 : ndensO1 => istate%ndensO1(1:ncol,1:pver)
1137 21888 : ndensE => istate%ndensE(1:ncol,1:pver)
1138 21888 : ndensOp => istate%ndensOp(1:ncol,1:pver)
1139 21888 : ndensO2p => istate%ndensO2p(1:ncol,1:pver)
1140 21888 : ndensNOp => istate%ndensNOp(1:ncol,1:pver)
1141 21888 : ndensN2 => istate%ndensN2(1:ncol,1:pver)
1142 :
1143 21888 : sourceg4 => istate%sourceg4(1:ncol,1:pver)
1144 :
1145 21888 : dipMag => istate%dipMag(1:ncol,1:pver)
1146 :
1147 21888 : zenAngD => istate%zenAngD(1:ncol)
1148 :
1149 : !-------------------------------------------------------------------------------------------------------------------
1150 : ! Set electron temperature limits
1151 : !-------------------------------------------------------------------------------------------------------------------
1152 74003328 : tE(1:ncol,1:pver) = MAX(tN(1:ncol,1:pver),tE(1:ncol,1:pver))
1153 37012608 : tE(1:ncol,1:pver) = MIN(temax,tE(1:ncol,1:pver))
1154 :
1155 74003328 : tI(1:ncol,1:pver) = MAX(tN(1:ncol,1:pver),ti(1:ncol,1:pver))
1156 74003328 : tI(1:ncol,1:pver) = MIN(ti(1:ncol,1:pver),tE(1:ncol,1:pver))
1157 :
1158 : ! set Te and Ti to Tn below the levels where this module applies
1159 26199936 : tE(1:ncol,teTiBotP:pver) = tN(1:ncol,teTiBotP:pver)
1160 26199936 : tI(1:ncol,teTiBotP:pver) = tN(1:ncol,teTiBotP:pver)
1161 :
1162 23923584 : wrk2(1:ncol,1:teTiBot) = ndensE(1:ncol,1:teTiBot)/wrk1/(SIN(dipMag(1:ncol,1:teTiBot)))**2._r8
1163 :
1164 284544 : dlatm(:ncol) = rads2Degs* alatm(:ncol,lchnk)
1165 :
1166 : !-----------------------------------------------------------------------------
1167 : ! Get terms needed for loss term g3 for electron temperature update which do
1168 : ! not need to be updated in iteration loop.
1169 : !-----------------------------------------------------------------------------
1170 284544 : do iCol = 1, ncol
1171 :
1172 284544 : if (.not. colConv(iCol)) then
1173 22325760 : do iVer = 1, teTiBot
1174 :
1175 22063104 : lossc4p(iCol,iVer) = lossc4pCoef * ndensN2(iCol,iVer) * ndensE(iCol,iVer) ! e-N2 elastic collision
1176 22063104 : lossc6p(iCol,iVer) = lossc6pCoef * ndensO2(iCol,iVer) * ndensE(iCol,iVer) ! e-O2 elastic collision
1177 22063104 : lossc8p(iCol,iVer) = lossc8pCoef * ndensO1(iCol,iVer) * ndensE(iCol,iVer) ! e-O elastic collision
1178 22063104 : lossc10p(iCol,iVer) = lossc10pCoef * ndensN2(iCol,iVer) * ndensE(iCol,iVer) ! e-N2(vib)
1179 22063104 : lossc11p(iCol,iVer) = lossc11pCoef * ndensO2(iCol,iVer) * ndensE(iCol,iVer) ! e-O2(vib)
1180 22063104 : lossc12p(iCol,iVer) = lossc12pCoef * ndensO1(iCol,iVer) * ndensE(iCol,iVer) ! e-O (fine)
1181 22063104 : lossc14p(iCol,iVer) = lossc14pCoef * ndensO1(iCol,iVer) * ndensE(iCol,iVer) ! e-O(1D)
1182 22063104 : lossc15p(iCol,iVer) = lossc15pCoef * ndensN2(iCol,iVer) * ndensE(iCol,iVer) ! e-N2(rot)
1183 22063104 : lossc16p(iCol,iVer) = lossc16pCoef * ndensO2(iCol,iVer) * ndensE(iCol,iVer) ! e-O2(rot)
1184 : lossc3p(iCol,iVer) = lossc3pC1 * lossc3pC2 * (ndensOP(iCol,iVer) + &
1185 22063104 : 0.5_r8 * ndensO2P(iCol,iVer) + lossc3pC3 * ndensNOP(iCol,iVer)) * ndensE(iCol,iVer) ! e-i
1186 :
1187 : losscin(iCol,iVer) = (losscinCoef1*ndensN2(iCol,iVer) + losscinCoef2*ndensO2(iCol,iVer) &
1188 : + losscinCoef3*ndensO1(iCol,iVer)*SQRT(2._r8*tN(iCol,iVer)))*ndensOP(iCol,iVer) &
1189 : +(losscinCoef4*ndensN2(iCol,iVer) + losscinCoef5*ndensO2(iCol,iVer) &
1190 : + losscinCoef6*ndensO1(iCol,iVer))*ndensNOP(iCol,iVer) &
1191 : +(losscinCoef7*ndensN2(iCol,iVer) + losscinCoef8*ndensO2(iCol,iVer)*SQRT(tN(iCol,iVer)) &
1192 22325760 : + losscinCoef9*ndensO1(iCol,iVer)) * ndensO2P(iCol,iVer)
1193 :
1194 :
1195 : enddo !iVer loop
1196 :
1197 : !----------------------------------------------------------------------------------
1198 : ! Calculate upper boundary heat flux
1199 : !----------------------------------------------------------------------------------
1200 262656 : if (ABS(dlatm(iCol)) < 10.0_r8) then
1201 : FeDB = 0._r8
1202 234555 : else if (ABS(dlatm(iCol)) >= 10.0_r8 .and. ABS(dlatm(iCol)) < 40.0_r8) then
1203 87400 : FeDB = 0.5_r8 * (1._r8 + COS(pi * (ABS(dlatm(iCol)) - 40.0_r8) /30.0_r8))
1204 : else
1205 : FeDB = 1._r8
1206 : end if
1207 :
1208 262656 : FeD = FeDCoef1 * f107 * FeDB
1209 262656 : FeN = .5_r8 * FeD
1210 : !---------------------------------------------------
1211 : ! Set upper boundary condition for right hand side
1212 : !---------------------------------------------------
1213 262656 : if (zenAngD(iCol) <= 80.0_r8) FeUB(iCol) = FeD
1214 262656 : if (zenAngD(iCol) > 80.0_r8 .AND. zenAngD(iCol) < 100.0_r8) FeUB(iCol) = 0.5_r8 * (FeD + FeN) &
1215 : + 0.5_r8 * (FeD - FeN) * &
1216 44172 : COS(pi * ((zenAngD(iCol) - 80.0_r8) / 20.0_r8))
1217 262656 : if (zenAngD(iCol) >= 100.0_r8) FeUB(iCol) = FeN
1218 :
1219 : !------------------------------------------------------------------------------------------
1220 : ! Calculate thickness terms for vertical derivative
1221 : !------------------------------------------------------------------------------------------
1222 22325760 : do iVer = 1, teTiBot
1223 :
1224 44126208 : delZ(iCol,iVer) = (pInt(iCol,iVer+1) - pInt(iCol,iVer)) * rairv(iCol,iVer,lchnk) * &
1225 66451968 : tN(iCol,iVer) / pMid(iCol,iVer) / gravit
1226 :
1227 : enddo
1228 :
1229 22325760 : do iVer = 2, teTiBotP ! Assuming teTiBotP < pverp
1230 44126208 : delZi(iCol,iVer) = (pMid(iCol,iVer) - pMid(iCol,iVer-1)) * rairvi(iCol,iVer) * &
1231 66451968 : tNInt(iCol,iVer) / pInt(iCol,iVer) / gravit
1232 : enddo
1233 262656 : delZi(iCol,1) = 1.5_r8*delZi(iCol,2) - .5_r8*delZi(iCol,3)
1234 :
1235 : !----------------------------------------------------------
1236 : ! Convert delZ variables from meters to centimeters
1237 : !----------------------------------------------------------
1238 22588416 : delZi(iCol,1:teTiBotP) = delZi(iCol,1:teTiBotP)*100._r8
1239 22325760 : delZ(iCol,1:teTiBot) = delZ(iCol,1:teTiBot)*100._r8
1240 :
1241 : endif ! Column not converged
1242 :
1243 : enddo !iCol loop
1244 :
1245 : !-------------------------------------------------------------------------------------------------------
1246 : ! Iterate to calculate new electron temperature.
1247 : ! Time splitting is used: first solve the heating/cooling equation, then solve the diffusion equations.
1248 : ! Also, set convergence flag to false and iterate until true or 6 iterations, whichever comes first
1249 : !-------------------------------------------------------------------------------------------------------
1250 : converged = .false.
1251 : iter = 0
1252 48746 : do while (.not. converged .and. iter < maxIter)
1253 :
1254 : !--------------------------------------------------------------------------------------------------------
1255 : ! Increment iteration loop counter and save electron temperature from previous iteration for convergence
1256 : ! test at end of interation loop. Also, take square root of electron temperature to be used later
1257 : !--------------------------------------------------------------------------------------------------------
1258 26858 : iter = iter + 1
1259 :
1260 29355794 : tEPrevI(1:ncol,1:teTiBot) = tE(1:ncol,1:teTiBot)
1261 :
1262 : !--------------------------------------------------------------------------------------------------------
1263 : ! Loop over columns then vertical levels and call tridiagonal solver for each column to get electron
1264 : ! temperature
1265 : !--------------------------------------------------------------------------------------------------------
1266 349154 : do iCol = 1, ncol
1267 :
1268 349154 : if (.not. colConv(iCol)) then
1269 :
1270 23076055 : sqrtTE(1:teTiBot) = SQRT(tE(iCol,1:teTiBot))
1271 :
1272 23076055 : do iVer = 1, teTiBot
1273 :
1274 : !-----------------------------------------------------------------------------
1275 : ! Get loss term g3 for electron temperature update. Need to calculate
1276 : ! constituent dependent loss terms which make up g3
1277 : !-----------------------------------------------------------------------------
1278 22804572 : lossceN2(iCol,iVer) = lossc4p(iCol,iVer) * (1._r8 - lossc5 * tE(iCol,iVer)) * tE(iCol,iVer)
1279 22804572 : lossceO2(iCol,iVer) = lossc6p(iCol,iVer) * (1._r8 + lossc7 * sqrtTE(iVer)) * sqrtTE(iVer)
1280 22804572 : lossceO1(iCol,iVer) = lossc8p(iCol,iVer) * (1._r8 + lossc9 * tE(iCol,iVer)) * sqrtTE(iVer)
1281 :
1282 22804572 : if (tE(iCol,iVer) < 1000.0_r8) then
1283 13353163 : losscA(iCol,iVer) = losscACoef1 * EXP(losscACoef2 / tE(iCol,iVer))
1284 : endif
1285 22804572 : if (tE(iCol,iVer) >= 1000.0_r8 .AND. tE(iCol,iVer) <= 2000.0_r8) then
1286 5700289 : losscA(iCol,iVer) = losscACoef3 * EXP(losscACoef4 / tE(iCol,iVer))
1287 : endif
1288 22804572 : if (tE(iCol,iVer) > 2000.0_r8) then
1289 3751120 : losscA(iCol,iVer) = losscACoef5 * sqrtTE(iVer) * EXP(losscACoef6 / tE(iCol,iVer))
1290 : endif
1291 :
1292 22804572 : tENDiff(iCol,iVer) = tE(iCol,iVer) - tN(iCol,iVer)
1293 22804572 : if (ABS(tENDiff(iCol,iVer)) < 0.1_r8) tENDiff(iCol,iVer) = 0.1_r8
1294 :
1295 : lossceN2v(iCol,iVer) = lossc10p(iCol,iVer) * losscA(iCol,iVer) * &
1296 22804572 : (1._r8 - EXP(loss10pCoef * (1._r8 / tE(iCol,iVer) - 1._r8 / tN(iCol,iVer)))) / tENDiff(iCol,iVer)
1297 22804572 : lossceO2v(iCol,iVer) = lossc11p(iCol,iVer) * tE(iCol,iVer) * tE(iCol,iVer)
1298 : lossceOf(iCol,iVer) = lossc12p(iCol,iVer) * (1._r8 - lossc13 * tE(iCol,iVer)) * &
1299 22804572 : (lossc12pC1 + lossc12pC2 / tE(iCol,iVer)) / tN(iCol,iVer)
1300 : losscf2d(iCol,iVer) = losscf2dC1 + losscf2dC2 * (tE(iCol,iVer) - losscf2dC3) - &
1301 22804572 : losscf2dC4 * (tE(iCol,iVer) - losscf2dC3) * (tE(iCol,iVer) - losscf2dC5)
1302 22804572 : losscf2(iCol,iVer) = losscf2d(iCol,iVer) * (1._r8 / losscf2C1 - 1._r8 / tE(iCol,iVer))
1303 22804572 : losscf3(iCol,iVer) = losscf3c1 * (1._r8 / tN(iCol,iVer) - 1._r8 / tE(iCol,iVer))
1304 : lossceO1D(iCol,iVer) = lossc14p(iCol,iVer) * EXP(losscf2(iCol,iVer)) * &
1305 22804572 : (1._r8 - EXP(losscf3(iCol,iVer))) / tENDiff(iCol,iVer)
1306 22804572 : lossceN2Rot(iCol,iVer) = lossc15p(iCol,iVer) / sqrtTE(iVer)
1307 22804572 : lossceO2Rot(iCol,iVer) = lossc16p(iCol,iVer) / sqrtTE(iVer)
1308 :
1309 22804572 : losscei(iCol,iVer) = lossc3p(iCol,iVer) / tE(iCol,iVer)**1.5_r8
1310 :
1311 : !------------------------------------------------
1312 : ! Loss term: lossg3*tE/sin(I)^2
1313 : !------------------------------------------------
1314 : lossg3(iCol,iVer) = lossceN2(iCol,iVer) + lossceO2(iCol,iVer) + lossceO1(iCol,iVer) + lossceN2v(iCol,iVer) &
1315 : + lossceO2v(iCol,iVer) + lossceOf(iCol,iVer) + lossceO1D(iCol,iVer) &
1316 23076055 : + lossceN2Rot(iCol,iVer) + lossceO2Rot(iCol,iVer)
1317 :
1318 : enddo !iVer loop
1319 :
1320 : endif ! Column not converged
1321 :
1322 : enddo ! End of column loop
1323 :
1324 : !-----------------------------------------------------
1325 : ! Calculate thermal conductivity of electron gas
1326 : !-----------------------------------------------------
1327 349154 : do iCol = 1, ncol
1328 :
1329 349154 : if (.not. colConv(iCol)) then
1330 :
1331 23076055 : sqrtTE(1:teTiBot) = SQRT(tE(iCol,1:teTiBot))
1332 :
1333 23076055 : do iVer = 1, teTiBot
1334 :
1335 22804572 : f1Ted1 = f1Ted1C1 * sqrtTE(iVer) - f1Ted1C2 * tE(iCol,iVer)**1.5_r8
1336 22804572 : f1Ted2 = f1Ted2C1 + f1Ted2C2 * sqrtTE(iVer)
1337 22804572 : f1Ted3 = f1Ted3C1 * (1._r8 + f1Ted3C2 * tE(iCol,iVer))
1338 :
1339 22804572 : f1Te = ndensN2(iCol,iVer) / ndensE(iCol,iVer) * f1Ted1 + ndensO2(iCol,iVer) / &
1340 22804572 : ndensE(iCol,iVer) * f1Ted2 + ndensO1(iCol,iVer) / ndensE(iCol,iVer) * f1Ted3
1341 :
1342 : !-----------------------------------------------------------------------------
1343 : ! Calculate electron conductivity using parameters set in module and f1(Te)
1344 : !-----------------------------------------------------------------------------
1345 23076055 : Ke(iVer) = Kec1 * tE(iCol,iVer)**2.5_r8 / (1._r8 + Kec2 * tE(iCol,iVer)**2._r8 * f1Te)
1346 :
1347 : enddo !iVer loop
1348 :
1349 : !----------------------------------------------------------------------
1350 : ! Get electron conductivity at interface levels to be used later
1351 : !----------------------------------------------------------------------
1352 22804572 : do iVer = 2,teTiBot
1353 22804572 : Kei(iVer) = SQRT(Ke(iVer-1)*Ke(iVer))
1354 : enddo
1355 271483 : Kei(1) = 1.5_r8*Kei(2)-.5_r8*Kei(3)
1356 271483 : Kei(teTiBotP) = 1.5_r8*Kei(teTiBot)-.5_r8*Kei(teTiBot-1)
1357 :
1358 : !------------------------------------------------------------------------------------------------------
1359 : ! Derive subdiagonal, superdiagonal, and diagonal as input to solver for electron temperature tendency
1360 : !------------------------------------------------------------------------------------------------------
1361 22533089 : do iVer = 2, teTiBot-1
1362 22261606 : subDiag(iVer) = -Kei(iVer) / delZi(iCol,iVer) / delZ(iCol,iVer)
1363 22261606 : superDiag(iVer) = -Kei(iVer+1) / delZi(iCol,iVer+1) / delZ(iCol,iVer)
1364 22261606 : diag(iVer) = wrk2(iCol,iVer)/ztodt + (lossg3(iCol,iVer)+losscei(iCol,iVer))/SIN(dipMag(iCol,iVer))**2._r8 &
1365 22261606 : -subDiag(iVer)-superDiag(iVer)
1366 0 : rHS(iVer) = tE(iCol,iVer) * wrk2(iCol,iVer)/ztodt + sourceg4(iCol,iVer)/SIN(dipMag(iCol,iVer))**2._r8 &
1367 22533089 : +(lossg3(iCol,iVer)*tN(iCol,iVer)+losscei(iCol,iVer)*ti(iCol,iVer))/SIN(dipMag(iCol,iVer))**2._r8
1368 : enddo !iVer loop
1369 :
1370 : !-------------------------------------------------------------------------------------
1371 : ! Calculate diagonal, superdiagonal, and right hand side upper boundary values
1372 : !-------------------------------------------------------------------------------------
1373 271483 : superDiag(1) = -Kei(2) / delZi(iCol,2) / delZ(iCol,1)
1374 271483 : diag(1) = wrk2(iCol,1)/ztodt - superDiag(1)
1375 271483 : rHS(1) = tE(iCol,1) * wrk2(iCol,1) / ztodt - FeUB(iCol) / delZ(iCol,1)
1376 :
1377 : !---------------------------------------------------------------------------------------------
1378 : ! Calculate subdiagonal, diagonal, superdiagonal, and right hand side lower boundary values
1379 : !---------------------------------------------------------------------------------------------
1380 271483 : subDiag(teTiBot) = -Kei(teTiBot) / delZi(iCol,teTiBot) / delZ(iCol,teTiBot)
1381 271483 : superDiag(teTiBot) = -Kei(teTiBotP) / delZi(iCol,teTiBotP) / delZ(iCol,teTiBot)
1382 : diag(teTiBot) = wrk2(iCol,teTiBot)/ztodt &
1383 271483 : + (lossg3(iCol,teTiBot)+losscei(iCol,teTiBot))/SIN(dipMag(iCol,teTiBot))**2._r8 &
1384 271483 : - subDiag(teTiBot)-superDiag(teTiBot)
1385 0 : rHS(teTiBot) = tE(iCol,teTiBot) * wrk2(iCol,teTiBot)/ztodt &
1386 : + sourceg4(iCol,teTiBot)/SIN(dipMag(iCol,teTiBot))**2._r8 &
1387 0 : + (lossg3(iCol,teTiBot)*tN(iCol,teTiBot)+losscei(iCol,teTiBot)*ti(iCol,teTiBot)) &
1388 271483 : / SIN(dipMag(iCol,teTiBot))**2._r8 - superDiag(teTiBot) * tN(iCol,teTiBotP)
1389 :
1390 : !-------------------------------------------------
1391 : ! Call solver to get electron temperature update
1392 : !-------------------------------------------------
1393 271483 : call tridag(subDiag,diag,superDiag,rHS,tETemp,teTiBot)
1394 :
1395 23076055 : tE(iCol,1:teTiBot) = tETemp(1:teTiBot)
1396 23076055 : do iVer = 1,teTiBot
1397 22804572 : tE(iCol,iVer) = min(temax,tE(iCol,iVer))
1398 23076055 : tE(iCol,iVer) = max(tN(iCol,iVer),tE(iCol,iVer))
1399 : enddo
1400 :
1401 : !---------------------------------------------------------------------------------------------------------
1402 : ! Calculate ion temperature from electron temperature, ion-neutral and electron-ion loss terms, neutral
1403 : ! temperature, mass density and joule heating. Set minimum value to neutral temperature and maximum
1404 : ! value to electron temperature for each column and vertical level
1405 : !---------------------------------------------------------------------------------------------------------
1406 23076055 : do iVer = 1,teTiBot
1407 45609144 : ti(iCol,iVer) = (losscei(iCol,iVer) * tE(iCol,iVer) + losscin(iCol,iVer) * tN(iCol,iVer) + &
1408 0 : rho(iCol,iVer) * qjoule(iCol,iVer) * mbarv(iCol,iVer,lchnk) / (mbarv(iCol,iVer,lchnk)+rMassOp)) / &
1409 68413716 : (losscei(iCol,iVer) + losscin(iCol,iVer))
1410 22804572 : ti(iCol,iVer) = max(tN(iCol,iVer),ti(iCol,iVer))
1411 23076055 : ti(iCol,iVer) = min(tE(iCol,iVer),ti(iCol,iVer))
1412 : enddo
1413 :
1414 : !--------------------------------------------------------------------------------------------------------
1415 : ! Check for convergence which is a change of electron temperature ratio to previous loop for all levels
1416 : ! and columns of less than 0.05K. Had to modify this to do convergence check on each column since
1417 : ! checking all columns in a chunk gives different answers depending on number of tasks and tasks per node.
1418 : !--------------------------------------------------------------------------------------------------------
1419 22393028 : if (ALL(ABS(tE(iCol,1:teTiBot) / tEPrevI(iCol,1:teTiBot) - 1._r8) < 0.05_r8)) then
1420 :
1421 262656 : colConv(iCol) = .true.
1422 :
1423 : endif
1424 :
1425 : endif ! Column not converged
1426 :
1427 : enddo ! iCol loop
1428 : !--------------------------------------------------------------
1429 : ! Check to see if all columns have converged and set flag
1430 : !--------------------------------------------------------------
1431 349368 : if (ALL(colConv(1:ncol))) converged = .true.
1432 :
1433 : enddo ! End of iteration loop
1434 :
1435 : !--------------------------------------------------------------------------------------------------------
1436 : ! Calculate electron-neutral heating and electron-ion Coulomb heating. Then update dry static energy.
1437 : !--------------------------------------------------------------------------------------------------------
1438 1860480 : do iVer = 1, teTiBot
1439 23923584 : do iCol = 1, ncol
1440 22063104 : sqrtTE(iVer) = SQRT(tE(iCol,iVer))
1441 22063104 : lossceN2(iCol,iVer) = lossc4p(iCol,iVer) * (1._r8 - lossc5 * tE(iCol,iVer)) * tE(iCol,iVer)
1442 22063104 : lossceO2(iCol,iVer) = lossc6p(iCol,iVer) * (1._r8 + lossc7 * sqrtTE(iVer)) * sqrtTE(iVer)
1443 23901696 : lossceO1(iCol,iVer) = lossc8p(iCol,iVer) * (1._r8 + lossc9 * tE(iCol,iVer)) * sqrtTE(iVer)
1444 : enddo
1445 : enddo
1446 :
1447 : qen(1:ncol,1:teTiBot) = (lossceN2(1:ncol,1:teTiBot)+lossceO2(1:ncol,1:teTiBot)+lossceO1(1:ncol,1:teTiBot)+ &
1448 : lossceN2v(1:ncol,1:teTiBot)+lossceO2v(1:ncol,1:teTiBot)+lossceOf(1:ncol,1:teTiBot)+ &
1449 : lossceO1D(1:ncol,1:teTiBot)+lossceN2Rot(1:ncol,1:teTiBot)+lossceO2Rot(1:ncol,1:teTiBot))* &
1450 23923584 : (tE(1:ncol,1:teTiBot)-tN(1:ncol,1:teTiBot)) / rho(1:ncol,1:teTiBot)
1451 23923584 : qei(1:ncol,1:teTiBot) = losscei(1:ncol,1:teTiBot) * (tE(1:ncol,1:teTiBot)-ti(1:ncol,1:teTiBot)) / rho(1:ncol,1:teTiBot)
1452 23923584 : qin(1:ncol,1:teTiBot) = losscin(1:ncol,1:teTiBot) * (tI(1:ncol,1:teTiBot)-tN(1:ncol,1:teTiBot)) / rho(1:ncol,1:teTiBot)
1453 :
1454 23923584 : dSETendOut(1:ncol,1:teTiBot) = (qei(1:ncol,1:teTiBot)+qen(1:ncol,1:teTiBot)) / sToQConv ! J/kg/s
1455 :
1456 37012608 : qrate(:ncol,:) = qen(:ncol,:)/sToQConv/cpairv(:ncol,:,lchnk) ! K/s
1457 21888 : call outfld ('QEN', qrate, pcols, lchnk)
1458 :
1459 37012608 : qrate(:ncol,:) = qei(:ncol,:)/sToQConv/cpairv(:ncol,:,lchnk) ! K/s
1460 21888 : call outfld ('QEI', qrate, pcols, lchnk)
1461 :
1462 37012608 : qrate(:ncol,:) = qin(:ncol,:)/sToQConv/cpairv(:ncol,:,lchnk) ! K/s
1463 21888 : call outfld ('QIN', qrate, pcols, lchnk)
1464 :
1465 21888 : return
1466 :
1467 21888 : end subroutine update_teti
1468 :
1469 : !-----------------------------------------------------------------------
1470 : ! Simple tridiagonal solver routine
1471 : !-----------------------------------------------------------------------
1472 :
1473 271483 : SUBROUTINE tridag(a,b,c,r,u,n)
1474 :
1475 : INTEGER,INTENT(IN) :: n
1476 : REAL(r8),INTENT(IN) :: a(n),b(n),c(n),r(n)
1477 : REAL(r8),INTENT(INOUT) :: u(n)
1478 : !------------------------------
1479 : ! Local variables
1480 : !------------------------------
1481 : INTEGER j
1482 542966 : REAL(r8) :: bet,gam(n)
1483 :
1484 271483 : if(b(1).eq.0._r8) call endrun('ion_electron_temp: bt(1)=0 in tridag')
1485 271483 : bet=b(1)
1486 271483 : u(1)=r(1)/bet
1487 22804572 : do j=2,n
1488 22533089 : gam(j)=c(j-1)/bet
1489 22533089 : bet=b(j)-a(j)*gam(j)
1490 22533089 : if(bet.eq.0._r8) call endrun('ion_electron_temp: bet=0 in tridag')
1491 22804572 : u(j)=(r(j)-a(j)*u(j-1))/bet
1492 : end do
1493 :
1494 22804572 : do j=n-1,1,-1
1495 22804572 : u(j)=u(j)-gam(j+1)*u(j+1)
1496 : end do
1497 :
1498 271483 : return
1499 :
1500 : END SUBROUTINE tridag
1501 :
1502 : end module ion_electron_temp
|