-
Notifications
You must be signed in to change notification settings - Fork 4
/
m_insitu.F90
784 lines (677 loc) · 24.1 KB
/
m_insitu.F90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
! File: m_insitu.F90
!
! Created: 6 Feb 2008
!
! Last modified: 13 Feb 2008
!
! Author: Pavel Sakov
! NERSC
!
! Purpose: The code to deal with insitu observations.
!
! Description: This module contains the following subroutines:
! - insitu_setprofiles
! breaks the measurements into profiles and returns
! arrays of start and end indices for each profile
! - insitu_writeprofiles
! writes profiles to a netCDF file
! - insitu_prepareobs
! sorts out the measurements within profiles so they
! go in surface to bottom order and thins the measurements
! by keeping max 1 measurements per layer of the first
! ensemble member
! It also contains the following data:
! nprof
! - the number of profiles
! pstart(nprof)
! - start indices for each profile in the array "obs" of
! type(measurement) stored in module m_obs
! pend(nprof)
! - end indices for each profile
!
! Modifications:
! 30/7/2010 PS: added profile pivot points to profile output
! files (SAL.nc etc.)
! 29/7/2010 PS: some rather minor changes, including interface
! of insitu_writeforecast()
! 13/02/2008 PS: added insitu_writeprofiles()
! 26/02/2008 PS: put "nprof", "pstart" and "pend" as public data
! in this module
! 20/04/2008 PS: added insitu_QC() and insitu_writeforecast()
! 29/07/2010 PS: removed insitu_QC(). There is a generic obs QC
! procedure in m_obs.F90 now.
module m_insitu
use mod_measurement
use m_parse_blkdat
use m_get_mod_fld
#if defined (QMPI)
use qmpi
#else
use qmpi_fake
#endif
implicit none
!
! public stuff
!
integer, allocatable, dimension(:), public :: pstart
integer, allocatable, dimension(:), public :: pend
integer, public :: nprof
public insitu_setprofiles
public insitu_prepareobs
public insitu_writeprofiles
!
! private stuff
!
real, parameter, private :: ONEMETER = 9806.0
integer, parameter, private :: STRLEN = 512
! The portion of the layer thickness at which the variability in
! vertical data will be used for estimating the vertical representativeness
! error.
!
real, parameter, private :: VARCOEFF1 = 0.15
! A factor by which a calculated vertical representativeness error variance
! will be reduced if the data is in different layers
!
real, parameter, private :: VARCOEFF2 = 2.0
! Write information about this profile. Set to < 1 to switch off.
!
integer, parameter, private :: PDEBUGINFO = 0
! Integers used to tag the fields (to avoid parsing the string tags)
!
integer, parameter, private :: NONE = 0
integer, parameter, private :: TEMPERATURE = 1
integer, parameter, private :: SALINITY = 2
real, parameter, private :: TEM_MIN = -2.0
real, parameter, private :: TEM_MAX = 35.0
real, parameter, private :: SAL_MIN = 5.0
real, parameter, private :: SAL_MAX = 41.0
! Maximum allowed deviation between the observation and ensemble mean in
! terms of combined standard deviation.
!
real, parameter, private :: SAL_MAXRATIO = 10.0
real, parameter, private :: TEM_MAXRATIO = 5.0
! If an observation is not considered an outlier, the observation error
! variance is modified so that the distance between the observation and the
! endemble mean is within DIST_MAX * sqrt(sigma_obs^2 + sigma_ens^2).
! Bigger values of DIST_MAX result in a more violent assimilation.
!
real, parameter, private :: DIST_MAX = 2.0
contains
! Work out the number of profiles, each identified by "obs % i_orig_grid"
! and return start id of the first and the last obs in the profile in
! arrays "pstart" and "pend". "pstart" and "pend" are publicly available
! arrays stored in this module.
!
subroutine insitu_setprofiles(obstag, nobs, obs)
character(*), intent(in) :: obstag
integer, intent(in) :: nobs
type(measurement), dimension(:), intent(inout) :: obs
integer, allocatable, dimension(:) :: tmp, tmp1
integer :: o, o1, o2, p, nobsp
type(measurement), allocatable, dimension(:) :: tmpobs
if (nobs == 0) then
return
end if
if (allocated(pstart)) then
deallocate(pstart)
deallocate(pend)
end if
! find the very first obs of the right kind
!
o1 = 1
do while (trim(obs(o1) % id) /= trim(obstag) .and. o1 <= nobs)
o1 = o1 + 1
end do
if (o1 > nobs) then
return
end if
! find the very last obs of the right kind
!
o2 = nobs
do while (trim(obs(o2) % id) /= trim(obstag) .and. o2 >= 0)
o2 = o2 - 1
end do
nprof = 1
do o = 2, o2
if (obs(o) % ipiv /= obs(o - 1) % ipiv .or.&
obs(o) % jpiv /= obs(o - 1) % jpiv .or.&
obs(o) % date /= obs(o - 1) % date) then
nprof = nprof + 1
end if
end do
allocate(pstart(nprof))
allocate(pend(nprof))
! identify profiles
!
! PS: This is a tricky cycle but it seems it is doing the job. Do not
! meddle with it.
!
pend = 0
nprof = 1
pstart(1) = o1
do o = o1, o2
! find obs from the same profile
!
if (trim(obs(o) % id) == trim(obstag) .and.&
((obs(o) % i_orig_grid > 0 .and.&
obs(o) % i_orig_grid == obs(pstart(nprof)) % i_orig_grid) .or.&
(obs(o) % i_orig_grid <= 0 .and.&
obs(o) % ipiv == obs(pstart(nprof)) % ipiv .and.&
obs(o) % jpiv == obs(pstart(nprof)) % jpiv .and.&
obs(o) % date == obs(pstart(nprof)) % date))) then
pend(nprof) = o
cycle
end if
if (trim(obs(o) % id) /= trim(obstag)) then
print *, 'ERROR: insitu_setprofiles(): obs id does not match processed obs tag'
stop
end if
! if there were no obs of the right type in this profile yet,
! then pend(nprof) has not been set yet and therefore the condition
! below will yield "false"
!
if (pend(nprof) >= pstart(nprof)) then
nprof = nprof + 1
end if
if (PDEBUGINFO > 0) then
print *, ' DEBUG: new profile #', nprof, ', o =', o, ', id =', obs(o) % i_orig_grid
end if
pstart(nprof) = o
pend(nprof) = o
end do
if (pend(nprof) < pstart(nprof)) then
nprof = nprof - 1
end if
! truncate "pstat" and "pend" to length "nprof"
!
allocate(tmp(nprof))
tmp = pstart(1 : nprof)
deallocate(pstart)
allocate(pstart(nprof))
pstart = tmp
tmp = pend(1 : nprof)
deallocate(pend)
allocate(pend(nprof))
pend = tmp
deallocate(tmp)
! for glider data - sort observations in each profile by increasing depth
!
if (trim(obstag) == 'GSAL'.or. trim(obstag) == 'GTEM') then
allocate(tmp(nobs))
allocate(tmp1(nobs))
allocate(tmpobs(nobs))
do p = 1, nprof
nobsp = pend(p) - pstart(p) + 1
do o = 1, nobsp
tmp(o) = o
end do
!
! (using procedure from pre_local_analysis())
!
call order(dble(nobsp), obs(pstart(p) : pend(p)) % depth,&
dble(nobsp), tmp, tmp1)
tmpobs(1 : nobsp) = obs(pstart(p) : pend(p))
do o = 1, nobsp
obs(pstart(p) + o - 1) = tmpobs(tmp1(o))
end do
end do
deallocate(tmp, tmp1, tmpobs)
end if
end subroutine insitu_setprofiles
! 1. Sort out the obs within profiles so that they are stored in order of
! increasing depth.
! 2. Thin observations by keeping a single obs within a layer using the
! layers from the first ensemble member
!
subroutine insitu_prepareobs(obstag, nobs, obs)
character(*), intent(in) :: obstag
integer, intent(inout) :: nobs
type(measurement), dimension(:), intent(inout) :: obs
! profiles
!
integer, allocatable, dimension(:) :: pnow
integer :: nobs_max
integer :: p, o
type(measurement), allocatable, dimension(:) :: profile
integer, allocatable, dimension(:) :: ipiv, jpiv
real, allocatable, dimension(:) :: a1, a2, a3, a4
real, allocatable, dimension(:) :: z1, z2
integer :: nrev
integer :: ndel
integer :: oo
real :: rdummy
integer :: k, nk, ni, nj
character(80) :: fname
integer :: tlevel
real, allocatable, dimension(:, :) :: dz2d
real, dimension(2, 2) :: dz_cell
real :: dz, zcentre
integer :: best
logical :: isrogue
! As we thin the measurements within each layer, it still may be a good
! idea to update the obs error variance if the variability within the layer
! is big enough. `dmin' and `dmax' give the min and max measured values
! within the layer.
!
real :: dmin, dmax
real :: var1, var2
integer :: nobsnew, nobs_thistype, nobs_othertype
if (master) then
print '(a, a, a)', ' insitu_prepareobs(', trim(obstag), '):'
print '(a, i6)', ' total # of obs = ', nobs
end if
if (nobs == 0) then
return
end if
call insitu_setprofiles(trim(obstag), nobs, obs)
if (master) then
print '(a, a, a, i6)', ' # of obs of type "', trim(obstag), '" = ',&
sum(pend(1 : nprof) - pstart(1 : nprof)) + nprof
print '(a, i4)', ' # of profiles = ', nprof
end if
! find the maximal # of obs in a single profile
!
nobs_max = 0
do p = 1, nprof
nobs_max = max(nobs_max, pend(p) - pstart(p) + 1)
end do
if (master) then
print '(a, i4)', ' max # of obs in a profile before thinning = ', nobs_max
end if
! reverse the obs in profiles that go from bottom to surface
!
allocate(profile(nobs_max))
nrev = 0
do p = 1, nprof
if (obs(pstart(p)) % depth > obs(pend(p)) % depth) then
profile(1 : pend(p) - pstart(p) + 1) = obs(pstart(p) : pend(p))
do o = 0, pend(p) - pstart(p)
obs(pstart(p) + o) = profile(pend(p) - o)
end do
nrev = nrev + 1
end if
end do
deallocate(profile)
if (nrev > 0 .and. master) then
print *, ' ', nrev, ' profile(s) reversed'
end if
! check for rogue obs
!
ndel = 0
do p = 1, nprof
isrogue = .false.
do o = pstart(p) + 1, pend(p)
! shift the remaining obs in this profile one obs down
!
if (obs(o) % depth <= obs(o - 1) % depth) then
isrogue = .true.
do oo = o + 1, pend(p)
obs(oo - 1) = obs(oo)
end do
ndel = ndel + 1
pend(p) = pend(p) - 1
end if
end do
if (isrogue .and. master) then
print *, ' a rogue obs detected in profile # ', p
end if
end do
if (ndel > 0 .and. master) then
print *, ' ', ndel, 'rogue obs deleted'
end if
!
! Now to the thinning of the profiles.
!
allocate(ipiv(nprof))
allocate(jpiv(nprof))
allocate(a1(nprof))
allocate(a2(nprof))
allocate(a3(nprof))
allocate(a4(nprof))
ipiv = obs(pstart(1 : nprof)) % ipiv
jpiv = obs(pstart(1 : nprof)) % jpiv
a1 = obs(pstart(1 : nprof)) % a1
a2 = obs(pstart(1 : nprof)) % a2
a3 = obs(pstart(1 : nprof)) % a3
a4 = obs(pstart(1 : nprof)) % a4
! get the grid dimensions
!
call parse_blkdat('kdm ','integer', rdummy, nk)
call parse_blkdat('idm ','integer', rdummy, ni)
call parse_blkdat('jdm ','integer', rdummy, nj)
! get the data file name
!
if (trim(obstag) /= 'SAL' .and. trim(obstag) /= 'TEM' .and.&
trim(obstag) /= 'GSAL'.and. trim(obstag) /= 'GTEM') then
print *, 'ERROR: get_S(): unknown observation tag "', trim(obstag), '"'
stop
end if
fname = 'forecast001'
allocate(z1(nprof))
allocate(z2(nprof))
allocate(pnow(nprof))
allocate(dz2d(ni, nj))
! data thinning cycle
!
if (master) then
print *, ' maximum one observation per layer will be retained after thinning'
end if
tlevel = 1
z1 = 0.0
pnow = pstart
if (master .and. PDEBUGINFO > 0) then
p = PDEBUGINFO
print *, 'DEBUG dumping the info for profile #', p
print *, 'DEBUG p =', p, ': lon =', obs(pstart(p)) % lon, ', lat =', obs(pstart(p)) % lat
print *, 'DEBUG now dumping the layer depths:'
end if
! mark all obs of this type as bad; unmask the best obs within a layer
!
do o = 1, nobs
if (trim(obs(o) % id) == trim(obstag)) then
obs(o) % status = .false.
end if
end do
do k = 1, nk
call get_mod_fld_new(trim(fname), dz2d, 1, 'dp ', k, tlevel, ni, nj,0)
do p = 1, nprof
dz_cell(:, :) = dz2d(ipiv(p) : ipiv(p) + 1, jpiv(p) : jpiv(p) + 1)
dz = dz_cell(1, 1) * a1(p) + dz_cell(2, 1) * a2(p)&
+ dz_cell(1, 2) * a3(p) + dz_cell(2, 2) * a4(p)
dz = dz / ONEMETER
z2(p) = z1(p) + dz
zcentre = (z1(p) + z2(p)) / 2.0
best = -1
dmin = 1.0d+10
dmax = -1.0d+10
if (master .and. PDEBUGINFO > 0 .and. p == PDEBUGINFO) then
print *, 'DEBUG p =', p, ', k =', k, ', z =', z1(p), '-', z2(p)
end if
do while (pnow(p) <= pend(p))
o = pnow(p)
! check that the depth is within the layer
!
if (obs(o) % depth > z2(p)) then
! go to next profile; this obs will be dealt with when
! processing the next layer
exit
end if
! from this point on, the obs counter will be increased at the
! end of this loop
! store profile and layer number (overwrite the original profile
! id and vertical counter value)
!
obs(o) % i_orig_grid = p
obs(o) % j_orig_grid = k
obs(o) % h = z2(p) - z1(p)
if (obs(o) % depth < z1(p)) then
pnow(p) = pnow(p) + 1
cycle ! next obs
end if
! update `dmin' and `dmax'
!
dmin = min(dmin, obs(o) % d)
dmax = max(dmax, obs(o) % d)
if (best < 1) then
best = o
obs(best) % status = .true.
else if (abs(obs(o) % depth - zcentre) < abs(obs(best) % depth - zcentre)) then
obs(best) % status = .false. ! thrash the previous best obs
best = o
obs(best) % status = .true.
end if
pnow(p) = pnow(p) + 1
end do ! o
! update the observation error variance if the difference between
! `dmin' and `dmax' is big enough
!
if (best < 1) then
cycle
end if
if (.false.) then ! out for now; use the closest obs instead
if (dmax - dmin > 0) then
obs(best) % var = sqrt(obs(best) % var + ((dmax - dmin) / 2) ** 2)
end if
end if
end do ! p
z1 = z2
end do ! k
! There are a number of ways the vertical variability can be
! used for updating the obs error variance.
!
! Below, the following approach is used.
!
! Calculate two estimates for vertical gradient using the closest data
! points (if available). Estimate the difference at (VARCOEFF1 * h)
! vertical distance from the current obs, where VARCOEFF1 is the portion
! of the layer thickness (typically around 0.1-0.3), and h is the layer
! thickness. Use the square of this difference as an estimate for the
! respresentation error variance. If the closest obs is in another layer
! -- decrease this estimate by a factor of VARCOEFF2 (typically around 2).
! Use the largest estimate between the two (when both are avalaible).
!
do p = 1, nprof
do o = pstart(p), pend(p)
k = obs(o) % j_orig_grid
if (obs(o) % status) then
var1 = -999.0
var2 = -999.0
if (o - 1 >= pstart(p)) then
var1 = ((obs(o) % d - obs(o - 1) % d) /&
(obs(o) % depth - obs(o - 1) % depth) * obs(o) % h * VARCOEFF1) ** 2
if (obs(o - 1) % j_orig_grid /= k) then
var1 = var1 / VARCOEFF2
end if
end if
if (o + 1 <= pend(p)) then
var2 = ((obs(o) % d - obs(o + 1) % d) /&
(obs(o) % depth - obs(o + 1) % depth) * obs(o) % h * VARCOEFF1) ** 2
if (obs(o + 1) % j_orig_grid /= k) then
var2 = var2 / VARCOEFF2
end if
end if
if (var1 < 0.0 .and. var2 < 0.0) then
cycle
end if
obs(o) % var = obs(o) % var + max(var1, var2)
end if
end do
end do
if (master .and. PDEBUGINFO > 0) then
p = PDEBUGINFO
print *, 'DEBUG now dumping the obs info:'
do o = pstart(p), pend(p)
print *, 'DEBUG o =', o, ', status =', obs(o) % status, &
', d =', obs(o) % d, ', z =', obs(o) % depth,&
', k =', obs(o) % j_orig_grid, ', h =', obs(o) % h,&
', var =', obs(o) % var
end do
end if
deallocate(dz2d)
deallocate(pnow)
deallocate(z2)
deallocate(z1)
deallocate(a4)
deallocate(a3)
deallocate(a2)
deallocate(a1)
deallocate(jpiv)
deallocate(ipiv)
! now compact the obs array
!
nobsnew = 0
nobs_thistype = 0
nobs_othertype = 0
do o = 1, nobs
if (obs(o) % status) then
nobsnew = nobsnew + 1
obs(nobsnew) = obs(o)
if (trim(obs(o) % id) == trim(obstag)) then
nobs_thistype = nobs_thistype + 1
else
nobs_othertype = nobs_othertype + 1
end if
end if
end do
obs(nobsnew + 1 : nobs) % status = .false.
nobs = nobsnew
! replace the original profiles by the thinned ones
!
call insitu_setprofiles(trim(obstag), nobs, obs)
if (master) then
print *, ' thinning completed:', nobs_thistype, ' "', trim(obstag), '" obs retained'
if (nobs_othertype > 0) then
print *, ' ', nobs_othertype, 'obs of other type(s) retained'
end if
end if
end subroutine insitu_prepareobs
! Write profiles to a NetCDF file
!
subroutine insitu_writeprofiles(fname, obstag, nobs, obs)
use nfw_mod
character(*), intent(in) :: fname
character(*), intent(in) :: obstag
integer, intent(inout) :: nobs
type(measurement), dimension(:), intent(inout) :: obs
! profiles
!
integer :: p
integer :: npoints, npoints_max
! I/O
!
integer :: ncid
integer :: nprof_id(1), nk_id(1), dids(2)
integer :: lat_id, lon_id, ipiv_id, jpiv_id, npoints_id, depth_id, v_id, variance_id
character(STRLEN) :: varname
real(8), allocatable, dimension(:, :) :: v
if (.not. allocated(pstart)) then
call insitu_setprofiles(trim(obstag), nobs, obs)
end if
call nfw_create(fname, nf_write, ncid)
call nfw_def_dim(fname, ncid, 'nprof', nprof, nprof_id(1))
call nfw_def_var(fname, ncid, 'lat', nf_double, 1, nprof_id, lat_id)
call nfw_def_var(fname, ncid, 'lon', nf_double, 1, nprof_id, lon_id)
call nfw_def_var(fname, ncid, 'ipiv', nf_int, 1, nprof_id, ipiv_id)
call nfw_def_var(fname, ncid, 'jpiv', nf_int, 1, nprof_id, jpiv_id)
call nfw_def_var(fname, ncid, 'npoints', nf_int, 1, nprof_id, npoints_id)
npoints_max = maxval(pend - pstart) + 1
call nfw_def_dim(fname, ncid, 'nk', npoints_max, nk_id(1))
dids(1) = nk_id(1)
dids(2) = nprof_id(1)
call nfw_def_var(fname, ncid, 'depth', nf_double, 2, dids, depth_id)
if (trim(obstag) == 'SAL' .or. trim(obstag) == 'GSAL') then
varname = 'salt'
else if (trim(obstag) == 'TEM' .or. trim(obstag) == 'GTEM') then
varname = 'temp'
else
varname = trim(obstag)
end if
call nfw_def_var(fname, ncid, trim(varname), nf_double, 2, dids, v_id)
call nfw_def_var(fname, ncid, 'variance', nf_double, 2, dids, variance_id)
call nfw_enddef(fname, ncid)
call nfw_put_var_double(fname, ncid, lat_id, obs(pstart) % lat)
call nfw_put_var_double(fname, ncid, lon_id, obs(pstart) % lon)
call nfw_put_var_int(fname, ncid, ipiv_id, obs(pstart) % ipiv)
call nfw_put_var_int(fname, ncid, jpiv_id, obs(pstart) % jpiv)
call nfw_put_var_int(fname, ncid, npoints_id, pend - pstart + 1)
! depth
!
allocate(v(npoints_max, nprof))
v = -999.0
do p = 1, nprof
npoints = pend(p) - pstart(p) + 1
v(1 : npoints, p) = obs(pstart(p) : pend(p)) % depth
end do
call nfw_put_var_double(fname, ncid, depth_id, v)
! data
!
v = -999.0
do p = 1, nprof
npoints = pend(p) - pstart(p) + 1
v(1 : npoints, p) = obs(pstart(p) : pend(p)) % d
end do
call nfw_put_var_double(fname, ncid, v_id, v)
!call nfw_put_att_double(fname, ncid, v_id, '_FillValue',nf_double,1,-999.0)
!nfw_put_att_double(fname, ncid, varid, attname, type, length, v)
! data error variance
!
v = -999.0
do p = 1, nprof
npoints = pend(p) - pstart(p) + 1
v(1 : npoints, p) = obs(pstart(p) : pend(p)) % var
end do
call nfw_put_var_double(fname, ncid, variance_id, v)
call nfw_close(fname, ncid)
deallocate(v)
deallocate(pstart)
deallocate(pend)
end subroutine insitu_writeprofiles
! This subroutine appends the interpolated ensemble mean and the ensemble
! error variance to the assimilated profile data SAL.nc or TEM.nc. It also
! overwrites the observation error variance with latest values.
!
subroutine insitu_writeforecast(obstag, nobs, nrens, S, obs)
use nfw_mod
implicit none
character(*), intent(in) :: obstag
integer, intent(in) :: nobs
integer, intent(in) :: nrens
real, dimension(nobs, nrens), intent(in) :: S
type(measurement), dimension(nobs), intent(inout) :: obs
character(STRLEN) :: fname
real, dimension(nobs) :: Smean, Svar
integer :: i, p
integer :: ncid
integer :: dids(2)
integer :: v_id, variance_id
integer :: npoints_max, npoints
real(8), allocatable, dimension(:, :) :: v
! need to set profiles for the given observation type
!
call insitu_setprofiles(obstag, nobs, obs)
write(fname, '(a, ".nc")') trim(obstag)
print *, 'Appending interpolated forecast for "', trim(obstag),&
'" to "', trim(fname), '"'
Smean = sum(S, DIM = 2) / nrens
Svar = 0.0
do i = 1, nobs
Svar(i) = sum((S(i, :) - Smean(i)) ** 2)
end do
Svar = Svar / real(nrens - 1)
call nfw_open(fname, nf_write, ncid)
call nfw_inq_dimid(fname, ncid, 'nk', dids(1))
call nfw_inq_dimid(fname, ncid, 'nprof', dids(2))
call nfw_redef(fname, ncid)
call nfw_def_var(fname, ncid, 'forecast', nf_double, 2, dids, v_id)
call nfw_def_var(fname, ncid, 'forecast_variance', nf_double, 2, dids, variance_id)
call nfw_enddef(fname, ncid)
npoints_max = maxval(pend - pstart) + 1
allocate(v(npoints_max, nprof))
v = -999.0
do p = 1, nprof
npoints = pend(p) - pstart(p) + 1
v(1 : npoints, p) = Smean(pstart(p) : pend(p))
end do
call nfw_put_var_double(fname, ncid, v_id, v)
v = -999.0
do p = 1, nprof
npoints = pend(p) - pstart(p) + 1
v(1 : npoints, p) = Svar(pstart(p) : pend(p))
end do
call nfw_put_var_double(fname, ncid, variance_id, v)
! update observation error variance
!
call nfw_redef(fname, ncid)
call nfw_rename_var(fname, ncid, 'variance', 'variance_orig')
call nfw_def_var(fname, ncid, 'variance', nf_double, 2, dids, variance_id)
call nfw_enddef(fname, ncid)
v = -999.0
do p = 1, nprof
npoints = pend(p) - pstart(p) + 1
v(1 : npoints, p) = obs(pstart(p) : pend(p)) % var
end do
call nfw_put_var_double(fname, ncid, variance_id, v)
call nfw_close(fname, ncid)
deallocate(v)
end subroutine insitu_writeforecast
end module m_insitu