dfm-tools 0.10.55__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dfm_tools/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ """
2
+ .. include:: ../README.md
3
+ """
4
+
5
+ __author__ = """Jelmer Veenstra"""
6
+ __email__ = 'jelmer.veenstra@deltares.nl'
7
+ __version__ = '0.10.55'
8
+
9
+ from dfm_tools.deprecated import *
10
+ from dfm_tools.errors import *
11
+ from dfm_tools.download import *
12
+ from dfm_tools.get_nc import *
13
+ from dfm_tools.get_nc_helpers import *
14
+ from dfm_tools.hydrolib_helpers import *
15
+ from dfm_tools.meshkernel_helpers import *
16
+ from dfm_tools.interpolate_grid2bnd import *
17
+ from dfm_tools.linebuilder import *
18
+ from dfm_tools.modplot import *
19
+ from dfm_tools.regulargrid import *
20
+ from dfm_tools.xarray_helpers import *
21
+ from dfm_tools.energy_dissipation import *
22
+ from dfm_tools.bathymetry import *
23
+ #from dfm_tools.modelbuilder import * #commented since we do not want to expose these functions with hardcoded parameters
24
+
25
+ import warnings
26
+ warnings.filterwarnings('always',category=DeprecationWarning)
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon May 31 17:17:09 2021
4
+
5
+ @author: veenstra
6
+ """
7
+
8
+ import numpy as np
9
+
10
+
11
+ def write_bathy_toasc(filename_asc,lon_sel_ext,lat_sel_ext,elev_sel_ext,asc_fmt='%9.2f',nodata_val=32767):
12
+
13
+ print('writing to asc file')
14
+ if elev_sel_ext.shape != (lat_sel_ext.shape[0], lon_sel_ext.shape[0]):
15
+ raise ValueError('resulting shape of elev_sel_ext %s is not consistent with lat_sel_ext/lon_sel_ext vars %s'%(elev_sel_ext.shape,(lat_sel_ext.shape[0], lon_sel_ext.shape[0])))
16
+ if isinstance(elev_sel_ext,np.ma.core.MaskedArray): #masked has to be filled in order for the nans to be visible
17
+ elev_sel_ext = elev_sel_ext.filled(np.nan)
18
+ if np.isnan(elev_sel_ext).sum()>0:
19
+ elev_sel_ext = elev_sel_ext.copy()
20
+ elev_sel_ext[np.isnan(elev_sel_ext)] = nodata_val
21
+ print('replaced nan values with %d'%(nodata_val))
22
+ resinv_lonlat = np.round(1/np.diff(lon_sel_ext[:2]),2)
23
+ resinv_lat = np.round(1/np.diff(lat_sel_ext[:2]),2)
24
+ if resinv_lonlat!=resinv_lat:
25
+ raise ValueError('inconsistent resolution over lat/lon')
26
+
27
+ with open(filename_asc,'w') as file_asc:
28
+ file_asc.write('ncols %d\n'%(elev_sel_ext.shape[1]))
29
+ file_asc.write('nrows %d\n'%(elev_sel_ext.shape[0]))
30
+ file_asc.write('xllcenter %13.8f\n'%(lon_sel_ext[0]))
31
+ file_asc.write('yllcenter %13.8f\n'%(lat_sel_ext[0]))
32
+ file_asc.write('cellsize %16.11f\n'%(1/resinv_lonlat))
33
+ #file_asc.write('NODATA_value %d\n'%(nodata_val))
34
+ file_asc.write('NODATA_value '+asc_fmt%(nodata_val)+'\n')
35
+ with open(filename_asc,'a') as file_asc:
36
+ np.savetxt(file_asc,np.flip(elev_sel_ext,axis=0),fmt=asc_fmt)
37
+ print('...finished')
38
+
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sun Apr 23 22:42:27 2023
4
+
5
+ @author: veenstra
6
+ """
7
+
8
+
9
+ def plot_background(ax=None, projection=None, google_style='satellite', resolution=1, features=None, nticks=6, latlon_format=False, gridlines=False, **kwargs):
10
+ raise DeprecationWarning('dfmt.plot_background() is deprecated, use contextily and cartopy instead like in https://github.com/Deltares/dfm_tools/blob/main/tests/examples_workinprogress/workinprogress_cartopy_satellite_coastlines.py')
11
+
12
+
13
+ def get_ugrid_verts(data_xr_map):
14
+ """
15
+ getting ugrid verts from xugrid mapfile.
16
+ """
17
+ raise DeprecationWarning('dfmt.get_ugrid_verts() is deprecated, use uds.grid.face_node_coordinates instead (https://github.com/Deltares/xugrid/issues/48)')
18
+
19
+
20
+ def scatter_to_regulargrid(xcoords, ycoords, values, ncellx=None, ncelly=None, reg_x_vec=None, reg_y_vec=None, method='nearest', maskland_dist=None):
21
+ raise DeprecationWarning('dfm_tools.regulargrid.scatter_to_regulargrid() is deprecated, use ds = dfmt.rasterize_ugrid(uds) instead')
22
+
23
+
24
+ def get_varnamefromattrs(data_xr, varname):
25
+ raise DeprecationWarning('dfmt.get_varnamefromattrs() will be deprecated in a future version of dfm_tools, ds=dfmt.rename_waqvars(ds) is a more convenient alternative')
26
+
27
+
28
+ class Polygon:
29
+ def __init__(self, data, name, comments):
30
+ raise DeprecationWarning('the function dfm_tools.polygon.Polygon() is deprecated, please use the new hydrolib alternative.')
31
+
32
+ def fromfile(self, file_pol, pd_output=False, tekmap_output=False):
33
+ raise DeprecationWarning('the function dfm_tools.polygon.Polygon.fromfile() is deprecated, please use the new hydrolib alternative. Example script: https://github.com/Deltares/dfm_tools/blob/main/tests/examples/preprocess_hydrolib_readwritepol.py')
34
+
35
+
36
+ def write_bcfile(filename, datablocks, metadatas, refdate=None, tzone=0, float_format='%6.2f'):
37
+ raise DeprecationWarning('the function dfm_tools.io.bc.write_bcfile() is deprecated, please use the new hydrolib alternative. Example script: dfm_tools/tests/examples/CMEMS_interpolate_example.py')
38
+
39
+
40
+ def read_bcfile(filename, converttime=False):
41
+ raise DeprecationWarning('the function dfm_tools.io.bc.read_bcfile() is deprecated, please use the new hydrolib alternative. Example script: dfm_tools/tests/examples/hydrolib_readbc.py')
42
+
43
+
44
+ def write_timfile(filename, datablock, header, converttime=False, refdate=None, float_format='%6.2f'):
45
+ raise DeprecationWarning('the function dfm_tools.write_timfile() is deprecated, please use the new hydrolib alternative: https://github.com/Deltares/dfm_tools/blob/301-convert-timmodel-to-pandasdataframe/tests/examples/preprocess_hydrolib_readtim.py.')
46
+
47
+
48
+ def read_timfile(filename, converttime=False, refdate=None):
49
+ raise DeprecationWarning('the function dfm_tools.read_timfile() is deprecated, please use the new hydrolib alternative: https://github.com/Deltares/dfm_tools/blob/301-convert-timmodel-to-pandasdataframe/tests/examples/preprocess_hydrolib_readtim.py.')
50
+
dfm_tools/download.py ADDED
@@ -0,0 +1,311 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Tue Oct 18 15:09:26 2022
4
+
5
+ @author: veenstra
6
+ """
7
+
8
+ import os
9
+ import pandas as pd
10
+ from pathlib import Path
11
+ import xarray as xr
12
+ from pydap.client import open_url
13
+ from pydap.cas.get_cookies import setup_session
14
+ import warnings
15
+ from dfm_tools.errors import OutOfRangeError
16
+ import cdsapi
17
+ import cftime
18
+
19
+
20
+ def download_ERA5(varkey,
21
+ longitude_min, longitude_max, latitude_min, latitude_max,
22
+ date_min, date_max,
23
+ dir_output='.', overwrite=False):
24
+
25
+ #TODO: describe something about the .cdsapirc file
26
+ #TODO: make this function cdsapi generic, instead of ERA5 hardcoded (make flexible for product_type/name/name_output) (variables_dict is not used actively anymore, so this is possible)
27
+
28
+ c = cdsapi.Client() # import cdsapi and create a Client instance # https://cds.climate.copernicus.eu/api-how-to
29
+
30
+ #dictionary with ERA5 variables #this is not actively used
31
+ variables_dict = {'ssr':'surface_net_solar_radiation',
32
+ 'sst':'sea_surface_temperature',
33
+ 'strd':'surface_thermal_radiation_downwards',
34
+ 'slhf':'surface_latent_heat_flux',
35
+ 'sshf':'surface_sensible_heat_flux',
36
+ 'str':'surface_net_thermal_radiation',
37
+ 'chnk':'charnock',
38
+ 'd2m':'2m_dewpoint_temperature',
39
+ 't2m':'2m_temperature',
40
+ 'tcc':'total_cloud_cover',
41
+ 'msl':'mean_sea_level_pressure',
42
+ 'u10':'10m_u_component_of_wind',
43
+ 'u10n':'10m_u_component_of_neutral_wind',
44
+ 'v10':'10m_v_component_of_wind',
45
+ 'v10n':'10m_v_component_of_neutral_wind',
46
+ 'mer':'mean_evaporation_rate',
47
+ 'mtpr':'mean_total_precipitation_rate',
48
+ }
49
+ if varkey not in variables_dict.keys(): #TODO: how to get list of available vars? mean_sea_level_pressure and msl both return a dataset with msl varkey, but standard_name air_pressure_at_mean_sea_level returns an error
50
+ raise KeyError(f'"{varkey}" not available, choose from: {list(variables_dict.keys())}')
51
+
52
+ period_range = pd.period_range(date_min,date_max,freq='M')
53
+ print(f'retrieving data from {period_range[0]} to {period_range[-1]} (freq={period_range.freq})')
54
+
55
+ for date in period_range:
56
+ name_output = f'era5_{varkey}_{date.strftime("%Y-%m")}.nc'
57
+ file_out = Path(dir_output,name_output)
58
+ if file_out.is_file() and not overwrite:
59
+ print(f'"{name_output}" found and overwrite=False, continuing.')
60
+ continue
61
+ print (f'retrieving ERA5 data for variable "{varkey}" and month {date.strftime("%Y-%m")} (YYYY-MM)')
62
+
63
+ request_dict = {'product_type':'reanalysis',
64
+ 'variable':variables_dict[varkey],
65
+ 'year':date.strftime('%Y'),
66
+ 'month':date.strftime('%m'),
67
+ #'month':[f'{x:02d}' for x in range(1,12+1)], #all months, but instead retrieving per month
68
+ 'day':[f'{x:02d}' for x in range(1,31+1)], #all days
69
+ 'time':[f'{x:02d}:00' for x in range(0,23+1)], #all times/hours
70
+ 'area':[latitude_max,longitude_min,latitude_min,longitude_max], # north, west, south, east. default: global - option not available through the Climate Data Store (CDS) web interface (for cmip data)
71
+ #'grid': [1.0, 1.0], # latitude/longitude grid: east-west (longitude) and north-south resolution (latitude). default: 0.25 x 0.25 - option not available through the Climate Data Store (CDS) web interface
72
+ 'format':'netcdf'}
73
+
74
+ c.retrieve(name='reanalysis-era5-single-levels', request=request_dict, target=file_out)
75
+
76
+
77
+ def download_CMEMS(varkey,
78
+ longitude_min, longitude_max, latitude_min, latitude_max,
79
+ date_min, date_max, freq='D',
80
+ dir_output='.', file_prefix='', overwrite=False,
81
+ credentials=None):
82
+
83
+ date_min = pd.Timestamp(date_min)-pd.Timedelta(days=1) #CMEMS has daily noon values (not midnight), so subtract one day from date_min to cover desired time extent
84
+
85
+ if 'my_datemax' not in globals(): #set multiyear date_max (my_datemax) as global variable, so it only has to be retreived once per download run (otherwise once per variable)
86
+ print('retrieving enddate of multiyear CMEMS dataset')
87
+ dataset_url = 'https://my.cmems-du.eu/thredds/dodsC/cmems_mod_glo_phy_my_0.083_P1D-m' #assuming here that physchem and bio reanalyisus/multiyear datasets have the same enddate, this seems safe
88
+ ds = open_OPeNDAP_xr(dataset_url=dataset_url, credentials=credentials)
89
+ my_times = ds.time.to_series()
90
+ global my_datemax
91
+ my_datemax = my_times.iloc[-1]
92
+
93
+ if pd.Timestamp(date_max) <= my_datemax:
94
+ product = 'reanalysis'
95
+ elif pd.Timestamp(date_min) > my_datemax:
96
+ product = 'analysisforecast'
97
+ else:
98
+ raise ValueError(f'Requested timerange is {date_min} to {date_max}. Currently, it is only possible to query periods before OR after the multiyear/reanalysis enddate ({my_datemax}).')
99
+
100
+ Path(dir_output).mkdir(parents=True, exist_ok=True)
101
+ if varkey in ['bottomT','tob','mlotst','siconc','sithick','so','thetao','uo','vo','usi','vsi','zos']: #for physchem
102
+ if product == 'analysisforecast': #forecast: https://data.marine.copernicus.eu/product/GLOBAL_ANALYSISFORECAST_PHY_001_024/description
103
+ if varkey=='bottomT': #rename old to new anfc varname (still called bottomT in reanalysis)
104
+ varkey = 'tob'
105
+ if varkey in ['uo','vo']:
106
+ varkey_name = 'phy-cur'
107
+ elif varkey in ['so','thetao']:
108
+ varkey_name = 'phy-'+varkey
109
+ else:
110
+ varkey_name = 'phy'
111
+ dataset_url = f'https://nrt.cmems-du.eu/thredds/dodsC/cmems_mod_glo_{varkey_name}_anfc_0.083deg_P1D-m'#.html' #TODO: also PT6H-i timeresolution available but not for all variables and not for reanalysis
112
+ else: #reanalysis: https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/description
113
+ dataset_url = 'https://my.cmems-du.eu/thredds/dodsC/cmems_mod_glo_phy_my_0.083_P1D-m'
114
+ else: #for bio
115
+ if product == 'analysisforecast': #forecast: https://data.marine.copernicus.eu/product/GLOBAL_ANALYSISFORECAST_PHY_001_024/description
116
+ dataset_url = 'https://nrt.cmems-du.eu/thredds/dodsC/global-analysis-forecast-bio-001-028-daily' #contains ['chl','fe','no3','nppv','o2','ph','phyc','po4','si','spco2']
117
+ else: #https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_BGC_001_029/description
118
+ dataset_url = 'https://my.cmems-du.eu/thredds/dodsC/cmems_mod_glo_bgc_my_0.25_P1D-m' #contains ['chl','no3','nppv','o2','po4','si']
119
+
120
+ download_OPeNDAP(dataset_url=dataset_url,
121
+ credentials=credentials, #credentials=['username','password'], or create "%USERPROFILE%/CMEMS_credentials.txt" with username on line 1 and password on line 2. Register at: https://resources.marine.copernicus.eu/registration-form'
122
+ varkey=varkey,
123
+ longitude_min=longitude_min, longitude_max=longitude_max, latitude_min=latitude_min, latitude_max=latitude_max,
124
+ date_min=date_min, date_max=date_max,
125
+ dir_output=dir_output, file_prefix=file_prefix, overwrite=overwrite)
126
+
127
+
128
+ def open_OPeNDAP_xr(dataset_url, credentials=None):
129
+ """
130
+ How to get the opendap dataset_url (CMEMS example):
131
+ - https://data.marine.copernicus.eu/products
132
+ - go to the data access tab of a product, e.g.: https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/services
133
+ - click the opendap link of the dataset of your choice
134
+ - copy the dataset_url from the adress bar (excl .html), e.g.: https://my.cmems-du.eu/thredds/dodsC/cmems_mod_glo_phy_my_0.083_P1D-m
135
+
136
+ Example multiyear phys/chem:
137
+ https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_PHY_001_030/services
138
+ https://my.cmems-du.eu/thredds/dodsC/cmems_mod_glo_phy_my_0.083_P1D-m
139
+ ['bottomT','mlotst','siconc','sithick','so','thetao','uo','usi','vo','vsi','zos']
140
+ Example multiyear bio:
141
+ https://data.marine.copernicus.eu/product/GLOBAL_MULTIYEAR_BGC_001_029/services
142
+ https://my.cmems-du.eu/thredds/dodsC/cmems_mod_glo_bgc_my_0.25_P1D-m
143
+ ['chl','no3','nppv','o2','po4','si']
144
+ Example forecast phys/chem:
145
+ https://data.marine.copernicus.eu/product/GLOBAL_ANALYSIS_FORECAST_PHY_001_024/services
146
+ https://nrt.cmems-du.eu/thredds/dodsC/global-analysis-forecast-phy-001-024
147
+ ['bottomT','mlotst','siconc','sithick','so','thetao','uo','usi','vo','vsi','zos']
148
+ Example forecast bio:
149
+ https://data.marine.copernicus.eu/product/GLOBAL_ANALYSIS_FORECAST_BIO_001_028/services
150
+ https://nrt.cmems-du.eu/thredds/dodsC/global-analysis-forecast-bio-001-028-daily
151
+ ['chl','fe','no3','nppv','o2','ph','phyc','po4','si','spco2']
152
+
153
+ How to get the opendap dataset_url (HYCOM example):
154
+ - https://www.hycom.org/dataserver
155
+ - Select a product and search for THREDDS, e.g.: https://www.hycom.org/dataserver/gofs-3pt1/analysis
156
+ - find an opendap dataset_url, it depends per product/run where to find it.
157
+ Some examples:
158
+ https://tds.hycom.org/thredds/dodsC/GLBu0.08/expt_19.1/2010
159
+ https://tds.hycom.org/thredds/dodsC/GLBy0.08/expt_93.0
160
+
161
+ """
162
+
163
+ def copernicusmarine_datastore(dataset_url, username, password):
164
+ #https://help.marine.copernicus.eu/en/articles/5182598-how-to-consume-the-opendap-api-and-cas-sso-using-python
165
+ cas_url = 'https://cmems-cas.cls.fr/cas/login'
166
+ session = setup_session(cas_url, username, password)
167
+ cookies_dict = session.cookies.get_dict()
168
+ if 'CASTGC' not in cookies_dict.keys():
169
+ raise KeyError('CASTGC key missing from session cookies_dict, probably authentication failure')
170
+ session.cookies.set("CASTGC", cookies_dict['CASTGC'])
171
+ #TODO: add check for wrong dataset_id (now always "AttributeError: You cannot set the charset when no content-type is defined")
172
+ DAP_dataset = open_url(dataset_url, session=session)#, user_charset='utf-8') # TODO: user_charset needs PyDAP >= v3.3.0 see https://github.com/pydap/pydap/pull/223/commits
173
+ data_store = xr.backends.PydapDataStore(DAP_dataset)
174
+ return data_store
175
+
176
+ if isinstance(dataset_url,list):
177
+ dataset_url_one = dataset_url[0]
178
+ else:
179
+ dataset_url_one = dataset_url
180
+
181
+ if 'cmems-du.eu' in dataset_url_one:
182
+ if isinstance(dataset_url,list):
183
+ raise TypeError('list not supported by opendap method used for cmems')
184
+
185
+ #parse credentials to username/password #TODO: now CMEMS specific, make more generic
186
+ if credentials is None:
187
+ file_credentials = f'{os.path.expanduser("~")}/CMEMS_credentials.txt'
188
+ if not os.path.exists(file_credentials):
189
+ raise FileNotFoundError(f'credentials argument not supplied and file_credentials not available ({file_credentials})')
190
+ with open(file_credentials) as fc:
191
+ username = fc.readline().strip()
192
+ password = fc.readline().strip()
193
+ else:
194
+ username,password = credentials
195
+
196
+ print(f'opening pydap connection to opendap dataset: {dataset_url}.html')
197
+ data_store = copernicusmarine_datastore(dataset_url=dataset_url, username=username, password=password)
198
+ print('xarray opening opendap dataset')
199
+ data_xr = xr.open_dataset(data_store)
200
+ elif 'hycom.org' in dataset_url_one:
201
+ if isinstance(dataset_url,list):
202
+ print(f'xarray opening opendap dataset like: {dataset_url[0]}.html ({len(dataset_url)} urls/years)')
203
+ data_xr = xr.open_mfdataset(dataset_url,decode_times=False) #TODO: for some reason decode_times does not work: "ValueError: unable to decode time units 'hours since analysis' with 'the default calendar'."
204
+ else:
205
+ print(f'xarray opening opendap dataset: {dataset_url}.html')
206
+ data_xr = xr.open_dataset(dataset_url,decode_times=False) #TODO: for some reason decode_times does not work: "ValueError: unable to decode time units 'hours since analysis' with 'the default calendar
207
+ data_xr['time'] = cftime.num2date(data_xr.time,units=data_xr.time.units,calendar=data_xr.time.calendar)
208
+ data_xr = data_xr.rename({'lon':'longitude','lat':'latitude'})
209
+ else:
210
+ print(f'unspecified dataset_url, might fail: {dataset_url}')
211
+ if isinstance(dataset_url,list):
212
+ print(f'xarray opening opendap dataset like: {dataset_url[0]}.html ({len(dataset_url)} urls/years)')
213
+ data_xr = xr.open_mfdataset(dataset_url)
214
+ else:
215
+ print(f'xarray opening opendap dataset: {dataset_url}.html')
216
+ data_xr = xr.open_dataset(dataset_url)
217
+ if 'lon' in data_xr.dims:
218
+ data_xr = data_xr.rename({'lon':'longitude','lat':'latitude'})
219
+
220
+ return data_xr
221
+
222
+
223
+ def download_OpenDAP_gettimes(dataset_url,credentials=None):
224
+ ds = open_OPeNDAP_xr(dataset_url=dataset_url, credentials=credentials)
225
+ ds_time = ds.time.to_series()
226
+ return ds_time
227
+
228
+
229
+ def download_OPeNDAP(dataset_url,
230
+ varkey,
231
+ longitude_min, longitude_max, latitude_min, latitude_max,
232
+ date_min, date_max, freq='D',
233
+ dir_output='.', file_prefix='', overwrite=False,
234
+ credentials=None):
235
+ """
236
+
237
+
238
+ Parameters
239
+ ----------
240
+ dataset_url : TYPE
241
+ DESCRIPTION.
242
+ varkey : TYPE
243
+ DESCRIPTION.
244
+ longitude_min : TYPE
245
+ DESCRIPTION.
246
+ longitude_max : TYPE
247
+ DESCRIPTION.
248
+ latitude_min : TYPE
249
+ DESCRIPTION.
250
+ latitude_max : TYPE
251
+ DESCRIPTION.
252
+ date_min : TYPE
253
+ DESCRIPTION.
254
+ date_max : TYPE
255
+ DESCRIPTION.
256
+ freq : TYPE, optional
257
+ DESCRIPTION. The default is 'D'.
258
+ dir_output : TYPE, optional
259
+ DESCRIPTION. The default is '.'.
260
+ file_prefix : TYPE, optional
261
+ DESCRIPTION. The default is ''.
262
+ overwrite : TYPE, optional
263
+ DESCRIPTION. The default is False.
264
+ credentials : TYPE, optional
265
+ for CMEMS: credentials=['username','password'], or create "%USERPROFILE%/CMEMS_credentials.txt" with username on line 1 and password on line 2. Register at: https://resources.marine.copernicus.eu/registration-form'. The default is None.
266
+
267
+ Raises
268
+ ------
269
+ KeyError
270
+ DESCRIPTION.
271
+ OutOfRangeError
272
+ DESCRIPTION.
273
+
274
+ Returns
275
+ -------
276
+ None.
277
+
278
+ """
279
+
280
+ data_xr = open_OPeNDAP_xr(dataset_url=dataset_url, credentials=credentials)
281
+
282
+ print(f'xarray subsetting data (variable \'{varkey}\' and lon/lat extents)')
283
+ if varkey not in data_xr.data_vars:
284
+ raise KeyError(f'"{varkey}" not found in dataset, available are: {list(data_xr.data_vars)}')
285
+ data_xr_var = data_xr[[varkey]]
286
+ data_xr_var = data_xr_var.sel(longitude=slice(longitude_min,longitude_max), #TODO: add depth selection?
287
+ latitude=slice(latitude_min,latitude_max))
288
+ data_xr_times = data_xr_var.time.to_series()
289
+ print(f'available time range in dataset from {data_xr_times.index[0]} to {data_xr_times.index[-1]}')
290
+ period_range = pd.period_range(date_min,date_max,freq=freq)
291
+
292
+ #check if date_min/date_max are available in dataset
293
+ if not (data_xr_times.index[0] <= period_range[0].to_timestamp() <= data_xr_times.index[-1]):
294
+ raise OutOfRangeError(f'date_min ({period_range[0]}) is outside available time range in dataset: {data_xr_times.index[0]} to {data_xr_times.index[-1]}')
295
+ if not (data_xr_times.index[0] <= period_range[-1].to_timestamp() <= data_xr_times.index[-1]):
296
+ raise OutOfRangeError(f'date_max ({period_range[-1]}) is outside available time range in dataset: {data_xr_times.index[0]} to {data_xr_times.index[-1]}')
297
+
298
+ for date in period_range:
299
+ date_str = str(date)
300
+ name_output = f'{file_prefix}{varkey}_{date_str}.nc'
301
+ file_out = Path(dir_output,name_output)
302
+ if file_out.is_file() and not overwrite:
303
+ print(f'"{name_output}" found and overwrite=False, continuing.')
304
+ continue
305
+
306
+ print(f'xarray subsetting data per {period_range.freq}: {date_str}')
307
+ data_xr_var_seltime = data_xr_var.sel(time=slice(date_str,date_str)) #+' 12:00:00',
308
+
309
+ print(f'xarray writing netcdf file: {name_output}')
310
+ data_xr_var_seltime.to_netcdf(os.path.join(dir_output,name_output)) #TODO: add chunks={'time':1} or only possible with opening?
311
+ data_xr_var_seltime.close()
@@ -0,0 +1,73 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Thu Dec 8 22:07:22 2022
4
+
5
+ @author: veenstra
6
+ """
7
+ import datetime as dt
8
+ from dask.diagnostics import ProgressBar
9
+
10
+
11
+ def compute_energy_dissipation(data_xr_map,file_ED_computed):
12
+ """
13
+ Example:
14
+ data_xr_map = dfmt.open_partitioned_dataset(file_nc_map,chunks={'time':100}) #TODO: important to have time>1, otherwise time-mean floods memory (100-200 seems optimal for GTSM 1month, but memory still floods so 1 year would probably be impossible).
15
+ data_xr_map = data_xr_map.sel(time=slice('2014-01-01','2014-02-01'))
16
+ dfmt.compute_energy_dissipation(data_xr_map,file_ED_computed)
17
+
18
+
19
+ TODO: clean up these comments
20
+
21
+ Stel de bodemwrijving is (tau_x,tau_y) dan is het verlies aan energie in W/m^2
22
+ E_loss = tau_x*u_x + tau_y*u_y
23
+
24
+ Voor Chezy geldt (zo uit mijn hoofd):
25
+ tau_x = - (rho*g)/(C^2) u_x * sqrt(u_x^2+u_y^2)
26
+ tau_y = - (rho*g)/(C^2) u_y * sqrt(u_x^2+u_y^2)
27
+
28
+ En gecombineerd
29
+ E_loss = - (rho*g)/(C^2) * (u_x^2+u_y^2)^(3/2)
30
+
31
+ Approximation: using velocities in cell centers, this is averaged but easiest
32
+
33
+ E_loss(area) = sum_cells [ - (rho*g)/(C^2) * (sqrt(u_x^2+u_y^2))^3 ] * cell_area
34
+
35
+ Dit is dan voor een tijdstip. Ik zou middelen over een iets langere periode zodat het de variatie over het getij verdwijnt, want de waarde is bij springtij waarschijnlijk groter.
36
+
37
+ """
38
+ rho = 1020 #kg/m3
39
+ g = 9.81 #m/s2
40
+
41
+ attrs_ED = {'long_name':'energy dissipation','units':'W'}
42
+ attrs_ED_pm2 = {'long_name':'energy dissipation','units':'W/m^2'}
43
+ attrs_ED_areasum = {'long_name':'area-sum energy dissipation', 'units':'W'}
44
+ attrs_ED_timemean = {'long_name':'springneaptide-mean energy dissipation', 'units': 'W'}
45
+ attrs_ED_pm2_timemean = {'long_name':'springneaptide-mean energy dissipation', 'units':'Wm^2'}
46
+
47
+ print('>> compute energyloss from C/u/area: ',end='')
48
+ dtstart = dt.datetime.now()
49
+ data_ucmag = data_xr_map.mesh2d_ucmag #m/s
50
+ data_czs = data_xr_map.mesh2d_czs #m0.5s-1
51
+ data_czs = data_czs.where(data_czs!=0) #0 will result in inf energyloss, so replace by nan
52
+ data_ba = data_xr_map.mesh2d_flowelem_ba #m2
53
+ data_xr_map['ED'] = ((rho*g)/(data_czs**2) * data_ucmag**3 * data_ba).assign_attrs(attrs_ED) #equation: (kg/m3)*(m/s2)/(m/s2)*(m3/s3)*m2 = kg/s3*m2 = W = J/s
54
+ data_xr_map['ED_pm2'] = (data_xr_map['ED'] / data_ba).assign_attrs(attrs_ED_pm2) #TODO: maybe include ba instead of supplying _pm2 separately?
55
+
56
+ #TODO: check da.sum(split_every=4): https://github.com/dask/dask/issues/883. Does not seem to work: "TypeError: nansum() got an unexpected keyword argument 'split_every'"
57
+ #TODO: chunking: float32 = 32 bits = 4 bytes >> use this to calculate chunking sizes (100-200MB per chunk is optimal)
58
+ data_xr_map['ED_areasum'] = data_xr_map.ED.sum(dim='mesh2d_nFaces').assign_attrs(attrs_ED_areasum)
59
+ data_xr_map['ED_timemean'] = data_xr_map.ED.mean(dim='time').assign_attrs(attrs_ED_timemean)
60
+ data_xr_map['ED_pm2_timemean'] = data_xr_map.ED_pm2.mean(dim='time').assign_attrs(attrs_ED_pm2_timemean)
61
+
62
+
63
+ print(f'{(dt.datetime.now()-dtstart).total_seconds():.2f} sec')
64
+
65
+ data_xr_map_computed = data_xr_map[['mesh2d_flowelem_ba','ED_areasum','ED_timemean','ED_pm2_timemean']] #has to contain some var with cells (not only ED_areasum), otherwise ugrid accessor is not valid
66
+ #data_xr_map_computed.ED_timemean.data.visualize() #does not yet work >> visualizes tasks of dask array
67
+
68
+ print('>> compute and save data_xr_map_computed to netcdf: ')
69
+ with ProgressBar():
70
+ data_xr_map_computed.ugrid.to_netcdf(file_ED_computed)
71
+
72
+ data_xr_map_computed.close()
73
+
dfm_tools/errors.py ADDED
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon Apr 3 14:38:20 2023
4
+
5
+ @author: veenstra
6
+ """
7
+
8
+ class OutOfRangeError(Exception):
9
+ pass