diff --git a/docs/releases.md b/docs/releases.md
index 587315aa..1d5430af 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -10,10 +10,15 @@

 ### Internal Changes

+* Parallelize computation of radiation correction, leading to a hugely improved memory footprint for surface forcing generation ([#227](https://github.com/CWorthy-ocean/roms-tools/pull/227))
+* For computation of radiation correction, swap order of temporal and spatial interpolation to further improve memory footprint ([#227](https://github.com/CWorthy-ocean/roms-tools/pull/227))
+
 ### Documentation

 ### Bugfixes

+* Fix bug in validation step for surface forcing ([#227](https://github.com/CWorthy-ocean/roms-tools/pull/227))
+
 ## v2.3.0

 ### New Features
diff --git a/roms_tools/setup/datasets.py b/roms_tools/setup/datasets.py
index 634e2734..7b0ce441 100644
--- a/roms_tools/setup/datasets.py
+++ b/roms_tools/setup/datasets.py
@@ -2058,6 +2058,7 @@ def _select_relevant_times(
                 )
             if not end_time:
                 # Interpolate from climatology for initial conditions
+                ds["time"] = ds["time"].dt.days
                 ds = interpolate_from_climatology(ds, time_dim, start_time)
         else:
             time_type = get_time_type(ds[time_dim])
diff --git a/roms_tools/setup/surface_forcing.py b/roms_tools/setup/surface_forcing.py
index cff894af..3ce9f9dd 100644
--- a/roms_tools/setup/surface_forcing.py
+++ b/roms_tools/setup/surface_forcing.py
@@ -238,14 +238,14 @@ def _set_variable_info(self, data):
                 "qair": {**default_info, "validate": True},
                 "rain": {**default_info, "validate": False},
                 "uwnd": {
-                    "location": "u",
+                    "location": "rho",
                     "is_vector": True,
                     "vector_pair": "vwnd",
                     "is_3d": False,
                     "validate": True,
                 },
                 "vwnd": {
-                    "location": "v",
+                    "location": "rho",
                     "is_vector": True,
                     "vector_pair": "uwnd",
                     "is_3d": False,
@@ -266,7 +266,7 @@ def _set_variable_info(self, data):
     def _apply_correction(self, processed_fields, data):

         correction_data = self._get_correction_data()
-        # choose same subdomain as forcing data so that we can use same mask
+        # Match subdomain to forcing data to reuse the mask
         coords_correction = {
             "lat": data.ds[data.dim_names["latitude"]],
             "lon": data.ds[data.dim_names["longitude"]],
@@ -275,23 +275,39 @@ def _apply_correction(self, processed_fields, data):
             coords_correction, straddle=self.target_coords["straddle"]
         )
         correction_data.ds["mask"] = data.ds["mask"]  # use mask from ERA5 data
+        correction_data.ds["time"] = correction_data.ds["time"].dt.days
+
         correction_data.apply_lateral_fill()
-        # regrid
-        lateral_regrid = LateralRegrid(self.target_coords, correction_data.dim_names)
-        corr_factor = lateral_regrid.apply(
-            correction_data.ds[correction_data.var_names["swr_corr"]]
-        )

-        # temporal interpolation
-        corr_factor = interpolate_from_climatology(
-            corr_factor,
-            correction_data.dim_names["time"],
-            time=processed_fields["swrad"].time,
-        )
+        # Temporal interpolation: Perform before spatial regridding for better performance
+        if self.use_dask:
+            # Perform temporal interpolation for each time slice to enforce chunking in time.
+            # This reduces memory usage by processing one time step at a time.
+            # The interpolated slices are then concatenated along the "time" dimension.
+            corr_factor = xr.concat(
+                [
+                    interpolate_from_climatology(
+                        correction_data.ds[correction_data.var_names["swr_corr"]],
+                        correction_data.dim_names["time"],
+                        time=time,
+                    )
+                    for time in processed_fields["swrad"].time
+                ],
+                dim="time",
+            )
+        else:
+            # Interpolate across all time steps at once
+            corr_factor = interpolate_from_climatology(
+                correction_data.ds[correction_data.var_names["swr_corr"]],
+                correction_data.dim_names["time"],
+                time=processed_fields["swrad"].time,
+            )

-        processed_fields["swrad"] = processed_fields["swrad"] * corr_factor
+        # Spatial regridding
+        lateral_regrid = LateralRegrid(self.target_coords, correction_data.dim_names)
+        corr_factor = lateral_regrid.apply(corr_factor)

-        del corr_factor
+        processed_fields["swrad"] = processed_fields["swrad"] * corr_factor

         return processed_fields

@@ -357,12 +373,8 @@ def _validate(self, ds):

         for var_name in ds.data_vars:
             if self.variable_info[var_name]["validate"]:
-                if self.variable_info[var_name]["location"] == "rho":
-                    mask = self.target_coords["mask"]
-                elif self.variable_info[var_name]["location"] == "u":
-                    mask = self.target_coords["mask_u"]
-                elif self.variable_info[var_name]["location"] == "v":
-                    mask = self.target_coords["mask_v"]
+                # all variables are at rho-points
+                mask = self.target_coords["mask"]
                 nan_check(ds[var_name].isel(time=0), mask)

     def _add_global_metadata(self, ds=None):
diff --git a/roms_tools/setup/utils.py b/roms_tools/setup/utils.py
index 6823f543..10e37afa 100644
--- a/roms_tools/setup/utils.py
+++ b/roms_tools/setup/utils.py
@@ -130,23 +130,36 @@ def interpolate_from_climatology(
     time_dim_name: str,
     time: Union[xr.DataArray, pd.DatetimeIndex],
 ) -> Union[xr.DataArray, xr.Dataset]:
-    """Interpolates the given field temporally based on the specified time points.
+    """Temporally interpolates a field based on specified time points.

-    If `field` is an xarray.Dataset, this function applies the interpolation to all data variables in the dataset.
+    This function performs temporal interpolation on the input `field` to match the provided `time` values.
+    If the input `field` is an `xarray.Dataset`, the interpolation is applied to all its data variables individually.

     Parameters
     ----------
     field : xarray.DataArray or xarray.Dataset
-        The field data to be interpolated. Can be a single DataArray or a Dataset.
+        The input field to be interpolated.
+        - If `field` is an `xarray.DataArray`, it should have a time dimension identified by `time_dim_name`.
+        - If `field` is an `xarray.Dataset`, all variables within the dataset are interpolated along the specified time dimension.
+        The time dimension is assumed to represent `day_of_year` for climatological purposes.
     time_dim_name : str
-        The name of the dimension in `field` that represents time.
+        The name of the time dimension in the `field`. This dimension is used for interpolation.
     time : xarray.DataArray or pandas.DatetimeIndex
-        The target time points for interpolation.
+        The target time points for interpolation. The time values should be compatible with the time format used in the `field`.

     Returns
     -------
     xarray.DataArray or xarray.Dataset
-        The field values interpolated to the specified time points. The type matches the input type.
+        The interpolated field, with the same type as the input (`xarray.DataArray` or `xarray.Dataset`),
+        but aligned to the specified `time` values.
+
+    Notes
+    -----
+    - The interpolation assumes the time dimension in `field` corresponds to `day_of_year`.
+      If the input time values are in a datetime format, ensure they are converted to `day_of_year` before calling this function.
+      For example, you can preprocess the time as follows:
+
+      >>> field["time"] = field["time"].dt.dayofyear
     """

     def interpolate_single_field(data_array: xr.DataArray) -> xr.DataArray:
@@ -156,11 +169,11 @@ def interpolate_single_field(data_array: xr.DataArray) -> xr.DataArray:
             day_of_year = time.dt.dayofyear
         else:
             if np.size(time) == 1:
-                day_of_year = time.timetuple().tm_yday
+                # Convert single datetime64 object to pandas.Timestamp
+                day_of_year = pd.Timestamp(time).dayofyear
             else:
-                day_of_year = np.array([t.timetuple().tm_yday for t in time])
-
-        data_array[time_dim_name] = data_array[time_dim_name].dt.days
+                # Convert each datetime64 object in the array to pandas.Timestamp
+                day_of_year = np.array([pd.Timestamp(t).dayofyear for t in time])

         # Concatenate across the beginning and end of the year
         time_concat = xr.concat(
@@ -200,6 +213,7 @@ def interpolate_single_field(data_array: xr.DataArray) -> xr.DataArray:
             for var, data_array in field.data_vars.items()
         }
         return xr.Dataset(interpolated_data_vars, attrs=field.attrs)
+
     else:
         raise TypeError("Input 'field' must be an xarray.DataArray or xarray.Dataset.")

@@ -633,8 +647,6 @@ def get_target_coords(grid, use_coarse_grid=False):
         mask = grid.ds.get("mask_coarse")
         if mask is not None:
             mask = mask.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"})
-            mask_u = interpolate_from_rho_to_u(mask, method="multiplicative")
-            mask_v = interpolate_from_rho_to_v(mask, method="multiplicative")

         lat_psi = grid.ds.get("lat_psi_coarse")
         lon_psi = grid.ds.get("lon_psi_coarse")
@@ -644,8 +656,6 @@ def get_target_coords(grid, use_coarse_grid=False):
         lon = grid.ds.lon_rho
         angle = grid.ds.angle
         mask = grid.ds.get("mask_rho")
-        mask_u = grid.ds.get("mask_u")
-        mask_v = grid.ds.get("mask_v")
         lat_psi = grid.ds.get("lat_psi")
         lon_psi = grid.ds.get("lon_psi")

@@ -668,8 +678,6 @@ def get_target_coords(grid, use_coarse_grid=False):
         "lon_psi": lon_psi,
         "angle": angle,
         "mask": mask,
-        "mask_u": mask_u,
-        "mask_v": mask_v,
         "straddle": straddle,
     }

diff --git a/roms_tools/tests/test_setup/test_utils.py b/roms_tools/tests/test_setup/test_utils.py
index de4faad6..1012b839 100644
--- a/roms_tools/tests/test_setup/test_utils.py
+++ b/roms_tools/tests/test_setup/test_utils.py
@@ -15,6 +15,7 @@ def test_interpolate_from_climatology(use_dask):

     climatology = ERA5Correction(use_dask=use_dask)
     field = climatology.ds["ssr_corr"]
+    field["time"] = field["time"].dt.days

     interpolated_field = interpolate_from_climatology(field, "time", era5_times)
     assert len(interpolated_field.time) == len(era5_times)

Radiation correction is not done in parallel (even when using Dask)
## Issue

If the user chooses to apply a radiation correction for the surface forcing while using Dask via
```
SurfaceForcing(
    grid=grid,
    ...,
    correct_radiation=True,
    use_dask=True
)
```
this radiation correction is not done in parallel. The reason for this is the internal use of the function
https://github.com/CWorthy-ocean/roms-tools/blob/67df5fc400b1daeb903ba9b2efe11899d8b55dfa/roms_tools/setup/utils.py#L128-L150

which interpolates a pre-computed climatology for ERA5 shortwave downward radiation (i.e., 12 time stamps) to the hourly ERA5 data of interest. Specifically, this line is the issue:

https://github.com/CWorthy-ocean/roms-tools/blob/67df5fc400b1daeb903ba9b2efe11899d8b55dfa/roms_tools/setup/utils.py#L184-L187

Both `data_array_concat` and `day_of_year` are chunked in time with chunk size 1, where `data_array_concat.time` has a length of 12 + 2, and `day_of_year.time` a length of however many ERA5 times there are: for a month of ERA5 data, that is 744 hourly time slices, i.e., 744 time chunks. However, the `xarray.interp` function cannot deal with chunked data along the core dimension and will return interpolated data with a single time chunk (containing 744 time slices). This means that this operation is not done in parallel.

Note that substituting this by something like
```
        data_array_interpolated = xr.apply_ufunc(
            np.interp,
            day_of_year,
            time_concat,
            data_array_concat,
            input_core_dims=[[time_dim_name], [time_dim_name], [time_dim_name]],
            output_core_dims=[[time_dim_name]],
            vectorize=True,
            dask="allowed",
            exclude_dims=set((time_dim_name,)),
        )
```
would not help because we are trying to chunk along a core dimension, which `xr.apply_ufunc` is not equipped for (I think).

## Implications for validation step and memory footprint

In the validation step during the surface forcing initialization, a single time slice of the corrected shortwave radiation is checked for NaNs:

https://github.com/CWorthy-ocean/roms-tools/blob/67df5fc400b1daeb903ba9b2efe11899d8b55dfa/roms_tools/setup/surface_forcing.py#L366

However, due to the issue above, this will imply a huge memory footprint because the shortwave radiation **correction** is computed for all time slices (e.g., 744 time stamps), not just one.

This can be seen in the following example, where we use Grid A from https://github.com/CWorthy-ocean/roms-tools/issues/201 and one month of hourly ERA5 data:

```
SurfaceForcing(
    grid=grid,
    ...,
    correct_radiation=True,
    use_dask=True,
    bypass_validation=True
)
```
has the following performance:
```
peak memory: 562.75 MiB, increment: 25.68 MiB
CPU times: user 54.8 s, sys: 1.26 s, total: 56.1 s
Wall time: 30.1 s
```
The memory footprint is minimal because the validation step was skipped.

However,

```
SurfaceForcing(
    grid=grid,
    ...,
    correct_radiation=True,
    use_dask=True,
    bypass_validation=False
)
```
has the following performance and big memory footprint:
```
peak memory: 32898.70 MiB, increment: 32335.71 MiB
CPU times: user 2min 24s, sys: 5.01 s, total: 2min 29s
Wall time: 43.8 s
```


diff --git a/src/klein/_resource.py b/src/klein/_resource.py
index b12f711c..4107357b 100644
--- a/src/klein/_resource.py
+++ b/src/klein/_resource.py
@@ -91,12 +91,11 @@ def extractURLparts(request: IRequest) -> Tuple[str, str, int, str, str]:
         server_port = request.getHost().port
     else:
         server_port = 0
-    if (bool(request.isSecure()), server_port) not in [
+    is_secure = bool(request.isSecure())
+    if (is_secure, server_port) not in [
         (True, 443),
         (False, 80),
-        (False, 0),
-        (True, 0),
-    ]:
+    ] or server_port == 0:
         server_name = b"%s:%d" % (server_name, server_port)

     script_name = b""
@@ -113,7 +112,7 @@ def extractURLparts(request: IRequest) -> Tuple[str, str, int, str, str]:
         if not path_info.startswith(b"/"):
             path_info = b"/" + path_info

-    url_scheme = "https" if request.isSecure() else "http"
+    url_scheme = "https" if is_secure else "http"

     utf8Failures = []
     try:
@@ -232,6 +231,12 @@ def process(r: object) -> Any:
             returns an IRenderable, then render it and let the result of that
             bubble back up.
             """
+            # isinstance() is faster than providedBy(), so this speeds up the
+            # very common case of returning pre-rendered results, at the cost
+            # of slightly slowing down other cases.
+            if isinstance(r, (bytes, str)):
+                return r
+
             if isinstance(r, Response):
                 r = r._applyToRequest(request)

Klein adds significant performance overhead over a twisted.web server
A minimal hello world benchmark, just routing "/" and returning a string, is half the speed of the equivalent minimal twisted.web server.

I will start investigating where the performance overhead is, and hopefully find some places to optimize.


diff --git a/packages/opentelemetry-instrumentation-groq/opentelemetry/instrumentation/groq/utils.py b/packages/opentelemetry-instrumentation-groq/opentelemetry/instrumentation/groq/utils.py
index e283307b9..f8d750d4c 100644
--- a/packages/opentelemetry-instrumentation-groq/opentelemetry/instrumentation/groq/utils.py
+++ b/packages/opentelemetry-instrumentation-groq/opentelemetry/instrumentation/groq/utils.py
@@ -9,6 +9,8 @@
 GEN_AI_SYSTEM = "gen_ai.system"
 GEN_AI_SYSTEM_GROQ = "groq"

+_PYDANTIC_VERSION = version("pydantic")
+

 def set_span_attribute(span, name, value):
     if value is not None and value != "":
@@ -68,7 +70,7 @@ def error_metrics_attributes(exception):

 def model_as_dict(model):
-    if version("pydantic") < "2.0.0":
+    if _PYDANTIC_VERSION < "2.0.0":
         return model.dict()
     if hasattr(model, "model_dump"):
         return model.model_dump()
diff --git a/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py b/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py
index e54902ac5..87965a3fe 100644
--- a/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py
+++ b/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py
@@ -23,6 +23,8 @@
 PROMPT_FILTER_KEY = "prompt_filter_results"
 PROMPT_ERROR = "prompt_error"

+_PYDANTIC_VERSION = version("pydantic")
+
 # tiktoken encodings map for different model, key is model_name, value is tiktoken encoding
 tiktoken_encodings = {}

@@ -236,7 +238,7 @@ def is_streaming_response(response):
 def model_as_dict(model):
     if isinstance(model, dict):
         return model
-    if version("pydantic") < "2.0.0":
+    if _PYDANTIC_VERSION < "2.0.0":
         return model.dict()
     if hasattr(model, "model_dump"):
         return model.model_dump()
diff --git a/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py b/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py
index 9030e3004..e0ab375a1 100644
--- a/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py
+++ b/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py
@@ -9,9 +9,11 @@
 import openai
 from opentelemetry.instrumentation.openai.shared.config import Config

+_OPENAI_VERSION = version("openai")
+

 def is_openai_v1():
-    return version("openai") >= "1.0.0"
+    return _OPENAI_VERSION >= "1.0.0"

 def is_azure_openai(instance):

🐛 Bug Report: Performance issues with opentelemetry-instrumentation-openai
### Which component is this bug for?

Anthropic Instrumentation

### 📜 Description

Hi! First, thank you for opentelemetry-instrumentation-openai. It makes my life easier :-)

But, it's computationally extensive, significantly slowing the service of my team. I'm raising a bug report as I couldn't find a better issue type from the ones suggested. Feel free to change this as needed.

Overall, the impact of this issue is reduced throughput. In our production environment removing the library increased our throughput under load 6 folds! The minimal reproducible project [here](https://github.com/Nagasaki45/OTEL-OpenAI-performance-investigation) shows more than 4x throughput increase when dropping the library, without any other change.

### 👟 Reproduction steps

This is a bit elaborated, so I created a [minimal reproducible example](https://github.com/Nagasaki45/OTEL-OpenAI-performance-investigation) for demonstrating the problem and for going through the investigation steps. The README of that repo explains the steps in details. If more info is needed I'm happy to provide it.

### 👍 Expected behavior

Adding opentelemetry-instrumentation-openai to a project should have a minimal impact on a system's performance.

### 👎 Actual Behavior with Screenshots

Adding opentelemetry-instrumentation-openai to a project reduces the throughput under load by ~4-6 times.

### 🤖 Python Version

3.12.0

### 📃 Provide any additional context for the Bug.

_No response_

### 👀 Have you spent some time to check if this bug has been raised before?

- [x] I checked and didn't find similar issue

### Are you willing to submit PR?

Yes I am willing to submit a PR!
🐛 Bug Report: Performance issues with opentelemetry-instrumentation-openai
### Which component is this bug for?

Anthropic Instrumentation

### 📜 Description

Hi! First, thank you for opentelemetry-instrumentation-openai. It makes my life easier :-)

But, it's computationally extensive, significantly slowing the service of my team. I'm raising a bug report as I couldn't find a better issue type from the ones suggested. Feel free to change this as needed.

Overall, the impact of this issue is reduced throughput. In our production environment removing the library increased our throughput under load 6 folds! The minimal reproducible project [here](https://github.com/Nagasaki45/OTEL-OpenAI-performance-investigation) shows more than 4x throughput increase when dropping the library, without any other change.

### 👟 Reproduction steps

This is a bit elaborated, so I created a [minimal reproducible example](https://github.com/Nagasaki45/OTEL-OpenAI-performance-investigation) for demonstrating the problem and for going through the investigation steps. The README of that repo explains the steps in details. If more info is needed I'm happy to provide it.

### 👍 Expected behavior

Adding opentelemetry-instrumentation-openai to a project should have a minimal impact on a system's performance.

### 👎 Actual Behavior with Screenshots

Adding opentelemetry-instrumentation-openai to a project reduces the throughput under load by ~4-6 times.

### 🤖 Python Version

3.12.0

### 📃 Provide any additional context for the Bug.

_No response_

### 👀 Have you spent some time to check if this bug has been raised before?

- [x] I checked and didn't find similar issue

### Are you willing to submit PR?

Yes I am willing to submit a PR!

Thanks @Nagasaki45 for reporting! We'll investigate what's causing it. Since you're using streaming I have an assumption it's related to the token count enrichment which may affect latency (but some users find useful!). Can you try disabling it and see the effect? (Setting `enrich_token_usage` to false in the initialization).
Thanks for the prompt response @nirga! I'm using OTel automatic instrumentation (i.e. `opentelemetry-instrument my_app.py`). Is there a way to control this config with environment variables or flags to `opentelemetry-instrument` itself? I tried to look it up on the docs / repo and didn't find an obvious answer.
I actually see it's off by default @Nagasaki45 so nvm. We'll take a look at it and keep you posted! This should have a negligible impact on performance.
Thanks again @nirga. I might have the capacity to help investigate this. I now have decent understanding of how to measure this bottleneck so might be able to help. Let me know.
I looked a bit more into it and I think the issue is the `version` calls. There's one [here](https://github.com/traceloop/openllmetry/blob/3539e34026a06d4cccdc1b73fd26d3f6b97fee02/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py#L14) and another one [here](https://github.com/traceloop/openllmetry/blob/3539e34026a06d4cccdc1b73fd26d3f6b97fee02/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py#L238). I just tried to replace these with `True` / `False` matching the packages versions I have installed and I think the throughput is back where it should be. This is inconclusive because I'm testing on a different machine, so CPU load behaves a bit differently. I will test on the same machine I used to create the ticket and report back.
Interesting @Nagasaki45, that actually makes sense! We should cache them during initialization
Thanks @Nagasaki45 for reporting! We'll investigate what's causing it. Since you're using streaming I have an assumption it's related to the token count enrichment which may affect latency (but some users find useful!). Can you try disabling it and see the effect? (Setting `enrich_token_usage` to false in the initialization).
Thanks for the prompt response @nirga! I'm using OTel automatic instrumentation (i.e. `opentelemetry-instrument my_app.py`). Is there a way to control this config with environment variables or flags to `opentelemetry-instrument` itself? I tried to look it up on the docs / repo and didn't find an obvious answer.
I actually see it's off by default @Nagasaki45 so nvm. We'll take a look at it and keep you posted! This should have a negligible impact on performance.
Thanks again @nirga. I might have the capacity to help investigate this. I now have decent understanding of how to measure this bottleneck so might be able to help. Let me know.
I looked a bit more into it and I think the issue is the `version` calls. There's one [here](https://github.com/traceloop/openllmetry/blob/3539e34026a06d4cccdc1b73fd26d3f6b97fee02/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/utils.py#L14) and another one [here](https://github.com/traceloop/openllmetry/blob/3539e34026a06d4cccdc1b73fd26d3f6b97fee02/packages/opentelemetry-instrumentation-openai/opentelemetry/instrumentation/openai/shared/__init__.py#L238). I just tried to replace these with `True` / `False` matching the packages versions I have installed and I think the throughput is back where it should be. This is inconclusive because I'm testing on a different machine, so CPU load behaves a bit differently. I will test on the same machine I used to create the ticket and report back.
Interesting @Nagasaki45, that actually makes sense! We should cache them during initialization


diff --git a/src/everest/bin/config_branch_script.py b/src/everest/bin/config_branch_script.py
index 5df90cb9010..d8ad052e66f 100644
--- a/src/everest/bin/config_branch_script.py
+++ b/src/everest/bin/config_branch_script.py
@@ -66,7 +66,7 @@ def _updated_initial_guess(conf_controls, opt_controls):
     conf_controls = copy(conf_controls)

     for control in conf_controls:
-        control_name = f"{control['name']}_"
+        control_name = f"{control['name']}."
         control.pop("initial_guess", None)
         batch_controls = {
             key.split(control_name)[-1]: val
@@ -78,7 +78,7 @@ def _updated_initial_guess(conf_controls, opt_controls):
             var_index = variable.get("index", None)

             if var_index is not None:
-                opt_control_name = f"{variable['name']}-{var_index}"
+                opt_control_name = f"{variable['name']}.{var_index}"
             else:
                 opt_control_name = variable["name"]

diff --git a/src/everest/config/everest_config.py b/src/everest/config/everest_config.py
index 4f0df35f9af..6fa24869066 100644
--- a/src/everest/config/everest_config.py
+++ b/src/everest/config/everest_config.py
@@ -45,6 +45,7 @@
 from ..config_file_loader import yaml_file_to_substituted_config_dict
 from ..strings import (
     DEFAULT_OUTPUT_DIR,
+    EVEREST,
     OPTIMIZATION_LOG_DIR,
     OPTIMIZATION_OUTPUT_DIR,
     STORAGE_DIR,
@@ -419,27 +420,26 @@ def validate_input_constraints_weight_definition(self) -> Self:
         controls = self.controls
         if controls is None:
             return self
-        control_full_name = []
+        control_names = self.formatted_control_names
+        control_names_deprecated = self.formatted_control_names_dotdash
         errors = []
-        for c in controls:
-            for v in c.variables:
-                if isinstance(v, ControlVariableGuessListConfig):
-                    control_full_name.extend(
-                        f"{c.name}.{v.name}-{index}"
-                        for index, _ in enumerate(v.initial_guess, start=1)
-                    )
-                elif v.index is not None:
-                    control_full_name.append(f"{c.name}.{v.name}-{v.index}")
-                else:
-                    control_full_name.append(f"{c.name}.{v.name}")

         for input_const in input_constraints:
             for key in input_const.weights:
-                if key not in control_full_name:
+                if key in control_names_deprecated and key not in control_names:
+                    logging.getLogger(EVEREST).warning(
+                        f"Deprecated reference to control: {key} in input constraint."
+                    )
+                    print(
+                        f"Deprecated input control name: {key} "
+                        f"reference in input constraint. This format is deprecated, "
+                        f"please use only '.' as delimiters: {key.replace('-', '.')}"
+                    )
+                elif key not in control_names and key not in control_names_deprecated:
                     errors.append(
                         f"Input control weight name {key} "
                         f"does not match any instance of "
-                        f"control_name.variable_name-variable_index"
+                        f"control_name.variable_name.variable_index"
                     )

         if len(errors) > 0:  # Note: python3.11 ExceptionGroup will solve this nicely
@@ -628,11 +628,27 @@ def formatted_control_names(self) -> list[str]:
             for variable in control.variables:
                 if isinstance(variable, ControlVariableGuessListConfig):
                     for index in range(1, len(variable.initial_guess) + 1):
-                        names.append(f"{control.name}_{variable.name}-{index}")
+                        names.append(f"{control.name}.{variable.name}.{index}")
+                elif variable.index is not None:
+                    names.append(f"{control.name}.{variable.name}.{variable.index}")
+                else:
+                    names.append(f"{control.name}.{variable.name}")
+        return names
+
+    @property
+    def formatted_control_names_dotdash(self) -> list[str]:
+        # Note: Should be removed as the .- way of referencing controls
+        # from input constraints is removed
+        names = []
+        for control in self.controls:
+            for variable in control.variables:
+                if isinstance(variable, ControlVariableGuessListConfig):
+                    for index in range(1, len(variable.initial_guess) + 1):
+                        names.append(f"{control.name}.{variable.name}-{index}")
                 elif variable.index is not None:
-                    names.append(f"{control.name}_{variable.name}-{variable.index}")
+                    names.append(f"{control.name}.{variable.name}-{variable.index}")
                 else:
-                    names.append(f"{control.name}_{variable.name}")
+                    names.append(f"{control.name}.{variable.name}")
         return names

     @property
diff --git a/src/everest/config/input_constraint_config.py b/src/everest/config/input_constraint_config.py
index 1bf9dc1fa38..564444ad5c4 100644
--- a/src/everest/config/input_constraint_config.py
+++ b/src/everest/config/input_constraint_config.py
@@ -7,9 +7,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"):  # type: ignore
 If we are trying to constrain only one control (i.e the z control) value:
 | input_constraints:
 | - weights:
-|   point_3D.x-0: 0
-|   point_3D.y-1: 0
-|   point_3D.z-2: 1
+|   point_3D.x.0: 0
+|   point_3D.y.1: 0
+|   point_3D.z.2: 1
 | upper_bound: 0.2

 Only control values (x, y, z) that satisfy the following equation will be allowed:
@@ -21,9 +21,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"):  # type: ignore
         description="""**Example**
 | input_constraints:
 | - weights:
-|   point_3D.x-0: 1
-|   point_3D.y-1: 2
-|   point_3D.z-2: 3
+|   point_3D.x.0: 1
+|   point_3D.y.1: 2
+|   point_3D.z.2: 3
 | target: 4

 Only control values (x, y, z) that satisfy the following equation will be allowed:
@@ -35,9 +35,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"):  # type: ignore
         description="""**Example**
 | input_constraints:
 | - weights:
-|   point_3D.x-0: 1
-|   point_3D.y-1: 2
-|   point_3D.z-2: 3
+|   point_3D.x.0: 1
+|   point_3D.y.1: 2
+|   point_3D.z.2: 3
 | lower_bound: 4

 Only control values (x, y, z) that satisfy the following
@@ -50,9 +50,9 @@ class InputConstraintConfig(BaseModel, extra="forbid"):  # type: ignore
         description="""**Example**
 | input_constraints:
 | - weights:
-|   point_3D.x-0: 1
-|   point_3D.y-1: 2
-|   point_3D.z-2: 3
+|   point_3D.x.0: 1
+|   point_3D.y.1: 2
+|   point_3D.z.2: 3
 | upper_bound: 4

 Only control values (x, y, z) that satisfy the following equation will be allowed:
diff --git a/src/everest/optimizer/everest2ropt.py b/src/everest/optimizer/everest2ropt.py
index 20930704170..e6bd5742067 100644
--- a/src/everest/optimizer/everest2ropt.py
+++ b/src/everest/optimizer/everest2ropt.py
@@ -107,22 +107,23 @@ def _parse_objectives(objective_functions: list[ObjectiveFunctionConfig], ropt_c
 def _parse_input_constraints(
     controls: FlattenedControls,
     input_constraints: list[InputConstraintConfig] | None,
+    formatted_control_names: list[str],
+    formatted_control_names_dotdash: list[str],
     ropt_config,
 ):
     if not input_constraints:
         return

-    # TODO: Issue #9816 is intended to address the need for a more general
-    # naming scheme. This code should be revisited once that issue is resolved.
-    # Ideally the formatted_control_names property of the config is used.
-    formatted_names = [
-        (
-            f"{control_name[0]}.{control_name[1]}-{control_name[2]}"
-            if len(control_name) > 2
-            else f"{control_name[0]}.{control_name[1]}"
-        )
-        for control_name in controls.names
-    ]
+    def _get_control_index(name: str):
+        try:
+            matching_index = formatted_control_names.index(name.replace("-", "."))
+            return matching_index
+        except ValueError:
+            pass
+
+        # Dash is deprecated, should eventually be removed
+        # along with formatted_control_names_dotdash
+        return formatted_control_names_dotdash.index(name)

     coefficients_matrix = []
     rhs_values = []
@@ -135,9 +136,11 @@ def _add_input_constraint(rhs_value, coefficients, constraint_type):
             types.append(constraint_type)

     for constr in input_constraints:
-        coefficients = [0.0] * len(formatted_names)
+        coefficients = [0.0] * len(formatted_control_names)
         for name, value in constr.weights.items():
-            coefficients[formatted_names.index(name)] = value
+            index = _get_control_index(name)
+            coefficients[index] = value
+
         target = constr.target
         upper_bound = constr.upper_bound
         lower_bound = constr.lower_bound
@@ -358,7 +361,11 @@ def everest2ropt(
     _parse_controls(flattened_controls, ropt_config)
     _parse_objectives(ever_config.objective_functions, ropt_config)
     _parse_input_constraints(
-        flattened_controls, ever_config.input_constraints, ropt_config
+        flattened_controls,
+        ever_config.input_constraints,
+        ever_config.formatted_control_names,
+        ever_config.formatted_control_names_dotdash,
+        ropt_config,
     )
     _parse_output_constraints(ever_config.output_constraints, ropt_config)
     _parse_optimization(

diff --git a/test-data/everest/math_func/config_advanced.yml b/test-data/everest/math_func/config_advanced.yml
index b9952f51557..96a7b24015f 100644
--- a/test-data/everest/math_func/config_advanced.yml
+++ b/test-data/everest/math_func/config_advanced.yml
@@ -20,9 +20,9 @@ objective_functions:

 input_constraints:
   - weights:
-      point.x-0: 0
-      point.x-1: 0
-      point.x-2: 1
+      point.x.0: 0
+      point.x.1: 0
+      point.x.2: 1
     upper_bound: 0.4

 output_constraints:
diff --git a/tests/everest/functional/test_main_everest_entry.py b/tests/everest/functional/test_main_everest_entry.py
index 6ab44d1c795..414503af368 100644
--- a/tests/everest/functional/test_main_everest_entry.py
+++ b/tests/everest/functional/test_main_everest_entry.py
@@ -59,9 +59,9 @@ def test_everest_entry_run(cached_example):
     storage.read_from_output_dir()
     optimal = storage.get_optimal_result()

-    assert optimal.controls["point_x"] == pytest.approx(0.5, abs=0.05)
-    assert optimal.controls["point_y"] == pytest.approx(0.5, abs=0.05)
-    assert optimal.controls["point_z"] == pytest.approx(0.5, abs=0.05)
+    assert optimal.controls["point.x"] == pytest.approx(0.5, abs=0.05)
+    assert optimal.controls["point.y"] == pytest.approx(0.5, abs=0.05)
+    assert optimal.controls["point.z"] == pytest.approx(0.5, abs=0.05)

     assert optimal.total_objective == pytest.approx(0.0, abs=0.0005)

diff --git a/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_advanced.yml/snapshot.json b/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_advanced.yml/snapshot.json
index f333faaf641..c5f8cd79c3b 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_advanced.yml/snapshot.json
+++ b/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_advanced.yml/snapshot.json
@@ -16,298 +16,298 @@
     5
   ],
   "control_names": [
-    "point_x-0",
-    "point_x-1",
-    "point_x-2"
+    "point.x.0",
+    "point.x.1",
+    "point.x.2"
   ],
   "control_values": [
     {
       "batch": 0,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.25
     },
     {
       "batch": 0,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.25
     },
     {
       "batch": 0,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.25
     },
     {
       "batch": 0,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.25
     },
     {
       "batch": 0,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.25
     },
     {
       "batch": 0,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.25
     },
     {
       "batch": 1,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.25904478
     },
     {
       "batch": 1,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.13792475
     },
     {
       "batch": 1,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.17336687
     },
     {
       "batch": 1,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.25904478
     },
     {
       "batch": 1,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.13792475
     },
     {
       "batch": 1,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.17336687
     },
     {
       "batch": 2,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.31863568
     },
     {
       "batch": 2,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.00504358
     },
     {
       "batch": 2,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.13385209
     },
     {
       "batch": 2,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.31863568
     },
     {
       "batch": 2,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.00504358
     },
     {
       "batch": 2,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.13385209
     },
     {
       "batch": 3,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.24117771
     },
     {
       "batch": 3,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.03015417
     },
     {
       "batch": 3,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.24583776
     },
     {
       "batch": 3,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.24117771
     },
     {
       "batch": 3,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.03015417
     },
     {
       "batch": 3,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.24583776
     },
     {
       "batch": 4,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.15091481
     },
     {
       "batch": 4,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": -0.00394611
     },
     {
       "batch": 4,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.36960914
     },
     {
       "batch": 4,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.15091481
     },
     {
       "batch": 4,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": -0.00394611
     },
     {
       "batch": 4,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.36960914
     },
     {
       "batch": 5,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.1145273
     },
     {
       "batch": 5,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.00635029
     },
     {
       "batch": 5,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.38836369
     },
     {
       "batch": 5,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "value": 0.1145273
     },
     {
       "batch": 5,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "value": 0.00635029
     },
     {
       "batch": 5,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "value": 0.38836369
     }
   ],
   "gradient_values": [
     {
       "batch": 0,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "function": "distance",
       "value": -0.49076627
     },
     {
       "batch": 0,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "function": "distance",
       "value": -0.50191014
     },
     {
       "batch": 0,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "function": "distance",
       "value": 0.50424549
     },
     {
       "batch": 1,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "function": "distance",
       "value": -0.52209326
     },
     {
       "batch": 1,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "function": "distance",
       "value": -0.28579331
     },
     {
       "batch": 1,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "function": "distance",
       "value": 0.64869691
     },
     {
       "batch": 2,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "function": "distance",
       "value": -0.64281291
     },
     {
       "batch": 2,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "function": "distance",
       "value": -0.00521952
     },
     {
       "batch": 2,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "function": "distance",
       "value": 0.73152662
     },
     {
       "batch": 3,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "function": "distance",
       "value": -0.47276201
     },
     {
       "batch": 3,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "function": "distance",
       "value": -0.0785477
     },
     {
       "batch": 3,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "function": "distance",
       "value": 0.51539765
     },
     {
       "batch": 4,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "function": "distance",
       "value": -0.30369426
     },
     {
       "batch": 4,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "function": "distance",
       "value": 0.02314375
     },
     {
       "batch": 4,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "function": "distance",
       "value": 0.25834193
     },
     {
       "batch": 5,
-      "control": "point_x-0",
+      "control": "point.x.0",
       "function": "distance",
       "value": -0.21656122
     },
     {
       "batch": 5,
-      "control": "point_x-1",
+      "control": "point.x.1",
       "function": "distance",
       "value": -0.00578828
     },
     {
       "batch": 5,
-      "control": "point_x-2",
+      "control": "point.x.2",
       "function": "distance",
       "value": 0.2172204
     }
@@ -428,9 +428,9 @@
   "optimal_result_json": {
     "batch": 5,
     "controls": {
-      "point_x-0": 0.1145273,
-      "point_x-1": 0.00635029,
-      "point_x-2": 0.38836369
+      "point.x.0": 0.1145273,
+      "point.x.1": 0.00635029,
+      "point.x.2": 0.38836369
     },
     "total_objective": -1.52561897
   },
diff --git a/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_minimal.yml/snapshot.json b/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_minimal.yml/snapshot.json
index 0523d94c811..20789d4f099 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_minimal.yml/snapshot.json
+++ b/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_minimal.yml/snapshot.json
@@ -9,73 +9,73 @@
     2
   ],
   "control_names": [
-    "point_x",
-    "point_y",
-    "point_z"
+    "point.x",
+    "point.y",
+    "point.z"
   ],
   "control_values": [
     {
       "batch": 0,
-      "control": "point_x",
+      "control": "point.x",
       "value": 0.1
     },
     {
       "batch": 0,
-      "control": "point_y",
+      "control": "point.y",
       "value": 0.1
     },
     {
       "batch": 0,
-      "control": "point_z",
+      "control": "point.z",
       "value": 0.1
     },
     {
       "batch": 1,
-      "control": "point_x",
+      "control": "point.x",
       "value": 0.90038639
     },
     {
       "batch": 1,
-      "control": "point_y",
+      "control": "point.y",
       "value": 0.90054756
     },
     {
       "batch": 1,
-      "control": "point_z",
+      "control": "point.z",
       "value": 0.89933796
     },
     {
       "batch": 2,
-      "control": "point_x",
+      "control": "point.x",
       "value": 0.50014777
     },
     {
       "batch": 2,
-      "control": "point_y",
+      "control": "point.y",
       "value": 0.50022835
     },
     {
       "batch": 2,
-      "control": "point_z",
+      "control": "point.z",
       "value": 0.49962361
     }
   ],
   "gradient_values": [
     {
       "batch": 0,
-      "control": "point_x",
+      "control": "point.x",
       "function": "distance",
       "value": 0.80038641
     },
     {
       "batch": 0,
-      "control": "point_y",
+      "control": "point.y",
       "function": "distance",
       "value": 0.80054758
     },
     {
       "batch": 0,
-      "control": "point_z",
+      "control": "point.z",
       "function": "distance",
       "value": 0.79933798
     }
@@ -115,9 +115,9 @@
   "optimal_result_json": {
     "batch": 2,
     "controls": {
-      "point_x": 0.50014777,
-      "point_y": 0.50022835,
-      "point_z": 0.49962361
+      "point.x": 0.50014777,
+      "point.y": 0.50022835,
+      "point.z": 0.49962361
     },
     "total_objective": -2.2e-7
   },
diff --git a/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_multiobj.yml/snapshot.json b/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_multiobj.yml/snapshot.json
index 7f2c9e71518..005584974fd 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_multiobj.yml/snapshot.json
+++ b/tests/everest/snapshots/test_api_snapshots/test_api_snapshots/config_multiobj.yml/snapshot.json
@@ -9,91 +9,91 @@
     2
   ],
   "control_names": [
-    "point_x",
-    "point_y",
-    "point_z"
+    "point.x",
+    "point.y",
+    "point.z"
   ],
   "control_values": [
     {
       "batch": 0,
-      "control": "point_x",
+      "control": "point.x",
       "value": 0.0
     },
     {
       "batch": 0,
-      "control": "point_y",
+      "control": "point.y",
       "value": 0.0
     },
     {
       "batch": 0,
-      "control": "point_z",
+      "control": "point.z",
       "value": 0.0
     },
     {
       "batch": 1,
-      "control": "point_x",
+      "control": "point.x",
       "value": -0.00420218
     },
     {
       "batch": 1,
-      "control": "point_y",
+      "control": "point.y",
       "value": -0.0112982
     },
     {
       "batch": 1,
-      "control": "point_z",
+      "control": "point.z",
       "value": 1.0
     },
     {
       "batch": 2,
-      "control": "point_x",
+      "control": "point.x",
       "value": -0.00210079
     },
     {
       "batch": 2,
-      "control": "point_y",
+      "control": "point.y",
       "value": -0.00564829
     },
     {
       "batch": 2,
-      "control": "point_z",
+      "control": "point.z",
       "value": 0.49992811
     }
   ],
   "gradient_values": [
     {
       "batch": 0,
-      "control": "point_x",
+      "control": "point.x",
       "function": "distance_p",
       "value": 0.99589284
     },
     {
       "batch": 0,
-      "control": "point_x",
+      "control": "point.x",
       "function": "distance_q",
       "value": -3.00456477
     },
     {
       "batch": 0,
-      "control": "point_y",
+      "control": "point.y",
       "function": "distance_p",
       "value": 0.98866227
     },
     {
       "batch": 0,
-      "control": "point_y",
+      "control": "point.y",
       "function": "distance_q",
       "value": -3.011388
     },
     {
       "batch": 0,
-      "control": "point_z",
+      "control": "point.z",
       "function": "distance_p",
       "value": 1.00465235
     },
     {
       "batch": 0,
-      "control": "point_z",
+      "control": "point.z",
       "function": "distance_q",
       "value": 1.0044895
     }
@@ -161,9 +161,9 @@
   "optimal_result_json": {
     "batch": 2,
     "controls": {
-      "point_x": -0.00210079,
-      "point_y": -0.00564829,
-      "point_z": 0.49992811
+      "point.x": -0.00210079,
+      "point.y": -0.00564829,
+      "point.z": 0.49992811
     },
     "total_objective": -2.00004834
   },
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/batch_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/batch_df.csv
index ebeed50de39..7434e3160de 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/batch_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/batch_df.csv
@@ -1,4 +1,4 @@
-batch_id,total_objective_value,distance,grad(distance wrt point_x-0),grad(distance wrt point_x-1),grad(distance wrt point_x-2),grad(distance.total wrt point_x-0),grad(distance.total wrt point_x-1),grad(distance.total wrt point_x-2),merit_value,point_x-0,point_x-1,point_x-2,x-0_coord
+batch_id,total_objective_value,distance,grad(distance wrt point.x.0),grad(distance wrt point.x.1),grad(distance wrt point.x.2),grad(distance.total wrt point.x.0),grad(distance.total wrt point.x.1),grad(distance.total wrt point.x.2),merit_value,point.x.0,point.x.1,point.x.2,x-0_coord
 0,-1.6875,-1.6875,-0.4908,-0.5019,0.5042,-0.4908,-0.5019,0.5042,,1.0,-0.0,-0.0,0.25
 1,-1.6928,-1.6928,-0.5221,-0.2858,0.6487,-0.5221,-0.2858,0.6487,389.8557,1.0,0.0,0.0001,0.259
 2,-1.7356,-1.7356,-0.6428,-0.0052,0.7315,-0.6428,-0.0052,0.7315,381.4612,1.0,0.0,0.0,0.3186
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/combined_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/combined_df.csv
index 0eb87738e1f..6fe35f89fa3 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/combined_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/combined_df.csv
@@ -1,4 +1,4 @@
-batch_id,realization,simulation_id,perturbation,batch_distance,batch_merit_value,batch_point_x-0,batch_point_x-1,batch_point_x-2,batch_total_objective_value,batch_x-0_coord,distance,grad(distance wrt point_x-0),grad(distance wrt point_x-1),grad(distance wrt point_x-2),grad(distance.total wrt point_x-0),grad(distance.total wrt point_x-1),grad(distance.total wrt point_x-2),point_x-0,point_x-1,point_x-2,x-0_coord
+batch_id,realization,simulation_id,perturbation,batch_distance,batch_merit_value,batch_point.x.0,batch_point.x.1,batch_point.x.2,batch_total_objective_value,batch_x-0_coord,distance,grad(distance wrt point.x.0),grad(distance wrt point.x.1),grad(distance wrt point.x.2),grad(distance.total wrt point.x.0),grad(distance.total wrt point.x.1),grad(distance.total wrt point.x.2),point.x.0,point.x.1,point.x.2,x-0_coord
 0,0,,0,-1.6875,,1.0,-0.0,-0.0,-1.6875,0.25,-6.1975,-0.4908,-0.5019,0.5042,-0.4908,-0.5019,0.5042,0.2538,0.2475,0.2397,0.2538
 0,0,,1,-1.6875,,1.0,-0.0,-0.0,-1.6875,0.25,-6.1979,-0.4908,-0.5019,0.5042,-0.4908,-0.5019,0.5042,0.25,0.2526,0.2474,0.25
 0,0,,2,-1.6875,,1.0,-0.0,-0.0,-1.6875,0.25,-6.169,-0.4908,-0.5019,0.5042,-0.4908,-0.5019,0.5042,0.2485,0.2465,0.2523,0.2485
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/pert_real_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/pert_real_df.csv
index 7177555072f..33185ce8441 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/pert_real_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_advanced.yml/pert_real_df.csv
@@ -1,4 +1,4 @@
-batch_id,realization,perturbation,simulation_id,distance,point_x-0,point_x-1,point_x-2,x-0_coord
+batch_id,realization,perturbation,simulation_id,distance,point.x.0,point.x.1,point.x.2,x-0_coord
 0,0,,0,-6.1875,0.25,0.25,0.25,0.25
 0,0,0,,-6.1975,0.2538,0.2475,0.2397,0.2538
 0,0,1,,-6.1979,0.25,0.2526,0.2474,0.25
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/batch_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/batch_df.csv
index ea7608bc92c..efb053bfc1e 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/batch_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/batch_df.csv
@@ -1,4 +1,4 @@
-batch_id,total_objective_value,distance,grad(distance wrt point_x),grad(distance wrt point_y),grad(distance wrt point_z),grad(distance.total wrt point_x),grad(distance.total wrt point_y),grad(distance.total wrt point_z)
+batch_id,total_objective_value,distance,grad(distance wrt point.x),grad(distance wrt point.y),grad(distance wrt point.z),grad(distance.total wrt point.x),grad(distance.total wrt point.y),grad(distance.total wrt point.z)
 0,-0.48,-0.48,0.8004,0.8005,0.7993,0.8004,0.8005,0.7993
 1,-0.4802,-0.4802,,,,,,
 2,-0.0,-0.0,,,,,,
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/combined_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/combined_df.csv
index 8b6acfeecb0..e77dd34b2d0 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/combined_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/combined_df.csv
@@ -1,4 +1,4 @@
-batch_id,realization,simulation_id,perturbation,batch_distance,batch_total_objective_value,distance,grad(distance wrt point_x),grad(distance wrt point_y),grad(distance wrt point_z),grad(distance.total wrt point_x),grad(distance.total wrt point_y),grad(distance.total wrt point_z),point_x,point_y,point_z
+batch_id,realization,simulation_id,perturbation,batch_distance,batch_total_objective_value,distance,grad(distance wrt point.x),grad(distance wrt point.y),grad(distance wrt point.z),grad(distance.total wrt point.x),grad(distance.total wrt point.y),grad(distance.total wrt point.z),point.x,point.y,point.z
 0,0,,0,-0.48,-0.48,-0.4801,0.8004,0.8005,0.7993,0.8004,0.8005,0.7993,0.099,0.0996,0.1013
 0,0,,1,-0.48,-0.48,-0.4786,0.8004,0.8005,0.7993,0.8004,0.8005,0.7993,0.1002,0.1009,0.1006
 0,0,,2,-0.48,-0.48,-0.4803,0.8004,0.8005,0.7993,0.8004,0.8005,0.7993,0.0994,0.1005,0.0997
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/pert_real_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/pert_real_df.csv
index b3519c25ef3..d3ecd5d5187 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/pert_real_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_minimal.yml/pert_real_df.csv
@@ -1,4 +1,4 @@
-batch_id,realization,perturbation,simulation_id,distance,point_x,point_y,point_z
+batch_id,realization,perturbation,simulation_id,distance,point.x,point.y,point.z
 0,0,,0,-0.48,0.1,0.1,0.1
 0,0,0,,-0.4801,0.099,0.0996,0.1013
 0,0,1,,-0.4786,0.1002,0.1009,0.1006
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/batch_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/batch_df.csv
index 37c64f69046..41cef3ed4d7 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/batch_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/batch_df.csv
@@ -1,4 +1,4 @@
-batch_id,total_objective_value,distance_p,distance_q,grad(distance_p wrt point_x),grad(distance_p wrt point_y),grad(distance_p wrt point_z),grad(distance_p.total wrt point_x),grad(distance_p.total wrt point_y),grad(distance_p.total wrt point_z),grad(distance_q wrt point_x),grad(distance_q wrt point_y),grad(distance_q wrt point_z),grad(distance_q.total wrt point_x),grad(distance_q.total wrt point_y),grad(distance_q.total wrt point_z)
+batch_id,total_objective_value,distance_p,distance_q,grad(distance_p wrt point.x),grad(distance_p wrt point.y),grad(distance_p wrt point.z),grad(distance_p.total wrt point.x),grad(distance_p.total wrt point.y),grad(distance_p.total wrt point.z),grad(distance_q wrt point.x),grad(distance_q wrt point.y),grad(distance_q wrt point.z),grad(distance_q.total wrt point.x),grad(distance_q.total wrt point.y),grad(distance_q.total wrt point.z)
 0,-2.3333,-0.75,-4.75,0.9959,0.9887,1.0047,-0.0056,-0.0151,1.3395,-3.0046,-3.0114,1.0045,-0.0056,-0.0151,1.3395
 1,-2.3335,-0.7656,-4.7036,,,,,,,,,,,,
 2,-2.0,-0.5078,-4.4768,,,,,,,,,,,,
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/combined_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/combined_df.csv
index 90cdffc6e57..fbaa46ebc5e 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/combined_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/combined_df.csv
@@ -1,4 +1,4 @@
-batch_id,realization,simulation_id,perturbation,batch_distance_p,batch_distance_q,batch_total_objective_value,distance_p,distance_q,grad(distance_p wrt point_x),grad(distance_p wrt point_y),grad(distance_p wrt point_z),grad(distance_p.total wrt point_x),grad(distance_p.total wrt point_y),grad(distance_p.total wrt point_z),grad(distance_q wrt point_x),grad(distance_q wrt point_y),grad(distance_q wrt point_z),grad(distance_q.total wrt point_x),grad(distance_q.total wrt point_y),grad(distance_q.total wrt point_z),point_x,point_y,point_z
+batch_id,realization,simulation_id,perturbation,batch_distance_p,batch_distance_q,batch_total_objective_value,distance_p,distance_q,grad(distance_p wrt point.x),grad(distance_p wrt point.y),grad(distance_p wrt point.z),grad(distance_p.total wrt point.x),grad(distance_p.total wrt point.y),grad(distance_p.total wrt point.z),grad(distance_q wrt point.x),grad(distance_q wrt point.y),grad(distance_q wrt point.z),grad(distance_q.total wrt point.x),grad(distance_q.total wrt point.y),grad(distance_q.total wrt point.z),point.x,point.y,point.z
 0,0,,0,-0.75,-4.75,-2.3333,-0.7683,-4.7791,0.9959,0.9887,1.0047,-0.0056,-0.0151,1.3395,-3.0046,-3.0114,1.0045,-0.0056,-0.0151,1.3395,0.0077,-0.005,-0.0205
 0,0,,1,-0.75,-4.75,-2.3333,-0.75,-4.7708,0.9959,0.9887,1.0047,-0.0056,-0.0151,1.3395,-3.0046,-3.0114,1.0045,-0.0056,-0.0151,1.3395,-0.0001,0.0053,-0.0051
 0,0,,2,-0.75,-4.75,-2.3333,-0.7553,-4.7157,0.9959,0.9887,1.0047,-0.0056,-0.0151,1.3395,-3.0046,-3.0114,1.0045,-0.0056,-0.0151,1.3395,-0.0029,-0.007,0.0047
diff --git a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/pert_real_df.csv b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/pert_real_df.csv
index 7ff7373a50a..22c7b72d571 100644
--- a/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/pert_real_df.csv
+++ b/tests/everest/snapshots/test_api_snapshots/test_csv_export/config_multiobj.yml/pert_real_df.csv
@@ -1,4 +1,4 @@
-batch_id,realization,perturbation,simulation_id,distance_p,distance_q,point_x,point_y,point_z
+batch_id,realization,perturbation,simulation_id,distance_p,distance_q,point.x,point.y,point.z
 0,0,,0,-0.75,-4.75,0.0,0.0,0.0
 0,0,0,,-0.7683,-4.7791,0.0077,-0.005,-0.0205
 0,0,1,,-0.75,-4.7708,-0.0001,0.0053,-0.0051
diff --git a/tests/everest/test_config_branch.py b/tests/everest/test_config_branch.py
index 9b1c6f3a2e3..841b41a5437 100644
--- a/tests/everest/test_config_branch.py
+++ b/tests/everest/test_config_branch.py
@@ -25,7 +25,7 @@ def test_get_controls_for_batch(cached_example):
         Path(path) / "everest_output" / "optimization_output", 1
     )
     control_names = set(opt_controls.keys())
-    expected_control_names = {"point_x-0", "point_x-1", "point_x-2"}
+    expected_control_names = {"point.x.0", "point.x.1", "point.x.2"}

     assert control_names == expected_control_names

diff --git a/tests/everest/test_controls.py b/tests/everest/test_controls.py
index 7cbaab07d42..5358b7a932d 100644
--- a/tests/everest/test_controls.py
+++ b/tests/everest/test_controls.py
@@ -119,7 +119,7 @@ def test_variable_name_index_validation(copy_test_data_to_tmp):
     # Not valid equal names
     config.controls[0].variables[1].name = "w00"
     with pytest.raises(
-        ValidationError, match=r"Subfield\(s\) `name-index` must be unique"
+        ValidationError, match=r"Subfield\(s\) `name.index` must be unique"
     ):
         EverestConfig.model_validate(config.model_dump(exclude_none=True))
     # Not valid index inconsistency
@@ -135,7 +135,7 @@ def test_variable_name_index_validation(copy_test_data_to_tmp):
     for v in config.controls[0].variables:
         v.index = 0
     with pytest.raises(
-        ValidationError, match=r"Subfield\(s\) `name-index` must be unique"
+        ValidationError, match=r"Subfield\(s\) `name.index` must be unique"
     ):
         EverestConfig.model_validate(config.model_dump(exclude_none=True))

@@ -153,7 +153,7 @@ def test_variable_name_index_validation(copy_test_data_to_tmp):
     with pytest.raises(
         ValidationError,
         match="does not match any instance of "
-        "control_name\\.variable_name-variable_index",
+        "control_name\\.variable_name\\.variable_index",
     ):
         EverestConfig.model_validate(config_dict)

diff --git a/tests/everest/test_cvar.py b/tests/everest/test_cvar.py
index bdcb0f607ee..2c687f91c52 100644
--- a/tests/everest/test_cvar.py
+++ b/tests/everest/test_cvar.py
@@ -31,7 +31,7 @@ def test_mathfunc_cvar(
     run_model.run_experiment(evaluator_server_config)

     # Assert
-    x0, x1, x2 = (run_model.result.controls["point_" + p] for p in ["x", "y", "z"])
+    x0, x1, x2 = (run_model.result.controls["point." + p] for p in ["x", "y", "z"])

     assert x0 == pytest.approx(0.5, 0.05)
     assert x1 == pytest.approx(0.5, 0.05)
diff --git a/tests/everest/test_detached.py b/tests/everest/test_detached.py
index 464f58038ac..0e8da76d500 100644
--- a/tests/everest/test_detached.py
+++ b/tests/everest/test_detached.py
@@ -346,17 +346,17 @@ def test_get_opt_status(cached_example):
     )

     assert np.allclose(
-        opts["control_history"]["point_x"],
+        opts["control_history"]["point.x"],
         [0.0, -0.004202181916184627, -0.0021007888698514315],
         atol=1e-4,
     )
     assert np.allclose(
-        opts["control_history"]["point_y"],
+        opts["control_history"]["point.y"],
         [0.0, -0.011298196942730383, -0.0056482862617779715],
         atol=1e-4,
     )
     assert np.allclose(
-        opts["control_history"]["point_z"], [0.0, 1.0, 0.4999281115746754], atol=1e-4
+        opts["control_history"]["point.z"], [0.0, 1.0, 0.4999281115746754], atol=1e-4
     )

     assert np.allclose(
@@ -375,24 +375,24 @@ def test_get_opt_status(cached_example):
     cmond = opts["cli_monitor_data"]

     assert cmond["batches"] == [0, 1, 2]
-    assert cmond["controls"][0]["point_x"] == 0.0
-    assert cmond["controls"][0]["point_y"] == 0.0
-    assert cmond["controls"][0]["point_z"] == 0.0
+    assert cmond["controls"][0]["point.x"] == 0.0
+    assert cmond["controls"][0]["point.y"] == 0.0
+    assert cmond["controls"][0]["point.z"] == 0.0

     assert np.allclose(
-        cmond["controls"][1]["point_x"], -0.004202181916184627, atol=1e-4
+        cmond["controls"][1]["point.x"], -0.004202181916184627, atol=1e-4
     )
     assert np.allclose(
-        cmond["controls"][1]["point_y"], -0.011298196942730383, atol=1e-4
+        cmond["controls"][1]["point.y"], -0.011298196942730383, atol=1e-4
     )
-    assert np.allclose(cmond["controls"][1]["point_z"], 1.0, atol=1e-4)
+    assert np.allclose(cmond["controls"][1]["point.z"], 1.0, atol=1e-4)
     assert np.allclose(
-        cmond["controls"][2]["point_x"], -0.0021007888698514315, atol=1e-4
+        cmond["controls"][2]["point.x"], -0.0021007888698514315, atol=1e-4
     )
     assert np.allclose(
-        cmond["controls"][2]["point_y"], -0.0056482862617779715, atol=1e-4
+        cmond["controls"][2]["point.y"], -0.0056482862617779715, atol=1e-4
     )
-    assert np.allclose(cmond["controls"][2]["point_z"], 0.4999281115746754, atol=1e-4)
+    assert np.allclose(cmond["controls"][2]["point.z"], 0.4999281115746754, atol=1e-4)

     assert np.allclose(
         cmond["objective_value"],
diff --git a/tests/everest/test_discrete.py b/tests/everest/test_discrete.py
index a293f523716..486cfb65077 100644
--- a/tests/everest/test_discrete.py
+++ b/tests/everest/test_discrete.py
@@ -45,5 +45,5 @@ def test_discrete_optimizer(
     run_model.run_experiment(evaluator_server_config)

     # Assert
-    assert run_model.result.controls["point_x"] == 3
-    assert run_model.result.controls["point_y"] == 7
+    assert run_model.result.controls["point.x"] == 3
+    assert run_model.result.controls["point.y"] == 7
diff --git a/tests/everest/test_fix_control.py b/tests/everest/test_fix_control.py
index 9c8d902e5c1..90c5f775d9e 100644
--- a/tests/everest/test_fix_control.py
+++ b/tests/everest/test_fix_control.py
@@ -19,4 +19,4 @@ def test_fix_control(
     run_model.run_experiment(evaluator_server_config)

     # Check that the first variable remains fixed:
-    assert run_model.result.controls["point_x-0"] == config.controls[0].initial_guess
+    assert run_model.result.controls["point.x.0"] == config.controls[0].initial_guess
diff --git a/tests/everest/test_input_constraints_config.py b/tests/everest/test_input_constraints_config.py
index 84b2a9462a3..5996915be5c 100644
--- a/tests/everest/test_input_constraints_config.py
+++ b/tests/everest/test_input_constraints_config.py
@@ -1,3 +1,7 @@
+import os
+
+from ruamel.yaml import YAML
+
 from everest.config import EverestConfig
 from tests.everest.utils import relpath

@@ -31,3 +35,82 @@ def test_input_constraint_initialization():
     # Check the weights
     exp_weights = [0.1, 0.2, 0.3]
     assert exp_weights == [input_constraint.weights[v] for v in exp_vars]
+
+
+def test_input_constraint_control_references(tmp_path, capsys):
+    os.chdir(tmp_path)
+    controls_config = [
+        {
+            "name": "dummy",
+            "type": "generic_control",
+            "min": 0,
+            "max": 1,
+            "initial_guess": 0,
+            "variables": [
+                {"name": "x", "index": "0"},
+                {"name": "x", "index": "1"},
+                {"name": "x", "index": "2"},
+                {"name": "y", "index": "0"},
+            ],
+        }
+    ]
+
+    input_constraints_config_new = [
+        {
+            "weights": {
+                "dummy.x.0": 1.0,
+                "dummy.x.1": 1.0,
+                "dummy.x.2": 1.0,
+                "dummy.y.0": 1.0,
+            }
+        }
+    ]
+
+    input_constraints_config_deprecated = [
+        {
+            "weights": {
+                "dummy.x-0": 1.0,
+                "dummy.x-1": 1.0,
+                "dummy.x-2": 1.0,
+                "dummy.y-0": 1.0,
+            }
+        }
+    ]
+
+    yaml = YAML(typ="safe", pure=True)
+
+    with open("config_nowarns.yml", "w+", encoding="utf-8") as f:
+        yaml.dump(
+            {
+                "model": {"realizations": ["0", "2", "4"]},
+                "controls": controls_config,
+                "objective_functions": [{"name": "dummyobj"}],
+                "input_constraints": input_constraints_config_new,
+            },
+            f,
+        )
+
+    with open("config_warns.yml", "w+", encoding="utf-8") as f:
+        yaml.dump(
+            {
+                "model": {"realizations": ["0", "2", "4"]},
+                "controls": controls_config,
+                "objective_functions": [{"name": "dummyobj"}],
+                "input_constraints": input_constraints_config_deprecated,
+            },
+            f,
+        )
+
+    # Expect no errors/warnings
+    EverestConfig.load_file("config_nowarns.yml")
+    out1 = capsys.readouterr().out
+    assert "Deprecated input control name" not in out1
+
+    EverestConfig.load_file("config_warns.yml")
+    out2 = capsys.readouterr().out
+
+    assert all(
+        f"Deprecated input control name: {control_ref} reference in input constraint."
+        in out2
+        for control_ref in input_constraints_config_deprecated[0]["weights"]
+    )
diff --git a/tests/everest/test_math_func.py b/tests/everest/test_math_func.py
index 082ceebe201..076ba204fc3 100644
--- a/tests/everest/test_math_func.py
+++ b/tests/everest/test_math_func.py
@@ -23,7 +23,7 @@ def test_math_func_multiobj(cached_example):
     storage.read_from_output_dir()
     result = storage.get_optimal_result()
     # Check resulting points
-    x, y, z = (result.controls["point_" + p] for p in ("x", "y", "z"))
+    x, y, z = (result.controls["point." + p] for p in ("x", "y", "z"))
     assert x == pytest.approx(0.0, abs=0.05)
     assert y == pytest.approx(0.0, abs=0.05)
     assert z == pytest.approx(0.5, abs=0.05)
@@ -43,9 +43,9 @@ def test_math_func_advanced(cached_example):
     storage.read_from_output_dir()
     result = storage.get_optimal_result()

-    point_names = ["x-0", "x-1", "x-2"]
+    point_names = ["x.0", "x.1", "x.2"]
     # Check resulting points
-    x0, x1, x2 = (result.controls["point_" + p] for p in point_names)
+    x0, x1, x2 = (result.controls["point." + p] for p in point_names)
     assert x0 == pytest.approx(0.1, abs=0.05)
     assert x1 == pytest.approx(0.0, abs=0.05)
     assert x2 == pytest.approx(0.4, abs=0.05)
@@ -148,7 +148,7 @@ def test_math_func_auto_scaled_controls(
     run_model.run_experiment(evaluator_server_config)

     # Assert
-    x, y, z = (run_model.result.controls["point_" + p] for p in ("x", "y", "z"))
+    x, y, z = (run_model.result.controls["point." + p] for p in ("x", "y", "z"))

     assert x == pytest.approx(0.25, abs=0.05)
     assert y == pytest.approx(0.25, abs=0.05)
diff --git a/tests/everest/test_objective_type.py b/tests/everest/test_objective_type.py
index d4623ba15cd..538014f5f1b 100644
--- a/tests/everest/test_objective_type.py
+++ b/tests/everest/test_objective_type.py
@@ -30,7 +30,7 @@ def test_objective_type(
     run_model.run_experiment(evaluator_server_config)

     # Assert
-    x0, x1, x2 = (run_model.result.controls["point_" + p] for p in ["x", "y", "z"])
+    x0, x1, x2 = (run_model.result.controls["point." + p] for p in ["x", "y", "z"])
     assert x0 == pytest.approx(0.5, abs=0.025)
     assert x1 == pytest.approx(0.5, abs=0.025)
     assert x2 == pytest.approx(0.5, abs=0.025)

Make control naming consistent
**Is your feature request related to a problem? Please describe.**
The variables that are optimized (controls in Everest nomenclature) are specified in a nested fashion: We have controls, which defines groups of variables, which in turn can have multiple components labeled with an integer index. For example (from `config_advanced.yml`):

```yml
controls:
  - name: point
    max: 1.0
    min: -1.0
    initial_guess: 0.25
    perturbation_magnitude: 0.005
    type: generic_control
    variables:
      - name: x
        index: 0
      - name: x
        index: 1
      - name: x
        index: 2
```
 This defines three controls, with the following `control`, `variable`, and `index` fields:

- `point`, `x`, `0`
- `point`, `x`, `1`
- `point`, `x`, `2`

When reporting results, control names are constructed from the three fields as follows `control_variable-index`, so we get:
- `point_x-0`
- `point_x-1`
- `point_x-2`

When specifying input constraints, weights need to be set for a sub-set of controls, so controls are needed, but in this case they follow the form `control.variable-index`. For instance (from `config_advanced.yml`):

```yml
input_constraints:
  - weights:
      point.x-0: 0
      point.x-1: 0
      point.x-2: 1
    upper_bound: 0.4
```

This is inconsistent and potentially confusing for the user.

**Describe the solution you'd like**
When a string representation of controls is shown, or required as an input, it should always have the same form. There should be a singe function/method responsible for formatting the string from `control`, `variable`, `index` fields. Preferably this transformation should be invertible, although we should not rely on that in code. If possible, the delimiters used should not be allowed in control and variable names. We should discuss the delimiters to use, but using `.` and `-` would be backwards compatible for specifying input constraints and it would leave the underscore available for control and variable naming.


diff --git a/src/ert/config/design_matrix.py b/src/ert/config/design_matrix.py
index 1ecd5995bd8..0df73886271 100644
--- a/src/ert/config/design_matrix.py
+++ b/src/ert/config/design_matrix.py
@@ -191,7 +191,7 @@ def read_design_matrix(

         if error_list := DesignMatrix._validate_design_matrix(design_matrix_df):
             error_msg = "\n".join(error_list)
-            raise ValueError(f"Design matrix is not valid, error:\n{error_msg}")
+            raise ValueError(f"Design matrix is not valid, error(s):\n{error_msg}")

         defaults_to_use = DesignMatrix._read_defaultssheet(
             self.xls_filename, self.default_sheet, design_matrix_df.columns.to_list()
@@ -259,13 +259,6 @@ def _validate_design_matrix(design_matrix: pd.DataFrame) -> list[str]:
             return []
         errors = []
         column_na_mask = design_matrix.columns.isna()
-        column_indexes_unnamed = [
-            index for index, value in enumerate(column_na_mask) if value
-        ]
-        if len(column_indexes_unnamed) > 0:
-            errors.append(
-                f"Column headers not present in column {column_indexes_unnamed}"
-            )
         if not design_matrix.columns[~column_na_mask].is_unique:
             errors.append("Duplicate parameter names found in design sheet")
         empties = [
@@ -274,6 +267,16 @@ def _validate_design_matrix(design_matrix: pd.DataFrame) -> list[str]:
         ]
         if len(empties) > 0:
             errors.append(f"Design matrix contains empty cells {empties}")
+
+        for column_num, param_name in enumerate(design_matrix.columns):
+            if pd.isna(param_name) or len(param_name.split()) == 0:
+                errors.append(f"Empty parameter name found in column {column_num}.")
+            elif len(param_name.split()) > 1:
+                errors.append(
+                    f"Multiple words in parameter name found in column {column_num} ({param_name})."
+                )
+            elif param_name.isnumeric():
+                errors.append(f"Numeric parameter name found in column {column_num}.")
         return errors

     @staticmethod
diff --git a/src/ert/gui/simulation/ensemble_experiment_panel.py b/src/ert/gui/simulation/ensemble_experiment_panel.py
index 342f703d9fc..f17e4847312 100644
--- a/src/ert/gui/simulation/ensemble_experiment_panel.py
+++ b/src/ert/gui/simulation/ensemble_experiment_panel.py
@@ -15,7 +15,7 @@
 from ert.gui.tools.design_matrix.design_matrix_panel import DesignMatrixPanel
 from ert.mode_definitions import ENSEMBLE_EXPERIMENT_MODE
 from ert.run_models import EnsembleExperiment
-from ert.validation import ActiveRange, RangeStringArgument
+from ert.validation import ActiveRange, RangeStringArgument, RangeSubsetStringArgument
 from ert.validation.proper_name_argument import ExperimentValidation, ProperNameArgument

 from .experiment_config_panel import ExperimentConfigPanel
@@ -88,6 +88,11 @@ def __init__(
             self._active_realizations_field.setText(
                 ActiveRange(design_matrix.active_realizations).rangestring
             )
+            self._active_realizations_field.setValidator(
+                RangeSubsetStringArgument(
+                    ActiveRange(design_matrix.active_realizations)
+                )
+            )
             show_dm_param_button = QPushButton("Show parameters")
             show_dm_param_button.setObjectName("show-dm-parameters")
             show_dm_param_button.setMinimumWidth(50)
diff --git a/src/ert/validation/__init__.py b/src/ert/validation/__init__.py
index a33dc1d4ffe..f1e4642505a 100644
--- a/src/ert/validation/__init__.py
+++ b/src/ert/validation/__init__.py
@@ -5,7 +5,7 @@
 from .number_list_string_argument import NumberListStringArgument
 from .proper_name_argument import ExperimentValidation, ProperNameArgument
 from .proper_name_format_argument import ProperNameFormatArgument
-from .range_string_argument import RangeStringArgument
+from .range_string_argument import RangeStringArgument, RangeSubsetStringArgument
 from .rangestring import mask_to_rangestring, rangestring_to_list, rangestring_to_mask
 from .runpath_argument import RunPathArgument
 from .string_definition import StringDefinition
@@ -21,6 +21,7 @@
     "ProperNameArgument",
     "ProperNameFormatArgument",
     "RangeStringArgument",
+    "RangeSubsetStringArgument",
     "RunPathArgument",
     "StringDefinition",
     "ValidationStatus",
diff --git a/src/ert/validation/active_range.py b/src/ert/validation/active_range.py
index 517db6acdac..90b0886e532 100644
--- a/src/ert/validation/active_range.py
+++ b/src/ert/validation/active_range.py
@@ -8,6 +8,7 @@ def __init__(
         rangestring: str | None = None,
         length: int | None = None,
     ):
+        self.length = length
         if mask is None and rangestring is None and length is None:
             raise ValueError("Supply mask or rangestring and length to IndexRange.")
         if mask is None:
@@ -74,3 +75,13 @@ def validate_rangestring_vs_length(
                     f"for size {length}"
                 )
         return (rangestring, length)
+
+    def validate_range_is_subset(self, other: "ActiveRange") -> None:
+        if (
+            not [a and b for a, b in zip(self.mask, other.mask, strict=False)]
+            == other.mask
+        ):
+            raise ValueError(
+                f"Specified rangestring ({other.rangestring}) is not a subset "
+                f"of the active realizations ({self.rangestring})"
+            )
diff --git a/src/ert/validation/range_string_argument.py b/src/ert/validation/range_string_argument.py
index 633b756f277..033c26022e4 100644
--- a/src/ert/validation/range_string_argument.py
+++ b/src/ert/validation/range_string_argument.py
@@ -41,3 +41,24 @@ def validate(self, token: str) -> ValidationStatus:
         validation_status.setValue(token)

         return validation_status
+
+
+class RangeSubsetStringArgument(RangeStringArgument):
+    def __init__(self, source_active_range: ActiveRange) -> None:
+        super().__init__()
+        self._source_range = source_active_range
+
+    def validate(self, token: str) -> ValidationStatus:
+        validation_status = super().validate(token)
+
+        if not validation_status:
+            return validation_status
+        try:
+            other_range = ActiveRange(rangestring=token, length=len(self._source_range))
+            self._source_range.validate_range_is_subset(other_range)
+        except ValueError as e:
+            validation_status.setFailed()
+            validation_status.addToMessage(str(e))
+
+        validation_status.setValue(token)
+        return validation_status

diff --git a/tests/ert/unit_tests/gui/ide/test_range_string_argument.py b/tests/ert/unit_tests/gui/ide/test_range_string_argument.py
index 0a342924b48..af21dfd0cad 100644
--- a/tests/ert/unit_tests/gui/ide/test_range_string_argument.py
+++ b/tests/ert/unit_tests/gui/ide/test_range_string_argument.py
@@ -1,4 +1,5 @@
-from ert.validation import RangeStringArgument
+from ert.validation import RangeStringArgument, RangeSubsetStringArgument
+from ert.validation.active_range import ActiveRange

 def test_proper_name_argument():
@@ -23,3 +24,15 @@ def test_proper_name_argument():

     assert argument.validate("1-5, 9")
     assert not argument.validate("10")
+
+
+def test_range_subset():
+    source_range = ActiveRange(rangestring="0-3,5,7-9", length=10)
+    argument_range = RangeSubsetStringArgument(source_active_range=source_range)
+
+    assert argument_range.validate("1")
+    assert argument_range.validate("0-3")
+    assert argument_range.validate("0-3,5,7-9")
+    assert not argument_range.validate("10")
+    assert not argument_range.validate("1-10")
+    assert not argument_range.validate("0-4")
diff --git a/tests/ert/unit_tests/sensitivity_analysis/test_design_matrix.py b/tests/ert/unit_tests/sensitivity_analysis/test_design_matrix.py
index 9e9ab76344d..639a96c66dc 100644
--- a/tests/ert/unit_tests/sensitivity_analysis/test_design_matrix.py
+++ b/tests/ert/unit_tests/sensitivity_analysis/test_design_matrix.py
@@ -238,9 +238,25 @@ def test_reading_design_matrix_validate_reals(tmp_path, real_column, error_msg):
         ),
         pytest.param(
             ["a", "b  ", ""],
-            r"Column headers not present in column \[2\]",
+            r"Empty parameter name found in column 2",
             id="missing entries",
         ),
+        pytest.param(
+            ["a", "b", "parameter name with spaces"],
+            "Multiple words in parameter name found in column 2.",
+            id="multiple words in parameter name",
+        ),
+        pytest.param(
+            ["a", "b", " "],
+            "Empty parameter name found in column 2",
+            id="dataframe loads parameter name as whitespace",
+        ),
+        pytest.param(["a", "b", "3"], "Numeric parameter name found in column 2"),
+        pytest.param(
+            ["a", "b c d e", 33],
+            r"Multiple words in parameter name found in column 1 \(b c d e\)\.\nNumeric parameter name found in column 2",
+            id="multiple errors",
+        ),
     ],
 )
 def test_reading_design_matrix_validate_headers(tmp_path, column_names, error_msg):

Validate that DESIGN_MATRIX is correct upon initialization
The validation should include checking whether the design sheet part handles `NUM_REALIZATIONS` correctly, which means whether the all the design values combined with default sheet will handle the total number of realizations.

- `REAL` column (if provided) should contain iens; which yield active realizations and thus filling the active realizations edit box.
- in default sheet if the parameter is already present in the design sheet the default sheet entry is ignored otherwise it is appended as a new column with a singular default value.
- make sure that the parameter names are unique

~~Blocked by: https://github.com/equinor/ert/issues/8902~~

Currently design2params will fail if you run more realizations than entries in design matrix. Maybe we should do the same, just ignore `NUM_REALIZATIONS` if you choose design matrix and use number of realizations specified in the design matrix.
For instance if you only specify realization 1,4,7 in the design matrix, than it is probably expected that only realizations 1,4,7 are actually run?
> Currently design2params will fail if you run more realizations than entries in design matrix. Maybe we should do the same, just ignore `NUM_REALIZATIONS` if you choose design matrix and use number of realizations specified in the design matrix. For instance if you only specify realization 1,4,7 in the design matrix, than it is probably expected that only realizations 1,4,7 are actually run?

This makes sense for `ensemble_experiment`, but not sure if we are to use `DESIGN_MATRIX` in the update step. Let's focus only for the ensemble experiment for now. If we would choose a subset (active realizations) from the list of realizations in the design matrix, why would this not work?
Some of the points of this issue has been covered but are still a lot of validation issues, some of them are:
- Disallow spaces in parameter names
- Decide if we will allow parameter names to be numbers or not
- Handle empty input properly or misconfigured input, might rely on pandas for this. But maybe we should check what kind of error messages we receive and decide if we want to show these or display our own errors.
- Find out what errors are dependent on each others and what are not, so that we can validate as much as possible before raising error?
The semeio forward models accept whitespace in parameter names, so I think we should do the same (and update gen_kw to do so too). It is a very small change, changing `file_line.split()` ->`shlex.split(file_line)`

**EDIT**: after discussing with @oyvindeide, we concluded that we will not be changing this for gen_kw, and we will not support it in design matrix either. We are aware that this makes design_matrix differ from design2params, and if it is crucial for some users, we will add it as a feature down the line.


diff --git a/src/ert/gui/simulation/experiment_panel.py b/src/ert/gui/simulation/experiment_panel.py
index 7be4a058423..3de30c78a4d 100644
--- a/src/ert/gui/simulation/experiment_panel.py
+++ b/src/ert/gui/simulation/experiment_panel.py
@@ -192,7 +192,7 @@ def addExperimentConfigPanel(
         experiment_type = panel.get_experiment_type()
         self._experiment_widgets[experiment_type] = panel
         self._experiment_type_combo.addDescriptionItem(
-            experiment_type.name(),
+            experiment_type.display_name(),
             experiment_type.description(),
             experiment_type.group(),
         )
@@ -221,9 +221,11 @@ def getActions() -> list[QAction]:
         return []

     def get_current_experiment_type(self) -> Any:
-        experiment_type_name = self._experiment_type_combo.currentText()
+        experiment_type_display_name = self._experiment_type_combo.currentText()
         return next(
-            w for w in self._experiment_widgets if w.name() == experiment_type_name
+            w
+            for w in self._experiment_widgets
+            if w.display_name() == experiment_type_display_name
         )

     def get_experiment_arguments(self) -> Any:
diff --git a/src/ert/run_models/base_run_model.py b/src/ert/run_models/base_run_model.py
index d8c795e10ac..040c18d978b 100644
--- a/src/ert/run_models/base_run_model.py
+++ b/src/ert/run_models/base_run_model.py
@@ -220,6 +220,10 @@ def log_at_startup(self) -> None:
     @abstractmethod
     def name(cls) -> str: ...

+    @classmethod
+    def display_name(cls) -> str:
+        return cls.name()
+
     @classmethod
     @abstractmethod
     def description(cls) -> str: ...
diff --git a/src/ert/run_models/iterated_ensemble_smoother.py b/src/ert/run_models/iterated_ensemble_smoother.py
index e89c7935533..497cc0f1165 100644
--- a/src/ert/run_models/iterated_ensemble_smoother.py
+++ b/src/ert/run_models/iterated_ensemble_smoother.py
@@ -244,4 +244,4 @@ def name(cls) -> str:

     @classmethod
     def description(cls) -> str:
-        return "Sample parameters → [evaluate → update] several iterations"
+        return "Sample parameters → [evaluate → update] several iterations.\nDeprecated algorithm. Prefer ES-MDA."
diff --git a/src/ert/run_models/multiple_data_assimilation.py b/src/ert/run_models/multiple_data_assimilation.py
index 9d7a43ad802..7f6fe4a65b4 100644
--- a/src/ert/run_models/multiple_data_assimilation.py
+++ b/src/ert/run_models/multiple_data_assimilation.py
@@ -208,9 +208,13 @@ def parse_weights(weights: str) -> list[float]:
     def name(cls) -> str:
         return "Multiple data assimilation"

+    @classmethod
+    def display_name(cls) -> str:
+        return cls.name() + " - Recommended algorithm"
+
     @classmethod
     def description(cls) -> str:
-        return "[Sample|restart] → [evaluate → update] for each weight"
+        return "[Sample|restart] → [evaluate → update] for each weight."

     @classmethod
     def group(cls) -> str | None:

diff --git a/tests/ert/ui_tests/gui/conftest.py b/tests/ert/ui_tests/gui/conftest.py
index bfdddf77a97..2100ced7685 100644
--- a/tests/ert/ui_tests/gui/conftest.py
+++ b/tests/ert/ui_tests/gui/conftest.py
@@ -239,7 +239,7 @@ def func(experiment_mode, gui, click_done=True):
         assert isinstance(experiment_panel, ExperimentPanel)
         simulation_mode_combo = experiment_panel.findChild(QComboBox)
         assert isinstance(simulation_mode_combo, QComboBox)
-        simulation_mode_combo.setCurrentText(experiment_mode.name())
+        simulation_mode_combo.setCurrentText(experiment_mode.display_name())
         simulation_settings = experiment_panel._experiment_widgets[
             experiment_panel.get_current_experiment_type()
         ]
@@ -255,7 +255,7 @@ def handle_dialog():
                 lambda: handle_run_path_dialog(gui, qtbot, delete_run_path=False),
             )

-        if not experiment_mode.name() in {
+        if experiment_mode.name() not in {
             "Ensemble experiment",
             "Evaluate ensemble",
         }:
diff --git a/tests/ert/ui_tests/gui/test_csv_export.py b/tests/ert/ui_tests/gui/test_csv_export.py
index 9452c236b86..263b0278cd8 100644
--- a/tests/ert/ui_tests/gui/test_csv_export.py
+++ b/tests/ert/ui_tests/gui/test_csv_export.py
@@ -89,7 +89,7 @@ def test_csv_export(esmda_has_run, qtbot, ensemble_select):
 def run_experiment_via_gui(gui, qtbot):
     experiment_panel = get_child(gui, ExperimentPanel)
     simulation_mode_combo = get_child(experiment_panel, QComboBox)
-    simulation_mode_combo.setCurrentText(EnsembleExperiment.name())
+    simulation_mode_combo.setCurrentText(EnsembleExperiment.display_name())
     ensemble_experiment_panel = get_child(experiment_panel, EnsembleExperimentPanel)
     ensemble_experiment_panel._ensemble_name_field.setText("iter-0")

diff --git a/tests/ert/ui_tests/gui/test_main_window.py b/tests/ert/ui_tests/gui/test_main_window.py
index f31f6ff0928..7d51123c6f0 100644
--- a/tests/ert/ui_tests/gui/test_main_window.py
+++ b/tests/ert/ui_tests/gui/test_main_window.py
@@ -242,7 +242,7 @@ def test_that_es_mda_is_disabled_when_weights_are_invalid(qtbot):
         combo_box = get_child(gui, QComboBox, name="experiment_type")
         combo_box.setCurrentIndex(3)

-        assert combo_box.currentText() == "Multiple data assimilation"
+        assert combo_box.currentText() == MultipleDataAssimilation.display_name()

         es_mda_panel = get_child(gui, QWidget, name="ES_MDA_panel")
         assert es_mda_panel
@@ -701,7 +701,7 @@ def test_that_es_mda_restart_run_box_is_disabled_when_there_are_no_cases(qtbot):
         assert combo_box.count() == 7
         combo_box.setCurrentIndex(3)

-        assert combo_box.currentText() == "Multiple data assimilation"
+        assert combo_box.currentText() == MultipleDataAssimilation.display_name()

         es_mda_panel = get_child(gui, QWidget, name="ES_MDA_panel")
         assert es_mda_panel
@@ -751,10 +751,13 @@ def test_validation_of_experiment_names_in_run_models(
     run_experiment = get_child(experiment_panel, QWidget, name="run_experiment")

     experiment_types_to_test = (
-        (EnsembleExperiment.name(), "Ensemble_experiment_panel"),
-        (EnsembleSmoother.name(), "ensemble_smoother_panel"),
-        (MultipleDataAssimilation.name(), "ES_MDA_panel"),
-        (IteratedEnsembleSmoother.name(), "iterated_ensemble_smoother_panel"),
+        (EnsembleExperiment.display_name(), "Ensemble_experiment_panel"),
+        (EnsembleSmoother.display_name(), "ensemble_smoother_panel"),
+        (
+            MultipleDataAssimilation.display_name(),
+            "ES_MDA_panel",
+        ),
+        (IteratedEnsembleSmoother.display_name(), "iterated_ensemble_smoother_panel"),
     )
     for exp_type, panel_name in experiment_types_to_test:
         experiment_types.setCurrentText(exp_type)
diff --git a/tests/ert/ui_tests/gui/test_missing_parameters_to_update.py b/tests/ert/ui_tests/gui/test_missing_parameters_to_update.py
index 02fc047604a..e8d744924a1 100644
--- a/tests/ert/ui_tests/gui/test_missing_parameters_to_update.py
+++ b/tests/ert/ui_tests/gui/test_missing_parameters_to_update.py
@@ -5,6 +5,12 @@
 from qtpy.QtWidgets import QComboBox

 from ert.gui.simulation.experiment_panel import ExperimentPanel
+from ert.run_models import (
+    EnsembleExperiment,
+    EnsembleSmoother,
+    IteratedEnsembleSmoother,
+    MultipleDataAssimilation,
+)
 from tests.ert.ui_tests.gui.conftest import get_child, open_gui_with_config

@@ -20,19 +26,19 @@ def test_no_updateable_parameters(qtbot):
     for gui in open_gui_with_config("poly.ert"):
         experiment_panel = get_child(gui, ExperimentPanel)
         simulation_mode_combo = get_child(experiment_panel, QComboBox)
-        idx = simulation_mode_combo.findText("Ensemble smoother")
+        idx = simulation_mode_combo.findText(EnsembleSmoother.display_name())
         assert not (
             simulation_mode_combo.model().item(idx).flags() & Qt.ItemFlag.ItemIsEnabled
         )
-        idx = simulation_mode_combo.findText("Multiple data assimilation")
+        idx = simulation_mode_combo.findText(MultipleDataAssimilation.display_name())
         assert not (
             simulation_mode_combo.model().item(idx).flags() & Qt.ItemFlag.ItemIsEnabled
         )
-        idx = simulation_mode_combo.findText("Iterated ensemble smoother")
+        idx = simulation_mode_combo.findText(IteratedEnsembleSmoother.display_name())
         assert not (
             simulation_mode_combo.model().item(idx).flags() & Qt.ItemFlag.ItemIsEnabled
         )
-        idx = simulation_mode_combo.findText("Ensemble experiment")
+        idx = simulation_mode_combo.findText(EnsembleExperiment.display_name())
         assert (
             simulation_mode_combo.model().item(idx).flags() & Qt.ItemFlag.ItemIsEnabled
         )
diff --git a/tests/ert/ui_tests/gui/test_restart_esmda.py b/tests/ert/ui_tests/gui/test_restart_esmda.py
index 6c7c8e312ed..e99f39e4350 100644
--- a/tests/ert/ui_tests/gui/test_restart_esmda.py
+++ b/tests/ert/ui_tests/gui/test_restart_esmda.py
@@ -18,7 +18,7 @@ def test_restart_esmda(ensemble_experiment_has_run_no_failure, qtbot):

     experiment_panel = get_child(gui, ExperimentPanel)
     simulation_mode_combo = get_child(experiment_panel, QComboBox)
-    simulation_mode_combo.setCurrentText(MultipleDataAssimilation.name())
+    simulation_mode_combo.setCurrentText(MultipleDataAssimilation.display_name())

     es_mda_panel = gui.findChild(QWidget, name="ES_MDA_panel")
     assert es_mda_panel
@@ -49,7 +49,7 @@ def test_active_realizations_esmda(opened_main_window_poly, qtbot):

     experiment_panel = get_child(gui, ExperimentPanel)
     simulation_mode_combo = get_child(experiment_panel, QComboBox)
-    simulation_mode_combo.setCurrentText(SingleTestRun.name())
+    simulation_mode_combo.setCurrentText(SingleTestRun.display_name())

     single_test_run_panel = gui.findChild(QWidget, name="Single_test_run_panel")
     assert single_test_run_panel
@@ -63,7 +63,7 @@ def test_active_realizations_esmda(opened_main_window_poly, qtbot):
         == "Total progress 100% — Experiment completed."
     )

-    simulation_mode_combo.setCurrentText(MultipleDataAssimilation.name())
+    simulation_mode_combo.setCurrentText(MultipleDataAssimilation.display_name())
     es_mda_panel = gui.findChild(QWidget, name="ES_MDA_panel")
     assert es_mda_panel
     active_reals = gui.findChild(StringBox, "active_realizations_box")
@@ -88,7 +88,7 @@ def test_custom_weights_stored_and_retrieved_from_metadata_esmda(

     experiment_panel = get_child(gui, ExperimentPanel)
     simulation_mode_combo = get_child(experiment_panel, QComboBox)
-    simulation_mode_combo.setCurrentText(MultipleDataAssimilation.name())
+    simulation_mode_combo.setCurrentText(MultipleDataAssimilation.display_name())

     es_mda_panel = gui.findChild(QWidget, name="ES_MDA_panel")
     assert es_mda_panel

Mark ES-MDA as recommended
The ES-MDA algorithm is the recommended algorithm for multi iteration use. We should mark it as such. This should not affect the logging and usage statistics of methods.