UFO
ufo_rttovonedvarcheck_pcemis_mod.f90
Go to the documentation of this file.
1 ! (C) copyright 2020 Met Office
2 !
3 ! this software is licensed under the terms of the apache licence version 2.0
4 ! which can be obtained at http://www.apache.org/licenses/license-2.0.
5 
6 !> Fortran module which contains the methods for Infrared
7 !> Principal Component Emissivity
8 !> The science can be found at:
9 !> Pavelin, E., Candy, B., 2014. Assimilation of surface-sensitive infrared radiances over land:
10 !> Estimation of land surface temperature and emissivity. Q. J. R. Metorol. Soc., 140, 1198-1208.
11 
13 
14 use fckit_log_module, only : fckit_log
15 use kinds
18 
19 implicit none
20 private
21 
22 !< Emissivity eigen vector type definition
24  integer :: nchans
25  integer :: numev
26  integer, pointer :: channels(:) => null()
27  real(kind_real), pointer :: mean(:) => null()
28  real(kind_real), pointer :: pcmin(:) => null()
29  real(kind_real), pointer :: pcmax(:) => null()
30  real(kind_real), pointer :: pcguess(:) => null()
31  real(kind_real), pointer :: ev(:,:) => null()
32  real(kind_real), pointer :: ev_inverse(:,:) => null()
34 
35 !< Emissivity eigen vector atlas type definition
37  integer :: nlat
38  integer :: nlon
39  integer :: npc
40  real(kind_real) :: gridstep
41  real(kind_real), POINTER :: emispc(:,:,:)
43 
44 !< Principal component emissivity type definition
46 
48  type(ufo_rttovonedvarcheck_emisatlas) :: emis_atlas
49  logical :: initialised = .false.
50 
51 contains
52  procedure :: setup => ufo_rttovonedvarcheck_initpcemis
53  procedure :: delete => ufo_rttovonedvarcheck_deletepcemis
54  procedure :: info => ufo_rttovonedvarcheck_printpcemis
55  procedure :: emistopc => ufo_rttovonedvarcheck_emistopc
56  procedure :: pctoemis => ufo_rttovonedvarcheck_pctoemis
57  procedure :: emisktopc => ufo_rttovonedvarcheck_emisktopc
58 
60 
61 contains
62 
63 !-------------------------------------------------------------------------------
64 !> Initialize PC emissivity object
65 !!
66 !! \author Met Office
67 !!
68 !! \date 04/08/2020: Created
69 !!
70 subroutine ufo_rttovonedvarcheck_initpcemis(self, filepath, atlaspath)
71 
72 implicit none
73 
74 ! subroutine arguments:
75 class(ufo_rttovonedvarcheck_pcemis), intent(out) :: self !< PC emissivity type
76 character(len=*), intent(in) :: filepath
77 character(len=*), intent(in), optional :: atlaspath
78 
79 character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_InitPCEmis"
80 logical :: file_exists ! Check if a file exists logical
81 integer :: fileunit ! Unit number for reading in files
82 
83 ! Read eigenvector file
84 inquire(file=trim(filepath), exist=file_exists)
85 if (file_exists) then
86  fileunit = ufo_utils_iogetfreeunit()
87  open(unit = fileunit, file = trim(filepath))
88  call ufo_rttovonedvarcheck_getemiseigenvec(self, fileunit)
89  close(unit = fileunit)
90  call fckit_log % info("rttovonedvarcheck EmisEigenVec file exists and read in")
91 else
92  call abor1_ftn("rttovonedvarcheck EmisEigenVec file not found: aborting")
93 end if
94 
95 self % initialised = .true.
96 
97 ! Read in emissivity atlas if file path present -
98 ! if not a first guess will be used from eigenvector file
99 if (present(atlaspath)) then
100  inquire(file=trim(atlaspath), exist=file_exists)
101  if (file_exists) then
102  fileunit = ufo_utils_iogetfreeunit()
103  open(unit = fileunit, file = trim(filepath))
104  call ufo_rttovonedvarcheck_getemisatlas(self, fileunit)
105  close(unit = fileunit)
106  call fckit_log % info("rttovonedvarcheck Emis Atlas file exists and read in")
107  else
108  call abor1_ftn("rttovonedvarcheck Emis Atlas file not found but requested: aborting")
109  end if
110 end if
111 
112 call self % info()
113 
115 
116 !-------------------------------------------------------------------------------
117 !> Read the emissivity eigen vector from file
118 !!
119 !! Heritage: Ops_SatRad_GetEmisEigenVec
120 !!
121 !! \author Met Office
122 !!
123 !! \date 04/08/2020: Created
124 !!
126  fileunit )
127 
128 implicit none
129 
130 ! Subroutine arguments:
131 type(ufo_rttovonedvarcheck_pcemis), intent(out) :: self !< PC emissivity type
132 integer, intent(in) :: fileunit
133 
134 ! Local declarations:
135 character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_GetEmisEigenVec"
136 integer :: eigversion
137 integer :: i
138 integer :: readstatus
139 character(len=max_string) :: message
140 
141 !----------------------------------------------
142 ! 1. Read header information and allocate arrays
143 !----------------------------------------------
144 
145 read(fileunit, *, iostat = readstatus) eigversion, self % emis_eigen % Nchans, &
146  self % emis_eigen % NumEV
147 
148 allocate (self % emis_eigen % Channels( self % emis_eigen % Nchans))
149 allocate (self % emis_eigen % Mean( self % emis_eigen % Nchans))
150 allocate (self % emis_eigen % PCmin( self % emis_eigen % NumEV))
151 allocate (self % emis_eigen % PCmax( self % emis_eigen % NumEV))
152 allocate (self % emis_eigen % PCguess( self % emis_eigen % NumEV))
153 allocate (self % emis_eigen % EV( self % emis_eigen % NumEV, self % emis_eigen % Nchans))
154 if (eigversion >= 2) then
155  allocate (self % emis_eigen % EV_Inverse( self % emis_eigen % Nchans, self % emis_eigen % NumEV))
156 end if
157 
158 !--------------------------------------------------------
159 ! 2. Read the channels, mean emissivities and eigenvectors
160 !--------------------------------------------------------
161 
162 read (fileunit, *, iostat = readstatus) self % emis_eigen % Channels(:)
163 read (fileunit, *, iostat = readstatus) self % emis_eigen % Mean(:)
164 read (fileunit, *, iostat = readstatus) self % emis_eigen % PCmin(:)
165 read (fileunit, *, iostat = readstatus) self % emis_eigen % PCmax(:)
166 read (fileunit, *, iostat = readstatus) self % emis_eigen % PCguess(:)
167 do i = 1, self % emis_eigen % NumEV
168  read (fileunit, *, iostat = readstatus) self % emis_eigen % EV(i,:)
169 end do
170 
171 ! Has there been an error in the read?
172 if (readstatus /= 0) then
173  write(message,*) routinename, &
174  'Problem reading in emis eigenvectors - please check the file '
175  call abor1_ftn(message)
176 end if
177 
178 if (eigversion >= 2) then
179  do i = 1, self % emis_eigen % Nchans
180  read (fileunit, *, iostat = readstatus) self % emis_eigen % EV_Inverse(i,:)
181  end do
182  ! Has there been an error in the read?
183  if (readstatus /= 0) then
184  write(message,*) routinename, &
185  'Problem reading in inverse emis eigenvectors - please check the file '
186  call abor1_ftn(message)
187  end if
188 end if
189 
190 write(*, '(A,I0,A,I0,A)') 'Finished reading ',self % emis_eigen % NumEV, &
191  ' emissivity eigenvectors on ', &
192  self % emis_eigen % Nchans,' channels.'
193 
195 
196 !-------------------------------------------------------------------------------
197 !> Read the emissivity eigen atlas from file
198 !!
199 !! Heritage: Ops_SatRad_GetEmisAtlas
200 !!
201 !! \author Met Office
202 !!
203 !! \date 16/09/2020: Created
204 !!
205 subroutine ufo_rttovonedvarcheck_getemisatlas (self, fileunit)
206 
207 implicit none
208 
209 ! Subroutine arguments:
210 type(ufo_rttovonedvarcheck_pcemis), intent(out) :: self !< PC emissivity type
211 integer, intent(in) :: fileunit
212 
213 ! Local declarations:
214 character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_GetEmisAtlas"
215 character(len=max_string) :: message
216 integer :: readstatus
217 integer :: i
218 integer :: j
219 
220 !----------------------------------------------
221 ! 1. Read header information and allocate arrays
222 !----------------------------------------------
223 
224 read (fileunit, *, iostat = readstatus) self % emis_atlas % Nlat, &
225  self % emis_atlas % Nlon, &
226  self % emis_atlas % Npc, &
227  self % emis_atlas % gridstep
228 
229 allocate (self % emis_atlas % EmisPC(self % emis_atlas % Nlon, &
230  self % emis_atlas % Nlat, &
231  self % emis_atlas % Npc))
232 
233 !--------------------------------------------------------
234 ! 2. Read the emissivity PCs
235 !--------------------------------------------------------
236 
237 do i = 1, self % emis_atlas % nlon
238  do j = 1, self % emis_atlas % nlat
239  read (fileunit, '(12F10.6)', iostat = readstatus) self % emis_atlas % EmisPC(i,j,:)
240  end do
241 end do
242 
243 ! Has there been an error in the read?
244 if (readstatus /= 0) then
245  write(message,*) routinename, &
246  'Problem reading in EmisAtlas - please check the file'
247  call abor1_ftn(message)
248 else
249  write (*, '(A,I0,A)') 'Finished reading IR emissivity atlas with ', &
250  self % emis_atlas % Npc, ' principal components.'
251 end if
252 
254 
255 !------------------------------------------------------------------------------
256 !> Delete the PC emissivity object
257 !!
258 !! \details Heritage: Ops_SatRad_KillEmisEigenVec
259 !!
260 !! \author Met Office
261 !!
262 !! \date 04/08/2020: Created
263 !!
264 subroutine ufo_rttovonedvarcheck_deletepcemis(self) ! inout
265 
266 implicit none
267 
268 ! subroutine arguments:
269 class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type
270 
271 character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_DeletePCEmis"
272 
273 self % emis_eigen % NChans = 0
274 self % emis_eigen % NumEV = 0
275 if (associated (self % emis_eigen % Channels)) deallocate (self % emis_eigen % Channels)
276 if (associated (self % emis_eigen % Mean)) deallocate (self % emis_eigen % Mean)
277 if (associated (self % emis_eigen % PCmin)) deallocate (self % emis_eigen % PCmin)
278 if (associated (self % emis_eigen % PCmax)) deallocate (self % emis_eigen % PCmax)
279 if (associated (self % emis_eigen % PCguess)) deallocate (self % emis_eigen % PCguess)
280 if (associated (self % emis_eigen % EV)) deallocate (self % emis_eigen % EV)
281 if (associated (self % emis_eigen % EV_Inverse)) deallocate (self % emis_eigen % EV_Inverse)
282 
284 
285 !------------------------------------------------------------------------------
286 !> Print information about the PC Emissivity object
287 !!
288 !! \author Met Office
289 !!
290 !! \date 04/08/2020: Created
291 !!
293 
294 implicit none
295 
296 ! subroutine arguments:
297 class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type
298 
299 write(*,*) "Printing contents of PC emiss"
300 write(*,*) "emis_eigen % Channels = ",self % emis_eigen % Channels
301 
303 
304 !------------------------------------------------------------------------------
305 !> Convert from spectral emissivity to principal component weights.
306 !! This is used to convert CAMEL emissivities to PC weights for the 1D-Var.
307 !!
308 !! \details Heritage: Ops_SatRad_EmissToPC.f90
309 !!
310 !! \author Met Office
311 !!
312 !! \date 04/08/2020: Created
313 !!
315  Channels, &
316  Emissivity, &
317  PC)
318 
319 implicit none
320 
321 ! Subroutine arguments
322 class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type
323 integer, intent(in) :: Channels(:)
324 real(kind_real), intent(in) :: Emissivity(:)
325 real(kind_real), intent(out) :: PC(:)
326 
327 ! Local declarations:
328 character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_EmisToPC"
329 real(kind_real) :: Temp_Emissivity(size(Emissivity))
330 integer :: npc
331 character(len=max_string) :: message
332 
333 npc = size(pc)
334 
335 if (associated(self % emis_eigen % EV_Inverse)) then
336 
337  ! Convert from emissivity to sine transform
338  temp_emissivity(:) = asin( 2.0_kind_real * emissivity(:) - 1.0_kind_real )
339 
340  ! Subtract means from transformed emissivities
341  temp_emissivity(:) = temp_emissivity(:) - self % emis_eigen % Mean(channels(:))
342 
343  ! Calculate PC weights from emissivity spectrum
344  pc(1:npc) = matmul(temp_emissivity(:), self % emis_eigen % EV_Inverse(channels(:),1:npc))
345 
346 else
347 
348  write(message, *) routinename, &
349  'Missing inverse eigenvector matrix - cannot convert emissivities to PCs'
350  call abor1_ftn(message)
351 
352 end if
353 
354 end subroutine ufo_rttovonedvarcheck_emistopc
355 
356 !-------------------------------------------------------------------------------
357 !> Transform from principal components to emissivity spectrum.
358 !!
359 !! \details Heritage: Ops_SatRad_PCToEmiss.f90
360 !!
361 !! \author Met Office
362 !!
363 !! \date 04/08/2020: Created
364 !!
366  NumChans, &
367  Channels, &
368  NumPC, &
369  PC, &
370  Emissivity)
371 
372 implicit none
373 
374 ! Subroutine arguments:
375 class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type
376 integer, intent(in) :: NumChans
377 integer, intent(in) :: Channels(NumChans)
378 integer, intent(in) :: NumPC
379 real(kind_real), intent(in) :: PC(NumPC)
380 real(kind_real), intent(out) :: Emissivity(NumChans)
381 
382 ! Local declarations:
383 character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_PCToEmis"
384 real(kind_real) :: BigEOF(NumChans,NumChans)
385 real(kind_real) :: BigPC(NumChans)
386 
387 ! Populate PC array with nchans elements
388 bigpc(:) = 0.0_kind_real
389 bigpc(1:numpc) = pc(:)
390 
391 ! Populate EOF array with nchans elements
392 bigeof(:,:) = 0.0_kind_real
393 bigeof(1:numpc,:) = self % emis_eigen % EV(1:numpc,channels)
394 
395 ! Calculate reconstructed emissivity spectrum
396 emissivity = matmul(bigpc, bigeof)
397 
398 ! Add means (these may have been subtracted off, otherwise they are zero)
399 emissivity(:) = emissivity(:) + self % emis_eigen % Mean(channels)
400 
401 ! Convert from sine transform to physical emissivity
402 emissivity(1:numchans) = 0.5_kind_real * (sin(emissivity(1:numchans)) + 1.0_kind_real)
403 
404 end subroutine ufo_rttovonedvarcheck_pctoemis
405 
406 !-------------------------------------------------------------------------------
407 !> Transform from principal components to emissivity spectrum.
408 !!
409 !! \details Heritage: Ops_SatRad_EmissKToPC.f90
410 !!
411 !! \author Met Office
412 !!
413 !! \date 04/08/2020: Created
414 !!
416  NumChans, &
417  Channels, &
418  NumPC, &
419  Emissivity, &
420  Emissivity_K, &
421  PC_K)
422 
423 implicit none
424 
425 ! Subroutine arguments
426 class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type
427 integer, intent(in) :: NumChans
428 integer, intent(in) :: Channels(NumChans)
429 integer, intent(in) :: NumPC
430 real(kind_real), intent(in) :: Emissivity(NumChans)
431 real(kind_real), intent(in) :: Emissivity_K(NumChans)
432 real(kind_real), intent(out) :: PC_K(NumChans,NumPC)
433 
434 ! Local declarations:
435 character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_EmisKToPC"
436 real(kind_real) :: JEMatrix_element
437 integer :: ichan
438 
439 do ichan = 1, numchans
440  ! Calculate diagonal matrix elements of emissivity Jacobians
441  ! Convert Emissivity_K to sine transform
442  ! cos(asin(2x-1)) === sqrt(1-(1-2x)^2)
443  jematrix_element = emissivity_k(ichan) * 0.5_kind_real* &
444  sqrt(1.0_kind_real - (1.0_kind_real - 2.0_kind_real * emissivity(ichan)) ** 2)
445 
446 ! EOF array === EmisEigenvec % EV for the appropriate channel selection
447 ! N.B. Note 'manual' transposition of matrix here
448  pc_k(ichan,1:numpc) = self % emis_eigen % EV(1:numpc,channels(ichan)) * jematrix_element
449 end do
450 
451 end subroutine ufo_rttovonedvarcheck_emisktopc
452 
453 !-------------------------------------------------------------------------------
454 
Fortran module constants used throughout the rttovonedvarcheck filter.
Fortran module which contains the methods for Infrared Principal Component Emissivity The science can...
subroutine ufo_rttovonedvarcheck_initpcemis(self, filepath, atlaspath)
Initialize PC emissivity object.
subroutine ufo_rttovonedvarcheck_deletepcemis(self)
Delete the PC emissivity object.
subroutine ufo_rttovonedvarcheck_getemisatlas(self, fileunit)
Read the emissivity eigen atlas from file.
subroutine ufo_rttovonedvarcheck_printpcemis(self)
Print information about the PC Emissivity object.
subroutine ufo_rttovonedvarcheck_pctoemis(self, NumChans, Channels, NumPC, PC, Emissivity)
Transform from principal components to emissivity spectrum.
subroutine ufo_rttovonedvarcheck_emisktopc(self, NumChans, Channels, NumPC, Emissivity, Emissivity_K, PC_K)
Transform from principal components to emissivity spectrum.
subroutine ufo_rttovonedvarcheck_getemiseigenvec(self, fileunit)
Read the emissivity eigen vector from file.
subroutine ufo_rttovonedvarcheck_emistopc(self, Channels, Emissivity, PC)
Convert from spectral emissivity to principal component weights. This is used to convert CAMEL emissi...
Fortran module with various useful routines.
integer function, public ufo_utils_iogetfreeunit()
Find a free file unit.