Technical interoperability: Data access protocols
Last updated on 2026-07-14 | Edit this page
Overview
Questions
- What is technical interoperability?
- What is the DAP (Data Access Protocol)?
- How does OPeNDAP enable remote access without full download?
- What happens when we open a remote NetCDF file using
xarray.open_dataset()? - Why are streaming protocols essential for large-scale scientific workflows?
Objectives
By the end of this episode, learners will be able to:
- Define technical interoperability in the context of scientific data infrastructures.
- Explain how DAP enables interoperable machine-to-machine data access.
- Access a remote NetCDF dataset via OPeNDAP using Python.
- Perform server-side subsetting of variables and dimensions.
- Distinguish between metadata access and actual data transfer.
What is technical interoperability?
Technical interoperability concerns machine-to-machine communication.
A system is technically interoperable when independent systems can exchange and access data through standardized protocols without manual intervention.
If structural interoperability answers:
“Can I read this file?”
Technical interoperability answers:
“Can I access and exchange this data across systems in a scalable way?”
This layer operates below semantics.
It is about transport, protocol, and
infrastructure.
Examples include:
In scientific data infrastructures, technical interoperability enables remote analysis workflows.
Why file download is not scalable
Large scientific datasets (climate reanalysis, ocean models, satellite archives) often reach:
- Tens of gigabytes
- Terabytes
- Petabytes
Downloading entire files:
- Is inefficient
- Consumes bandwidth
- Duplicates storage
- Breaks reproducibility pipelines
Modern workflows require:
- Remote access
- Server-side filtering
- On-demand subsetting
- Integration into automated pipelines
This is where streaming protocols become essential.
DAP and OPeNDAP
The Data Access Protocol (DAP) is a protocol designed to enable remote access to structured scientific data.
OPeNDAP is a widely adopted implementation of DAP.
DAP allows:
- Access to metadata without full download
- Server-side slicing (e.g., select time range, variable subset)
- Transmission of only requested data
In practice, this means:
You interact with a dataset hosted on a remote server as if it were local — but only the necessary data is transferred.
This is technical interoperability in action.
Hands-on: Accessing NetCDF via OPeNDAP in Python
We now move from concept to practice.
We will use:
xarray- A remote OPeNDAP endpoint
- A NetCDF dataset hosted on a THREDDS server
- Jupyter Lab
Step 1 – Open a remote dataset
Open Jupyter Lab and choose the appropiate environment of the lesson (see Setup)
Launch Jupyter Lab, open a terminal and type:
- Open a new notebook
- Check installed libraries
- Open a dataset
PYTHON
url = "https://opendap.4tu.nl/thredds/dodsC/IDRA/2019/01/02/IDRA_2019-01-02_12-00_raw_data.nc"
ds = xr.open_dataset(url,engine="pydap")
ds
Observe:
The dataset structure loads immediately.
Dimensions and metadata are visible.
The file has not been fully downloaded.
What happened?
Only metadata and coordinate information were accessed.
Step 3 – Perform server-side subsetting
Actual data transfer occurs
Now lets select a variable → “spectrum_width”, using positional indexing and we will take a 10×10 subset along two dimensions.
- Now lets print the values of this subsetting
PYTHON
ds["spectrum_width"].isel(time_processed_data=slice(0,10),range=slice(0,10)).values # to print values in the scren
- Slicing by the names of the dimensions
PYTHON
ds["spectrum_width"].sel(
time_processed_data=slice("2019-01-02T12:00:00.000000000", "2019-01-02T12:00:02.097152173"),
range=slice(0, 1000)
)
- Using
head
PYTHON
ds["spectrum_width"].head()
ds["spectrum_width"].head(time_processed_data=10)
ds["spectrum_width"].head(range=2)
ds["spectrum_width"].head(range=2).to_pandas() # tabular view
PYTHON
ds["spectrum_width"].isel(time_processed_data=0).values #one radar profile (1D slice)
ds["spectrum_width"].isel(range=1).values # One time series
Now actual data transfer occurs — but only for:
One variable
A limited time window
This is server-side subsetting enabled by DAP.
Step 4 Plotting a profile
PYTHON
import matplotlib.pyplot as plt
ds["spectrum_width"].isel(time_processed_data=0).plot()
ds["spectrum_width"].head(range=10).plot()
You have multiple equivalent ways to express the same operation:
.isel() → positional slicing (what you used)
.sel() → coordinate-aware slicing .head() →
quick inspection .values → raw data extraction
.plot() → visual interpretation
Technical interoperability — True or False?
Indicate whether each statement is True or False and justify your answer.
Opening a remote dataset with xarray.open_dataset() automatically downloads the entire file.
DAP enables server-side filtering before data transfer.
Streaming protocols replace the need for structural interoperability.
OPeNDAP works independently of file formats.
Technical interoperability enables automated workflows across infrastructures.
False. Only metadata is accessed initially; data is transferred upon explicit selection.
True. Subsetting occurs on the server before transmission.
False. Technical interoperability depends on structural interoperability.
False. DAP operates on structured data models (e.g., NetCDF).
True. It enables scalable machine-to-machine access.
Demo: Can Ash combine two IDRA radar datasets?
Ash has found two IDRA radar files exposed through OPeNDAP:
IDRA_2009-04-27_06-08_raw_data.ncIDRA_2019-01-02_12-00_raw_data.nc
Both files come from the same radar system and both are available remotely through OPeNDAP. At first, this suggests that they should be easy to compare. But before Ash can combine them, she needs to inspect whether they are structurally and semantically compatible.
In this demo, we will compare the two files using Python and
xarray.
Questions
- Can we open both datasets remotely without downloading the full files?
- Do both datasets contain the same variables?
- Do the key radar variables have the same dimensions and units?
- Can we extract the same variable from both years?
- Can we combine a selected variable into one analysis-ready object?
- Why might we want to save this combined subset as Zarr?
Objectives
After this demo, learners should be able to:
- open NetCDF files remotely through OPeNDAP;
- inspect dimensions, variables, units, and metadata;
- compare the structure of two related datasets;
- select a common radar variable from both datasets;
- create a small combined dataset for comparison;
- detect and quantify missing values in each subset;
- visualise and compare the selected radar variable across two years.
- understand why Zarr can be useful for repeated or scalable analysis.
Setup
We use the OPeNDAP data URLs, not the .html inspection
pages.
PYTHON
url_2009 = "https://opendap.4tu.nl/thredds/dodsC/IDRA/2009/04/27/IDRA_2009-04-27_06-08_raw_data.nc"
url_2019 = "https://opendap.4tu.nl/thredds/dodsC/IDRA/2019/01/02/IDRA_2019-01-02_12-00_raw_data.nc"
- To supress the warning when reading the file with
pydap:
Step 1: Open the datasets remotely
PYTHON
ds_2009 = xr.open_dataset(url_2009,engine="pydap")
ds_2019 = xr.open_dataset(url_2019,engine="pydap")
At this point, Ash has not manually downloaded the full files. She is inspecting the datasets remotely through OPeNDAP.
Step 2: Inspect dimensions
Ash checks whether both files organise the data in a similar way. For example, she expects to see dimensions such as:
time_raw_datasample_beat_signaltime_processed_datarange
This matters because variables can only be compared directly if their dimensions are compatible.
Step 3: Compare variable names
PYTHON
vars_2009 = set(ds_2009.data_vars)
vars_2019 = set(ds_2019.data_vars)
common_vars = sorted(vars_2009.intersection(vars_2019))
only_2009 = sorted(vars_2009.difference(vars_2019))
only_2019 = sorted(vars_2019.difference(vars_2009))
print("Common variables:")
print(common_vars)
print("\nOnly in 2009:")
print(only_2009)
print("\nOnly in 2019:")
print(only_2019)
This is Ash’s first interoperability check. If the files do not contain the same variables, she cannot simply reuse the same analysis code for both years.
Step 4: Inspect key radar variables
Ash focuses on a few processed radar observables:
PYTHON
radar_variables = [
"equivalent_reflectivity_factor",
"differential_reflectivity",
"radial_velocity",
"spectrum_width",
"differential_phase",
]
PYTHON
for var in radar_variables:
print(f"\nVariable: {var}")
print("2009 dimensions:", ds_2009[var].dims)
print("2019 dimensions:", ds_2019[var].dims)
print("2009 units:", ds_2009[var].attrs.get("units"))
print("2019 units:", ds_2019[var].attrs.get("units"))
This check helps Ash answer practical questions:
- Does the same variable exist in both files?
- Is it organised over the same dimensions?
- Are the units the same?
- Is the meaning of the variable described in metadata?
For example, equivalent_reflectivity_factor is a radar
variable related to precipitation, but it is not the same as rainfall
amount. It is usually expressed in dBZ, while rainfall
amount may be expressed in units such as mm or
mm h-1.
Step 5: Select one variable for comparison
Ash starts with equivalent_reflectivity_factor.
Now she checks whether both arrays use the same dimensions.
If both use time_processed_data and range,
Ash can compare them more easily.
Step 6: Create a small subset
To keep the demo fast, Ash selects only the first few time steps and the first part of the range dimension.
PYTHON
subset_2009 = refl_2009.isel(time_processed_data=slice(0, 20), range=slice(0, 100))
subset_2019 = refl_2019.isel(time_processed_data=slice(0, 20), range=slice(0, 100))
This is an important practical benefit of OPeNDAP: Ash can request a subset of the remote data instead of downloading everything manually.
Step 6b: Check missing values in each subset (Optional)
Before combining the subsets, Ash checks whether the selected parts of the data contain many missing values.
This matters because a visual comparison can be misleading if one subset contains much less valid data than the other.
PYTHON
def missing_value_summary(data_array, label):
total_values = data_array.size
nan_values = int(data_array.isnull().sum().item())
valid_values = total_values - nan_values
nan_percentage = 100 * nan_values / total_values
return {
"subset": label,
"total_values": total_values,
"valid_values": valid_values,
"nan_values": nan_values,
"nan_percentage": round(nan_percentage, 2),
}
PYTHON
nan_summary = pd.DataFrame(
[
missing_value_summary(subset_2009, "2009 subset"),
missing_value_summary(subset_2019, "2019 subset"),
]
)
nan_summary
Ash can also add a simple warning threshold. Here, the threshold is set to 50%, but this is only a teaching choice.
PYTHON
nan_threshold = 50
nan_summary["interpretation"] = nan_summary["nan_percentage"].apply(
lambda value: "High number of missing values" if value > nan_threshold else "Acceptable for this demo"
)
nan_summary
This check helps Ash avoid comparing two subsets blindly. If one year contains many more missing values than the other, the difference in the plots may reflect data availability rather than a real difference in the radar signal.
Step 7: Add a year coordinate and combine the subsets
Because the two files come from different dates, Ash first converts the selected time dimension into a simple relative index.
This means she compares the first 20 selected time steps from 2009 with the first 20 selected time steps from 2019.
PYTHON
subset_2009 = subset_2009.assign_coords(
time_processed_data=range(subset_2009.sizes["time_processed_data"])
)
subset_2019 = subset_2019.assign_coords(
time_processed_data=range(subset_2019.sizes["time_processed_data"])
)
PYTHON
subset_2009 = subset_2009.expand_dims(year=[2009])
subset_2019 = subset_2019.expand_dims(year=[2019])
Ash now has one small combined object containing the same radar variable from two different years.
PYTHON
combined.name = "equivalent_reflectivity_factor"
combined_ds = combined.to_dataset()
combined_ds
Optional Checking for nan values after merging
PYTHON
nan_comparison = pd.DataFrame(
[
{
"year": 2009,
"nan_before_combining": int(subset_2009.isnull().sum().item()),
"nan_after_combining": int(combined_ds[var].sel(year=2009).isnull().sum().item()),
},
{
"year": 2019,
"nan_before_combining": int(subset_2019.isnull().sum().item()),
"nan_after_combining": int(combined_ds[var].sel(year=2019).isnull().sum().item()),
},
]
)
nan_comparison["extra_nans_after_combining"] = (
nan_comparison["nan_after_combining"] - nan_comparison["nan_before_combining"]
)
nan_comparison
Step 8: Plot the two years for comparison
Now Ash can make a visual comparison between the 2009 and 2019 subsets.
First, she plots the selected radar variable as a two-dimensional
image, with range on one axis and
time_processed_data on the other.
PYTHON
combined_ds[var].plot(
x="range",
y="time_processed_data",
col="year",
robust=True,
)
plt.suptitle("Equivalent reflectivity factor comparison: 2009 and 2019", y=1.05)
plt.show()
This plot helps Ash visually inspect whether the structure of the radar signal looks similar or different between the two selected files.
However, two-dimensional plots can be difficult to compare in detail. Ash can also reduce each subset to a simple profile by averaging over time.
PYTHON
mean_over_time = combined_ds[var].mean(dim="time_processed_data", skipna=True)
mean_over_time.plot.line(
x="range",
hue="year",
)
plt.title("Mean equivalent reflectivity factor over range")
plt.ylabel(combined_ds[var].attrs.get("units", "value"))
plt.show()
This plot shows how the average value of the selected radar variable changes across the range dimension for each year.
Ash can also average over range and compare how the signal changes across the selected time steps.
PYTHON
mean_over_range = combined_ds[var].mean(dim="range", skipna=True)
mean_over_range.plot.line(
x="time_processed_data",
hue="year",
)
plt.title("Mean equivalent reflectivity factor over selected time steps")
plt.ylabel(combined_ds[var].attrs.get("units", "value"))
plt.show()
These plots are not a full scientific analysis. They are a first exploratory comparison that helps Ash understand whether the two datasets can be handled with a shared workflow.
Step 9: Add useful metadata
PYTHON
combined_ds.attrs["title"] = "Small combined IDRA reflectivity subset for interoperability demo"
combined_ds.attrs["source_datasets"] = "IDRA OPeNDAP files from 2009-04-27 and 2019-01-02"
combined_ds.attrs["purpose"] = "Demonstration of remote access, variable inspection, subsetting, and combination"
combined_ds.attrs["warning"] = (
"This is a small teaching subset. It is not a complete scientific rainfall or drizzle analysis."
)
This step shows learners that combining data is not only a technical operation. Ash also needs to preserve enough metadata to explain where the data came from and what processing decisions were made.
Step 10: Save the combined subset as Zarr
For repeated analysis, Ash may want to store the small combined subset in a format that is efficient for chunked, cloud-friendly access.
Later, she can reopen it directly:
This creates a small analysis-ready version of the subset. Instead of repeating the same remote access and harmonisation steps every time, Ash can reuse the prepared Zarr version in later notebooks or workflows.
Discussion
This demo shows three different interoperability layers.
First, technical interoperability: OPeNDAP allows Ash to access the files remotely and subset the data programmatically.
Second, structural interoperability: NetCDF exposes dimensions, variables, coordinates, and attributes in a predictable structure.
Third, workflow interoperability: Zarr allows Ash to store a prepared subset in a chunked format that can be reopened and reused in later analysis.
However, the demo also shows that interoperability is not automatic. Ash still has to inspect the variables, units, dimensions, coordinates, metadata, and provenance before deciding whether the datasets can be compared safely.
Technical interoperability enables machine-to-machine data exchange through standardized protocols.
OPeNDAP implements the DAP protocol for remote access to structured scientific datasets.
Remote datasets can be explored without full download.
Server-side subsetting reduces bandwidth and supports scalable workflows.
Streaming protocols transform data repositories into interoperable computational infrastructure.