1. Why a stability score?

When you ask a conventional weather app "what will the weather be tomorrow?", the answer is always the same: the values from the latest run of the last model computed. This approach has a fundamental limitation — it says nothing about how trustworthy that forecast is.

Two identical forecasts can carry very different levels of confidence. Consider two scenarios for tomorrow at 2 pm:

Scenario A 9 / 10
For the past 5 days, every run of every model has predicted a 15-knot north-westerly. The forecast hasn't budged. The situation is robust and well established.
Scenario B 3 / 10
The latest run predicts 15 knots, but yesterday it called for 5, and the day before, 25. The forecast swings from one run to the next. The situation is chaotic and hard to predict.

In both cases a conventional app would display "15 knots". Solano would show you 9/10 for the first scenario and 3/10 for the second. This is the heart of the Solano method: measuring the temporal consistency of forecasts, not just their point value.

2. Weather model runs: what are they?

A numerical weather prediction model is a program that solves the equations of fluid dynamics on a three-dimensional grid of the global atmosphere. It is initialised from an "initial state" — a snapshot of the atmosphere at a precise moment, built from thousands of observations: surface weather stations, radiosondes, satellites, marine buoys, aircraft measurements in flight…

This initialisation happens several times a day, at conventional hours expressed in Coordinated Universal Time: 00Z, 06Z, 12Z, 18Z. Each execution of the model from an initial state is called a run.

A few examples of publication frequency:

At each run, the model "starts from scratch" with a slightly different initial state (the most recent observations have been folded in), and can produce slightly — or sometimes very — different forecasts for the same target date. It is these differences between successive runs that Solano measures and quantifies.

A run doesn't replace the previous one: it coexists with it. For a target date three days out, there are already dozens of runs issued over the past few days that have forecast the conditions of that day. Solano keeps this full history.

3. The models Solano analyses

Solano relies on Open-Meteo's Single Runs API, which gives access to the historical archive of every run from the world's major forecast centres. Up to 16 atmospheric models are available depending on location, sorted from highest resolution to broadest coverage:

Model Centre Coverage Grid Horizon Frequency
ICON-CH1MeteoSwissSwitzerland and the Alps1 km34 h8×/day
AROME HDMétéo-FranceFrance1.5 km3 d8×/day
ICON-2IARPAE (Italy)Mediterranean2 km3 d2×/day
UKMO UK HDMet Office (UK)United Kingdom2 km2 d24×/day
ICON-CH2MeteoSwissSwitzerland and the Alps2.1 km5 d8×/day
ICON-D2DWD (Germany)Germany and surroundings2.2 km2 d8×/day
GEM HRDPSEnvironment CanadaCanada2.5 km2 d4×/day
ICON-EUDWD (Germany)Europe7 km5 d8×/day
ECMWF IFS HRESECMWFGlobal9 km15 d4×/day
GEM RDPSEnvironment CanadaNorth America10 km3 d4×/day
UKMO ¹Met Office (UK)Global10 km7 d2×/day
ARPEGEMétéo-FranceEurope11 km5 d4×/day
GFSNOAA (United States)Global13 km16 d4×/day
ICON ¹DWD (Germany)Global13 km7 d4×/day
ARPEGE World ¹Météo-FranceGlobal25 km4 d4×/day
GEM GDPS ¹Environment CanadaGlobal25 km10 d2×/day

¹ Global fallback model — shown only when the high-resolution regional variant is not available for this point (e.g. ICON global outside Europe, UKMO global outside the United Kingdom).

For conditions at sea, Solano also analyses 5 wave and current models: ARPEGE-Wave and WAM (Météo-France and ECMWF), GFS-Wave (NCEP), EWAM and the Météo-France Currents model. Marine models do not contribute to the atmospheric stability score — they are shown separately for points at sea.

Each model covers different geographic areas. When a GPS point falls outside a model's coverage, that model is automatically excluded from the analysis. For multi-resolution families (ARPEGE, ICON, UKMO, GEM), the high-resolution regional model is automatically preferred over the global model when the point is within its area.

4. The "movie of the runs": watching forecasts evolve

For a given GPS point and target date, Solano reconstructs every forecast issued by each model for that date: from the J-5 run to the most recent one. If the target date is three days out, dozens of runs have already forecast the conditions of that day — from different distances in time.

Seeing a forecast at a 3-day lead time (the model runs today and predicts for the day after tomorrow) is not the same as seeing one at a 1-day lead time (the model predicts for tomorrow with fresher, more accurate initialisation data). In general, the closer you get to the target day, the more accurate the forecast — but not always, and that is precisely what Solano measures.

Solano's spaghetti chart overlays all of these curves on a single graph: each line is a different run, coloured by its age. The interpretation is immediate:

This visualisation lets you distinguish structural uncertainty (the weather situation is inherently hard to predict) from transient uncertainty (the models struggled at 5 days but agree now at 2 days).

5. Computing the score — formula and weighting

The stability score is computed by a four-step algorithm, applied independently for each model and each target date.

Step 1 — Hour-by-hour comparison

For each hour of the target date, Solano computes the weighted average of all previous runs (each run weighted by its age — see section 6), then measures the gap between that average and the value of the latest run:

Δ(hour) = |value_latest_run(hour) − Σ(wi × value_runi(hour)) / Σwi|

The weighted average represents the "historical consensus" of the forecasts for that moment. If every older run said 15 knots and the latest one announces 22 knots, the gap is 7 knots — and the score will drop.

For wind direction, the gap is not an arithmetic difference but the short angular arc (the minimum angular difference between two headings, always between 0° and 180°). This avoids treating 355° and 5° as very different — they differ by only 10°.

Step 2 — Average over all hours

The hour-by-hour gaps are averaged across the whole day, producing a mean gap per variable. It is this single value — the typical gap over the entire day — that feeds into the score formula.

Step 3 — Conversion to a 0–10 score per variable

The mean gap is converted into a score by an exponential decay, parameterised by a threshold S specific to each variable:

score = 10 × e−mean_gap / S

Some concrete reference points: at zero gap, score = 10 (perfect stability). At a gap of S, score ≈ 3.7 — so the threshold is not the point where the score reaches 0, but the point where it has already dropped substantially. Beyond 3×S, the score is below 0.5 and can be treated as zero. The exponential is deliberately lenient on small gaps: a 1-knot divergence for gusts (S = 6) only brings the score down from 10 to about 8.5.

Variable Threshold S Default weight
💨 Wind gusts6 knots30 %
🌬️ Mean wind4 knots20 %
🌧️ Precipitation1 mm/h20 %
🧭 Wind direction45°10 %
👁️ Visibility2,000 m10 %
🌡️ Temperature3 °C5 %
☁️ Cloud cover20 %5 %

Step 4 — Weighted aggregation

The scores of all variables are combined into a weighted average. The default weights reflect the relative importance of each variable for marine and outdoor activities (gusts are the most safety-critical criterion, followed by mean wind and precipitation). These weights are fully customisable in the app's preferences.

When a model lacks a given variable (for example, AROME HD does not cover marine visibility), that variable's weight is automatically redistributed across the other available variables, so the score stays comparable between models.

6. Temporal decay

Not all past runs carry equal weight in the score. A run issued 5 days ago was based on far less recent observations than one issued yesterday — so it is less relevant for assessing the consistency of the current forecast.

Solano applies an exponentially decreasing weighting to each run according to its age, parameterised by a time constant τ:

weight(run) = e−age_in_days / τ with τ = 2.5 days

τ is the decay constant: at t = τ, the weight reaches e⁻¹ ≈ 37 % (not 50 % — the true half-life would be τ × ln 2 ≈ 1.7 days). In practice: a run one day old carries about 67 % of the weight of a run from today, a run 4 days old about 20 %, and a run 7 days old less than 6 %.

This decay reflects the meteorological intuition: what was forecast a week ago tells us less about current stability than what was forecast yesterday or the day before. A model that "changes its mind" at the last minute is less concerning than one that has been oscillating for several days.

Example: for a target date two days out, today's run carries 100 %, yesterday's 67 %, the day before's 45 %, and the J-5 run 14 %. Recent stability is therefore far more decisive than distant fluctuations.

7. ERA5 verification — forecast vs. reality

The stability score measures the internal consistency of forecasts — it doesn't tell you whether the forecasts were accurate. To complement this indicator, Solano includes a direct comparison between historical forecasts and the reference reanalysis ERA5T.

ERA5 is the atmospheric reanalysis from ECMWF (the European Centre for Medium-Range Weather Forecasts): a coherent, homogeneous reconstruction of the state of the atmosphere hour by hour since 1940, obtained by data assimilation of sparse observations (surface stations, radiosondes, satellites, buoys) into a state-of-the-art numerical model. It is not a direct measurement, but the best possible estimate of what actually happened. ERA5T is the "real-time" version of this reanalysis, available with roughly a 1-to-2-day delay.

The 📡 Verif. tab of the Spaghetti panel shows, for any day within the last 15 available:

This visualisation answers a concrete question: was the model that predicted 20 knots for the day before yesterday right? Does ERA5T give 18 knots (a good forecast) or 8 knots (a strong overestimate)?

Only the atmospheric variables available in recent ERA5 are shown: wind, gusts, direction, temperature, precipitation, cloud cover, pressure. Marine data (waves, currents) is not covered by recent ERA5.

8. Recent accuracy — MAE per lead time

By automating the forecast/ERA5T comparison over the last 15 days and every lead time, Solano computes for each model a measure of real local accuracy: the MAE (Mean Absolute Error).

MAE(model, variable, lead J-n) = mean(|forecast(J-n) − ERA5T|) over the last 15 days

The MAE is computed separately:

This MAE-per-lead-time profile reveals the real behaviour of each model on your exact geographic point. A model may be excellent at J-2 but mediocre at J-7; another may hold remarkable accuracy up to J-5. This information helps you intuitively weight forecasts according to their lead time.

Variable Good (<) Acceptable Weak (>)
Mean wind3 kt3–6 kt6 kt
Gusts4 kt4–8 kt8 kt
Wind direction20°20–40°40°
MSL pressure1.5 hPa1.5–3 hPa3 hPa
Temperature1.5 °C1.5–3 °C3 °C

MAE values are shown in the app as green/orange/red coloured badges on the model cards, and as a J-15 → J-1 time chart in the Spaghetti view. This chart shows the accuracy-degradation curve with lead time, characteristic of each model on your point.

In Quick weather, this measure is presented per day under the name "Accuracy": it indicates, for each model and at that day's lead time (a forecast for three days out is checked against past forecasts at a 3-day lead), the model's recent track record — how accurate it has been at that lead. It is the model's track record, to be distinguished from Confidence (agreement of the ensemble scenarios for this run, still to come): accuracy looks at the model's past, confidence looks at the uncertainty of the present situation. The two are complementary, not interchangeable.

An important nuance about the reference. ERA5 is a coarse-grid (~31 km) reanalysis, whereas some verified models run on a fine grid (AROME ~1.5 km). Comparing a fine model to a coarse reference penalises it on the local variability it legitimately resolves (sea breezes, terrain effects) — this is a "representativeness error", not a real model error. The ERA5 MAE nonetheless remains a useful signal because it is relative: all models are judged against the same reference, so their ranking stays informative. It reads as a "gap from a reference reanalysis", not as ground truth at the exact point — which is why Solano complements it with a direct comparison to real observations (buoys, airports) where they exist.

9. Ensemble confidence — the agreement of scenarios

Stability looks at how a model evolves from one run to the next ("the movie"), the MAE measures its past error. Ensemble confidence answers a third question, facing forward: for this precise lead time, do the possible scenarios agree?

Weather centres don't publish just a single "deterministic" forecast. They also run their model dozens of times with tiny perturbations to the starting conditions — this is the ensemble forecast. If all these scenarios tell the same story, the situation is predictable; if they diverge, the atmosphere is at a "tipping point" and no single forecast should be taken at face value.

Solano aggregates a pool of about 120 scenarios from three international ensemble systems — GEFS (NOAA, ~31 members), ICON-EPS (DWD, ~37) and ECMWF-ENS (European Centre, 51) — then, for each deterministic model shown, computes the share of these scenarios that give a wind practically equivalent to the model's value:

Confidence(model, day) = share of the ~120 members of the ensemble pool whose wind is within ±30 % / ±4 kt of the model's value that day

This measure combines two pieces of information into a single number:

The multi-model pool is more honest than a single ensemble: a model compared to its own family often judges itself too confident (it doesn't "see" its own systematic error). By confronting each forecast with the scenarios of three centres, confidence captures both the uncertainty of the scenarios and the disagreement between models. It also works for models with no ensemble of their own (ARPEGE, AROME), compared to the common cloud.

In the app, confidence is displayed as a coloured percentage pill, day by day, next to each day in the hourly strip of each model (Quick weather): green ≥ 70 %, orange ≥ 50 %, red below. It naturally decreases with lead time (the further out you look, the more the scenarios separate) and lets you spot at a glance which model is best supported by the scenarios, and which days are "worth watching".

10. The "When to leave?" departure score — formula and computation

The departure score condenses into a single 0–10 score the weather conditions encountered hour by hour over the whole route, for a given activity. It is distinct from the stability score — it doesn't assess run consistency but the absolute weather quality of a departure window.

Model aggregation

The values of all selected models and of every hour of the trip are pooled into a single distribution, from which the mean, the 90th percentile and the maximum are computed. This choice has a known limit: a model that has data for more hours (or for later windows) weighs more, and an outlying model pulls the score down. Solano reports this rather than hiding it; the activity verdict, by contrast, votes model by model (see section 15).

Candidates evaluated

Solano evaluates 13 candidate windows spaced 3 hours apart, from D−6h to D+30h (D = the departure time configured in the Route panel). For each candidate, the trip is reconstructed hour by hour — positions computed by the haversine formula at constant speed — and the weather is interpolated at each position.

Weather interpolation along the route

Between two consecutive waypoints, all scalar variables (wind, gusts, precipitation, CAPE, temperature…) are interpolated linearly according to the position on the segment:

value(t) = value_A × (1 − t) + value_B × t    with t ∈ [0, 1]

The directions (wind, waves, currents) use angular interpolation via sin/cos components (weighted circular mean) to avoid discontinuities around 0°/360°:

θ(t) = atan2(sin(θ_A)·(1−t) + sin(θ_B)·t, cos(θ_A)·(1−t) + cos(θ_B)·t) × 180/π mod 360

Score components

Each component is evaluated by a piecewise linear function over breakpoints [value → score 0–10], with linear interpolation between two adjacent points and clamping at the ends.

Mean wind (knots)

ActivityOptimumDegradation threshold0 kt
⛵ Sailing10/10 at 12 kt25 kt → 5 · 32 kt → 02 (dead calm = no progress)
🚢 Motoring10/10 from 0 to 10 kt20 kt → 7 · 30 kt → 3 · 40 kt → 010
🚴 Cycling10/10 from 0 to 5 kt15 kt → 6 · 25 kt → 2 · 35 kt → 010
🥾 Hiking10/10 from 0 to 10 kt20 kt → 5 · 30 kt → 2 · 40 kt → 010

For sailing, the optimum bell reflects that a dead calm prevents any progress under sail (score 2) and that strong wind becomes dangerous (0 at 32 kt). When motoring, wind only affects the hull through the sea it builds — wind alone is penalised gradually.

Gusts (knots)

Gusts follow their own piecewise curve, independent of the mean wind. For sailing, a score of 8 at 0 kt (not 10) reflects that a total calm with no puffs is less pleasant than a few knots of breeze:

⛵ Sailing  : 0 kt → 8 · 10 kt → 10 · 18 kt → 8 · 25 kt → 7 · 32 kt → 5 · 40 kt → 0
🚢 Motoring : 0–15 kt → 10 · 25 kt → 6 · 35 kt → 2 · 45 kt → 0
🚴 Cycling   : 0 kt → 10 · 20 kt → 5 · 40 kt → 0
🥾 Hiking  : 0 kt → 10 · 25 kt → 4 · 45 kt → 0

Sea state — seaStateScore(H, T) (sailing and motoring only)

Sea state can't be reduced to wave height. A 1.5 m sea at 3 seconds is incomparably harder than a 1.5 m swell at 12 seconds. Solano uses vertical acceleration (the real physical discomfort for a sinusoid) as the primary criterion, capped by absolute height:

acc = H × 2π² / T² ≈ H × 19.74 / T²   (in m/s²)

seaStateScore(H, T) = min( piecewise(acc, breaks_acc), piecewise(H, height_cap) )

The two breakpoint tables:

Acceleration acc (m/s²)ScoreHeight H (m)Cap score
0 – 0.1100 – 1.010
0.392.08
0.763.05
1.524.02
2.505.00

Example: H = 2 m, T = 6 s → acc = 2 × 19.74 / 36 = 1.10 m/s² → acc score ≈ 3.5; height cap = 8 → seaStateScore = 3.5. The same height at T = 12 s → acc = 0.27 → acc score ≈ 9.2; height cap = 8 → seaStateScore = 8.

Wind angle to heading (sailing and cycling)

The relative angle α is computed between the wind direction and the heading of the segment travelled, folded to 0–180°. Sailing favours a reach/broad reach (the optimal point of sail); cycling favours a tailwind:

Angle α⛵ Sailing🚴 Cycling
≤ 30–35° (head-on)0 (unusable)1
35–55° (close-hauled tight)43
55–80° (close-hauled good)75
80–130° (beam / broad reach)8 (optimal)7
130–170° (broad reach)69
> 170° (dead run)59

The angle score is only computed if the wind exceeds 4 knots — below that, direction has no practical meaning.

Sailability — sailability(wind, direction, heading)

The angle score above measures the quality of the point of sail, but it is ignored below 4 knots — which is precisely when you reach for the engine. A separate component therefore answers a binary question, hour by hour: can you make way under sail? Two cases prevent it:

α < 35° → head to wind: you must tack, or motor
wind < threshold(α) → too little air for the point of sail: motor

threshold(α) = 5 kt if α ≤ 120° (close-hauled, reach)
threshold(α) = 8 kt if α ≥ 150° (downwind)
threshold(α) = interpolated between the two from 120° to 150°

The threshold depends on the point of sail because apparent wind moves in opposite directions on each tack: close-hauled, boat speed adds to the true wind — apparent wind rises and you make way in little air; downwind, the boat runs away from the wind — apparent wind falls, and you need more of it to make progress. A single 8-knot threshold therefore wrongly penalised close-hauled windows.

The component is worth the share of sailable hours × 10. The detailed ratios (share under engine, of which head to wind and light air) are shown next to the score as "🔧 40 % (head to wind)".

CAPE, precipitation, temperature

CAPE (J/kg) — all activities: 0 → 10 · 300 → 8 · 800 → 5 · 1,500 → 2 · 2,000 → 0
Precipitation (mm/h): 0 → 10 · 0.5 → 9 · 2 → 6 · 5 → 3 · 10 → 0
Cycling temp. (°C): 0 → 3 · 10 → 8 · 15–22 → 10 · 28 → 7 · 33 → 3 · 38 → 0
Hiking temp. (°C): 0 → 3 · 10–20 → 10 · 28 → 6 · 38 → 0

Statistical aggregation along the route

For each component, Solano collects the value hour by hour over the whole trip (all interpolated positions, all active models aggregated). The final component is not the simple average — it gives more weight to the least favourable moments:

component_score = 0.30 × piecewise(mean) + 0.40 × piecewise(p90) + 0.30 × piecewise(max)

For sea state (scores are already computed hour by hour, not raw values):

sea_score = 0.30 × mean + 0.40 × p10 + 0.30 × minimum

The p90 (90th percentile of the raw values) or the p10 (10th percentile of the sea-state scores) captures the "worst quarter" of the route without being as extreme as the single maximum.

Per-activity weighting and final score

Component⛵ Sailing🚢 Motoring🚴 Cycling🥾 Hiking
Mean wind24 %30 %25 %20 %
Gusts18 %25 %15 %15 %
Sea state21 %30 %——
Wind angle to heading9 %—15 %—
Sailability (sailing / motoring)18 %———
CAPE5 %10 %10 %10 %
Precipitation5 %5 %20 %30 %
Temperature——15 %25 %

The final score is a weighted average normalised by the sum of the active weights (components with no data are dynamically excluded):

score = Σ(component_score × weight) / Σ(active_weights)   → rounded to one decimal
The 13 candidates are sorted by decreasing score. The best window is shown in green; the current departure in blue. Clicking a candidate recomputes the full weather table for that time.

11. Thunderstorm risk — beyond CAPE

CAPE (Convective Available Potential Energy, in J/kg) is often presented as the thunderstorm number. That is misleading: a CAPE of 2,000 doesn't have the same reach in every situation. It also depends on the steepness of the atmosphere, the cap that does — or doesn't — keep the air from rising, and the organisation of the cells. So Solano combines several ingredients into a single categorical index, hour by hour.

The ingredients

Following the ingredients-based approach (Doswell), the risk is built as a product: fuel, multiplied by a cap-opening factor, then refined by organisation.

risk = fuel(CAPE, Lifted Index, lapse rate) × cap_factor(CIN)   → 0 to 10

The score gives an intensity in four categories: Low (< 1.5) · Moderate (1.5–4) · Marked (4–7) · Severe (≥ 7). Shear then refines the category: "Severe" is reserved for organised storms — a high intensity but weakly sheared is brought back to "Marked · isolated", while a strongly sheared "Marked" can be promoted to "Severe".

The GFS anchor and model agreement

Among Open-Meteo's models, only GFS exposes every ingredient (CAPE, CIN inhibition, Lifted Index and the pressure levels needed for shear and lapse rate). The index is therefore anchored on GFS, regardless of the models you display — Solano fetches it even if GFS isn't selected. The queried point is brought back to the GFS 0.25° grid: the index therefore describes the environment at the basin scale (≈ 25 km), not precisely above your anchorage.

Added to this index is a measure of agreement between models: the spread of CAPE across all the models consulted. When they diverge strongly (for example CAPE 10 vs 560 at the same point), the risk is marked as uncertain — true to the "movie rather than the photo" philosophy. Every threshold is adjustable.

The index describes a favourable environment, never a thunderstorm at a precise time. Convection remains, by nature, the least predictable parameter: the indices say "the ingredients are in place", not "there will be a storm at 3 pm".

Triggering — what fine-scale models see

The GFS base answers "is the atmosphere charged?". It doesn't answer "will a cell fall here, and when?" — at a 25 km grid, a thunderstorm cell of a few kilometres simply isn't resolved, it's estimated by a formula. That is the role of a second reading, distinct and never folded into the /10 score.

Convection-permitting models (1–3 km grid: AROME HD, ICON-2I, ICON-D2, ICON-CH1/CH2) actually compute the updrafts: they place cells. When one of them is available on your point, Solano reads its hourly precipitation — reinforced by a simultaneous gust of at least 32 knots, the signature of a convective downdraft — and derives three levels: shower, formed cell, vigorous cell. The result is shown on one line below the thunderstorm index ("Cells forecast: AROME · ICON-2I → 4–6 pm") and, in detail, on a row of the hourly table specific to each fine model.

Two counter-intuitive points, both verified against real data. First, the criterion is total precipitation and not the "showers" field: on a model that resolves convection, the shower is already in the total rain and the dedicated field is now just a residue (observed on ICON-D2: 3.1 mm/h of rain for 0.2 of "showers"). Second, this signal cannot be added to the thunderstorm index: a model that triggers a cell consumes the CAPE, so a low CAPE at 5 pm may mean "storm in progress" and not "calm". Averaging the two would invert the reading at the worst moment.

These models have a short horizon (34 h to 5 days) and a regional footprint. The exact placement of a cell at a 2 km grid moves by tens of kilometres from one run to the next: read it as a possible scenario, not an appointment. When the queried point is near the edge of a model's domain, Solano flags it on its card — near the boundary, the solution there is driven by the model that feeds it rather than by its own dynamics.

From forecast to observation

When the risk is notable today, the "See the sky now" button switches the map to satellite imagery — infrared (cold tops), lightning (live strikes) and RDT (detection of developing thunderstorm cells, before electrification). This lets you check at a glance whether a cell is actually breaking out. Unlike ground radar, these satellite products also cover the open sea.

Thunderstorm risk along a route

In Trip Weather, the same index is computed all along the track. Rather than an illegible point-by-point grid, Solano evaluates the risk at each computation point — at the position and the moment the boat will be there. The band reads from left (departure) to right (arrival): each cell carries its category, and the expandable detail lists the at-risk points with their ingredients (CAPE, CIN, Lifted Index, shear, lapse rate). The computation stays anchored on GFS, regardless of the models displayed.

12. Anchorage analysis — the protection algorithm

The anchorage analysis computes, for any point at sea, a directional protection profile via topographic ray casting, complemented by a 0–100 % comfort score over 3 days and bathymetric data.

Ray casting — collecting elevations

For each point, Solano casts 1,728 rays (36 directions × 3 offsets ±5° × 16 distances) from 25 m out to 3,000 m. Elevations come from the SRTM 30 m digital terrain model (global coverage, which returns 0 over the sea = no obstacle).

The ±5° fan per direction lets it "see" a cliff even if the central ray passes over water. Extending to 3 km is essential for large bays whose flanking headlands are more than a kilometre apart (for example Portoferraio, Elba).

Breakwaters and harbour works — OpenStreetMap

A 30 m terrain model is blind to narrow harbour works: a mole 10 to 20 m wide is under-resolved, read at water level. Solano therefore overlays OpenStreetMap geometry around the point (breakwaters and jetties man_made=breakwater, as well as the coastline natural=coastline, which often carries the solid moles of harbours). Each ray tests its geometric intersection with these structures — not a simple point sample, which would miss a thin mole the same way the terrain does.

Merge rule: any coast or breakwater on the ray blocks the swell (the sea doesn't cross land); only moles add a flat-rate wind protection (emergent height ≈ 3 m), with the natural coastline leaving the real terrain relief to decide. The merge is purely additive: it never removes protection detected by the terrain.

Wind protection

Wind protection is estimated by the height / distance ratio of the tallest obstacle found on the ray:

wind_protection = min(1.0, (H/D) / 0.20) → rule: a 20 m obstacle at 100 m = maximum protection

The maximum over the ray's 3 offsets is kept. Venturi detection multiplies the wind penalty ×1.5 when an unblocked sector is flanked by two blocked sectors (a channelling effect).

Swell protection — adaptive threshold

Swell isn't stopped by the same obstacles as wind. Solano applies a terrain height threshold that varies with distance to avoid false positives (a distant flat island flagged as "blocking" the swell):

distance < 200 m → threshold 0.5 m (local chop) distance < 800 m → threshold 2.0 m (coastal obstacle) distance < 2,000 m → threshold 5.0 m (headland or notable relief) distance ≥ 2,000 m → threshold 15.0 m (major relief)

Swell protection is binary (blocked / not blocked) and computed by nearest-neighbour between adjacent sectors — never by OR interpolation, which would produce false "blocked" values between a protected sector and an exposed one.

Comfort score

For each hour over 3 days, the score combines the swell (70 %) and wind (30 %) forecasts with the protection profile:

swell_discomfort = (1 if swell not blocked) × clamp(Hs / 0.30 m, 0, 1) wind_discomfort = (1 − wind_protection) × venturi_factor × clamp(U10 / 25 kt, 0, 1) discomfort = swell_discomfort × 0.70 + wind_discomfort × 0.30 comfort(%) = 100 × (1 − discomfort)

The Hs = 0.30 m threshold reflects that 30 cm of chop is already noticeable at anchor. Gusts are included in the wind calculation.

Bathymetry — EMODnet

Depth comes from EMODnet Bathymetry DTM 2022 (~115 m resolution, REST API POINT(lon lat)), returning the smoothed value plus a min/max range and a plain-language sounding type. Seabed type comes from EMODnet Seabed Habitats — EUSeaMap 2025 (WFS emodnet_view:eusm2025_msfd_full, substrate read from the msfd_bbht field then classified: sand, mud, rock, Posidonia seagrass…).

180-day cache per anchorage (key anchor_v5_, 0.001° resolution ≈ 100 m). Increment the key in production if the algorithm changes.

13. Weather routing — isochrones and polar

Routing answers a different question from the rest of this page: not "what will the weather be here?", but "what is the fastest path between these two points, given the forecast wind and what this boat can do?". It is an optimisation problem in a field that varies in space and in time.

The polar

The polar is a wind angle × wind strength → speed table, specific to the boat (entered in its details, by hand or imported from CSV). The speed at an arbitrary pair is obtained by bilinear interpolation between the four neighbouring values. Outside the table the value is clamped to the last column rather than extrapolated — linear extrapolation would give absurd speeds in strong winds. With no polar entered, a generic 11 m cruiser is used as a fallback.

Isochrones

The engine advances in time steps (60, 30 or 15 minutes, your choice). At each step, from every position on the front, it evaluates every heading on a grid (5° by default): the polar gives the speed, hence the position reached at the next step. Those positions form the isochrone.

Without pruning, the front would be multiplied by the number of headings at every step. Pruning therefore keeps a single position per angular sector as seen from the start — the one furthest towards the goal:

sector(position) = floor(bearing(start → position) / sector_step) kept = argmax(progress towards the goal) within each sector

Neither tacks nor gybes are coded. They emerge from comparing headings: upwind, the polar gives almost no speed on the direct heading and a decent speed at 45°, so the best path runs through two tacks. Downwind, 150° often beats running dead. The engine is a special case: since its speed does not depend on heading, aiming at the goal is always optimal — a direct heading is therefore always added to the candidates, and it dominates the other motoring candidates whenever it is not blocked.

Reading the wind field

The wind is loaded once for the whole area as a cube (lead time × latitude × longitude) of U and V components — a routing reads the wind tens of thousands of times, so one network call per read is out of the question.

Interpolation is done on U and V, never on direction. Between 350° and 10°, averaging the angles gives 180° — the exact opposite of the real wind. The rule applies in space as well as in time.

Outside the downloaded geographic window or lead-time range, the read returns NaN and never the edge value: a routing that leaves the covered area must stop and say so, not carry on with invented data.

The window downloaded is cut to the passage rather than fixed:

horizon_h = (Σ distances × 1.4) / floor_speed × 1.25, clamped to [12 h, ceiling]

The 1.4 factor anticipates the detour (tacks, headlands), the 1.25 is a margin; the floor speed is the engine speed if motoring is allowed, otherwise 3 knots — a lower bound on speed gives an upper bound on duration. Lead times are hourly over the first six hours, then every three hours.

The coastline mask

The coastline is bundled with the application (Natural Earth 10 m, about 406,000 segments, loaded once into memory) — no network call, and therefore no dependency on a third-party service in the middle of a calculation. Each candidate step is a segment; it is refused if that segment crosses a coastline segment. A bounding-box pre-filter discards more than 99.9 % of the pairs before any geometric test.

The test applies to the jump, not to the end point: a step that straddles a narrow spit of land is rejected, whereas a point test would let it through. Rounding a headland then emerges from the same mechanism as tacking, with no dedicated logic.

Thresholds and penalties

Three thresholds forbid a step rather than penalise it: maximum wind, maximum sea (which triggers the download of a wave field — the heaviest part, so never done unasked) and maximum gust. An unknown value never blocks; on the other hand, a requested threshold whose field could not be loaded is flagged on the track, a silently inactive safety limit being worse than none at all.

Two factors modulate progress instead of forbidding it:

night : speed ×= night_factor (night = sun more than 6° below the horizon) manoeuvre : dt_effective = max(dt − loss/60, 0) if |Δheading| > threshold (25° default)

The manoeuvre cuts the progress of the step, not the polar speed: it represents time lost, not a simulation of the deceleration. It is ignored under engine and on the first step.

Cost

The isochrone search itself is fast — of the order of 0.3 to 0.5 s for a 200-mile crossing at hourly steps. The real time of a routing is dominated by downloading the fields (about thirty seconds from cold). Since a published model run never changes, it is kept: re-running immediately with other settings costs almost nothing, but coming back the next day will download again — they are no longer the same runs.

Routing does not account for current, knows nothing of draught or buoyage, and its coastline is deliberately coarse. See the article Weather routing for how to read the results in practice, and for the list of what a computed track does not contain.

14. Tides — model, tide table and storm surge

The tide is the only indicator in Solano that does not come from an atmospheric model, and the only one whose value depends on a vertical datum. Two sources coexist, with different rights: a global ocean model, available everywhere, and a harbour tide table, available wherever a reference station can be attached to the point. The app always states which one is speaking.

The model: differences, never a water depth

The level comes from the sea_level_height_msl variable (Mercator Ocean via Open-Meteo, global coverage, ~8 km grid), referenced to global mean sea level — not to chart datum.

So no absolute height is ever published on this path. What stays correct are the differences, where the unknown datum cancels out: the times of high and low water, and the tidal range. A charted sounding and a value from this model do not add up.

Three shortcuts were tried and dropped, with measurements to back it up. Estimating chart datum from the archive minimum: tested against Canada's official tide tables at Halifax, the estimate diverges instead of converging (−1.55 m over 2 years, −1.64 m over 3.8 years, for a true datum at −1.47 m), because a model minimum is not astronomical — and the error announces too much water, the worst direction for a sailor. Splicing sources (the time from one, the height from the other): the two do not place slack water at the same instant — 49 minutes apart, measured at Halifax — so a tide-table height pasted onto a model time is wrong twice over. Anchoring the datum to the displayed window: a reference computed over a rolling 7 days drifts by 69 cm across the year, against 19 cm over 365 days — the figures would change with the zoom level.

Finding slack water

The series is hourly, but slack water is flat: taking the highest sampled point would shave half an hour off the time of high water. The extremum is therefore refined by a parabola over the three surrounding points:

shift = 0.5 × (y₀ − y₂) / (y₀ − 2y₁ + y₂) t_slack = t₁ + shift × step, h_slack = y₁ − 0.25 × (y₀ − y₂) × shift

Measured result: −10 minutes on average on high/low water times, and 3 to 6 % error on tidal range along an open coast.

A flat stand is still a stand. Levels arrive rounded to the centimetre: with a small range, the crest flattens across two samples (0.21 · 0.22 · 0.22 · 0.21, observed in production) and the slope becomes zero on both sides. Comparing only the immediate neighbours made half the Mediterranean tides vanish — two low waters following one another with no high water in between. So we look for the first strictly different neighbour on each side of the plateau and place the stand at its midpoint; the parabola stays reserved for sharp crests, since its denominator is zero on a plateau.

A corollary: a tidal range runs from a high water to a low water. Two extrema of the same kind delimit none — better to show no range at all than to manufacture one that would read as missing data.

The model's tidal range is systematically underestimated, the more so the more enclosed the site: −6 % at Halifax, −9 % at Saint John, −20 % at the head of the Bay of Fundy. An 8 km grid cell does not make a funnel resonate. This is flagged on screen, never silently corrected by a factor.

Attaching a tide-table port

When a reference station can be attached to the point, the answer switches to it: times and heights become those of the tide table, referred to chart datum — hence addable to a sounding. Two conditions, both required:

distance(point, station) ≤ 20 km ratio = model_range(point) / model_range(station) ∈ [0.85 ; 1.15] over 14 days
The ratio is measured model against model, never model against tide table. Since the model underestimates range the more enclosed the site is, a model/table ratio would measure that bias and reject precisely the estuaries. Between two points of the same model, the bias cancels out.

The decision is taken in the background (three round trips, two of them over 14 days of archive): the first visit to a point is served by the model, the next by the tide table. It is cached for 90 days, under a key that depends on which tide-table providers are configured — so adding a country makes already-visited points decide again. Two safeguards: a validation that could not be carried out (network failure) is never cached as a refusal; and at the head of a bay, where the model is blind (a drying area falls on a land cell), attachment falls back on proximity alone, capped at 8 km, and says so on screen. A station whose tide table publishes no curve is discarded: the seven such stations in Canada are all at the head of Fundy, where the tide carries a bore — interpolating a sine wave there would give a false shape.

The tide tables covered. Canada — Canadian Hydrographic Service, 1,080 prediction stations; mainland France — api-maree.fr, 133 sites, the only source giving the French tidal coefficient; Ireland — Marine Institute, 38 stations; United Kingdom — UKHO ADMIRALTY, 608 ports. Everywhere else the model stands alone, with its caveats. Two points specific to Ireland: the dataset publishes two height columns, one on chart datum and one on a land levelling datum — Solano reads the first, and says so here because mixing them up would announce several metres of water too many; and twelve of the thirty-eight stations are modelled rather than adjusted against a local tide gauge, which the display flags without dropping them (they are sometimes the only station on a whole stretch of coast).

One point specific to the United Kingdom: the curve there is reconstructed. The UKHO's free subscription serves the tidal events — times and heights of high and low water, on chart datum — but not the continuous heights in between, which are reserved for the paid tiers. Solano therefore draws the curve as a half-cosine between two consecutive tide-table stands: the rule of twelfths, the one every navigation course teaches. Everything stays on chart datum and comes from the UKHO; only the shape between two stands is computed. The error was measured against the real five-minute curves of the Irish tide tables: 3 to 16 cm RMS, 37 cm at worst on a 3.8 m range, at most 10% of the range. The display says so. Two further limits of the same subscription: the window stops at today plus six days where the model reaches ten, and it holds no past. Finally, the 75 ports where the service itself declares there is no continuous curve — creeks and tidal rivers, “dries out except for river water” — are dropped and fall back on the model, just like the tidal bores of the Bay of Fundy.

Storm surge

A tide table is purely astronomical: it knows only the Moon and the Sun. The real sea departs from it because weather moves water — that is the surge (negative, and then called a negative surge). The inverse barometer (roughly 1 cm per hPa, in the opposite direction) is only part of it: measured on 21 September 2026 in the western Mediterranean, −6 cm for the barometer alone where the surge model gave −14 cm, the 8 cm gap being the wind effect a pressure term cannot see.

The source is HYCOM2D (Météo-France, GRIB2), in its ARPEGE-forced version: 0.04° grid (~4 km, twice as fine as the tide model), 102 h horizon, runs every 6 hours. Since the field covers a basin rather than a point, it is downloaded by a background job and read from cache only — never over the network on a request path.

Where to add it, and where that is forbidden. On the tide-table path the addition is exact: the table contains no surge, so it replaces the former pressure correction term for term. On the model path it is published but never added to the level: sea_level_height_msl already contains the inverse barometer, the steric term and whatever surge the model resolves — adding it would count the pressure twice, and part of the wind too, by an amount we cannot measure.

Beyond the field's horizon (102 h, while the curve runs out to ten days), the correction falls back on the barometer: a continuous curve is better than a step at hour 103. Each curve point therefore states where its correction comes from, and the response counts how many points the surge actually served — claiming "surge included" for the whole curve would be a lie about two thirds of it.

Limitations specific to tides

15. Activity verdict — prudent consensus

The verdict "can I practise here?" answers hour by hour, for the chosen activity (sailing, kitesurfing, hiking, cycling…), over the local daylight hours (7am–6pm). It appears in the wind and sea band, above the day strip.

1. Each selected model votes

Each atmospheric model you have selected judges the hour on its own wind, gust and rain values, with the activity's thresholds. It casts a vote: yes, marginal or no. Selected wave models vote the same way on sea height.

2. The consensus, cautious by design

The votes are combined with a rule that leans towards caution:

yes   : at least 2/3 of the models say yes and none says no
no    : more than half of the models say no
marginal: all other cases, including a clear disagreement (one yes and one no)

A single selected model gives its own verdict. Sea counts like wind: the hour takes the worst of the two verdicts.

3. Displayed values: the median

Wind, gusts, sea and rain are shown as the median of the models, not their mean. With three models or more, one outlying model does not move the median; it would move a mean.

4. Why disagreement gives "marginal"

If one model forecasts 15 knots of gusts and another 40, their mean (27) can look acceptable for a 33-knot limit. Solano does not average them: disagreement is the signal to be careful, so it is reported as "marginal".

The verdict is indicative. For a safety decision at sea, check the official bulletins and zone warnings.

16. Limitations of the method

The Solano method has several important limitations to be aware of when interpreting the scores:

Disclaimer: Solano is not an accredited weather service. The information displayed comes from third-party compute centres and is provided for guidance only. It does not replace official bulletins (Météo-France, SHOM, CROSS). The user assumes full responsibility for decisions made on the basis of this data.
Test the method on the map →