18 use missing_values_mod
19 use fckit_log_module,
only : fckit_log
37 integer :: nlevp, nlevq, nlocs, iflip
38 real(kind_real),
allocatable :: k(:,:)
39 real(kind_real),
allocatable :: dztd_dp(:,:)
40 real(kind_real),
allocatable :: dztd_dq(:,:)
41 logical :: vert_interp_ops
43 real(kind_real) :: min_temp_grad
60 use fckit_configuration_module,
only: fckit_configuration
63 type(fckit_configuration),
intent(in) :: f_conf
64 call f_conf%get_or_die(
"min_temp_grad", self % min_temp_grad)
79 type(c_ptr),
value,
intent(in) :: obss
82 character(len=*),
parameter :: myname_=
"ufo_groundgnss_metoffice_tlad_settraj"
85 character(max_string) :: err_msg
94 real(kind_real),
allocatable :: zStation(:)
95 real(kind_real),
allocatable :: pressure(:)
96 real(kind_real),
allocatable :: humidity(:)
97 real(kind_real),
allocatable :: za(:)
98 real(kind_real),
allocatable :: zb(:)
100 integer,
parameter :: max_string = 800
101 character(max_string) :: message
103 write(err_msg,*)
"TRACE: ufo_groundgnss_metoffice_tlad_settraj: begin"
104 call fckit_log%info(err_msg)
116 self % nlevp = prs % nval
117 self % nlevq = q % nval
118 self % nlocs = obsspace_get_nlocs(obss)
122 IF (prs % vals(1,1)-prs % vals(self%nlevp,1) < 0.0)
THEN
124 WRITE(message, *)
"Pressure is in ascending order. Reorder the variables in vertical direction"
125 CALL fckit_log % warning(message)
130 nobs = obsspace_get_nlocs(obss)
131 allocate(zstation(nobs))
133 call obsspace_get_db(obss,
"MetaData",
"station_height", zstation)
135 nstate = prs % nval + q % nval
136 ALLOCATE(self % K(1:self%nlocs, 1:nstate))
137 ALLOCATE(pressure(1:self%nlevp))
138 ALLOCATE(humidity(1:self%nlevq))
139 ALLOCATE(za(1:self%nlevp))
140 ALLOCATE(zb(1:self%nlevq))
143 obs_loop:
do iobs = 1, self % nlocs
144 IF (self%iflip == 1)
THEN
145 do ilev = 1, self%nlevp
146 pressure(ilev) = prs % vals(self%nlevp-ilev+1,iobs)
147 za(ilev) = rho_heights % vals(self%nlevp-ilev+1,iobs)
149 do ilev = 1, self%nlevq
150 humidity(ilev) = q % vals(self%nlevq-ilev+1,iobs)
151 zb(ilev) = theta_heights % vals(self%nlevq-ilev+1,iobs)
154 pressure = prs % vals(:,iobs)
155 humidity = q % vals(:,iobs)
156 za = rho_heights % vals(:,iobs)
157 zb = theta_heights % vals(:,iobs)
163 humidity(1:self%nlevq), &
164 pressure(1:self%nlevp), &
167 self % min_temp_grad, &
168 self % K(:, 1:nstate))
191 real(kind_real),
intent(inout) :: hofx(:)
192 type(c_ptr),
value,
intent(in) :: obss
195 character(len=*),
parameter :: myname_=
"ufo_groundgnss_metoffice_simobs_tl"
202 character(max_string) :: err_msg
205 real(kind_real),
allocatable :: pressure_d(:)
206 real(kind_real),
allocatable :: humidity_d(:)
207 real(kind_real),
allocatable :: x_d(:)
209 write(err_msg,*)
"TRACE: ufo_groundgnss_metoffice_simobs_tl: begin"
210 call fckit_log%info(err_msg)
213 if (.not. self%ltraj)
then
214 write(err_msg,*) myname_,
' trajectory wasnt set!'
215 call abor1_ftn(err_msg)
219 if (geovals%nlocs /=
size(hofx))
then
220 write(err_msg,*) myname_,
' error: nlocs inconsistent!'
221 call abor1_ftn(err_msg)
230 allocate(x_d(1:prs_d%nval+q_d%nval))
231 allocate(pressure_d(1:self % nlevp))
232 allocate(humidity_d(1:self % nlevq))
237 obs_loop:
do iobs = 1, nlocs
240 do ilev = 1, self % nlevp
241 pressure_d(ilev) = prs_d % vals(self % nlevp-ilev+1,iobs)
243 do ilev = 1, self % nlevq
244 humidity_d(ilev) = q_d % vals(self % nlevq-ilev+1,iobs)
247 pressure_d(1:self % nlevp) = prs_d % vals(:,iobs)
248 humidity_d(1:self % nlevq) = q_d % vals(:,iobs)
251 x_d(1:prs_d%nval) = pressure_d
252 x_d(prs_d%nval+1:prs_d%nval+q_d%nval) = humidity_d
253 hofx(iobs) = sum(self % K(iobs,:) * x_d)
258 deallocate(pressure_d)
259 deallocate(humidity_d)
261 write(err_msg,*)
"TRACE: ufo_groundgnss_metoffice_simobs_tl: complete"
262 call fckit_log%info(err_msg)
276 use typesizes,
only: wp => eightbytereal
283 real(kind_real),
intent(in) :: hofx(:)
284 type(c_ptr),
value,
intent(in) :: obss
287 character(len=*),
parameter :: myname_=
"ufo_groundgnss_metoffice_simobs_ad"
290 real(c_double) :: missing
296 real(kind_real),
allocatable :: x_d(:)
297 real(kind_real),
allocatable :: pressure_d(:)
298 real(kind_real),
allocatable :: humidity_d(:)
299 character(max_string) :: err_msg
301 write(err_msg,*)
"TRACE: ufo_groundgnss_metoffice_simobs_ad: begin"
302 call fckit_log%info(err_msg)
305 if (.not. self%ltraj)
then
306 write(err_msg,*) myname_,
' trajectory wasnt set!'
307 call abor1_ftn(err_msg)
311 if (geovals%nlocs /=
size(hofx))
then
312 write(err_msg,*) myname_,
' error: nlocs inconsistent!'
313 call abor1_ftn(err_msg)
320 missing = missing_value(missing)
321 allocate(x_d(1:prs_d%nval + q_d%nval))
322 allocate(pressure_d(1:prs_d%nval))
323 allocate(humidity_d(1:q_d%nval))
328 obs_loop:
do iobs = 1, self % nlocs
330 if (hofx(iobs) /= missing)
then
331 x_d = self % K(iobs,:) * hofx(iobs)
332 pressure_d(1:prs_d%nval) = x_d(1:prs_d%nval)
333 humidity_d(1:q_d%nval) = x_d(prs_d%nval+1:prs_d%nval+q_d%nval)
337 do ilev = 1, self % nlevp
338 prs_d % vals(self % nlevp-ilev+1,iobs) = pressure_d(ilev)
340 do ilev = 1, self % nlevq
341 q_d % vals(self % nlevq-ilev+1,iobs) = humidity_d(ilev)
344 prs_d % vals(:,iobs) = pressure_d(1:self % nlevp)
345 q_d % vals(:,iobs) = humidity_d(1:self % nlevq)
352 deallocate(pressure_d)
353 deallocate(humidity_d)
355 write(err_msg,*)
"TRACE: ufo_groundgnss_metoffice_simobs_ad: complete"
356 call fckit_log%info(err_msg)
371 character(len=*),
parameter :: myname_=
"ufo_groundgnss_metoffice_tlad_delete"
376 if (
allocated(self%K))
deallocate(self%K)
393 gbgnss_min_temp_grad, &
398 INTEGER,
INTENT(IN) :: nlevP
399 INTEGER,
INTENT(IN) :: nlevq
400 REAL(kind_real),
INTENT(IN) :: za(:)
401 REAL(kind_real),
INTENT(IN) :: zb(:)
402 REAL(kind_real),
INTENT(IN) :: q(1:nlevq)
403 REAL(kind_real),
INTENT(IN) :: prs(1:nlevP)
404 REAL(kind_real),
INTENT(IN) :: zStation
405 REAL(kind_real),
INTENT(IN) :: gbgnss_min_temp_grad
406 INTEGER,
INTENT(IN) :: iobs
408 REAL(kind_real),
INTENT(INOUT) :: K(:,:)
413 REAL(kind_real) :: T(1:nlevq)
414 REAL(kind_real),
ALLOCATABLE :: refrac(:)
419 REAL(kind_real) :: x(1:nlevp+nlevq)
421 CHARACTER(LEN=200) :: err_msg
423 REAL(kind_real) :: pN(nlevq)
425 REAL(kind_real),
ALLOCATABLE :: model_heights(:)
426 INTEGER :: nRefLevels
429 nstate = nlevp + nlevq
442 gbgnss_min_temp_grad, &
448 IF (.NOT. refracerr)
THEN
459 gbgnss_min_temp_grad, &
465 write(err_msg,*)
"Error in refractivity calculation"
466 CALL fckit_log % warning(err_msg)
486 gbgnss_min_temp_grad, &
496 INTEGER,
INTENT(IN) :: nstate
497 INTEGER,
INTENT(IN) :: nlevP
498 INTEGER,
INTENT(IN) :: nlevq
499 REAL(kind_real),
INTENT(IN) :: za(:)
500 REAL(kind_real),
INTENT(IN) :: zb(:)
501 REAL(kind_real),
INTENT(IN) :: P(:)
502 REAL(kind_real),
INTENT(IN) :: q(:)
503 REAL(kind_real),
INTENT(IN) :: zStation
504 INTEGER,
INTENT(IN) :: iobs
505 REAL(kind_real),
INTENT(IN) :: gbgnss_min_temp_grad
506 LOGICAL,
INTENT(INOUT) :: refracerr
507 REAL(kind_real),
INTENT(IN) :: refrac(:)
508 REAL(kind_real),
INTENT(INOUT) :: K(:,:)
510 REAL(kind_real),
ALLOCATABLE :: dref_dP(:, :)
511 REAL(kind_real),
ALLOCATABLE :: dref_dq(:, :)
516 CHARACTER (LEN=*),
PARAMETER :: RoutineName =
"Groundgnss_GetK"
517 REAL,
PARAMETER :: refrac_scale = 1.0e-6
522 INTEGER :: Lowest_Level
525 REAL(kind_real) :: p_local(nlevp)
526 REAL(kind_real) :: pN(nlevq)
527 REAL(kind_real) :: LocalZenithDelay
528 REAL(kind_real) :: h_diff, station_diff
529 REAL(kind_real) :: c_rep
530 REAL(kind_real) :: const, c, term1, term2
531 REAL(kind_real) :: StationRefrac
532 REAL(kind_real) :: z_weight1
533 REAL(kind_real) :: z_weight2
534 REAL(kind_real) :: dc_dref
535 REAL(kind_real) :: dc_dref2
536 REAL(kind_real) :: dztd_dc
537 REAL(kind_real) :: drefsta_dref
538 REAL(kind_real) :: drefsta_dc
539 REAL(kind_real) :: dztd_drefsta
540 REAL(kind_real),
ALLOCATABLE :: dp_local_dPin(:,:)
541 REAL(kind_real),
ALLOCATABLE :: dztd_dpN(:)
542 REAL(kind_real) :: dztd_dq(nlevq)
543 REAL(kind_real) :: dztd_dp(nlevP)
544 REAL(kind_real),
ALLOCATABLE :: dPb_dP(:,:)
545 REAL(kind_real),
ALLOCATABLE :: dztd_dref(:)
546 REAL(kind_real),
ALLOCATABLE :: x1(:,:)
547 REAL(kind_real),
ALLOCATABLE :: x2(:)
549 REAL(kind_real),
PARAMETER :: hpa_to_pa = 100.0
550 REAL(kind_real),
PARAMETER :: PressScale = 6000.0
551 CHARACTER(LEN=200) :: err_msg
552 integer,
parameter :: max_string = 800
553 character(max_string) :: message
561 ALLOCATE (dref_dp(nlevq,nlevp))
562 ALLOCATE (dref_dq(nlevq,nlevq))
563 ALLOCATE (dpb_dp(nlevq,nlevp))
564 ALLOCATE (dztd_dref(nlevq))
565 ALLOCATE (dztd_dpn(nlevq))
567 ALLOCATE (x1(nlevq,nlevp))
568 ALLOCATE (dp_local_dpin(nlevp,nlevp))
581 dp_local_dpin(:,:) = 0.0
585 localzenithdelay = 0.0
595 p_local(level) = p(level)
596 dp_local_dpin(level,level) = 1.0
598 IF ((p(level) <= 0.0 .OR. (firstneg /= 0 .AND. level > firstneg)) .OR. &
599 (p(level) > p(level-1)))
THEN
601 IF (firstneg == 0) firstneg = level
603 p_local(level) = p(firstneg-1) * exp(-(za(level) - za(firstneg-1)) / pressscale)
604 dp_local_dpin(level,firstneg-1) = p_local(level) / p(firstneg-1)
607 p_local(level) = p(level)
608 dp_local_dpin(level,level) = 1.0
616 DO level = 1, nlevp-1
618 z_weight1 = (za(level+1) - zb(level)) / (za(level+1) - za(level))
619 z_weight2 = 1.0 - z_weight1
621 pn(level) = exp(z_weight1 * log(p_local(level)) + z_weight2 * log(p_local(level+1)))
623 dpb_dp(level,level) = pn(level) * z_weight1 / p_local(level)
624 dpb_dp(level,level+1) = pn(level) * z_weight2 / p_local(level+1)
639 gbgnss_min_temp_grad, &
649 DO level = 1, nlevp-1
650 IF (zb(level) > zstation)
THEN
656 DO level = lowest_level, nlevq-1
657 IF (refrac(level+1) / refrac(level) <= 0.0)
THEN
659 write(err_msg,*)
"Refractivity error. Refractivity < 0.0"
660 CALL fckit_log % warning(err_msg)
673 DO level = lowest_level, nlevq
675 localzenithdelay = 0.0
677 IF (level == lowest_level .AND. level /= 1)
THEN
681 h_diff = zb(level - 1) - zb(level)
682 station_diff = zstation - zb(level)
683 c = (log(refrac(level) / refrac(level - 1))) / h_diff
684 stationrefrac = refrac(level - 1) * exp(-c * (zstation-zb(level - 1)))
685 const = (-stationrefrac / c) * exp(c * zstation)
686 term1 = exp(-c * (zb(level)))
687 term2 = exp(-c * zstation)
688 localzenithdelay = refrac_scale * const * (term1 - term2)
691 dztd_dc = (-refrac_scale * stationrefrac / c) * &
692 (c_rep + exp(c * station_diff) * (station_diff - c_rep))
693 dztd_dc = dztd_dc - (station_diff * localzenithdelay)
694 dc_dref = -1.0 / (refrac(level - 1) * h_diff)
695 dc_dref2 = 1.0 / (refrac(level) * h_diff)
696 drefsta_dref = stationrefrac / refrac(level - 1)
697 drefsta_dc = -(zstation - zb(level - 1)) * stationrefrac
698 drefsta_dref = drefsta_dref + drefsta_dc * dc_dref
699 dztd_drefsta = localzenithdelay / stationrefrac
701 dztd_dref(level-1) = dztd_drefsta * drefsta_dref + dztd_dc * dc_dref
702 dztd_dref(level) = dztd_dc * dc_dref2
704 ELSE IF (level == 1)
THEN
710 h_diff = zb(level) - zb(level + 1)
711 c = (log(refrac(level + 1) / refrac(level))) / h_diff
712 const = (-refrac(level) / c) * exp(c * (zb(level)))
713 term1 = exp(-c * (zb(level + 1)))
714 term2 = exp(-c * zstation)
715 localzenithdelay = refrac_scale * const * (term1 - term2)
718 dztd_dc = (-refrac_scale * refrac(level) / c) * &
719 (c_rep + exp(c * h_diff) * (h_diff - c_rep))
720 dc_dref = -1.0 / (refrac(level) * h_diff)
721 dc_dref2 = 1.0 / (refrac(level + 1) * h_diff)
723 dztd_dref(level) = localzenithdelay / refrac(level)
724 dztd_dref(level) = dztd_dref(level) + dztd_dc * dc_dref
725 dztd_dref(level+1)= dztd_dc * dc_dref2
727 ELSE IF (level <= nlevq .AND. level > 2 .AND. level > lowest_level)
THEN
731 h_diff = zb(level - 1) - zb(level)
732 c = (log(refrac(level) / refrac(level-1))) / h_diff
733 const = (-refrac(level - 1) / c) * exp(c * (zb(level - 1)))
734 term1 = exp(-c * (zb(level)))
735 term2 = exp(-c * (zb(level - 1)))
736 localzenithdelay = refrac_scale * const * (term1 - term2)
739 dztd_dc = (-refrac_scale * refrac(level - 1) / c) * (c_rep + exp(c * h_diff) * (h_diff - c_rep))
740 dc_dref = -1.0 / (refrac(level - 1) * h_diff)
741 dc_dref2 = 1.0 / (refrac(level) * h_diff)
743 dztd_dref(level-1) = dztd_dref(level - 1) + localzenithdelay / refrac(level - 1) + dztd_dc * dc_dref
744 dztd_dref(level) = dztd_dc * dc_dref2
755 dref_dp = matmul(dref_dp,dp_local_dpin)
757 dztd_dq(:) = matmul(dztd_dref,dref_dq)
758 dztd_dp(:) = matmul(dztd_dref,dref_dp)
761 dztd_dp(:) = matmul(dztd_dp,dp_local_dpin)
765 dztd_dpn(nlevq) = refrac_scale * n_alpha *
rd / (hpa_to_pa * grav)
766 x1 = matmul(dpb_dp, dp_local_dpin)
767 x2 = matmul(dztd_dpn, x1)
768 dztd_dp = x2 + dztd_dp
770 k(iobs, 1:nlevp) = dztd_dp
771 k(iobs, nlevp+1:nstate) = dztd_dq
773 DEALLOCATE (dp_local_dpin)
776 DEALLOCATE (dztd_dpn)
777 DEALLOCATE (dztd_dref)
real(kind_real), parameter, public rd
type(registry_t), public ufo_geovals_registry
Linked list interface - defines registry_t type.
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Fortran module for ground based GNSS Met Office's tangent linear and adjoint.
subroutine ufo_groundgnss_metoffice_tlad_delete(self)
subroutine groundgnss_getk(nstate, nlevP, nlevq, za, zb, P, q, zStation, iobs, gbgnss_min_temp_grad, refracerr, refrac, K)
integer, parameter max_string
subroutine ufo_groundgnss_metoffice_tlad_settraj(self, geovals, obss)
subroutine ufo_groundgnss_metoffice_simobs_ad(self, geovals, hofx, obss)
subroutine groundgnss_jacobian_interface(nlevp, nlevq, za, zb, q, prs, zStation, iobs, gbgnss_min_temp_grad, K)
subroutine ufo_groundgnss_metoffice_simobs_tl(self, geovals, hofx, obss)
subroutine ufo_groundgnss_metoffice_setup(self, f_conf)
Module for containing a general refractivity forward operator and its K-matrix.
subroutine, public ufo_calculate_refractivity(nlevP, nlevq, za, zb, P, q, vert_interp_ops, pseudo_ops, min_temp_grad, refracerr, nRefLevels, refractivity, model_heights, temperature, interp_pressure)
Calculation of the refractivity from the pressure and specific humidity.
subroutine, public ufo_refractivity_kmat(nlevP, nlevq, nRefLevels, za, zb, P, q, pseudo_ops, vert_interp_ops, min_temp_grad, dref_dP, dref_dq, dPb_dP, refractivity)
Calculate general refractivity K matrix.
character(len=maxvarlen), parameter, public var_prsi
character(len=maxvarlen), parameter, public var_q
character(len=maxvarlen), parameter, public var_zi
character(len=maxvarlen), parameter, public var_z
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators
Fortran derived type for groundgnss trajectory.