An real-time demonstration of the Borsuk-Ulam theorem

The Borsuk-Ulam theorem tells us that a sphere with two continuous scalar variables defined on its surface will always have at least 1 pair of antipodes with equal values of both variables. Cool! But what does that mean?

The classic way to think about it is to consider scalar variables like air temperature and barometric pressure that are defined everywhere on the Earth. Borsuk-Ulam says that at any given moment there are always at least two points on the surface that lie exactly opposite to one another, and have exactly equal temperature and pressure. It’s not necessarily intuitive - but it’s what the math says.

I thought it would be neat to figure out where those points are, in real time. My cursory Googling didn’t dig up any related projects, and I’m not really sure that the end result is that interesting (most of the matching points are over the ocean). Still, let’s go find these points. We’ll need to gather a few things:

Data

Obviously, nobody has exact real-time measurements of temperature and pressure everywhere on Earth, (especially over the oceans). Therefore we’ll have to use a model (in this case, the GFS) that provides coarse-grained (on a \(1^\circ\) grid) estimates of a bunch of different meteorological variables. The GFS model runs every 6 hours, and provides forecasts every 3 hours out to 16 days. Each model run produces gigabytes of data, but we only need instantaneous snapshots of temperature and pressure, which we can access via NOAA’s OpenDAP server.

When the app (below) instantiates, it identifies the time slice from the latest model run that corresponds to the current time. So, if you happen to load this page at 4 PM EST, it will fetch the nearest prediction for +4 hours from the model that ran at noon, which is the forecast for 3 PM EST. Not perfect, but it’s bound to be reasonably close.

Code

Because temperature and pressure are constantly changing, we have to code this up in a way that runs on demand. GitHub Pages (where this blog lives) just serves static HTML pages, and I have no interest in running my own web server - but we can use WebAssembly to run some simple Python code directly in the browser.

WebAssembly is nifty, but it presents some challenges. The Python binary (Pyodide) that is compiled for WebAssembly has a fairly extensive list of libraries that mostly work out of the box. But it also intercepts any network interactions, so loading data from an external source has to be done through Pyodide’s open_url method. This caused me all sorts of headaches when trying to use pydap and xarrow to pull in data from NOAA’s OpenDAP server, related to request headers. However, I learned that OpenDAP offers native slicing via the URL and returns raw ASCII text directly. So, instead of writing:

import xarray as xr

data = xr.open_dataset("https://nomads.ncep.noaa.gov/dods/gfs_1p00/gfs20250423/gfs_1p00_12z/")
temperature = data['tmp2m']

I can write:

from pyodide.http import open_url

url = "https://nomads.ncep.noaa.gov/dods/gfs_1p00/gfs20250423/gfs_1p00_12z.ascii?tmp2m[0:1:0][0:1:179][0:1:359]"
temperature = open_url(url).read()

With the same result. This might not work as well with very large datasets (for which lazy loading via open_dataset is superior), but for the 65,000 data points needed for this app, it’s not so bad.

Math

Now that we have the data being pulled dynamically, we need to actually go out and find the antipodes. Since we only have about 65,000 points to consider, it’s pretty easy to just compare each point to its opposite and measure the difference. The only trick is, since we have a grid of points, it’s possible that none of the grid points align exactly with the Borsuk-Ulam antipodes. So I set a threshold, where the points are considered matches if the temperatures and pressures at the antipodes are within 0.1% of each other. This seems to work pretty much every time, typically finding around 30 pairs of points.

App

You can find the app at the bottom of this paragraph. Once it has loaded, simply click the “Generate!” button to choose a random pair of antipodes and draw them (in violet circles) on the globe. You can hover over the points to see the temperature (tmp2m for the temperature at 2 meters above ground) or pressure (press, in atmospheres), and rotate the globe to convince yourself that the antipodes are indeed exactly opposite one another.




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • REPL-driven development in Helix
  • Hello world!