Sampling Points =============== Use :func:`arlmet.sample_points` to interpolate meteorological variables at arbitrary ``(lon, lat, z, time)`` points. This is the direct path when you have trajectory points, station locations, or receptors and want field values at each one without building a full Dataset. Basic usage ----------- Provide the points as a DataFrame (or dict) with ``lon``, ``lat``, ``z``, and ``time`` columns, plus the variables you want sampled. The first argument, ``files``, can be a path to an ARL file — :func:`arlmet.sample_points` opens and closes it for you. .. code-block:: python import pandas as pd import arlmet points = pd.DataFrame( { "lon": [-111.9, -112.0], "lat": [40.7, 40.8], "z": [850.0, 700.0], "time": ["2024-07-18 00:00", "2024-07-18 00:00"], } ) result = arlmet.sample_points("met.arl", points, ["UWND", "VWND", "TEMP"]) The returned DataFrame is a copy of ``points`` with one column added per requested variable. Every column of ``points`` (IDs, observations, anything else) and its index are preserved, so you can compare samples with observations row by row. A requested variable whose name matches an existing column raises ``ValueError`` rather than overwriting it. If you already have an open file, pass it instead of a path. The :class:`arlmet.File` object also exposes the same operation as a method: .. code-block:: python with arlmet.File("met.arl") as met: result = met.sample_points(points, ["UWND", "VWND", "TEMP"]) A file you open yourself is left open for you to manage; only paths that ``sample_points`` opens internally are closed for you. Choosing the vertical coordinate --------------------------------- The ``z`` column is interpreted according to ``z_kind`` (default ``"pressure"``): .. list-table:: :header-rows: 1 * - ``z_kind`` - Meaning of ``z`` - Requirements * - ``"native"`` - fractional level index - none * - ``"pressure"`` - hPa - ``PRSS`` for sigma/hybrid files * - ``"agl"`` - metres above ground level - flag=2: ``HGTS`` + ``SHGT``; flag=1/4: ``PRSS`` + ``TEMP``; flag=3: none * - ``"msl"`` - metres above mean sea level - flag=2: ``HGTS``; flag=1/4: ``PRSS`` + ``TEMP`` + ``SHGT``; flag=3: ``SHGT`` .. code-block:: python with arlmet.File("met.arl") as met: result = arlmet.sample_points( met, points, ["TEMP"], z_kind="agl" ) The virtual ``"pressure"`` variable can also be requested as a sampled column: .. code-block:: python result = arlmet.sample_points(met, points, ["pressure", "TEMP"]) Horizontal interpolation ------------------------ ``method`` controls horizontal interpolation between grid cells: - ``"linear"`` (default) — bilinear interpolation - ``"nearest"`` — nearest grid cell .. code-block:: python result = arlmet.sample_points(met, points, ["TEMP"], method="nearest") Earth-relative winds -------------------- Winds in ARL files on projected grids (Lambert conformal, polar stereographic, Mercator) are stored relative to the grid axes, which is what HYSPLIT expects. Sampled ``UWND``/``VWND`` and ``U10M``/``V10M`` are therefore not directly comparable with observed winds. Pass ``earth_relative=True`` to rotate each pair so that ``u`` points east and ``v`` north, using the meridian convergence of the file's grid at each point. Both components of a pair must be requested. Lat/lon grids are unaffected. .. code-block:: python result = arlmet.sample_points( "hrrr.arl", points, ["UWND", "VWND"], earth_relative=True ) The same rotation is available on the grid for winds you obtained another way, for example from :func:`arlmet.open_dataset`: .. code-block:: python u_earth, v_earth = met.grid.rotate_winds(u, v, lon, lat) Point times ----------- Each point's valid time comes from exactly one place: - the ``time`` column of ``points``, if it has one; - otherwise the ``time=`` argument, applied to every point; - otherwise, when the input file(s) hold a single valid time, that time. Passing both a ``time`` column and ``time=`` is ambiguous and raises ``ValueError``, as does giving neither for inputs that hold more than one time. A point time that no input file contains raises ``ValueError`` naming the missing times. .. code-block:: python points = pd.DataFrame({"lon": [-111.9], "lat": [40.7], "z": [850.0]}) result = arlmet.sample_points("single_time.arl", points, ["TEMP"]) result = arlmet.sample_points( "multi_time.arl", points, ["TEMP"], time="2024-07-18 06:00" ) Sampling across multiple files ------------------------------ Pass a sequence of paths (or open files) when your points span time periods stored in different files. Each timestamp must appear in at most one file; arl-met routes each point to the file that contains its time and closes any paths it opened. .. code-block:: python paths = ["met_00.arl", "met_06.arl", "met_12.arl"] result = arlmet.sample_points(paths, points, ["UWND", "VWND"]) You can mix open files and paths in the same sequence; files you opened stay open, and paths are closed for you. Limitations ----------- - Each timestamp must be present in at most one input file; overlapping times raise ``ValueError``. - Point times not covered by any input file raise ``ValueError``. - ``z_kind="agl"`` and ``"msl"`` use different field requirements depending on the vertical flag: pressure files (flag=2) require ``HGTS``; sigma/hybrid files (flag=1/4) use hypsometric integration from ``PRSS`` and ``TEMP``; terrain-following files (flag=3) use the stored level heights directly. See the :doc:`vertical` guide.