In May 2025, MeteoSwiss started publishing their weather forecasting model outputs as open government data. Here we will have a look at the weather model ICON, how MeteoSwiss implements open government data, and how this data gets used for lowlevelwind.ch - a new website that visualizes wind more granularly at low altitudes than what existing services are offering.
ICON is the name of MeteoSwiss' latest weather model. It was originally developed in Germany by a collaboration of a Max Planck Institute and Deutscher Wetter Dienst DWD, but now its development is continued by an international consortium of which Switzerland is part of. ICON gets its name from the geometry of the underlying grid: an icosahedral grid made of triangles.
MeteoSwiss operates two variants of the ICON model - CH1 and CH2. The CH1 model has a very high spatial resolution of roughly 1 km side length per grid cell. This allows to resolve a lot of terrain features like valleys, hills, and mountains. The forecast is re-initialized and re-computed every 3 hours and then operates with 10 seconds timesteps up to a total forecast duration, also called "horizon", of 33 hours. There are 80 vertical levels with z1 being at an altitude of almost 20'000 m and z80 being at ground level. The levels do not have constant altitude, they rather follow the ground level. At low altitudes the vertical difference between levels is smaller than at high altitudes probably because the scales over which changes happen are smaller at higher altitudes.
The CH2 model on the other hand has a lower spatial resolution of roughly 2.1 km, but it comes with a longer total forecast duration of 120 hours (5 days). It is re-initialized and re-computed every 6 hours.
The spatial extent of both models is Switzerland plus buffer. It goes from Northern Corsica to Luxembourg in the South-North direction and from Bordeaux to Vienna in the West-East direction.
Both CH1 and CH2 are ensemble prediction systems EPS. They operate on an ensemble of possible initial conditions of the atmosphere. One big problem of forecasting is to know what the initial state of the model should be. There are measurements available from ground weather stations, precipitation radars, aircraft observations, and from weather sounding balloons. From this data one guesses the initial state of the weather model. But since one does not know exactly what the initial state is, one perturbs the best guess in different directions to get an ensemble of initial conditions. The model is then run on all the ensemble members in parallel. If the weather is very stable, those different ensemble members will follow similar time evolutions. However, if the atmosphere is unstable for example when thunderstorms come, the time evolutions will diverge in different directions. Having multiple ensemble members allows one to generate probabilistic weather forecasts, e.g., if 5 out of 10 runs say it rains, the rain probability is 50 percent. ICON CH1 has 10 ensemble members and CH2 has 20.
MeteoSwiss made a beautiful video showing in 3D the icosahedral grid cells over the mountains. They explained really nicely the basics of ICON and made a fascinating animation in 3D. It is worth watching! https://vimeo.com/962909980
The weather models of MeteoSwiss used to run on dedicated supercomputers, but ICON now actually runs on a machine that is shared with researchers. It is the Alps supercomputing platform which on its own is a fascinating topic. Alps is distributed over multiple locations to provide geo-redundant supercomputing. There are two compute locations, one at the Swiss Supercomputing Center CSCS in Lugano and one at EPFL in Lausanne, then there is a storage location at PSI in Villingen, and finally meteorological data ingestion happens at ECMWF in Bologna. Alps can do 430 Petaflops with 7 MW power consumption. It uses AMD CPUs and NVIDIA GPUs, a few thousand of them. Cray built it, but they are not an independent company anymore as they were bought by HPE in 2019.
It is hard to overstate what an achievement this forecasting system is. The software was developed over decades in many collaborations between institutions and countries. The input data is assimilated around the world with standardized measurement systems and is shared between all members of the world meteorological organization WMO. To get a production system like this in place you need a lot of goodwill and willingness to collaborate from a lot of parties and it is good to see that this exists.
Starting from May 2025, MeteoSwiss publishes the forecast output data for free. It is a lot of data. We can estimate that each snapshot of ICON CH1 has the following size: 4 bytes for a float32 scalar field value, 50 single level fields (precipitation, ground sun intensity, etc), 11 multi level fields (temperature at each z-level) with 80 levels each, 10 ensemble members plus the non-perturbed run - all this together give something like 4 * (50 + 11 * 80) * (10 + 1) = 40'920 bytes per cell and ICON CH1 has 1.1M cells so that is roughly 47 gigabytes of data for a single snapshot in time in uncompressed form. Even though ICON CH1 has a 10 seconds timestep, the model data is only published for every full hour. With a total duration of 33 hours this gives another factor 34 for the total size of one model run, i.e., a total of 1.6 terabytes in uncompressed form.
MeteoSwiss recomputes ICON CH1 every 3 hours and keeps the model output available for 24 hours. The data is distributed in GRIB2 files, a format commonly used for weather and atmospheric data. To keep file sizes manageable, the data is split by field and forecast horizon into separate files.
To make it easier for users to get started with this open government data, they wrote a python library called meteodata-lab which has convenience functions to download data and load it into xarray DataArrays. The library also provides utilities to compute quantities like the relative humidity from the bare forecast output data, and it has helpers to reproject from the irregular icosahedral grid to a projected coordinate reference system for map visualizations. In addition to that library, they also published a set of jupyter notebooks with some great examples on how to use the data. Good examples are important for new users, but they are also valuable for training chatbots. Since probably more and more people will discover libraries through chatbots, well-written examples are more important than ever.
The MeteoSwiss team is very active on GitHub and responds quickly to user questions. They seem to stand fully behind their open-data initiative!
Let us now switch to the website lowlevelwind.ch. The idea here was to take MeteoSwiss' open weather forecast data and visualize something that standard products like windy.com are not offering: wind at low elevation levels. This is particularly useful for hobbies that depend on wind at low elevations, like for example flying hot-air balloons. Normally, wind is provided at some standard pressure levels like for example 900 hPa (roughly 900 m 3000 ft), 850 hPa (roughly 1500 m 5000 ft), 800 hPa (2000 m 6400 ft), etc. However, the underlying ICON model is much more granular vertically. There are a total of 80 levels reaching from ground to roughly 20'000 m, however, half of the levels are below 4500 m and they get closer together vertically the lower they are. On lowlevelwind.ch, we visualize each available vertical z-level individually giving balloon pilots maximal insight into the forecast data. Whether or not the data is accurate is of course a different question...
The color scheme used on lowlevelwind.ch follows the official MeteoSwiss wind speed coloring, going from green to yellow to brown to purple etc. The background map shows only place names, lake outlines, country borders, and some hillshading. There is a widget to select the forecast horizon and the z-level, and a wind speed picker shows the numeric value of the wind at a given location on the screen together with the elevation.
The basemap is rendered with MapLibre GL JS and uses the Protomaps basemap. The hillshading method is called "igor" which was recently introduced in v5.5. The terrain data is from tilezen joerd. Maybe this can be replaced with Mapterhorn data at some point in the future.
Wind is rendered with Jan Žák's Weather Layers GL library, available under the Mozilla Public License. It is a really great library which builds on top of deck.gl and Jan published it under MPL just recently. Weather Layers GL takes as input a single PNG containing the wind data in different color channels. The U variable (wind from West to East) is encoded in the red channel, and the V variable (wind from South to North) is encoded in the green channel. Blue can be filled with zeros and the alpha channel can indicate absence of data. With 8 bits per color channel the wind speed in km/h is scaled from the range [-128, 127] to [0, 255]. The PNG has to be in WGS84 and the user has to supply its spatial bounds. It is surprising how small the size is: To cover Switzerland we use roughly 1 km per pixel so that means we only need roughly 400 by 200 pixels. The resulting PNG is only roughly 70 kB in size. There is no need for tiling here, all wind data fits into a single image. To get a smooth looking wind field, Weather Layers GL performs cublic interpolation on the PNG.
Weather Layers GL can also read tiffs with float32 data, using the geotiffjs library. Since the vertical z-levels do not have uniform elevation, we supply a tiff with the elevation of each pixel of the PNG. This data is then used in the speed picker with the red dot.
The wind PNGs are prepared on a backend server. It is a 16 EUR per month Hetzner with 8 cores and 16 gigabytes of memory. A python script runs in a forever-loop and checks periodically if new data is available on the MeteoSwiss download service. If so, which happens every 3 hours, it first downloads the data, then iterates through the horizons and regrids the data for all 80 vertical levels to WGS84 and saves it as PNG.
Originally this process took a bit more than an hour to complete. But with a small improvement the time was reduced to less than 7 minutes: To sample the ICON grid data at a regular grid one first needs to perform a Delaunay triangulation on the ICON cells and then compute linear interpolation weights. The weights can then be applied to the actual values to sample the data. Computing the weights takes orders of magnitude longer than applying them to values. But since with the same target grid one always needs the same interpolation weights, those weights can be cached and the caching is what brought the performance improvement.
The source code for the front and backend is available on GitHub at http://github.com/wipfli/lowlevelwind. A lot of it was written with Claude and ChatGPT. It was fast to get started but afterwards there was little feeling of ownership. Some things were done in a seemingly clever way by Claude but it was too complicated to maintain. When asking the chatbot for a small change it would rewrite the entire file upside-down. Working with these tools requires a lot of patience and leadership, otherwise it is not satisfying in the long term.
Lowlevelwind.ch is a nice thing and maybe it could be built out a little bit in the future. Interesting additions could be an option to show the different ensemble members. Currently only the non-perturbed run is shown. It could also be interesting to have the longer forecast horizons of ICON CH2 which go 120 hours into the future instead of only 33. Then more variables could also be fun like for example cloud cover, temperature, precipitation, and maybe the vertical wind component W.
What would be challenging then is the required compute power. Going for example to all the ensemble members would mean a multiplier of 11, so 77 minutes instead of 7 minutes. While that would maybe still be OK if new data comes every 3 hours, with more variables like cloud cover, etc the computation duration could quickly become too long. And it is also worth noting that the more options you supply to users (or user, since probably only the author uses this website), the less each individual data asset gets looked at. It feels a bit wasteful to compute so many things if nobody looks at them. So what could be a good option here is to push work from the server to the client. For example, the server could produce upfront the interpolation weights and slice the GRIB2 data into smaller chunks. The actual interpolation and regridding would happen on the client.
Another fun direction to work on would be time-series forecasts for a given point location. For example, what is the wind speed and cloud cover over the next 33 hours in Zürich? Those visualizations are called meteograms and they require a different data organization on the server to run efficiently.