Acceso a ECCOv4 por medio de EarthData in the Cloud de la NASA#
Este tutorial demuestra acceso al producto ECCOv4 de un modelo numerico global. Para mayor informacion del producto de ECCO, puede ir aqui.
Requisitos
Tener una cuenta de Earth Data Login por medio de la NASA.
Tener un Token valido.
Tambien se puede utilizar el metodo de Nombre de Usuario / Contrasena descrito en el tutorial de Autenticacion
Objectivos
Utilizar pydap para demostrar
Acceso to archivos cientificos de la NASA por medio del uso de
tokensy EarthData como metodo de autenticacion.
Algunas variables de interes son:
Autor: Miguel Jimenez-Urias, ‘24
import matplotlib.pyplot as plt
import numpy as np
from pydap.net import create_session
from pydap.client import open_url
import xarray as xr
import pydap
print("xarray version: ", xr.__version__)
print("pydap version: ", pydap.__version__)
xarray version: 2026.4.0
pydap version: 3.5.10.dev14+g40484efcb
Acceso a EARTHDATA#
El listado de las variables de este producto pueden encontrarse aqui. En este caso, accederemos a las variables en su malla original, el LL90.
Grid_url = 'https://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/ECCO%20Geometry%20Parameters%20for%20the%20Lat-Lon-Cap%2090%20(llc90)%20Native%20Model%20Grid%20(Version%204%20Release%204)/granules/GRID_GEOMETRY_ECCO_V4r4_native_llc0090'
Autenticacion via .netrc#
Las credenciales son recuperadas automaticamente por pydap.
my_session = create_session()
Alternativa: El use the tokens#
session_extra = {"token": "YourToken"}
# initialize a requests.session object with the token headers. All handled by pydap.
my_session = create_session(session_kwargs=session_extra)
Accesso a los metadatos solamente, por medio de pydap#
pydap aprovecha el protocolo de OPeNDAP, el cual permite la separacion de los metadatos de los valores numericos. Esto permite una inspeccion remota de los datasets.
ds_grid = open_url(Grid_url, session=my_session, protocol="dap4")
ds_grid.tree()
.GRID_GEOMETRY_ECCO_V4r4_native_llc0090.nc
├──dxC
├──PHrefF
├──XG
├──dyG
├──rA
├──hFacS
├──Zp1
├──Zl
├──rAw
├──dxG
├──maskW
├──YC
├──XC
├──maskS
├──YG
├──hFacC
├──drC
├──drF
├──XC_bnds
├──Zu
├──Z_bnds
├──YC_bnds
├──PHrefC
├──rAs
├──Depth
├──dyC
├──SN
├──rAz
├──maskC
├──CS
├──hFacW
├──Z
├──i
├──i_g
├──j
├──j_g
├──k
├──k_l
├──k_p1
├──k_u
├──nb
├──nv
└──tile
Note
PyDAP accesa solo a los metadatos de archivo en el servidor de OPeNDAP, y ningun arreglo numerico ha sido descargardo hasta este punto!
Para descargar los arreglos numericos hay que indexar la variable
Esto es, de la siguiente manera:
# this fetches remote data into a pydap object container
pydap_Array = dataset[<VarName>][:]
donde <VarName> es el nombre de una de las variables. El producto sera una representation “en memoria” de tipo pydap.model.BaseType, el cual permite el acceso a los arreglos de numpy.
Para extraer los valores de la variable remota, hay que ejecutar el siguiente comando
# The `.data` command allows direct access to the Numpy array (e.g. for manipulation)
pydap_Array.data
# lets download some data
Depth = ds_grid['Depth'][:]
print(type(Depth))
<class 'pydap.model.BaseType'>
Depth.attributes
{'_FillValue': 9.969209968e+36,
'long_name': 'model seafloor depth below ocean surface at rest',
'units': 'm',
'coordinate': 'XC YC',
'coverage_content_type': 'modelResult',
'standard_name': 'sea_floor_depth_below_geoid',
'comment': "Model sea surface height (SSH) of 0m corresponds to an ocean surface at rest relative to the geoid. Depth corresponds to seafloor depth below geoid. Note: the MITgcm used by ECCO V4r4 implements 'partial cells' so the actual model seafloor depth may differ from the seafloor depth provided by the input bathymetry file.",
'coordinates': 'YC XC',
'origname': 'Depth',
'fullnamepath': '/Depth',
'Maps': (),
'_DAP4_Checksum_CRC32': np.uint32(3811813944)}
Depth.shape, Depth.dims
((13, 90, 90), ['/tile', '/j', '/i'])
Visualizando el fondo oceanico Depth en la malla original del modelo
En este caso, el producto ECCO esta definido en una malla con topologia de un Cubo Esferico. De esta manera, la malla horizontal contiene una dimension extra llamada: tile o face. A continuacion visualizamos la variable en su topologia original
Variable = [Depth[i].data for i in range(13)]
clevels = np.linspace(0, 6000, 100)
cMap = 'Greys_r'
fig, axes = plt.subplots(nrows=5, ncols=5, figsize=(8, 8), gridspec_kw={'hspace':0.01, 'wspace':0.01})
AXES = [
axes[4, 0], axes[3, 0], axes[2, 0], axes[4, 1], axes[3, 1], axes[2, 1],
axes[1, 1],
axes[1, 2], axes[1, 3], axes[1, 4], axes[0, 2], axes[0, 3], axes[0, 4],
]
for i in range(len(AXES)):
AXES[i].contourf(Variable[i], clevels, cmap=cMap)
for ax in np.ravel(axes):
ax.axis('off')
plt.setp(ax.get_xticklabels(), visible=False)
plt.setp(ax.get_yticklabels(), visible=False)
plt.show()
Fig. 1. La variable Depth visualizada en una malla horitonzal. Tiles con valores 0-5 tienen un ordenamiento de valores (indiciales) C-ordering, mientras que cualquier arreglo numerico tiles 7-13 siguen el F-ordering. Cualquier arreglo numerico en el arctic cap, tiene un comportamiento de una coordenada polar.
Visualizacion con topologia corregida
fig, axes = plt.subplots(nrows=4, ncols=4, figsize=(8, 8), gridspec_kw={'hspace':0.01, 'wspace':0.01})
AXES_NR = [
axes[3, 0], axes[2, 0], axes[1, 0], axes[3, 1], axes[2, 1], axes[1, 1],
]
AXES_CAP = [axes[0, 0]]
AXES_R = [
axes[1, 2], axes[2, 2], axes[3, 2], axes[1, 3], axes[2, 3], axes[3, 3],
]
for i in range(len(AXES_NR)):
AXES_NR[i].contourf(Variable[i], clevels, cmap=cMap)
for i in range(len(AXES_CAP)):
AXES_CAP[i].contourf(Variable[6].transpose()[:, ::-1], clevels, cmap=cMap)
for i in range(len(AXES_R)):
AXES_R[i].contourf(Variable[7+i].transpose()[::-1, :], clevels, cmap=cMap)
for ax in np.ravel(axes):
ax.axis('off')
plt.setp(ax.get_xticklabels(), visible=False)
plt.setp(ax.get_yticklabels(), visible=False)
plt.show()
Fig. 2. Visualizacion de la variable Depth en una malla horizontal que approxima una malla horizontal uniforme con ejes lat-lon. Sin embargo, cualquier arreglo numerico en el arctic cap, continua en una malla que approxima una malla en coordenadas polares
Utilizando xarray#
pydap se puede llamar dentro de xarray para acceder a los archivos expuestos mediante el servidor de OPeNDAP. En particular, xarray permite aggregar multiples archivos remotos de opendap. A continuacion demostramos un pequeno ejemplo.
Un URL individual –> un archivo remoto
Below we access remote Temperature and Salinity ECCO data with xarray via (internally) pydap.
baseURL = 'https://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/'
Temp_Salt = "ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_"
year = '2017-'
month = '01'
end_ = '_ECCO_V4r4_native_llc0090'
Temp_2017 = baseURL + Temp_Salt +year + month + end_
# Convertimos el URL en un DAP4 opendap url
Temp_2017 = Temp_2017.replace("https", "dap4")
Temp_2017
'dap4://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-01_ECCO_V4r4_native_llc0090'
pyds = open_url(Temp_2017, session=my_session)
pyds.tree()
.OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-01_ECCO_V4r4_native_llc0090.nc
├──XG
├──Zp1
├──Zl
├──YC
├──XC
├──SALT
├──YG
├──XC_bnds
├──Zu
├──THETA
├──Z_bnds
├──YC_bnds
├──time_bnds
├──Z
├──i
├──i_g
├──j
├──j_g
├──k
├──k_l
├──k_p1
├──k_u
├──nb
├──nv
├──tile
└──time
Para evitar descargas variables que no sean necesariamente de interest, se le puede instruir al Servidor Hyrax que variables requiere uno.
Para esto utilizaremos las Expresiones de Restriccion (CE, por sus siglas en ingles). Este metodo nos ayudara a construir datasets mucho mas simples,
y evitar descargar N veces, informacion que no es requerida.
A continuacion demostramos el caso de solo requerir la variable THETA y sus dimensiones.
dims = pyds['/THETA'].dims
Vars = ['/THETA'] + dims
# Below construct Contraint Expression
CE = "?dap4.ce="+(";").join(Vars)
print("Constraint Expression: ", CE)
Constraint Expression: ?dap4.ce=/THETA;/time;/k;/tile;/j;/i
Temp_2017 = baseURL + Temp_Salt +year + month + end_ + CE
Temp_2017
'https://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-01_ECCO_V4r4_native_llc0090?dap4.ce=/THETA;/time;/k;/tile;/j;/i'
Importante#
Ahora aplicamos la CE a todos los possibles URLs the nuestro dataset.
Temp_2017 = [baseURL.replace("https", "dap4") + Temp_Salt + year + f'{i:02}' + end_ + CE for i in range(1, 13)]
Temp_2017[:3]
['dap4://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-01_ECCO_V4r4_native_llc0090?dap4.ce=/THETA;/time;/k;/tile;/j;/i',
'dap4://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-02_ECCO_V4r4_native_llc0090?dap4.ce=/THETA;/time;/k;/tile;/j;/i',
'dap4://opendap.earthdata.nasa.gov/providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-03_ECCO_V4r4_native_llc0090?dap4.ce=/THETA;/time;/k;/tile;/j;/i']
#
Debajo inicializaremos una session que persista, cada vez que descargue datos de servidores de OPeNDAP. En este caso utilizaremost la libraria
requests_cache. El comportamiento es casi identico excepto la siguiente celda:
cache_session = create_session(use_cache=True, cache_kwargs={"cache_name":"data/ecco"}) # caching the session
cache_session.cache.clear() # clear all cache to demonstrate the behavior
Warning
A partir de la version de pydap >= 3.5.5, existe el nuevo metodo experimental consolidated_metadata que permite a pydap descargar todos los metadatos, y reusarlos. Este metodo continua en desarrollo y sus propiedades pueden cambiar en las siguientes versiones.
from pydap.client import consolidate_metadata
%%time
consolidate_metadata(Temp_2017, session=cache_session, concat_dim='time')
datacube has dimensions ['i[0:1:89]', 'j[0:1:89]', 'k[0:1:49]', 'tile[0:1:12]'] , and concat dim: `['time']`
CPU times: user 2.27 s, sys: 469 ms, total: 2.74 s
Wall time: 53.2 s
%%time
theta_salt_ds = xr.open_mfdataset(
Temp_2017,
engine='pydap',
session=cache_session,
parallel=True,
combine='nested',
concat_dim='time',
)
CPU times: user 1.05 s, sys: 141 ms, total: 1.19 s
Wall time: 8.46 s
---------------------------------------------------------------------------
ResponseError Traceback (most recent call last)
ResponseError: too many 500 error responses
The above exception was the direct cause of the following exception:
MaxRetryError Traceback (most recent call last)
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/adapters.py:696, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
695 try:
--> 696 resp = conn.urlopen(
697 method=request.method,
698 url=url,
699 body=request.body, # type: ignore[arg-type] # urllib3 stubs don't accept Iterable[bytes | str]
700 headers=request.headers, # type: ignore[arg-type] # urllib3#3072
701 redirect=False,
702 assert_same_host=False,
703 preload_content=False,
704 decode_content=False,
705 retries=self.max_retries,
706 timeout=resolved_timeout,
707 chunked=chunked,
708 )
710 except (ProtocolError, OSError) as err:
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/urllib3/connectionpool.py:955, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
954 log.debug("Retry: %s", url)
--> 955 return self.urlopen(
956 method,
957 url,
958 body,
959 headers,
960 retries=retries,
961 redirect=redirect,
962 assert_same_host=assert_same_host,
963 timeout=timeout,
964 pool_timeout=pool_timeout,
965 release_conn=release_conn,
966 chunked=chunked,
967 body_pos=body_pos,
968 preload_content=preload_content,
969 decode_content=decode_content,
970 **response_kw,
971 )
973 return response
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/urllib3/connectionpool.py:955, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
954 log.debug("Retry: %s", url)
--> 955 return self.urlopen(
956 method,
957 url,
958 body,
959 headers,
960 retries=retries,
961 redirect=redirect,
962 assert_same_host=assert_same_host,
963 timeout=timeout,
964 pool_timeout=pool_timeout,
965 release_conn=release_conn,
966 chunked=chunked,
967 body_pos=body_pos,
968 preload_content=preload_content,
969 decode_content=decode_content,
970 **response_kw,
971 )
973 return response
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/urllib3/connectionpool.py:945, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
944 try:
--> 945 retries = retries.increment(method, url, response=response, _pool=self)
946 except MaxRetryError:
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/urllib3/util/retry.py:543, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
542 reason = error or ResponseError(cause)
--> 543 raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]
545 log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)
MaxRetryError: HTTPSConnectionPool(host='opendap.earthdata.nasa.gov', port=443): Max retries exceeded with url: /providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-01_ECCO_V4r4_native_llc0090.dap?dap4.ce=i%5B0%3A1%3A89%5D%3Bj%5B0%3A1%3A89%5D%3Bk%5B0%3A1%3A49%5D%3Btile%5B0%3A1%3A12%5D&dap4.checksum=true (Caused by ResponseError('too many 500 error responses'))
During handling of the above exception, another exception occurred:
RetryError Traceback (most recent call last)
Cell In[21], line 1
----> 1 get_ipython().run_cell_magic('time', '', "theta_salt_ds = xr.open_mfdataset(\n Temp_2017, \n engine='pydap',\n session=cache_session, \n parallel=True, \n combine='nested', \n concat_dim='time',\n)\n")
File <timed exec>:1
----> 1 'Could not get source, probably due dynamically evaluated source code.'
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/api.py:1662, in open_mfdataset(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, errors, **kwargs)
1657 datasets = [preprocess(ds) for ds in datasets]
1659 if parallel:
1660 # calling compute here will return the datasets/file_objs lists,
1661 # the underlying datasets will still be stored as dask arrays
-> 1662 datasets, closers = dask.compute(datasets, closers)
1664 # Combine all datasets, closing them in case of a ValueError
1665 try:
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/dask/base.py:685, in compute(traverse, optimize_graph, scheduler, get, *args, **kwargs)
682 expr = expr.optimize()
683 keys = list(flatten(expr.__dask_keys__()))
--> 685 results = schedule(expr, keys, **kwargs)
687 return repack(results)
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/api.py:607, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, create_default_indexes, inline_array, chunked_array_type, from_array_kwargs, backend_kwargs, **kwargs)
595 decoders = _resolve_decoders_kwargs(
596 decode_cf,
597 open_backend_dataset_parameters=backend.open_dataset_parameters,
(...) 603 decode_coords=decode_coords,
604 )
606 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 607 backend_ds = backend.open_dataset(
608 filename_or_obj,
609 drop_variables=drop_variables,
610 **decoders,
611 **kwargs,
612 )
613 ds = _dataset_from_backend_dataset(
614 backend_ds,
615 filename_or_obj,
(...) 626 **kwargs,
627 )
628 return ds
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/pydap_.py:292, in PydapBackendEntrypoint.open_dataset(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta, group, application, session, output_grid, timeout, verify, user_charset, checksums)
290 store_entrypoint = StoreBackendEntrypoint()
291 with close_on_error(store):
--> 292 ds = store_entrypoint.open_dataset(
293 store,
294 mask_and_scale=mask_and_scale,
295 decode_times=decode_times,
296 concat_characters=concat_characters,
297 decode_coords=decode_coords,
298 drop_variables=drop_variables,
299 use_cftime=use_cftime,
300 decode_timedelta=decode_timedelta,
301 )
302 return ds
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/store.py:42, in StoreBackendEntrypoint.open_dataset(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, set_indexes, use_cftime, decode_timedelta)
27 def open_dataset(
28 self,
29 filename_or_obj: T_PathFileOrDataStore,
(...) 38 decode_timedelta=None,
39 ) -> Dataset:
40 assert isinstance(filename_or_obj, AbstractDataStore)
---> 42 vars, attrs = filename_or_obj.load()
43 encoding = filename_or_obj.get_encoding()
45 vars, attrs, coord_names = conventions.decode_cf_variables(
46 vars,
47 attrs,
(...) 54 decode_timedelta=decode_timedelta,
55 )
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/common.py:370, in AbstractDataStore.load(self)
351 def load(self):
352 """
353 This loads the variables and attributes simultaneously.
354 A centralized loading function makes it easier to create
(...) 367 are requested, so care should be taken to make sure its fast.
368 """
369 variables = FrozenDict(
--> 370 (_decode_variable_name(k), v) for k, v in self.get_variables().items()
371 )
372 attributes = FrozenDict(self.get_attrs())
373 return variables, attributes
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/pydap_.py:194, in PydapDataStore.get_variables(self)
185 from pydap.model import GroupType
187 _vars = [
188 var
189 for var in self.ds.keys()
190 # check the key is not a BaseType or GridType
191 if not isinstance(self.ds[var], GroupType)
192 ]
--> 194 return FrozenDict((k, self.open_store_variable(self.ds[k])) for k in _vars)
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/core/utils.py:522, in FrozenDict(*args, **kwargs)
521 def FrozenDict(*args, **kwargs) -> Frozen:
--> 522 return Frozen(dict(*args, **kwargs))
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/pydap_.py:194, in <genexpr>(.0)
185 from pydap.model import GroupType
187 _vars = [
188 var
189 for var in self.ds.keys()
190 # check the key is not a BaseType or GridType
191 if not isinstance(self.ds[var], GroupType)
192 ]
--> 194 return FrozenDict((k, self.open_store_variable(self.ds[k])) for k in _vars)
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/pydap_.py:169, in PydapDataStore.open_store_variable(self, var)
163 if (
164 self._protocol == "dap4"
165 and var.name in dimensions
166 and hasattr(var, "dataset") # only True for pydap>3.5.5
167 ):
168 var.dataset.enable_batch_mode()
--> 169 data_array = self._get_data_array(var)
170 data = indexing.LazilyIndexedArray(data_array)
171 var.dataset.disable_batch_mode()
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/xarray/backends/pydap_.py:228, in PydapDataStore._get_data_array(self, var)
223 from pydap.client import get_batch_data
225 if not var._is_data_loaded():
226 # data has not been deserialized yet
227 # runs only once per store/hierarchy
--> 228 get_batch_data(var, checksums=self._checksums)
230 return self.dataset[var.id].data
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/pydap/client.py:1265, in get_batch_data(array, cache_urls, checksums, key)
1261 try:
1262 if "consolidated" in ds.dataset.session.headers and set_dims:
1263 # need to add a check that consolidated has
1264 # been performed on that collection.
-> 1265 fetch_consolidated(ds, cache_urls=cache_urls, checksums=checksums)
1266 else:
1267 dataset = ds.dataset
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/pydap/client.py:1422, in fetch_consolidated(ds, cache_urls, checksums)
1420 for URL in set(dap_urls):
1421 try:
-> 1422 r = session.get(URL)
1423 except (sqlite3.InterfaceError, EOFError):
1424 with session.cache_disabled():
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests_cache/session.py:133, in CacheMixin.get(self, url, params, **kwargs)
131 def get(self, url: str, params=None, **kwargs) -> AnyResponse: # type: ignore
132 kwargs.setdefault('allow_redirects', True)
--> 133 return self.request('GET', url, params=params, **kwargs)
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests_cache/session.py:189, in CacheMixin.request(self, method, url, headers, expire_after, only_if_cached, refresh, force_refresh, *args, **kwargs)
187 headers = set_request_headers(headers, expire_after, only_if_cached, refresh, force_refresh)
188 with patch_form_boundary() if kwargs.get('files') else nullcontext():
--> 189 return super().request(method, url, *args, headers=headers, **kwargs)
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/sessions.py:651, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
646 send_kwargs = {
647 "timeout": timeout,
648 "allow_redirects": allow_redirects,
649 }
650 send_kwargs.update(settings)
--> 651 resp = self.send(prep, **send_kwargs)
653 return resp
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests_cache/session.py:255, in CacheMixin.send(self, request, expire_after, only_if_cached, refresh, force_refresh, **kwargs)
253 response = self._resend(request, actions, cached_response, **kwargs) # type: ignore
254 elif actions.send_request:
--> 255 response = self._send_and_cache(request, actions, cached_response, **kwargs)
256 else:
257 response = cached_response # type: ignore # Guaranteed to be non-None by this point
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests_cache/session.py:279, in CacheMixin._send_and_cache(self, request, actions, cached_response, **kwargs)
275 """Send a request and cache the response, unless disabled by settings or headers.
276 If applicable, also handle conditional requests.
277 """
278 request = actions.update_request(request)
--> 279 response = super().send(request, **kwargs)
280 actions.update_from_response(response)
282 if not actions.skip_write:
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/sessions.py:805, in Session.send(self, request, **kwargs)
802 if allow_redirects:
803 # Redirect resolving generator.
804 gen = self.resolve_redirects(r, request, **kwargs)
--> 805 history = [resp for resp in gen]
806 else:
807 history = []
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/sessions.py:805, in <listcomp>(.0)
802 if allow_redirects:
803 # Redirect resolving generator.
804 gen = self.resolve_redirects(r, request, **kwargs)
--> 805 history = [resp for resp in gen]
806 else:
807 history = []
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/sessions.py:292, in SessionRedirectMixin.resolve_redirects(self, resp, req, stream, timeout, verify, cert, proxies, yield_requests, **adapter_kwargs)
290 yield req # type: ignore[misc] # Internal use only, returns PreparedRequest
291 else:
--> 292 resp = self.send(
293 req,
294 stream=stream,
295 timeout=timeout,
296 verify=verify,
297 cert=cert,
298 proxies=proxies,
299 allow_redirects=False,
300 **adapter_kwargs,
301 )
303 extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
305 # extract redirect url, if any, for the next loop
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests_cache/session.py:255, in CacheMixin.send(self, request, expire_after, only_if_cached, refresh, force_refresh, **kwargs)
253 response = self._resend(request, actions, cached_response, **kwargs) # type: ignore
254 elif actions.send_request:
--> 255 response = self._send_and_cache(request, actions, cached_response, **kwargs)
256 else:
257 response = cached_response # type: ignore # Guaranteed to be non-None by this point
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests_cache/session.py:279, in CacheMixin._send_and_cache(self, request, actions, cached_response, **kwargs)
275 """Send a request and cache the response, unless disabled by settings or headers.
276 If applicable, also handle conditional requests.
277 """
278 request = actions.update_request(request)
--> 279 response = super().send(request, **kwargs)
280 actions.update_from_response(response)
282 if not actions.skip_write:
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/sessions.py:784, in Session.send(self, request, **kwargs)
781 start = preferred_clock()
783 # Send the request
--> 784 r = adapter.send(request, **kwargs)
786 # Total elapsed time of the request (approximately)
787 elapsed = preferred_clock() - start
File ~/mambaforge/envs/pydap_docs/lib/python3.11/site-packages/requests/adapters.py:720, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
717 raise ConnectTimeout(e, request=request)
719 if isinstance(e.reason, ResponseError):
--> 720 raise RetryError(e, request=request)
722 if isinstance(e.reason, _ProxyError):
723 raise ProxyError(e, request=request)
RetryError: HTTPSConnectionPool(host='opendap.earthdata.nasa.gov', port=443): Max retries exceeded with url: /providers/POCLOUD/collections/ECCO%20Ocean%20Temperature%20and%20Salinity%20-%20Monthly%20Mean%20llc90%20Grid%20(Version%204%20Release%204)/granules/OCEAN_TEMPERATURE_SALINITY_mon_mean_2017-01_ECCO_V4r4_native_llc0090.dap?dap4.ce=i%5B0%3A1%3A89%5D%3Bj%5B0%3A1%3A89%5D%3Bk%5B0%3A1%3A49%5D%3Btile%5B0%3A1%3A12%5D&dap4.checksum=true (Caused by ResponseError('too many 500 error responses'))
theta_salt_ds
Finalmente, visualizamos THETA#
Variable = [theta_salt_ds['THETA'][0, 0, i, :, :] for i in range(13)]
clevels = np.linspace(-5, 30, 100)
cMap='RdBu_r'
fig, axes = plt.subplots(nrows=4, ncols=4, figsize=(8, 8), gridspec_kw={'hspace':0.01, 'wspace':0.01})
AXES_NR = [
axes[3, 0], axes[2, 0], axes[1, 0], axes[3, 1], axes[2, 1], axes[1, 1],
]
AXES_CAP = [axes[0, 0]]
AXES_R = [
axes[1, 2], axes[2, 2], axes[3, 2], axes[1, 3], axes[2, 3], axes[3, 3],
]
for i in range(len(AXES_NR)):
AXES_NR[i].contourf(Variable[i], clevels, cmap=cMap)
for i in range(len(AXES_CAP)):
AXES_CAP[i].contourf(Variable[6].transpose()[:, ::-1], clevels, cmap=cMap)
for i in range(len(AXES_R)):
AXES_R[i].contourf(Variable[7+i].transpose()[::-1, :], clevels, cmap=cMap)
for ax in np.ravel(axes):
ax.axis('off')
plt.setp(ax.get_xticklabels(), visible=False)
plt.setp(ax.get_yticklabels(), visible=False)
plt.show()
Fig. 3. Visualizacion de la variable Surface temperature, similar al metodo utilizado en la Figura 2.