Repository: Ouranosinc/xclim

Issue:
Consistent namings for conversion functions
### Addressing a Problem?

This is a pet peeve of mine, with a very simple (albeit API-breaking) fix.

We currently offer several conversion tools that use the following naming conventions:
 - `x2x`:
   - `amount2rate`
   - `flux2rate`
   - `lwethickness2amount`
   - `pint2cfattrs`
   - `pint2cfunits`
   - `rate2amount`
   - `rate2flux`
   - `str2pint`
   - `units2pint`
 - `x_to_x`:
   - `days_since_to_doy`
   - `doy_to_says_since`
   - `prsn_to_prsnd`
   - `prsnd_to_prsn`
   - `snw_to_snd`
   - `snd_to_snw`
 - `x_2_x`:
   - `sfcwind_2_uas_vas`
   - `uas_vas_2_sfcwind`

### Potential Solution

We agree on the following conventions:
- `xclim.core` conversions that are typically used internally use the `[a-z]+2[a-z]` pattern.
- `xclim.indices` | `xclim.indicators` conversions use the `[a-z]+\_to\_[a-z]` pattern.

### Additional context

We can deprecate the `sfcwind_2_uas_vas` and `uas_vas_2_sfcwind` conversions now and replace them in the next two or three releases.

### Contribution

- [X] I would be willing/able to open a Pull Request to contribute this feature.

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 354ebb0a..0aebcd90 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -20,6 +20,9 @@ Breaking changes
 * `xclim` no longer supports Python 3.10. The minimum required version is now Python 3.11. (:pull:`2082`).
     * Reverted: Extended support for Python3.10 will continue until further notice. (:pull:`2100`).
 * The minimum versions of several key dependencies have been raised (`numpy` >=1.24.0; `scikit-learn` >=1.2.0; `scipy` >=1.11.0). (:pull:`2082`).
+* To ensure consistent naming of converters, the following indices have been deprecated with replacements. Changes will be made permanent in `xclim` v0.57.0 (:issue:`2039`, :pull:`2117`):
+    * ``sfcwind_2_uas_vas``: Use ``sfcwind_to_uas_vas`` instead.
+    * ``uas_vas_2_sfcwind``: Use ``uas_vas_to_sfcwind`` instead.
 
 Internal changes
 ^^^^^^^^^^^^^^^^
diff --git a/src/xclim/indicators/atmos/_conversion.py b/src/xclim/indicators/atmos/_conversion.py
index 5888344e..8c435d01 100644
--- a/src/xclim/indicators/atmos/_conversion.py
+++ b/src/xclim/indicators/atmos/_conversion.py
@@ -113,7 +113,7 @@ wind_speed_from_vector = Converter(
     cell_methods="",
     abstract="Calculation of the magnitude and direction of the wind speed "
     "from the two components west-east and south-north.",
-    compute=indices.uas_vas_2_sfcwind,
+    compute=indices.uas_vas_to_sfcwind,
 )
 
 
@@ -131,7 +131,7 @@ wind_vector_from_speed = Converter(
     cell_methods="",
     abstract="Calculation of the two components (west-east and north-south) of the wind "
     "from the magnitude of its speed and direction of origin.",
-    compute=indices.sfcwind_2_uas_vas,
+    compute=indices.sfcwind_to_uas_vas,
 )
 
 wind_power_potential = Converter(
diff --git a/src/xclim/indices/_conversion.py b/src/xclim/indices/_conversion.py
index 8f8c0fc7..078ef6de 100644
--- a/src/xclim/indices/_conversion.py
+++ b/src/xclim/indices/_conversion.py
@@ -17,6 +17,7 @@ from xclim.core.units import (
     rate2flux,
     units2pint,
 )
+from xclim.core.utils import deprecated
 from xclim.indices.helpers import (
     _gather_lat,
     _gather_lon,
@@ -43,6 +44,7 @@ __all__ = [
     "relative_humidity",
     "saturation_vapor_pressure",
     "sfcwind_2_uas_vas",
+    "sfcwind_to_uas_vas",
     "shortwave_upwelling_radiation_from_net_downwelling",
     "snd_to_snw",
     "snowfall_approximation",
@@ -51,6 +53,7 @@ __all__ = [
     "specific_humidity_from_dewpoint",
     "tas",
     "uas_vas_2_sfcwind",
+    "uas_vas_to_sfcwind",
     "universal_thermal_climate_index",
     "vapor_pressure_deficit",
     "wind_chill_index",
@@ -239,8 +242,23 @@ def tas(tasmin: xr.DataArray, tasmax: xr.DataArray) -> xr.DataArray:
     return tas
 
 
+@deprecated(from_version="0.56.0", suggested="uas_vas_to_sfcwind")
+def uas_vas_2_sfcwind(*args, **kwargs):  # numpydoc ignore=PR01,RT01
+    """
+    Wind speed and direction from the eastward and northward wind components.
+
+    Computes the magnitude and angle of the wind vector from its northward and eastward components,
+    following the meteorological convention that sets calm wind to a direction of 0° and northerly wind to 360°.
+
+    Warnings
+    --------
+    This function has been deprecated in favour of `uas_vas_to_sfcwind`.
+    """
+    return uas_vas_to_sfcwind(*args, **kwargs)
+
+
 @declare_units(uas="[speed]", vas="[speed]", calm_wind_thresh="[speed]")
-def uas_vas_2_sfcwind(
+def uas_vas_to_sfcwind(
     uas: xr.DataArray, vas: xr.DataArray, calm_wind_thresh: Quantified = "0.5 m/s"
 ) -> tuple[xr.DataArray, xr.DataArray]:
     """
@@ -274,8 +292,8 @@ def uas_vas_2_sfcwind(
 
     Examples
     --------
-    >>> from xclim.indices import uas_vas_2_sfcwind
-    >>> sfcWind = uas_vas_2_sfcwind(uas=uas_dataset, vas=vas_dataset, calm_wind_thresh="0.5 m/s")
+    >>> from xclim.indices import uas_vas_to_sfcwind
+    >>> sfcWind = uas_vas_to_sfcwind(uas=uas_dataset, vas=vas_dataset, calm_wind_thresh="0.5 m/s")
     """
     # Converts the wind speed to m s-1
     uas = convert_units_to(uas, "m/s")
@@ -301,8 +319,22 @@ def uas_vas_2_sfcwind(
     return wind, wind_from_dir
 
 
+@deprecated(from_version="0.56.0", suggested="sfcwind_to_uas_vas")
+def sfcwind_2_uas_vas(*args, **kwargs):  # numpydoc ignore=PR01,RT01
+    """
+    Eastward and northward wind components from the wind speed and direction.
+
+    Compute the eastward and northward wind components from the wind speed and direction.
+
+    Warnings
+    --------
+    This function has been deprecated in favour of `sfcwind_to_uas_vas`.
+    """
+    return sfcwind_to_uas_vas(*args, **kwargs)
+
+
 @declare_units(sfcWind="[speed]", sfcWindfromdir="[]")
-def sfcwind_2_uas_vas(
+def sfcwind_to_uas_vas(
     sfcWind: xr.DataArray,
     sfcWindfromdir: xr.DataArray,  # noqa
 ) -> tuple[xr.DataArray, xr.DataArray]:
@@ -327,8 +359,8 @@ def sfcwind_2_uas_vas(
 
     Examples
     --------
-    >>> from xclim.indices import sfcwind_2_uas_vas
-    >>> uas, vas = sfcwind_2_uas_vas(sfcWind=sfcWind_dataset, sfcWindfromdir=sfcWindfromdir_dataset)
+    >>> from xclim.indices import sfcwind_to_uas_vas
+    >>> uas, vas = sfcwind_to_uas_vas(sfcWind=sfcWind_dataset, sfcWindfromdir=sfcWindfromdir_dataset)
     """
     # Converts the wind speed to m s-1
     sfcWind = convert_units_to(sfcWind, "m/s")  # noqa


Test patch:
diff --git a/tests/test_indices.py b/tests/test_indices.py
index 05476f42..bf3c62e5 100644
--- a/tests/test_indices.py
+++ b/tests/test_indices.py
@@ -3033,13 +3033,15 @@ class TestWindConversion:
     da_windfromdir.attrs["units"] = "degree"
 
     def test_uas_vas_2_sfcwind(self):
-        wind, windfromdir = xci.uas_vas_2_sfcwind(self.da_uas, self.da_vas)
+        with pytest.deprecated_call():
+            wind, windfromdir = xci.uas_vas_2_sfcwind(self.da_uas, self.da_vas)
 
         assert np.all(np.around(wind.values, decimals=10) == np.around(self.da_wind.values / 3.6, decimals=10))
         assert np.all(np.around(windfromdir.values, decimals=10) == np.around(self.da_windfromdir.values, decimals=10))
 
     def test_sfcwind_2_uas_vas(self):
-        uas, vas = xci.sfcwind_2_uas_vas(self.da_wind, self.da_windfromdir)
+        with pytest.deprecated_call():
+            uas, vas = xci.sfcwind_2_uas_vas(self.da_wind, self.da_windfromdir)
 
         assert np.all(np.around(uas.values, decimals=10) == np.array([[1, -1], [0, 0]]))
         assert np.all(