diff --git a/lib/microwaveprop/weather/era5_batch_client.ex b/lib/microwaveprop/weather/era5_batch_client.ex index eb713d3a..a979b37f 100644 --- a/lib/microwaveprop/weather/era5_batch_client.ex +++ b/lib/microwaveprop/weather/era5_batch_client.ex @@ -24,6 +24,7 @@ defmodule Microwaveprop.Weather.Era5BatchClient do alias Microwaveprop.Weather.Era5Profile alias Microwaveprop.Weather.Grib2.Wgrib2 alias Microwaveprop.Weather.SoundingParams + alias Microwaveprop.Weather.ThetaE require Logger @@ -173,7 +174,7 @@ defmodule Microwaveprop.Weather.Era5BatchClient do with {:ok, single_msgs} <- Wgrib2.extract_grid_messages(single_grib, ":(TMP|DPT|PRES|UGRD|VGRD|PWAT|HPBL):", grid_spec), - {:ok, pressure_msgs} <- Wgrib2.extract_grid_messages(pressure_grib, ":(TMP|DPT|HGT):", grid_spec) do + {:ok, pressure_msgs} <- Wgrib2.extract_grid_messages(pressure_grib, ":(TMP|SPFH|HGT):", grid_spec) do # Index both message sets by (datetime, lat, lon) → var:level map. points_single = index_messages(single_msgs) points_pressure = index_messages(pressure_msgs) @@ -230,7 +231,11 @@ defmodule Microwaveprop.Weather.Era5BatchClient do %{ "pres" => level * 1.0, "tmpc" => kelvin_to_celsius(pressure_fields["TMP:#{pres_key}"]), - "dwpc" => kelvin_to_celsius(pressure_fields["DPT:#{pres_key}"]), + "dwpc" => + dewpoint_from_spfh( + pressure_fields["SPFH:#{pres_key}"], + level * 100.0 + ), "hght" => geopotential_to_height(pressure_fields["HGT:#{pres_key}"]) } end) @@ -275,6 +280,13 @@ defmodule Microwaveprop.Weather.Era5BatchClient do defp geopotential_to_height(nil), do: nil defp geopotential_to_height(gp), do: gp / 9.80665 + # ERA5 pressure-level moisture is specific humidity (kg/kg). Convert to + # dewpoint in °C via the Magnus-Tetens inverse already used for HRRR native + # profiles. `level_pa` is the pressure level itself in Pa. + defp dewpoint_from_spfh(nil, _level_pa), do: nil + defp dewpoint_from_spfh(spfh, _level_pa) when spfh <= 0, do: nil + defp dewpoint_from_spfh(spfh, level_pa), do: ThetaE.dewpoint_from_spfh(spfh, level_pa) + # -- Bulk insert ----------------------------------------------------------- # Insert in chunks to keep each query under Postgres' parameter limit diff --git a/lib/microwaveprop/weather/era5_client.ex b/lib/microwaveprop/weather/era5_client.ex index deda4440..20bc5886 100644 --- a/lib/microwaveprop/weather/era5_client.ex +++ b/lib/microwaveprop/weather/era5_client.ex @@ -33,7 +33,10 @@ defmodule Microwaveprop.Weather.Era5Client do boundary_layer_height ) - @pressure_level_vars ~w(temperature dewpoint_temperature geopotential) + # `dewpoint_temperature` is only a single-level 2m variable in ERA5 — CDS + # rejects pressure-level requests that include it. Use specific_humidity on + # pressure levels and derive Td in the profile builder via ThetaE. + @pressure_level_vars ~w(temperature specific_humidity geopotential) @doc false @spec pressure_levels() :: [String.t()]