• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bohlinger / wavy / 14492389701

16 Apr 2025 12:11PM UTC coverage: 38.726% (-0.01%) from 38.74%
14492389701

Pull #83

github

fcollas
Added the ceil and floor collocation time option for the regridded collocation.
Pull Request #83: Modified terms for the collocation time argument in collocation module

0 of 9 new or added lines in 2 files covered. (0.0%)

6 existing lines in 1 file now uncovered.

2164 of 5588 relevant lines covered (38.73%)

0.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.52
/wavy/collocation_module.py
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
# ---------------------------------------------------------------------#
4
"""
5
- Module that should take care of collocation of points or swaths
6
- Needs input from modules that retrieve from observational platforms
7
  and models
8
"""
9
# --- import libraries ------------------------------------------------#
10
# standard library imports
11
import numpy as np
1✔
12
import netCDF4
1✔
13
from datetime import datetime, timedelta
1✔
14
import time
1✔
15
import pyresample
1✔
16
from tqdm import tqdm
1✔
17
from copy import deepcopy
1✔
18
import xarray as xr
1✔
19
import pandas as pd
1✔
20
import copy
1✔
21
#import traceback
22
import logging
1✔
23
#logging.basicConfig(level=logging.DEBUG)
24
logging.basicConfig(level=30)
1✔
25
logger = logging.getLogger(__name__)
1✔
26

27
# own imports
28
from wavy.utils import collocate_times
1✔
29
from wavy.utils import make_fc_dates
1✔
30
from wavy.utils import hour_rounder_pd, hour_rounder
1✔
31
from wavy.utils import NoStdStreams
1✔
32
from wavy.utils import parse_date
1✔
33
from wavy.utils import flatten
1✔
34
from wavy.utils import compute_quantiles
1✔
35
from wavy.utils import haversineA
1✔
36
from wavy.wconfig import load_or_default
1✔
37
from wavy.ncmod import ncdumpMeta, get_filevarname
1✔
38
from wavy.model_module import model_class as mc
1✔
39
from wavy.gridder_module import gridder_class as gc
1✔
40
from wavy.grid_stats import apply_metric
1✔
41
from wavy.quicklookmod import quicklook_class_sat as qls
1✔
42
from wavy.validationmod import validate, disp_validation
1✔
43
# ---------------------------------------------------------------------#
44

45
# read yaml config files:
46
model_dict = load_or_default('model_cfg.yaml')
1✔
47
insitu_dict = load_or_default('insitu_cfg.yaml')
1✔
48
variable_def = load_or_default('variable_def.yaml')
1✔
49

50

51
def collocate_observations(co1, co2, twin=5, dist_max=200):
1✔
52
    '''
53
     Collocates observations from two wavy objects, keeping only closest
54
     data from the second dataset within a given time range around each 
55
     observation from the first given dataset.
56

57
    Args:
58
        co1 (insitu_class or satellite_class object): first observation dataset
59
                                                    the data from the second
60
                                                    dataset will be collocated
61
                                                    around this one.
62
        co2 (insitu_class or satellite_class object): second observation 
63
                                                      dataset
64
        twin (int): time window length in minutes
65
        dist_max (int | float): Maximum distance
66
                                accepted to collocate
67
                                data, in km
68

69
    Returns:
70
        tuple (co1_filter (insitu_class or satellite_class object),
71
               co2_filter (insitu_class or satellite_class object)): first and
72
                                               second objects given as inputs, 
73
                                               with data filtered to keep only 
74
                                               collocated observations
75
    '''
76
    list_time_co2 = []
1✔
77
    list_time_co1 = []
1✔
78
    list_dist_min = []
1✔
79
    datetimes_co1 = [pd.to_datetime(t) for t in co1.vars['time'].values]
1✔
80

81
    for i, time_co1 in enumerate(datetimes_co1):
1✔
82

83
        # For each co1 measure, create a dataset
84
        # ds_tmp, of co2 data measured at times
85
        # between [time_co1 - twin, time_co1 + twin]
86
        time_sup = time_co1 + timedelta(minutes=twin)
1✔
87
        time_inf = time_co1 - timedelta(minutes=twin)
1✔
88
        ds_tmp = co2.vars.sel(time=slice(time_inf, time_sup))
1✔
89
        ds_tmp_size = ds_tmp.sizes['time']
1✔
90

91
        # if some co2 data are found
92
        if ds_tmp_size > 0:
1✔
93

94
            # Gets co1 corresponding datetime,
95
            # lat and lon and adds it to ds_tmp
96
            # as variables
97
            co1_vars_tmp = co1.vars.isel(time=i)
1✔
98
            lats_co1 = co1_vars_tmp['lats'].values
1✔
99
            lons_co1 = co1_vars_tmp['lons'].values
1✔
100

101
            ds_tmp = ds_tmp.assign(
1✔
102
                {
103
                    'time_co1': (
104
                        'time',
105
                        np.repeat(time_co1, ds_tmp_size)
106
                    ),
107
                    'lats_co1': (
108
                        'time',
109
                        np.repeat(lats_co1, ds_tmp_size)
110
                    ),
111
                    'lons_co1': (
112
                        'time',
113
                        np.repeat(lons_co1, ds_tmp_size)
114
                    )
115
                }
116
            )
117

118
            # Calculates distance between first and second
119
            # measurements and adds it as a variable to ds_tmp
120
            ds_tmp = ds_tmp.assign(
1✔
121
                {
122
                    'dist_co1_co2': (
123
                        'time',
124
                        haversineA(
125
                            ds_tmp['lons_co1'],
126
                            ds_tmp['lats_co1'],
127
                            ds_tmp['lons'],
128
                            ds_tmp['lats']
129
                        )[0]
130
                    )
131
                }
132
            )
133

134
            # Calculates the minimum value for distances
135
            min_dist_tmp = min(ds_tmp['dist_co1_co2'].values)
1✔
136

137
            # If the minimum value is lesser than defined max distance
138
            if min_dist_tmp <= dist_max:
1✔
139

140
                # Times for co1 and co2 measurements,
141
                # corresponding to the minimum distance are fetched
142
                dist = list(ds_tmp['dist_co1_co2'].values)
1✔
143
                ds_tmp = ds_tmp.isel(time=dist.index(min_dist_tmp))
1✔
144
                list_time_co2.append(ds_tmp['time'].data)
1✔
145
                list_dist_min.append(min_dist_tmp)
1✔
146
                list_time_co1.append(ds_tmp['time_co1'].data)
1✔
147

148
    co1_filter = copy.deepcopy(co1)
1✔
149
    co2_filter = copy.deepcopy(co2)
1✔
150

151
    # Original co1 and co2 object vars are filtered using
152
    # lists of times corresponding to minimum distance
153
    # between co1 and co2 observation
154
    co2_filter.vars = co2_filter.vars.sel(time=list_time_co2)
1✔
155
    co2_filter.vars = co2_filter.vars.assign(
1✔
156
        {
157
            'colloc_dist': (
158
                'time',
159
                list_dist_min
160
            )
161
        }
162
    )
163
    co1_filter.vars = co1_filter.vars.sel(time=list_time_co1)
1✔
164

165
    return co1_filter, co2_filter 
1✔
166

167

168
def collocation_fct(obs_lons, obs_lats, model_lons, model_lats):
1✔
169
    grid = pyresample.geometry.GridDefinition(
1✔
170
                                lats=model_lats,
171
                                lons=model_lons)
172
    # Define some sample points
173
    swath = pyresample.geometry.SwathDefinition(lons=obs_lons,
1✔
174
                                                lats=obs_lats)
175
    # Determine nearest (great circle distance) neighbour in the grid.
176
    # valid_input_index, valid_output_index, index_array, distance_array = \
177
    _, valid_output_index, index_array, distance_array = \
1✔
178
                            pyresample.kd_tree.get_neighbour_info(
179
                                source_geo_def=grid,
180
                                target_geo_def=swath,
181
                                radius_of_influence=1000000000,
182
                                neighbours=1)
183
    # get_neighbour_info() returns indices in the
184
    # flattened lat/lon grid. Compute the 2D grid indices:
185
    index_array_2d = np.unravel_index(index_array, grid.shape)
1✔
186
    return index_array_2d, distance_array, valid_output_index
1✔
187

188

189
def get_model_filename(nID, d, leadtime, **kwargs):
1✔
190
    mco = mc(nID=nID, sd=d, ed=d, leadtime=leadtime)
1✔
191
    return mco._make_model_filename_wrapper(parse_date(str(d)),
1✔
192
                                            leadtime, **kwargs)
193

194

195
def find_valid_fc_dates_for_model_and_leadtime(fc_dates, model,
1✔
196
                                               leadtime, colloc_time_method,
197
                                               **kwargs):
198
    '''
199
    Finds valid dates that are close to desired dates at a precision
200
    of complete hours
201
    '''
202
    #fc_dates_new = hour_rounder_pd(fc_dates)
203

204
    dt_tmp = [parse_date(str(d)) for d in fc_dates]
1✔
205
    fc_dates_new = [hour_rounder(d, method=colloc_time_method) for d in dt_tmp]
1✔
206

207
    fc_dates_new = np.unique(fc_dates_new)
1✔
208
    #if (leadtime is None or leadtime == 'best'):
209
    #    pass
210
    #else:
211
    fc_dates_new = [d for d in fc_dates_new
1✔
212
                    if get_model_filename(model, d, leadtime, **kwargs)
213
                    is not None]
214
    return fc_dates_new
1✔
215

216

217
def check_if_file_is_valid(fc_date, model, leadtime, **kwargs):
1✔
218
    fname = get_model_filename(model, fc_date, leadtime, **kwargs)
×
219
    print('Check if requested file:\n', fname, '\nis available and valid')
×
220
    try:
×
221
        nc = netCDF4.Dataset(fname, mode='r')
×
222
        time = nc.variables['time']
×
223
        dt = netCDF4.num2date(time[:], time.units)
×
224
        if fc_date in list(dt):
×
225
            print('File is available and contains requested date')
×
226
            return True
×
227
        else:
228
            print('Desired date ' + str(fc_date) + ' is not in', fname)
×
229
            return False
×
230
    except (FileNotFoundError, OSError) as e:
×
231
        print('File is not available or does not contain requested date')
×
232
        print(e)
×
233
        return False
×
234

235

236
def get_closest_date(overdetermined_lst, target_lst):
1✔
237
    idx = []
×
238
    for i in range(len(target_lst)):
×
239
        diffs = np.abs([(target_lst[i]
×
240
                        - overdetermined_lst[j]).total_seconds()
241
                        for j in range(len(overdetermined_lst))])
242
        mindiff = np.min(diffs)
×
243
        idx.append(list(diffs).index(mindiff))
×
244
    return idx
×
245

246

247
def adjust_dict_for_idx(indict, idx, excl_keys_lst):
1✔
248
    outdict = deepcopy(indict)
×
249
    for k in indict.keys():
×
250
        if k not in excl_keys_lst:
×
251
            outdict[k] = np.array(indict[k])[idx]
×
252
    return outdict
×
253

254

255
class collocation_class(qls):
1✔
256
    '''
257
    draft of envisioned collocation class object
258
    '''
259

260
    def __init__(self, oco=None, model=None, poi=None,
1✔
261
    distlim=None, leadtime=None, **kwargs):
262
        print('# ----- ')
1✔
263
        print(" ### Initializing collocation_class object ###")
1✔
264
        print(" ")
1✔
265
        # make clones to prevent overwriting
266
        self.varalias = oco.varalias
1✔
267
        self.varalias_obs = oco.varalias
1✔
268
        self.varalias_mod = kwargs.get('varalias', oco.varalias)
1✔
269
        self.model = model
1✔
270
        self.leadtime = leadtime
1✔
271
        self.oco = oco
1✔
272
        self.nID = oco.nID
1✔
273
        self.model = model
1✔
274
        self.obstype = str(type(oco))[8:-2]
1✔
275
        self.stdvarname = oco.stdvarname
1✔
276
        self.region = oco.region
1✔
277
        self.units = variable_def[self.varalias].get('units')
1✔
278
        self.sd = oco.sd
1✔
279
        self.ed = oco.ed
1✔
280
        self.twin = kwargs.get('twin', oco.twin)
1✔
281
        self.distlim = kwargs.get('distlim', 6)
1✔
282
        self.method = kwargs.get('method', 'closest')
1✔
283
        self.colloc_time_method = kwargs.get('colloc_time_method', 'nearest')
1✔
284
        print(" ")
1✔
285
        print(" ### Collocation_class object initialized ###")
1✔
286
        
287
    def populate(self, **kwargs):
1✔
288
    
289
        new = deepcopy(self)
1✔
290
        print(" ")
1✔
291
        print(" ## Collocate ... ")
1✔
292
        #for i in range(1):
293
        try:
1✔
294
            t0 = time.time()
1✔
295
            results_dict = new.collocate(**kwargs)
1✔
296
            new.model_time = results_dict['model_time']
1✔
297
            # build xarray dataset from results
298
            ds = new._build_xr_dataset(results_dict)
1✔
299
            ds = ds.assign_coords(time=ds.time.values)
1✔
300
            new.vars = ds
1✔
301
            new = new._drop_duplicates(**kwargs)
1✔
302
            t1 = time.time()
1✔
303
            print(" ")
1✔
304
            print(" ## Summary:")
1✔
305
            print(len(new.vars['time']), " values collocated.")
1✔
306
            print("Time used for collocation:", round(t1-t0, 2), "seconds")
1✔
307
            print(" ")
1✔
308

309
        except Exception as e:
×
310
            print(e)
×
311
            new.error = e
×
312
            new.vars = None
×
313
            print("! collocation_class object may be empty !")
×
314
        # add class variables
315
        print('# ----- ')
1✔
316
        
317
        return new
1✔
318

319
    def _build_xr_dataset(self, results_dict):
1✔
320
        ds = xr.Dataset({
1✔
321
                'time': xr.DataArray(
322
                        data=results_dict['obs_time'],
323
                        dims=['time'],
324
                        coords={'time': results_dict['obs_time']},
325
                        attrs=variable_def['time'],
326
                        ),
327
                'dist': xr.DataArray(
328
                        data=results_dict['dist'],
329
                        dims=['time'],
330
                        coords={'time': results_dict['obs_time']},
331
                        attrs=variable_def['dist'],
332
                        ),
333
                'obs_values': xr.DataArray(
334
                        data=results_dict['obs_values'],
335
                        dims=['time'],
336
                        coords={'time': results_dict['obs_time']},
337
                        attrs=variable_def[self.varalias],
338
                        ),
339
                'obs_lons': xr.DataArray(
340
                        data=results_dict['obs_lons'],
341
                        dims=['time'],
342
                        coords={'time': results_dict['obs_time']},
343
                        attrs=variable_def['lons'],
344
                        ),
345
                'obs_lats': xr.DataArray(
346
                        data=results_dict['obs_lats'],
347
                        dims=['time'],
348
                        coords={'time': results_dict['obs_time']},
349
                        attrs=variable_def['lats'],
350
                        ),
351
                'model_values': xr.DataArray(
352
                        data=results_dict['model_values'],
353
                        dims=['time'],
354
                        coords={'time': results_dict['obs_time']},
355
                        attrs=variable_def[self.varalias],
356
                        ),
357
                'model_lons': xr.DataArray(
358
                        data=results_dict['model_lons'],
359
                        dims=['time'],
360
                        coords={'time': results_dict['obs_time']},
361
                        attrs=variable_def['lons'],
362
                        ),
363
                'model_lats': xr.DataArray(
364
                        data=results_dict['model_lats'],
365
                        dims=['time'],
366
                        coords={'time': results_dict['obs_time']},
367
                        attrs=variable_def['lats'],
368
                        ),
369
                'colidx_x': xr.DataArray(
370
                        data=results_dict['collocation_idx_x'],
371
                        dims=['time'],
372
                        coords={'time': results_dict['obs_time']},
373
                        attrs=variable_def['colidx_x'],
374
                        ),
375
                'colidx_y': xr.DataArray(
376
                        data=results_dict['collocation_idx_y'],
377
                        dims=['time'],
378
                        coords={'time': results_dict['obs_time']},
379
                        attrs=variable_def['colidx_y'],
380
                        ),
381
                    },
382
                attrs={'title': str(type(self))[8:-2] + ' dataset'}
383
                )
384
        return ds
1✔
385

386
    def _drop_duplicates(self, **kwargs):
1✔
387
        
388
        dim = kwargs.get('dim_duplicates', 'time')
1✔
389
        keep = kwargs.get('keep_duplicates', 'first')
1✔
390
        print('Removing duplicates according to', dim)
1✔
391
        print('Keeping', keep, 'value for the duplicates')
1✔
392
        new = deepcopy(self)
1✔
393
        new.vars = self.vars.drop_duplicates(dim=dim, keep=keep)
1✔
394
        print(str(int(abs(len(self.vars[dim])-len(new.vars[dim])))),
1✔
395
              'values removed')
396
        print('New number of footprints is:', str(int(len(new.vars[dim]))))
1✔
397
        return new
1✔
398

399
    def _collocate_field(self, mco, tmp_dict, **kwargs):
1✔
400
        """
401
        Some info
402
        """
403
        Mlons = mco.vars.lons.data
1✔
404
        Mlats = mco.vars.lats.data
1✔
405
        Mvars = mco.vars[mco.varalias].data
1✔
406
        if len(Mlons.shape) > 2:
1✔
407
            Mlons = Mlons[0, :].squeeze()
1✔
408
            Mlats = Mlats[0, :].squeeze()
1✔
409
            Mvars = Mvars[0, :].squeeze()
1✔
410
        elif len(Mlons.shape) == 2:
×
411
            Mlons = Mlons.squeeze()
×
412
            Mlats = Mlats.squeeze()
×
413
            Mvars = Mvars.squeeze()
×
414
        elif len(Mlons.shape) == 1:
×
415
            Mlons, Mlats = np.meshgrid(Mlons, Mlats)
×
416
            Mvars = Mvars.squeeze()
×
417
            assert len(Mlons.shape) == 2
×
418
        obs_vars = tmp_dict[self.varalias_obs]
1✔
419
        obs_lons = tmp_dict['lons']
1✔
420
        obs_lats = tmp_dict['lats']
1✔
421
        # Compare wave heights of satellite with model with
422
        # constraint on distance and time frame
423
        print("Perform collocation with distance limit\n",
1✔
424
              "distlim:", self.distlim)
425
        index_array_2d, distance_array, _ =\
1✔
426
                                    collocation_fct(
427
                                    obs_lons, obs_lats,
428
                                    Mlons, Mlats)
429
        # caution: index_array_2d is tuple
430
        # impose distlim
431
        dist_idx = np.where((distance_array < self.distlim*1000) &
1✔
432
                            (~np.isnan(Mvars[index_array_2d[0],
433
                             index_array_2d[1]])))[0]
434
        idx_x = index_array_2d[0][dist_idx]
1✔
435
        idx_y = index_array_2d[1][dist_idx]
1✔
436
        results_dict = {
1✔
437
                'dist': list(distance_array[dist_idx]),
438
                'model_values': list(Mvars[idx_x, idx_y]),
439
                'model_lons': list(Mlons[idx_x, idx_y]),
440
                'model_lats': list(Mlats[idx_x, idx_y]),
441
                'obs_values': list(obs_vars[dist_idx]),
442
                'obs_lons': list(obs_lons[dist_idx]),
443
                'obs_lats': list(obs_lats[dist_idx]),
444
                'collocation_idx_x': list(idx_x),
445
                'collocation_idx_y': list(idx_y),
446
                'time': tmp_dict['time'][dist_idx]
447
                }
448
        return results_dict
1✔
449

450
    def _collocate_track(self, **kwargs):
1✔
451
        """
452
        Some info
453
        """
454
        print("run: _collocate_track")
1✔
455

456
        # use only dates with a model time step closeby
457
        # given the time constrains
458
        print('Filtering valid dates ...')
1✔
459
        t1 = time.time()
1✔
460

461
        ndt = self.oco.vars.time.data
1✔
462
        ndt_datetime = [parse_date(str(d)) for d in ndt]
1✔
463

464
        ndt_valid = find_valid_fc_dates_for_model_and_leadtime(
1✔
465
                                    ndt, self.model, self.leadtime,
466
                                    self.colloc_time_method, **kwargs)
467

468
        ndt_valid = np.unique(ndt_valid)
1✔
469

470
        fc_date = ndt_valid
1✔
471
        t2 = time.time()
1✔
472

473
        print(f'... done, used {t2-t1:.2f} seconds')
1✔
474

475
        print("Start collocation ...")
1✔
476
        results_dict = {
1✔
477
                'model_time': [],
478
                'obs_time': [],
479
                'dist': [],
480
                'model_values': [],
481
                'model_lons': [],
482
                'model_lats': [],
483
                'obs_values': [],
484
                'obs_lons': [],
485
                'obs_lats': [],
486
                'collocation_idx_x': [],
487
                'collocation_idx_y': [],
488
                }
489

490
        for i in tqdm(range(len(fc_date))):
1✔
491
            print(fc_date[i])
1✔
492
        #for i in range(len(fc_date)):
493
            try:
1✔
494
                for j in range(1):
1✔
495
                #with NoStdStreams():
496
                    # filter needed obs within time period
497
                    target_date = [parse_date(str(fc_date[i]))]
1✔
498
                    
499
                    # if method is 'nearest', get the values that fall within
500
                    # a time window of +/- 30 minutes of model time by default 
501
                    if self.colloc_time_method=='nearest':
1✔
502
                        idx = collocate_times(ndt_datetime,
1✔
503
                                              target_t=target_date,
504
                                              twin=self.twin)
505
                    # if method is 'floor' get the values that fall between
506
                    # model time and model time + 1 hour
NEW
507
                    elif self.colloc_time_method=='floor':
×
508
                        sdate_colloc = target_date[0]
×
509
                        edate_colloc = target_date[0] + timedelta(hours=1)
×
510
                        idx = collocate_times(ndt_datetime,
×
511
                                              target_t=target_date,
512
                                              sdate=sdate_colloc,
513
                                              edate=edate_colloc,
514
                                              twin=0)
515
                    # if method is 'ceil' get the values that fall between
516
                    # model time - 1 hour and model time
NEW
517
                    elif self.colloc_time_method=='ceil':
×
518
                        sdate_colloc = target_date[0] - timedelta(hours=1)
×
519
                        edate_colloc = target_date[0] 
×
520
                        idx = collocate_times(ndt_datetime,
×
521
                                              target_t=target_date,
522
                                              sdate=sdate_colloc,
523
                                              edate=edate_colloc,
524
                                              twin=0)                                
525

526
                    print(len(idx), "footprints to be collocated")
1✔
527
                    # make tmp obs_obj with filtered data
528
                    tmp_dict = {}
1✔
529
                    tmp_dict['time'] = self.oco.vars['time'].values[idx]
1✔
530
                    tmp_dict['lats'] = self.oco.vars['lats'].values[idx]
1✔
531
                    tmp_dict['lons'] = self.oco.vars['lons'].values[idx]
1✔
532
                    tmp_dict[self.oco.varalias] = \
1✔
533
                        self.oco.vars[self.oco.varalias].values[idx]
534
                    mco = mc(sd=fc_date[i], ed=fc_date[i], nID=self.model,
1✔
535
                             leadtime=self.leadtime, varalias=self.varalias_mod,
536
                             **kwargs)
537
                    mco = mco.populate(**kwargs)
1✔
538
                    results_dict_tmp = self._collocate_field(
1✔
539
                                            mco, tmp_dict, **kwargs)
540
                    if (len(results_dict_tmp['model_values']) > 0):
1✔
541
                        # append to dict
542
                        results_dict['model_time'].append(fc_date[i])
1✔
543
                        results_dict['obs_time'].append(
1✔
544
                                results_dict_tmp['time'])
545
                        results_dict['dist'].append(
1✔
546
                                results_dict_tmp['dist'])
547
                        results_dict['model_values'].append(
1✔
548
                                results_dict_tmp['model_values'])
549
                        results_dict['model_lons'].append(
1✔
550
                                results_dict_tmp['model_lons'])
551
                        results_dict['model_lats'].append(
1✔
552
                                results_dict_tmp['model_lats'])
553
                        results_dict['obs_values'].append(
1✔
554
                                results_dict_tmp['obs_values'])
555
                        results_dict['obs_lats'].append(
1✔
556
                                results_dict_tmp['obs_lats'])
557
                        results_dict['obs_lons'].append(
1✔
558
                                results_dict_tmp['obs_lons'])
559
                        results_dict['collocation_idx_x'].append(
1✔
560
                                        results_dict_tmp['collocation_idx_x'])
561
                        results_dict['collocation_idx_y'].append(
1✔
562
                                        results_dict_tmp['collocation_idx_y'])
563
                    else:
564
                        pass
×
565
                    if 'results_dict_tmp' in locals():
1✔
566
                        del results_dict_tmp
1✔
567
            except (ValueError, FileNotFoundError, OSError) as e:
×
568
                # ValueError, pass if no collocation
569
                # FileNotFoundError, pass if file not accessible
570
                # OSError, pass if file not accessible from thredds
571
                logger.exception(e)
×
572
                print(e)
×
573
        # flatten all aggregated entries
574
        results_dict['model_time'] = results_dict['model_time']
1✔
575
        results_dict['obs_time'] = flatten(results_dict['obs_time'])
1✔
576
        results_dict['dist'] = flatten(results_dict['dist'])
1✔
577
        results_dict['model_values'] = flatten(results_dict['model_values'])
1✔
578
        results_dict['model_lons'] = flatten(results_dict['model_lons'])
1✔
579
        results_dict['model_lats'] = flatten(results_dict['model_lats'])
1✔
580
        results_dict['obs_values'] = flatten(results_dict['obs_values'])
1✔
581
        results_dict['obs_lats'] = flatten(results_dict['obs_lats'])
1✔
582
        results_dict['obs_lons'] = flatten(results_dict['obs_lons'])
1✔
583
        results_dict['collocation_idx_x'] = flatten(
1✔
584
                                    results_dict['collocation_idx_x'])
585
        results_dict['collocation_idx_y'] = flatten(
1✔
586
                                results_dict['collocation_idx_y'])
587
        return results_dict
1✔
588

589
    def _collocate_centered_model_value(self, time, lon, lat, **kwargs):
1✔
590

591
        #(time, lon, lat, nID_model, name_model, res):
592
   
593
        nID_model = self.model
×
594
        name_model = self.model 
×
595
        res = kwargs.get('res', (0.5,0.5))
×
NEW
596
        colloc_time_method = self.colloc_time_method
×
597
        
598
        print('Using resolution {}'.format(res))
×
599
        # ADD CHECK LIMITS FOR LAT AND LON
600
        res_dict = {}
×
601

NEW
602
        time = pd.to_datetime(time)
×
NEW
603
        time = hour_rounder(time, method=colloc_time_method)
×
604
        
NEW
605
        mco = mc(sd=time, ed=time,
×
606
                 nID=nID_model, name=name_model,
607
                 max_lt=12).populate(twin=5) # ADD AS PARAMETERS
608
   
609
        bb = (lon - res[0]/2, 
×
610
              lon + res[0]/2, 
611
              lat - res[1]/2, 
612
              lat + res[1]/2)
613

614
        gco = gc(lons=mco.vars.lons.squeeze().values.ravel(),
×
615
                 lats=mco.vars.lats.squeeze().values.ravel(),
616
                 values=mco.vars.Hs.squeeze().values.ravel(),
617
                 bb=bb, res=res,
618
                 varalias=mco.varalias,
619
                 units=mco.units,
620
                 sdate=mco.vars.time,
621
                 edate=mco.vars.time)
622
    
623
        gridvar, lon_grid, lat_grid = apply_metric(gco=gco)
×
624
    
625
        ts = gridvar['mor'].flatten()
×
626
        lon_flat = lon_grid.flatten()
×
627
        lat_flat = lat_grid.flatten()
×
628
    
629
        res_dict['hs'] = ts[0]
×
630
        res_dict['lon'] = lon_flat[0]
×
631
        res_dict['lat'] = lat_flat[0]
×
NEW
632
        res_dict['time'] = time
×
633

634
        return res_dict
×
635

636
    def _collocate_regridded_model(self, **kwargs):
1✔
637
    
638
        from joblib import Parallel, delayed
×
639
    
640
        hs_mod_list=[] 
×
641
        lon_mod_list=[]
×
642
        lat_mod_list=[]
×
643
        time_mod_list=[]
×
644
        
645
        nproc = kwargs.get('nproc', 16)
×
646
        
647
        oco_vars = self.oco.vars
×
648

649
        length = len(oco_vars.time.values)
×
650
        
651
        #Parallel should be optional, with nproc as parameter
652
        colloc_mod_list = Parallel(n_jobs=nproc)(
×
653
                               delayed(self._collocate_centered_model_value) (
654
                                           oco_vars.time.values[i],
655
                                           oco_vars.lons.values[i],
656
                                           oco_vars.lats.values[i],
657
                                           **kwargs) for i in range(length)
658
                                           )
659

660
        length_colloc_mod_list = len(colloc_mod_list)
×
661
        hs_mod_list = [colloc_mod_list[i]['hs'] for i in \
×
662
                       range(length_colloc_mod_list)]
663
        lon_mod_list = [colloc_mod_list[i]['lon'] for i in \
×
664
                       range(length_colloc_mod_list)]
665
        lat_mod_list = [colloc_mod_list[i]['lat'] for i in \
×
666
                       range(length_colloc_mod_list)]
667
        time_mod_list = [colloc_mod_list[i]['time'] for i in \
×
668
                       range(length_colloc_mod_list)]
669

670
        mod_colloc_vars = xr.Dataset(
×
671
            {
672
             "lats": (
673
                 ("time"),
674
                 lat_mod_list,
675
             ),
676
             "lons": (
677
                 ("time"), 
678
                 lon_mod_list
679
             ),
680
             "Hs": (
681
                 ("time"),
682
                 hs_mod_list
683
             )
684
          },
685
         coords={"time": time_mod_list},
686
        )
687

688
        results_dict = {
×
689
            'model_time': time_mod_list,
690
            'obs_time': oco_vars.time.values,
691
            'dist': [0]*length,
692
            'model_values': hs_mod_list,
693
            'model_lons': lon_mod_list,
694
            'model_lats': lat_mod_list,
695
            'obs_values': oco_vars.Hs.values,
696
            'obs_lons': oco_vars.lons.values,
697
            'obs_lats': oco_vars.lats.values,
698
            'collocation_idx_x': [0]*length,
699
            'collocation_idx_y': [0]*length,
700
            }
701
        
702
        return results_dict
×
703

704
    def collocate(self, **kwargs):
1✔
705
        """
706
        get obs value for model value for given
707
            temporal and spatial constraints
708
        """
709
        if (self.oco is None and len(self.oco.vars[self.oco.stdvarname]) < 1):
1✔
710
            raise Exception('\n###\n'
×
711
                    + 'Collocation not possible, '
712
                    + 'no observation values for collocation!'
713
                    + '\n###')
714
        if (self.model is None):
1✔
715
            raise Exception ('\n###\n'
×
716
                            +'Collocation not possible, '
717
                            +'no model available for collocation!'
718
                            +'\n###'
719
                            )
720
        if ((self.model is not None) and (self.oco is not None)):
1✔
721
            if self.method == 'closest':
1✔
722
                results_dict = self._collocate_track(**kwargs)
1✔
723
            elif self.method == 'regridded':
×
724
                results_dict = self._collocate_regridded_model(**kwargs)
×
725

726
        return results_dict
1✔
727

728

729
    def validate_collocated_values(self, **kwargs):
1✔
730
        times = self.vars['time']
×
731
        dtime = [parse_date(str(t.data)) for t in times]
×
732
        mods = self.vars['model_values']
×
733
        obs = self.vars['obs_values']
×
734
        sdate = dtime[0]
×
735
        edate = dtime[-1]
×
736
        validation_dict = validate_collocated_values(
×
737
                                dtime, obs, mods,
738
                                sdate=sdate, edate=edate,
739
                                **kwargs)
740
        return validation_dict
×
741

742

743
def validate_collocated_values(dtime, obs, mods, **kwargs):
1✔
744
    target_t, sdate, edate, twin = None, None, None, None
×
745
    if ('col_obj' in kwargs.keys() and kwargs['col_obj'] is not None):
×
746
        col_obj = kwargs['col_obj']
×
747
        mods = col_obj.vars['model_values']
×
748
        obs = col_obj.vars['obs_values']
×
749
        dtime = col_obj.vars['time']
×
750
    # get idx for date and twin
751
    if 'target_t' in kwargs.keys():
×
752
        target_t = kwargs['target_t']
×
753
    if 'sdate' in kwargs.keys():
×
754
        sdate = kwargs['sdate']
×
755
    if 'edate' in kwargs.keys():
×
756
        edate = kwargs['edate']
×
757
    if 'twin' in kwargs.keys():
×
758
        twin = kwargs['twin']
×
759
    idx = collocate_times(dtime,
×
760
                          target_t=target_t,
761
                          sdate=sdate,
762
                          edate=edate,
763
                          twin=twin)
764
    mods = np.array(mods)[idx]
×
765
    obs = np.array(obs)[idx]
×
766
    results_dict = {'model_values': mods, 'obs_values': obs}
×
767

768
    # validate
769
    validation_dict = validate(results_dict)
×
770
    disp_validation(validation_dict)
×
771

772
    return validation_dict
×
773

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc