Ticket #314: ropp_fm_bg2ro_1d.f90.14072015

File ropp_fm_bg2ro_1d.f90.14072015, 35.7 KB (added by Ian Culverwell, 9 years ago)

ropp_fm_bg2ro_1d.f90.14072015

Line 
1! $Id: ropp_fm_bg2ro_1d.f90 4432 2015-01-19 14:26:46Z idculv $
2
3PROGRAM ropp_fm_bg2ro_1d
4
5!****p* Programs/ropp_fm_bg2ro_1d *
6!
7! NAME
8! ropp_fm_bg2ro_1d
9!
10! SYNOPSIS
11! Calculate radio occultation pseudo observation from background model
12! data using 1d forward models
13!
14! > ropp_fm_bg2ro_1d <infile(s)> -o <outfile>
15! [-l] <levels_file> [-f] [-use_logp] [-use_logq]
16! [-comp] [-check_qsat] [-new_op] [-direct_ion] [-247L]
17! -zmin <geop_min> -zmax <geop_max> -nz <n_geop>
18! [-d] [-h] [-v]
19!
20! ARGUMENTS
21! <infile(s)> One (or more) input file names.
22!
23! OPTIONS
24! -o <outfile> name of output file (default: bg2ro.nc)
25! -l <levels_file> name of file containing a set of altitudes and
26! impact heights. This will typically be an
27! observation file. Only works for single file.
28! -f forward model only, no gradient calculation.
29! -use_logp use log(pressure) for forward model
30! -use_logq use log(spec humidity) for forward model
31! -comp include non ideal gas compressibility
32! -check_qsat check against saturation
33! -new_op use alternative refrac and bangle interpolation
34! -direct_ion forward model L1 and L2 directly, using model ionosphere
35! -247L output refrac and bangle on 'standard' 247L
36! -zmin <geop_min> minimum refractivity geopotential (gpm)'
37! -zmax <geop_max> maximum refractivity geopotential (gpm)'
38! -nz <n_geop> number of uniformly spaced refractivity geopotentials (gpm)'
39! -d output additional diagnostics
40! -h help
41! -v version information
42!
43! DESCRIPTION
44! This program reads model data on model levels from the input data files
45! and calculates vertical profiles of bending angle and refractivity using
46! the 1d forward operators. The result is written to an ROPP formatted
47! output file.
48!
49! NOTES
50! If the input file is a multifile, or more than one input files are
51! specified, the output file is a multifile.
52!
53! Already existing output files will be overwritten.
54!
55! EXAMPLES
56! 1) To calculate bending angle and refractivity from one of the example
57! (single-) files in the data directory:
58!
59! > ropp_fm_bg2ro_1d ../data/bgr20090401_000329_M02_2030337800_N0007_XXXX.nc
60!
61! 2) To calculate bending angle and refractivity profiles from all singlefiles
62! in the data directory:
63!
64! > ropp_fm_bg2ro_1d ../data/bgr20090401*_N0007_XXXX.nc -o eg_02.nc
65!
66! Note that the resulting eg_02.nc file contains forward modelled data from
67! all example profiles.
68!
69! 3) To calculate forward modelled bending angle and refractivity profiles from
70! all profiles contained in the multifile bgr20090401_multi.nc:
71!
72! > ropp_fm_bg2ro_1d ../data/bgr20090401_multi.nc -o eg_03.nc
73!
74! Since the ecmwf_multi_* file was generated by concatenating the other
75! files in the data directory, eg_02.nc and eg_03.nc should be identical
76! apart from the file names.
77!
78! 4) To calculate forward modelled L1 and L2 bending angles, assuming
79! a model ionosphere, from one of the example (single-) files
80! in the data directory:
81!
82! > ropp_fm_bg2ro_1d --direct_ion ../data/bgr20090401_000329_M02_2030337800_N0007_YYYY.nc
83!
84! The model ionosphere comprises a set of Chapman layers, each of
85! whose {Ne_max, H_peak, H_width} parameters are contained in the input file.
86! (The L2ctype and state1dFM structures have been suitably extended.)
87!
88! SEE ALSO
89! ropp_fm_bangle_1d
90! ropp_fm_refrac_1d
91! ropp_fm_refrac_1d_new
92!
93! AUTHOR
94! Met Office, Exeter, UK.
95! Any comments on this software should be given via the ROM SAF
96! Helpdesk at http://www.romsaf.org
97!
98! COPYRIGHT
99! (c) EUMETSAT. All rights reserved.
100! For further details please refer to the file COPYRIGHT
101! which you should have received as part of this distribution.
102!
103!****
104
105!-------------------------------------------------------------------------------
106! 1. Declarations
107!-------------------------------------------------------------------------------
108
109 USE typesizes, ONLY: wp => EightByteReal
110 USE ropp_utils
111 USE ropp_io
112 USE ropp_io_types, ONLY: ROprof
113 USE ropp_fm
114 USE ropp_fm_types
115 USE ropp_fm_copy
116 USE ropp_fm_iono
117 USE ropp_fm_levels
118
119 IMPLICIT NONE
120
121 TYPE(ROprof) :: ro_data, l_data
122 TYPE(State1dFM) :: state
123 TYPE(Obs1dRefrac) :: obs_refrac
124 TYPE(Obs1dBangle) :: obs_bangle
125
126 REAL(wp), DIMENSION(:,:), ALLOCATABLE :: gradient_bangle
127 REAL(wp), DIMENSION(:,:), ALLOCATABLE :: gradient_refrac
128 REAL(wp) :: geop_min
129 REAL(wp) :: geop_max
130 INTEGER :: n_geop
131
132 INTEGER :: idummy
133 INTEGER :: i, iargc, argc, k
134 INTEGER :: n_files, n_profiles
135
136 LOGICAL :: give_help = .FALSE.
137 LOGICAL :: do_adj = .TRUE.
138 LOGICAL :: lfile_exist = .FALSE.
139 LOGICAL :: ranchk = .TRUE.
140 LOGICAL :: compress = .FALSE.
141 LOGICAL :: checksat = .FALSE.
142 LOGICAL :: new_op = .FALSE.
143 LOGICAL :: direct_ion = .FALSE.
144 LOGICAL :: use_247L = .FALSE.
145
146 CHARACTER(len = 4096), DIMENSION(:), ALLOCATABLE :: ifiles
147 CHARACTER(len = 4096) :: ofile = 'bg2ro.nc'
148 CHARACTER(len = 4096) :: lfile
149 CHARACTER(len = 256) :: buffer
150 CHARACTER(len = 4) :: istr
151 CHARACTER(len = 6) :: nstr
152 CHARACTER(len = 4) :: slev1b, slev2a
153
154!-------------------------------------------------------------------------------
155! 2. Default settings
156!-------------------------------------------------------------------------------
157
158 CALL message_set_routine ( 'ropp_fm_bg2ro_1d' )
159
160 CALL message(msg_noin, '')
161 CALL message(msg_noin, &
162 '-----------------------------------------------------------------------')
163 CALL message(msg_noin, &
164 ' ROPP Forward Model' )
165 CALL message(msg_noin, &
166 '-----------------------------------------------------------------------')
167 CALL message(msg_noin, '')
168
169!-------------------------------------------------------------------------------
170! 3. Command line arguments
171!-------------------------------------------------------------------------------
172
173 argc = iargc()
174 i = 1
175 n_files = 0
176 ALLOCATE(ifiles(argc))
177
178 geop_min = 200.0_wp
179 geop_max = 60000.0_wp
180 n_geop = 300
181
182 DO WHILE(i <= argc)
183 CALL getarg(i, buffer)
184 SELECT CASE (buffer)
185 CASE('-o') ! Output file name (netCDF output)
186 CALL getarg(i+1, buffer)
187 ofile = buffer
188 i = i + 1
189 CASE ('-l') ! Use level structure defined here
190 CALL getarg(i+1, buffer)
191 lfile = buffer
192 i = i + 1
193 lfile_exist = .TRUE.
194 CASE ('-f') ! Perform forward model only
195 do_adj = .FALSE.
196 CASE ('-use_logp') ! Use log(pressure) for FM
197 state%use_logp = .TRUE.
198 CASE ('-use_logq') ! Use log(shum) for FM
199 state%use_logq = .TRUE.
200 CASE ('-comp') ! Non ideal gas switch
201 compress = .TRUE.
202 CASE ('-check_qsat') ! Check against saturation
203 checksat = .TRUE.
204 CASE ('-new_op') ! New interpolation
205 new_op = .TRUE.
206 CASE ('-direct_ion') ! Model L1 and L2 bangles directly
207 direct_ion = .TRUE.
208 CASE ('--no-ranchk') ! Disable rangecheck; for experts only
209 CALL message ( msg_warn, "Range checking is disabled" )
210 ranchk = .FALSE. ! Do not document as a user option.
211 CASE ('-247L', '-247l', '-247') ! Output refrac and bangle on standard 247L
212 use_247L = .TRUE.
213 CASE ('-zmin', '-Zmin', '-ZMIN') ! Min refrac geopotential
214 CALL getarg(i+1, buffer)
215 READ(buffer, *) geop_min
216 i = i + 1
217 CASE ('-zmax', '-Zmax', '-ZMAX') ! Max refrac geopotential
218 CALL getarg(i+1, buffer)
219 READ(buffer, *) geop_max
220 i = i + 1
221 CASE ('-nz', '-Nz', '-NZ') ! Number of refractivity levels
222 CALL getarg(i+1, buffer)
223 READ(buffer, *) n_geop
224 i = i + 1
225 CASE('-d') ! Additional diagnostic mode
226 msg_MODE = VerboseMode
227 CASE('-h', '--help', '?') ! Help
228 give_help = .TRUE.
229 CASE('-v', '-V', '--version') ! Version information
230 CALL version_info()
231 CALL EXIT(msg_exit_ok)
232 CASE default ! Input file name
233 IF ( buffer(1:1) /= '-' ) THEN
234 n_files = n_files + 1
235 ifiles(n_files) = buffer
236 END IF
237 END SELECT
238 i = i + 1
239 END DO
240
241 IF ( n_files == 0 .AND. .NOT. give_help ) &
242 CALL message ( msg_error, "No input file(s) specified" )
243
244 IF (argc == 0 .OR. n_files == 0 .OR. give_help) THEN
245 CALL usage()
246 CALL EXIT(msg_exit_status)
247 END IF
248
249!-------------------------------------------------------------------------------
250! 4. Remove pre-existing output file
251!-------------------------------------------------------------------------------
252
253 CALL file_delete(ofile, idummy)
254
255!-------------------------------------------------------------------------------
256! 4. Set default units
257!-------------------------------------------------------------------------------
258
259 CALL ropp_fm_set_units(ro_data)
260
261!-------------------------------------------------------------------------------
262! 5. Loop over all input files
263!-------------------------------------------------------------------------------
264
265 DO k = 1, n_files
266
267!-------------------------------------------------------------------------------
268! 6. Loop over all profiles
269!-------------------------------------------------------------------------------
270
271 n_profiles = ropp_io_nrec(ifiles(k))
272
273 DO i = 1, n_profiles
274
275 WRITE(istr, '(i4)') i
276 WRITE(nstr, '(i6)') n_profiles
277 CALL message(msg_noin, '')
278 CALL message(msg_info, "Processing profile " // istr // " of " // nstr )
279
280!-------------------------------------------------------------------------------
281! 7. Read data
282!-------------------------------------------------------------------------------
283
284 CALL ropp_io_read(ro_data, ifiles(k), rec = i, ranchk = ranchk)
285
286 CALL message(msg_info, "(" // TRIM(ro_data%occ_id) // ") \n")
287
288 IF (lfile_exist) THEN
289
290 CALL message(msg_info, "Reading levels information from " // lfile // " \n")
291
292 CALL ropp_io_read(l_data, lfile, ranchk=ranchk)
293
294 ro_data%dtocc = l_data%dtocc
295 ro_data%georef = l_data%georef
296 ro_data%lev1b = l_data%lev1b
297 ro_data%lev2a = l_data%lev2a
298
299 WRITE (slev1b, '(i4)') ro_data%lev1b%Npoints
300 CALL message(msg_info, "There are " // slev1b // &
301 " level 1b points in " // lfile // " \n")
302
303 WRITE (slev2a, '(i4)') ro_data%lev2a%Npoints
304 CALL message(msg_info, "There are " // slev2a // &
305 " level 2a points in " // lfile // " \n")
306
307 CALL ropp_io_free(l_data)
308
309 END IF
310
311 IF (direct_ion) THEN
312
313 ro_data%lev2c%direct_ion = .TRUE.
314
315 CALL ropp_fm_iono_set_default(ro_data)
316
317 END IF
318
319!-------------------------------------------------------------------------------
320! 8. Copy data in RO structure to state and refrac obs vectors
321!-------------------------------------------------------------------------------
322
323 IF (checksat) state%check_qsat = .TRUE. ! Switch on check against saturation
324
325 CALL ropp_fm_roprof2state(ro_data, state)
326
327 IF (compress) state%non_ideal = .TRUE. ! Switch on non-ideal gas compressibility option
328
329 IF (new_op) THEN
330 state%new_ref_op = .TRUE. ! Use new refrac interpolation
331 state%new_bangle_op = .TRUE. ! Use new bangle interpolation
332 END IF
333
334 IF (ro_data%lev2a%Npoints > 0) THEN
335 CALL ropp_fm_roprof2obs(ro_data, obs_refrac) ! Set to obs levels
336 obs_refrac%refrac(:) = ropp_MDFV
337 ELSE
338 IF (use_247L) THEN
339 CALL set_obs_levels_refrac_247(ro_data, obs_refrac, state) ! Pre-defined levels
340 ELSE
341 CALL set_obs_levels_refrac(ro_data, obs_refrac, &
342 geop_min=geop_min, geop_max=geop_max, n_geop=n_geop) ! Uniformly spaced levels
343 END IF
344 END IF
345
346!-------------------------------------------------------------------------------
347! 9. Calculate refractivity and its gradient
348!-------------------------------------------------------------------------------
349
350 IF (state%state_ok) THEN
351
352 IF (state%new_ref_op) THEN
353 CALL ropp_fm_refrac_1d_new(state, obs_refrac)
354 ELSE
355 CALL ropp_fm_refrac_1d(state, obs_refrac)
356 END IF
357
358 IF (do_adj) THEN
359 ALLOCATE(gradient_refrac(SIZE(obs_refrac%refrac),SIZE(state%state)))
360 CALL ropp_fm_refrac_1d_grad(state, obs_refrac, gradient_refrac)
361 END IF
362
363 END IF
364
365!-------------------------------------------------------------------------------
366! 10. Copy data in RO and refrac structure to bending angle obs vector
367!-------------------------------------------------------------------------------
368
369 IF (ro_data%lev1b%Npoints > 0) THEN
370 CALL ropp_fm_roprof2obs(ro_data, obs_bangle) ! Set to obs levels
371 obs_bangle%bangle(:) = ropp_MDFV
372 ELSE
373 IF (use_247L) THEN
374 CALL set_obs_levels_bangle_247(ro_data, obs_bangle) ! Pre-defined levels
375 ELSE
376 CALL set_obs_levels_bangle(state, ro_data, obs_refrac, obs_bangle) !default
377 END IF
378 END IF
379
380!-------------------------------------------------------------------------------
381! 11. Calculate bending angle and its gradient
382!-------------------------------------------------------------------------------
383
384 IF (state%state_ok) THEN
385
386 CALL ropp_fm_bangle_1d(state, obs_bangle)
387
388 IF (do_adj .AND. (.NOT. direct_ion)) THEN
389 ALLOCATE(gradient_bangle(SIZE(obs_bangle%bangle),SIZE(state%state)))
390 CALL ropp_fm_bangle_1d_grad(state, obs_bangle, gradient_bangle)
391 END IF
392
393 END IF
394
395!-------------------------------------------------------------------------------
396! 12. Copy simulated observations to RO structure and write data
397!-------------------------------------------------------------------------------
398
399 CALL ropp_fm_obs2roprof(obs_refrac, ro_data)
400
401 CALL ropp_fm_obs2roprof(obs_bangle, ro_data)
402
403 IF (state%state_ok) THEN
404
405 IF (do_adj) THEN
406 CALL ropp_io_addvar(ro_data, &
407 name = "gradient_refrac", &
408 long_name = &
409 "Gradient of the refractivity forward model", &
410 units = "1", &
411 range = (/MINVAL(gradient_refrac), &
412 MAXVAL(gradient_refrac)/), &
413 DATA = gradient_refrac)
414 END IF
415
416 IF (do_adj .AND. (.NOT. direct_ion)) THEN
417 CALL ropp_io_addvar(ro_data, &
418 name = "gradient_bangle" , &
419 long_name = &
420 "Gradient of the bending angle forward model", &
421 units = "rad", &
422 range = (/MINVAL(gradient_bangle), &
423 MAXVAL(gradient_bangle)/), &
424 DATA = gradient_bangle)
425 END IF
426
427!-------------------------------------------------------------------------------
428! 13. Update RO structure with computed state vector variables
429!-------------------------------------------------------------------------------
430
431 CALL ropp_fm_state2roprof(state, ro_data)
432
433 END IF
434
435!-------------------------------------------------------------------------------
436! 14. Write data
437!-------------------------------------------------------------------------------
438
439 IF (direct_ion) CALL ropp_fm_iono_unpack(ro_data)
440
441 CALL ropp_io_write(ro_data, ofile, append=.TRUE., ranchk=ranchk)
442
443!-------------------------------------------------------------------------------
444! 15. Clean up
445!-------------------------------------------------------------------------------
446
447 IF (state%state_ok .AND. do_adj) THEN
448 DEALLOCATE (gradient_refrac)
449 END IF
450
451 IF (state%state_ok .AND. do_adj .AND. (.NOT. direct_ion)) THEN
452 DEALLOCATE (gradient_bangle)
453 END IF
454
455 CALL ropp_fm_free(state)
456 CALL ropp_fm_free(obs_refrac)
457 CALL ropp_fm_free(obs_bangle)
458 CALL ropp_io_free(ro_data)
459
460 END DO
461
462 END DO
463
464 CALL EXIT(msg_exit_status)
465
466CONTAINS
467
468!-------------------------------------------------------------------------------
469! 16a. Calculate uniformly spaced observation levels for refractivity
470!-------------------------------------------------------------------------------
471
472 SUBROUTINE set_obs_levels_refrac(ro_data, obs_refrac, &
473 geop_min, geop_max, n_geop)
474!
475! n_geop uniformly spaced geopotential heights between geop_min and geop_max (gpm).
476!
477
478! 16a.1 Declarations
479! -----------------
480
481 USE typesizes, ONLY: wp => EightByteReal
482 USE ropp_io_types
483 USE ropp_fm
484
485 IMPLICIT NONE
486
487! Input/Output variables
488 TYPE(ROprof) :: ro_data
489 TYPE(Obs1dRefrac) :: obs_refrac
490 REAL(wp), OPTIONAL :: geop_min
491 REAL(wp), OPTIONAL :: geop_max
492 INTEGER, OPTIONAL :: n_geop
493
494! Local variables
495 REAL(wp) :: zmin, zmax
496 INTEGER :: i, nz
497 CHARACTER(len=256) :: routine
498
499 CALL message_get_routine(routine)
500 CALL message_set_routine('set_obs_levels_refrac')
501
502! 16a. Vertical geopotential height levels between geop_min and geop_max
503! ----------------------------------------------------------------------
504
505 zmin = 200.0_wp
506 IF ( PRESENT(geop_min) ) zmin = geop_min
507
508 zmax = 60.0e3_wp
509 IF ( PRESENT(geop_max) ) zmax = geop_max
510
511 nz = 300
512 IF ( PRESENT(n_geop) ) nz = n_geop
513
514 IF ( (zmin >= zmax) .OR. (n_geop <= 1) ) THEN
515 CALL message(msg_warn, 'Invalid geop_min, geop_max or n_geop' // &
516 ' ... resetting all to default values')
517 zmin = 200.0_wp
518 zmax = 60000.0_wp
519 nz = 300
520 END IF
521
522 ALLOCATE(obs_refrac%refrac(nz))
523 ALLOCATE(obs_refrac%geop(nz))
524 ALLOCATE(obs_refrac%weights(nz))
525
526 obs_refrac%refrac(:) = 0.0_wp
527 obs_refrac%geop(:) = (/ (zmin+(i-1)*(zmax-zmin)/REAL(nz-1, wp), i=1,nz) /)
528 obs_refrac%weights(:) = 1.0_wp
529 obs_refrac%lon = ro_data%GEOref%lon
530 obs_refrac%lat = ro_data%GEOref%lat
531
532 CALL message_set_routine(routine)
533
534 END SUBROUTINE set_obs_levels_refrac
535
536
537!-------------------------------------------------------------------------------
538! 16b. Calculate standard 247 observation levels for refractivity
539!-------------------------------------------------------------------------------
540
541 SUBROUTINE set_obs_levels_refrac_247(ro_data, obs_refrac, state)
542!
543! 247 geopotential height levels, going up to about 60 km, chosen in such a
544! way that the resulting impact heights match the 'standard' ones in
545! impact_height_eum_247 in ropp_fm/common/ropp_fm_levels.f90
546!
547! 16b.1 Declarations
548! -----------------
549
550 USE typesizes, ONLY: wp => EightByteReal
551 USE ropp_io_types
552 USE ropp_fm
553
554 IMPLICIT NONE
555
556 TYPE(ROprof) :: ro_data
557 TYPE(Obs1dRefrac) :: obs_refrac
558 TYPE(State1dFM) :: state
559
560 INTEGER, PARAMETER :: n=247
561 REAL(wp), DIMENSION(n) :: impact_height
562 REAL(wp), DIMENSION(n) :: tmp
563 REAL(wp), PARAMETER :: refrac_surface_default=300.0_wp
564 REAL(wp) :: refrac_surface
565 REAL(wp), PARAMETER :: tol=1.0e-3_wp ! Demand < 1mm diffs
566 REAL(wp) :: max_refrac
567 REAL(wp) :: min_geop
568 REAL(wp) :: roc, und, lat
569 INTEGER, PARAMETER :: max_iter=1000
570 INTEGER :: iter, imiss
571 INTEGER :: lowest_refrac_index
572
573 CHARACTER(LEN=3) :: smax_iter
574 CHARACTER(LEN=256) :: routine
575
576! 16b.2 Initialisations
577! ---------------------
578
579 CALL message_get_routine(routine)
580 CALL message_set_routine('set_obs_levels_refrac_247')
581
582 impact_height = impact_height_eum_247 ! The particular 247L set
583
584 max_refrac = ro_data%lev2a%range%refrac(2)
585
586 min_geop = ro_data%lev2b%range%geop(1)
587
588 ALLOCATE(obs_refrac%refrac(n))
589 ALLOCATE(obs_refrac%geop(n))
590 ALLOCATE(obs_refrac%weights(n))
591
592! 16b.3 Vertical geopotential height levels
593! -----------------------------------------
594
595! We seek solutions h_i of
596! (nr = ) (1 + 1e-6 N(Z(h_i)))*(h_i + RoC + und) = a_i + RoC
597! where a_i are the given impact heights, RoC is the radius of curvature
598! and und is the undulation. h_i is the geometric height;
599! Z_i is the geopotential height. Non-linear because N depends on Z.
600! When geop_refrac is below the lowest state refrac, the refracs are
601! (now) missing data. In such cases, replace the missing refracs with ones
602! calculated with the old refrac scheme with this nullification switched off.
603! Because the same principle is applied in set_obs_levels_bangle, the
604! resulting impact heights are still good.
605
606! 16b.4 Generate radius of curvature
607! ----------------------------------
608
609 IF ( ro_data%GEOref%roc > ropp_MDTV ) THEN
610 roc = ro_data%GEOref%roc
611 ELSE
612 CALL message (msg_warn, 'RoC missing from data structure ... ' // &
613 'setting equal to effective radius')
614 roc = R_eff(ro_data%GEOref%lat) ! Maybe +21km? See RSR 14.
615 END IF
616
617! 16b.5 Generate undulation
618! -------------------------
619
620 IF ( ro_data%GEOref%undulation > ropp_MDTV ) THEN
621 und = ro_data%GEOref%undulation
622 ELSE
623 CALL message(msg_warn, "Undulation missing ... will assume to " // &
624 "be zero when calculating geopotential heights.")
625 und = 0.0_wp
626 END IF
627
628! 16b.6 Generate latitude
629! -----------------------
630
631 IF ( ro_data%GEOref%lat > ropp_MDTV ) THEN
632 lat = ro_data%GEOref%lat
633 ELSE
634 CALL message(msg_warn, "Latitude missing ... will assume to " // &
635 "be zero when calculating geopotential heights.")
636 lat = 0.0_wp
637 END IF
638
639! 16b.7 Initialise
640! ----------------
641
642 IF ( (state%temp(1) > ropp_MDTV) .AND. (state%pres(1) > ropp_MDTV) ) THEN
643 refrac_surface = kappa1 * state%pres(1) / state%temp(1) ! roughly
644 ELSE
645 refrac_surface = refrac_surface_default
646 END IF
647
648 obs_refrac%geop = ((impact_height + roc)/(1.0_wp + 1.0e-6_wp*refrac_surface)) - &
649 (roc + und) ! roughly
650
651 CALL ropp_fm_refrac_1d(state, obs_refrac, nonull=.TRUE.) ! Initialise with the old interp scheme sans missing data
652
653! 16b.8 Iterate on GPH
654! --------------------
655
656 iter = 0
657
658 DO
659
660 tmp = ((impact_height + roc)/(1.0_wp + 1.0e-6_wp*obs_refrac%refrac)) - &
661 (roc + und) ! geometric height
662
663 obs_refrac%geop = geometric2geopotential(lat, tmp)
664
665 IF ( state%new_ref_op ) THEN
666 CALL ropp_fm_refrac_1d_new(state, obs_refrac)
667 ELSE
668 CALL ropp_fm_refrac_1d(state, obs_refrac)
669 END IF
670
671 IF ( ANY(obs_refrac%refrac <= ropp_MDTV) ) THEN ! rederive approximately the zapped refracs
672
673 lowest_refrac_index = SUM( MINLOC( (/ (i, i=1,n) /), mask=obs_refrac%refrac>ropp_MDTV ) )
674
675 IF ( (lowest_refrac_index > 1) .AND. (lowest_refrac_index < n) ) THEN
676
677 DO imiss=lowest_refrac_index-1,1,-1
678 obs_refrac%refrac(imiss) = obs_refrac%refrac(imiss+1) + &
679 (obs_refrac%refrac(lowest_refrac_index) - obs_refrac%refrac(lowest_refrac_index+1))
680 END DO
681
682 tmp(1:lowest_refrac_index-1) = ((impact_height(1:lowest_refrac_index-1) + roc) / &
683 (1.0_wp + 1.0e-6_wp*obs_refrac%refrac(1:lowest_refrac_index-1))) - &
684 (roc + und) ! geometric height exactly right in this region
685 obs_refrac%geop(1:lowest_refrac_index-1) = geometric2geopotential(lat, tmp(1:lowest_refrac_index-1))
686
687 END IF
688
689 END IF
690
691 tmp = (1.0_wp + 1.0e-6_wp*obs_refrac%refrac)*(tmp + roc + und) - &
692 (impact_height + roc) ! residual
693
694 IF ( MAXVAL(ABS(tmp)) < tol ) EXIT
695
696 iter = iter + 1
697
698 IF ( iter > max_iter ) THEN
699 WRITE (smax_iter, '(i3)') max_iter
700 CALL message(msg_fatal, 'GPH iteration failed to converge in ' // &
701 smax_iter // ' iterations')
702 END IF
703
704 WHERE ( obs_refrac%refrac > max_refrac ) obs_refrac%refrac = max_refrac
705
706 WHERE ( obs_refrac%geop < min_geop ) obs_refrac%geop = min_geop
707
708 END DO
709
710 WRITE (smax_iter, '(i3)') iter
711 CALL message(msg_diag, 'GPH iteration converged in ' // &
712 smax_iter // ' iterations')
713
714! 16b.9 Set other elements of obs_refrac structure
715! ------------------------------------------------
716
717 obs_refrac%refrac = 0.0_wp ! Refrac will be calculated in the main program
718 obs_refrac%weights = 1.0_wp
719 obs_refrac%lon = ro_data%GEOref%lon
720 obs_refrac%lat = ro_data%GEOref%lat
721
722! 16b.10 Clean up
723! ---------------
724
725 CALL message_set_routine(routine)
726
727 END SUBROUTINE set_obs_levels_refrac_247
728
729
730!-------------------------------------------------------------------------------
731! 17. Calculate obs levels for bending angle (consistent with obs_refrac)
732!-------------------------------------------------------------------------------
733
734 SUBROUTINE set_obs_levels_bangle(state, ro_data, obs_refrac, obs_bangle)
735
736! 17.1 Declarations
737! -----------------
738
739 USE typesizes, ONLY: wp => EightByteReal
740 USE geodesy
741 USE ropp_io_types
742 USE ropp_fm
743
744 IMPLICIT NONE
745
746 TYPE(ROprof) :: ro_data
747 TYPE(Obs1dRefrac) :: obs_refrac
748 TYPE(Obs1dbangle) :: obs_bangle
749 TYPE(State1dFM) :: state
750
751 REAL(wp), DIMENSION(:), ALLOCATABLE :: tmp
752 REAL(wp) :: max_refrac
753 REAL(wp) :: min_geop
754
755 INTEGER :: n, imiss
756 INTEGER :: lowest_refrac_index
757
758 CHARACTER(LEN=256) :: routine
759
760 CALL message_get_routine(routine)
761 CALL message_set_routine('set_obs_levels_bangle')
762
763! 17.2 Allocate arrays
764! --------------------
765
766 max_refrac = ro_data%lev2a%range%refrac(2)
767
768 min_geop = ro_data%lev2b%range%geop(1)
769
770 n = SIZE(obs_refrac%geop)
771
772 obs_bangle%n_L1 = n
773
774 ALLOCATE(tmp(n))
775
776 IF ( .NOT. ro_data%Lev2c%direct_ion ) THEN
777 ALLOCATE(obs_bangle%bangle(n))
778 ALLOCATE(obs_bangle%impact(n))
779 ALLOCATE(obs_bangle%weights(n))
780 ELSE
781 ALLOCATE(obs_bangle%bangle(2*n))
782 ALLOCATE(obs_bangle%impact(2*n))
783 ALLOCATE(obs_bangle%weights(2*n))
784 END IF
785
786! 17.3 Calculate refractivities
787! -----------------------------
788
789 IF ( state%new_ref_op ) THEN
790 CALL ropp_fm_refrac_1d_new(state, obs_refrac)
791 ELSE
792 CALL ropp_fm_refrac_1d(state, obs_refrac)
793 END IF
794
795 IF ( ANY(obs_refrac%refrac <= ropp_MDTV) ) THEN ! rederive approximately the zapped refracs
796
797 lowest_refrac_index = SUM( MINLOC( (/ (i, i=1,n) /), mask=obs_refrac%refrac>ropp_MDTV ) )
798
799 IF ( (lowest_refrac_index > 1) .AND. (lowest_refrac_index < n) ) THEN
800
801 DO imiss=lowest_refrac_index-1,1,-1
802 obs_refrac%refrac(imiss) = obs_refrac%refrac(imiss+1) + &
803 (obs_refrac%refrac(lowest_refrac_index) - obs_refrac%refrac(lowest_refrac_index+1))
804 END DO
805
806 END IF
807
808 END IF
809
810 WHERE ( obs_refrac%refrac > max_refrac ) obs_refrac%refrac = max_refrac
811
812 WHERE ( obs_refrac%geop < min_geop ) obs_refrac%geop = min_geop
813
814! 17.4 Set scalar arguments of the observation vector
815! ---------------------------------------------------
816
817 obs_bangle%g_sfc = gravity(ro_data%GEOref%lat)
818 obs_bangle%r_earth = R_eff(ro_data%GEOref%lat)
819 obs_bangle%undulation = ro_data%GEOref%undulation
820 obs_bangle%lon = ro_data%GEOref%lon
821 obs_bangle%lat = ro_data%GEOref%lat
822 obs_bangle%azimuth = ro_data%GEOref%azimuth
823
824 IF ( ro_data%GEOref%roc > 0.0_wp ) THEN
825 obs_bangle%r_curve = ro_data%GEOref%roc
826 ELSE
827 CALL message (msg_warn, 'RoC missing from data structure ... ' // &
828 'setting equal to effective radius')
829 obs_bangle%r_curve = obs_bangle%r_earth ! Maybe +21km? See RSR 14.
830 END IF
831
832! 17.5 Calculate levels to coincide with the geopotential levels
833! --------------------------------------------------------------
834
835 tmp = geopotential2geometric(ro_data%GEOref%lat, obs_refrac%geop) + &
836 obs_bangle%r_curve
837
838 IF ( ro_data%GEOref%undulation > ropp_MDTV ) THEN
839 tmp = tmp + obs_bangle%undulation
840 ELSE
841 CALL message(msg_warn, "Undulation missing. Will assume to " // &
842 "be zero when calculating impact parameters.")
843 END IF
844
845 tmp = (1.0_wp + obs_refrac%refrac*1.e-6_wp) * tmp
846
847 IF ( .NOT. ro_data%Lev2c%direct_ion ) THEN
848 obs_bangle%impact = tmp
849 ELSE
850 obs_bangle%impact(1:n) = tmp
851 obs_bangle%impact(n+1:2*n) = tmp
852 END IF
853
854! 17.6 Fill other arrays
855! ----------------------
856
857 obs_bangle%bangle(:) = 0.0_wp
858 obs_bangle%weights(:) = 1.0_wp
859
860! 17.7 Clean up
861! -------------
862
863 IF ( state%new_ref_op ) THEN ! Regenerate avec missing data
864 CALL ropp_fm_refrac_1d_new(state, obs_refrac)
865 ELSE
866 CALL ropp_fm_refrac_1d(state, obs_refrac)
867 END IF
868
869 DEALLOCATE(tmp)
870
871 CALL message_set_routine(routine)
872
873 END SUBROUTINE set_obs_levels_bangle
874
875
876!-------------------------------------------------------------------------------
877! 17b. Calculate standard 247 observation levels for bending angle
878!-------------------------------------------------------------------------------
879
880 SUBROUTINE set_obs_levels_bangle_247(ro_data, obs_bangle)
881
882! 17b.1 Declarations
883! ------------------
884
885 USE typesizes, ONLY: wp => EightByteReal
886 USE geodesy
887 USE ropp_io_types
888 USE ropp_fm
889
890 IMPLICIT NONE
891
892 TYPE(ROprof) :: ro_data
893 TYPE(Obs1dbangle) :: obs_bangle
894
895 INTEGER, PARAMETER :: n=247
896 REAL(wp), DIMENSION(n) :: impact_height
897
898 CHARACTER(LEN=256) :: routine
899
900 CALL message_get_routine(routine)
901 CALL message_set_routine('set_obs_levels_bangle_247')
902
903! 17b.2 Allocate arrays
904! ---------------------
905
906 obs_bangle%n_L1 = n
907
908 IF ( .NOT. ro_data%Lev2c%direct_ion ) THEN
909 ALLOCATE(obs_bangle%bangle(n))
910 ALLOCATE(obs_bangle%impact(n))
911 ALLOCATE(obs_bangle%weights(n))
912 ELSE
913 ALLOCATE(obs_bangle%bangle(2*n))
914 ALLOCATE(obs_bangle%impact(2*n))
915 ALLOCATE(obs_bangle%weights(2*n))
916 END IF
917
918! 17b.3 Define standard impact heights
919! ------------------------------------
920
921 impact_height = impact_height_eum_247 ! The particular 247L set
922
923! 17b.4 Set scalar arguments of the observation vector
924! ----------------------------------------------------
925
926 obs_bangle%g_sfc = gravity(ro_data%GEOref%lat)
927 obs_bangle%r_earth = R_eff(ro_data%GEOref%lat)
928 obs_bangle%undulation = ro_data%GEOref%undulation
929 obs_bangle%lon = ro_data%GEOref%lon
930 obs_bangle%lat = ro_data%GEOref%lat
931 obs_bangle%azimuth = ro_data%GEOref%azimuth
932
933 IF ( ro_data%GEOref%roc > 0.0_wp ) THEN
934 obs_bangle%r_curve = ro_data%GEOref%roc
935 ELSE
936 CALL message (msg_warn, 'RoC missing from data structure ... ' // &
937 'setting equal to effective radius')
938 obs_bangle%r_curve = obs_bangle%r_earth ! Maybe +21km? See RSR 14.
939 END IF
940
941! 17b.5 Calculate impact parameters
942! ---------------------------------
943
944 IF ( .NOT. ro_data%Lev2c%direct_ion ) THEN
945 obs_bangle%impact = impact_height + obs_bangle%r_curve
946 ELSE
947 obs_bangle%impact(1:n) = impact_height + obs_bangle%r_curve
948 obs_bangle%impact(n+1:2*n) = impact_height + obs_bangle%r_curve
949 END IF
950
951! 17b.6 Fill other arrays
952! -----------------------
953
954 obs_bangle%bangle(:) = 0.0_wp
955 obs_bangle%weights(:) = 1.0_wp
956
957! 17b.7 Clean up
958! --------------
959
960 CALL message_set_routine(routine)
961
962 END SUBROUTINE set_obs_levels_bangle_247
963
964
965!-------------------------------------------------------------------------------
966! 18. Usage information
967!-------------------------------------------------------------------------------
968
969 SUBROUTINE usage()
970 PRINT *, 'Purpose:'
971 PRINT *, ' Bending angles and refractivity forward model'
972 PRINT *, 'Usage:'
973 PRINT *, ' > ropp_fm_bg2ro_1d [<options>] <input_file(s)>'
974 PRINT *, 'Options:'
975 PRINT *, ' -o <output_file> name of ROPP netCDF output file'
976 PRINT *, ' -l <levels_file> optional name of (observation) file'
977 PRINT *, ' (non-multi) containing output level information'
978 PRINT *, ' -f forward model only, no gradient calculation'
979 PRINT *, ' -use_logp use log(pressure) for forward model'
980 PRINT *, ' -use_logq use log(spec humidity) for forward model'
981 PRINT *, ' -comp include non ideal gas compressibility'
982 PRINT *, ' -check_qsat include check against saturation'
983 PRINT *, ' -new_op use alternative refrac and bangle interpolation'
984 PRINT *, ' -direct_ion model L1 and L2 directly'
985 PRINT *, ' -247L output refrac and bangle on standard 247L'
986 PRINT *, ' -zmin <geop_min> minimum refractivity geopotential (gpm)'
987 PRINT *, ' -zmax <geop_max> maximum refractivity geopotential (gpm)'
988 PRINT *, ' -nz <n_geop> number of uniformly spaced refractivity geopotentials (gpm)'
989 PRINT *, ' -d output additional diagnostics'
990 PRINT *, ' -h this help'
991 PRINT *, ' -v version information'
992 PRINT *, ''
993 END SUBROUTINE usage
994
995
996!-------------------------------------------------------------------------------
997! 19. Version information
998!-------------------------------------------------------------------------------
999
1000 SUBROUTINE version_info()
1001 CHARACTER (LEN=40) :: version
1002 version = ropp_fm_version()
1003 PRINT *, 'ropp_fm_bg2ro_1d - Bending angles and refractivity forward model.'
1004 PRINT *, ''
1005 PRINT *, 'This program is part of ROPP (FM) Release ' // TRIM(version)
1006 PRINT *, ''
1007 END SUBROUTINE version_info
1008
1009
1010END PROGRAM ropp_fm_bg2ro_1d