Repository: gammapy/gammapy

Issue:
`FoVBackgroundModel` can't be loaded from dict when it includes a spatial model
# Problem

It seems like all `<SpatialModel>.from_dict()` expect a dictionary with a key 'spatial' (Same for spectral models)

https://github.com/gammapy/gammapy/blob/dc9a88aea9e8f721c3c1f57b39bd98719a061118/gammapy/modeling/models/spatial.py#L286

The `FoVBackgroundModel` handles loading from dict correctly for spectral models
https://github.com/gammapy/gammapy/blob/dc9a88aea9e8f721c3c1f57b39bd98719a061118/gammapy/modeling/models/cube.py#L798

but not for spatial models

https://github.com/gammapy/gammapy/blob/dc9a88aea9e8f721c3c1f57b39bd98719a061118/gammapy/modeling/models/cube.py#L802-L807

# Solution

Should an easy fix
```python
     spatial_model = model_class.from_dict({"spatial": spatial_data}) 
```

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/gammapy/gammapy/pull/5082?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy) Report
Attention: `1 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`1474bb9`)](https://app.codecov.io/gh/gammapy/gammapy/commit/1474bb9ef65db64bf6a3d5098697ac787764777c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy) 75.04% compared to head [(`c2c1de7`)](https://app.codecov.io/gh/gammapy/gammapy/pull/5082?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy) 75.05%.
> Report is 28 commits behind head on main.

| [Files](https://app.codecov.io/gh/gammapy/gammapy/pull/5082?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy) | Patch % | Lines |
|---|---|---|
| [gammapy/modeling/parameter.py](https://app.codecov.io/gh/gammapy/gammapy/pull/5082?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy#diff-Z2FtbWFweS9tb2RlbGluZy9wYXJhbWV0ZXIucHk=) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/gammapy/gammapy/pull/5082?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #5082      +/-   ##
==========================================
+ Coverage   75.04%   75.05%   +0.01%     
==========================================
  Files         233      233              
  Lines       34622    34669      +47     
==========================================
+ Hits        25982    26021      +39     
- Misses       8640     8648       +8     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/gammapy/gammapy/pull/5082?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gammapy).


Patch:
diff --git a/gammapy/data/metadata.py b/gammapy/data/metadata.py
index feaa593c7..b1442ea7d 100644
--- a/gammapy/data/metadata.py
+++ b/gammapy/data/metadata.py
@@ -80,15 +80,6 @@ class ObservationMetaData(MetaData):
 
     @classmethod
     def from_header(cls, header, format="gadf"):
-        """Create and fill the observation metadata from the event list metadata.
-
-        Parameters
-        ----------
-        header : dict
-            Input FITS header.
-        format : str
-            The header data format. Default is gadf.
-        """
         meta = super().from_header(header, format)
 
         meta.creation = CreatorMetaData()
diff --git a/gammapy/datasets/core.py b/gammapy/datasets/core.py
index 1ba2d365f..8fe0d274c 100644
--- a/gammapy/datasets/core.py
+++ b/gammapy/datasets/core.py
@@ -379,7 +379,7 @@ class Datasets(collections.abc.MutableSequence):
         return copy.deepcopy(self)
 
     @classmethod
-    def read(cls, filename, filename_models=None, lazy=True, cache=True, checksum=True):
+    def read(cls, filename, filename_models=None, lazy=True, cache=True):
         """De-serialize datasets from YAML and FITS files.
 
         Parameters
@@ -392,8 +392,6 @@ class Datasets(collections.abc.MutableSequence):
             Whether to lazy load data into memory. Default is True.
         cache : bool
             Whether to cache the data after loading. Default is True.
-        checksum : bool
-            Whether to perform checksum verification. Default is False.
 
         Returns
         -------
@@ -403,7 +401,7 @@ class Datasets(collections.abc.MutableSequence):
         from . import DATASET_REGISTRY
 
         filename = make_path(filename)
-        data_list = read_yaml(filename, checksum=checksum)
+        data_list = read_yaml(filename)
 
         datasets = []
         for data in data_list["datasets"]:
@@ -419,7 +417,7 @@ class Datasets(collections.abc.MutableSequence):
         datasets = cls(datasets)
 
         if filename_models:
-            datasets.models = Models.read(filename_models, checksum=checksum)
+            datasets.models = Models.read(filename_models)
 
         return datasets
 
@@ -462,14 +460,13 @@ class Datasets(collections.abc.MutableSequence):
         if path.exists() and not overwrite:
             raise IOError(f"File exists already: {path}")
 
-        write_yaml(data, path, sort_keys=False, checksum=checksum)
+        write_yaml(data, path, sort_keys=False)
 
         if filename_models:
             self.models.write(
                 filename_models,
                 overwrite=overwrite,
                 write_covariance=write_covariance,
-                checksum=checksum,
             )
 
     def stack_reduce(self, name=None, nan_to_num=True):
diff --git a/gammapy/estimators/__init__.py b/gammapy/estimators/__init__.py
index a3f06994b..bdb554778 100644
--- a/gammapy/estimators/__init__.py
+++ b/gammapy/estimators/__init__.py
@@ -5,7 +5,6 @@ from .core import Estimator
 from .energydependentmorphology import EnergyDependentMorphologyEstimator
 from .map import ASmoothMapEstimator, ExcessMapEstimator, FluxMaps, TSMapEstimator
 from .parameter import ParameterEstimator
-from .metadata import FluxMetaData
 from .points import (
     FluxPoints,
     FluxPointsEstimator,
@@ -31,7 +30,6 @@ __all__ = [
     "SensitivityEstimator",
     "TSMapEstimator",
     "EnergyDependentMorphologyEstimator",
-    "FluxMetaData",
 ]
 
 
diff --git a/gammapy/estimators/metadata.py b/gammapy/estimators/metadata.py
deleted file mode 100644
index 428300db3..000000000
--- a/gammapy/estimators/metadata.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-import logging
-from enum import Enum
-from typing import ClassVar, Literal, Optional
-from gammapy.utils.metadata import (
-    METADATA_FITS_KEYS,
-    CreatorMetaData,
-    MetaData,
-    TargetMetaData,
-)
-
-__all__ = ["FluxMetaData"]
-
-
-FLUX_METADATA_FITS_KEYS = {
-    "flux": {
-        "sed_type": "SED_TYPE",
-        "sed_type_init": "SEDTYPEI",
-        "n_sigma": "N_SIGMA",
-        "n_sigma_ul": "NSIGMAUL",
-        "sqrt_ts_threshold_ul": "STSTHUL",
-        "n_sigma_sensitivity": "NSIGMSEN",
-    },
-}
-
-METADATA_FITS_KEYS.update(FLUX_METADATA_FITS_KEYS)
-
-log = logging.getLogger(__name__)
-
-
-class SEDTYPEEnum(str, Enum):
-    dnde = "dnde"
-    flux = "flux"
-    eflux = "eflux"
-    e2dnde = "e2dnde"
-    likelihood = "likelihood"
-
-
-class FluxMetaData(MetaData):
-    """Metadata containing information about the FluxPoints and FluxMaps.
-
-    Attributes
-    ----------
-    sed_type : {"dnde", "flux", "eflux", "e2dnde", "likelihood"}, optional
-        SED type.
-    sed_type_init : {"dnde", "flux", "eflux", "e2dnde", "likelihood"}, optional
-        SED type of the initial data.
-    n_sigma : float, optional
-        Significance threshold above which upper limits should be used.
-    n_sigma_ul : float, optional
-        Significance value used for the upper limit computation.
-    sqrt_ts_threshold_ul : float, optional
-        Threshold on the square root of the likelihood value above which upper limits should be used.
-    n_sigma_sensitivity : float, optional
-        Sigma number for which the flux sensitivity is computed
-    target : `~gammapy.utils.TargetMetaData`, optional
-        General metadata information about the target.
-    creation : `~gammapy.utils.CreatorMetaData`, optional
-        The creation metadata.
-    optional : dict, optional
-        additional optional metadata.
-
-    Note : these quantities are serialized in FITS header with the keywords stored in the dictionary FLUX_METADATA_FITS_KEYS
-    """
-
-    _tag: ClassVar[Literal["flux"]] = "flux"
-    sed_type: Optional[SEDTYPEEnum] = None
-    sed_type_init: Optional[SEDTYPEEnum] = None
-    n_sigma: Optional[float] = None
-    n_sigma_ul: Optional[float] = None
-    sqrt_ts_threshold_ul: Optional[float] = None
-    n_sigma_sensitivity: Optional[float] = None
-    target: Optional[TargetMetaData] = None
-    # TODO: add obs_ids: Optional[List[int]] and instrument: Optional[str], or a List[ObsInfoMetaData]
-    # TODO : add dataset_names: Optional[List[str]]
-    creation: Optional[CreatorMetaData] = CreatorMetaData()
-    optional: Optional[dict] = None
diff --git a/gammapy/modeling/models/core.py b/gammapy/modeling/models/core.py
index cb93d675f..67e695e08 100644
--- a/gammapy/modeling/models/core.py
+++ b/gammapy/modeling/models/core.py
@@ -3,7 +3,6 @@ import collections.abc
 import copy
 import html
 import logging
-import warnings
 from os.path import split
 import numpy as np
 import astropy.units as u
@@ -13,7 +12,6 @@ import matplotlib.pyplot as plt
 import yaml
 from gammapy.maps import Map, RegionGeom
 from gammapy.modeling import Covariance, Parameter, Parameters
-from gammapy.utils.check import add_checksum, verify_checksum
 from gammapy.utils.metadata import CreatorMetaData
 from gammapy.utils.scripts import make_name, make_path
 
@@ -412,32 +410,16 @@ class DatasetModels(collections.abc.Sequence):
         return [m.name for m in self._models]
 
     @classmethod
-    def read(cls, filename, checksum=False):
-        """Read from YAML file.
-
-        Parameters
-        ----------
-        filename : str
-            input filename
-        checksum : bool
-            Whether to perform checksum verification. Default is False.
-        """
+    def read(cls, filename):
+        """Read from YAML file."""
         yaml_str = make_path(filename).read_text()
         path, filename = split(filename)
-        return cls.from_yaml(yaml_str, path=path, checksum=checksum)
+        return cls.from_yaml(yaml_str, path=path)
 
     @classmethod
-    def from_yaml(cls, yaml_str, path="", checksum=False):
+    def from_yaml(cls, yaml_str, path=""):
         """Create from YAML string."""
         data = yaml.safe_load(yaml_str)
-        checksum_str = data.pop("checksum", None)
-        if checksum:
-            yaml_str = yaml.dump(
-                data, sort_keys=False, indent=4, width=80, default_flow_style=False
-            )
-            if not verify_checksum(yaml_str, checksum_str):
-                warnings.warn("Checksum verification failed.", UserWarning)
-
         # TODO : for now metadata are not kept. Add proper metadata creation.
         data.pop("metadata", None)
         return cls.from_dict(data, path=path)
@@ -486,7 +468,6 @@ class DatasetModels(collections.abc.Sequence):
         full_output=False,
         overwrite_templates=False,
         write_covariance=True,
-        checksum=False,
     ):
         """Write to YAML file.
 
@@ -502,9 +483,6 @@ class DatasetModels(collections.abc.Sequence):
             Overwrite templates FITS files. Default is False.
         write_covariance : bool, optional
             Whether to save the covariance. Default is True.
-        checksum : bool
-            When True adds a CHECKSUM entry to the file.
-            Default is False.
         """
         base_path, _ = split(path)
         path = make_path(path)
@@ -525,10 +503,7 @@ class DatasetModels(collections.abc.Sequence):
             self.write_covariance(base_path / filecovar, **kwargs)
             self._covar_file = filecovar
 
-        yaml_str = self.to_yaml(full_output, overwrite_templates)
-        if checksum:
-            yaml_str = add_checksum(yaml_str)
-        path.write_text(yaml_str)
+        path.write_text(self.to_yaml(full_output, overwrite_templates))
 
     def to_yaml(self, full_output=False, overwrite_templates=False):
         """Convert to YAML string."""
diff --git a/gammapy/modeling/models/cube.py b/gammapy/modeling/models/cube.py
index a31e707f0..9ffeeb340 100644
--- a/gammapy/modeling/models/cube.py
+++ b/gammapy/modeling/models/cube.py
@@ -802,7 +802,7 @@ class FoVBackgroundModel(ModelBase):
         spatial_data = data.get("spatial")
         if spatial_data is not None:
             model_class = SPATIAL_MODEL_REGISTRY.get_cls(spatial_data["type"])
-            spatial_model = model_class.from_dict(spatial_data)
+            spatial_model = model_class.from_dict({"spatial": spatial_data})
         else:
             spatial_model = None
 
@@ -1023,7 +1023,7 @@ class TemplateNPredModel(ModelBase):
         spatial_data = data.get("spatial")
         if spatial_data is not None:
             model_class = SPATIAL_MODEL_REGISTRY.get_cls(spatial_data["type"])
-            spatial_model = model_class.from_dict(spatial_data)
+            spatial_model = model_class.from_dict({"spatial": spatial_data})
         else:
             spatial_model = None
 
diff --git a/gammapy/utils/check.py b/gammapy/utils/check.py
index 3a0c5b00d..0a56ba97e 100644
--- a/gammapy/utils/check.py
+++ b/gammapy/utils/check.py
@@ -1,7 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import logging
 import os
-import yaml
 from gammapy.scripts.download import RELEASE, cli_download_datasets
 from gammapy.scripts.info import cli_info
 
@@ -24,31 +23,3 @@ def check_tutorials_setup(download_datasets_path="./gammapy-data"):
         os.environ["GAMMAPY_DATA"] = download_datasets_path
 
     cli_info.callback(system=True, version=True, dependencies=True, envvar=True)
-
-
-def yaml_checksum(yaml_content):
-    """Compute a MD5 checksum for a given yaml string input."""
-    import hashlib
-
-    # Calculate the MD5 checksum
-    checksum = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
-
-    return checksum
-
-
-def verify_checksum(yaml_content, checksum):
-    """Compare MD5 checksum for yaml_content with input checksum."""
-    return yaml_checksum(yaml_content) == checksum
-
-
-def add_checksum(yaml_str, sort_keys=False, indent=4, width=50):
-    """Append a checksum at the end of the yaml string."""
-    checksum = {"checksum": yaml_checksum(yaml_str)}
-    checksum = yaml.dump(
-        checksum,
-        sort_keys=True,
-        indent=4,  # indent,
-        width=80,  # width,
-        default_flow_style=False,
-    )
-    return yaml_str + checksum
diff --git a/gammapy/utils/metadata.py b/gammapy/utils/metadata.py
index ca6443bb0..2cf5251f8 100644
--- a/gammapy/utils/metadata.py
+++ b/gammapy/utils/metadata.py
@@ -10,13 +10,7 @@ from gammapy.utils.fits import skycoord_from_dict
 from gammapy.version import version
 from .types import AltAzSkyCoordType, ICRSSkyCoordType, SkyCoordType, TimeType
 
-__all__ = [
-    "MetaData",
-    "CreatorMetaData",
-    "ObsInfoMetaData",
-    "PointingInfoMetaData",
-    "TargetMetaData",
-]
+__all__ = ["MetaData", "CreatorMetaData"]
 
 METADATA_FITS_KEYS = {
     "creator": {
@@ -125,7 +119,6 @@ class MetaData(BaseModel):
         format : {'gadf'}
             Header format. Default is 'gadf'.
         """
-        # TODO: implement storage of optional metadata
         if format != "gadf":
             raise ValueError(f"Metadata from header: format {format} is not supported.")
 
@@ -174,9 +167,8 @@ class CreatorMetaData(MetaData):
     ----------
     creator : str
         The software used to create the data contained in the parent object.
-        Default is the used Gammapy version.
     date : `~astropy.time.Time` or str
-        The creation date. Default is the current date.
+        The creation date.
     origin : str
         The organization at the origin of the data.
     """
diff --git a/gammapy/utils/scripts.py b/gammapy/utils/scripts.py
index 77139a8ea..ea17f60ee 100644
--- a/gammapy/utils/scripts.py
+++ b/gammapy/utils/scripts.py
@@ -2,12 +2,10 @@
 """Utilities to create scripts and command-line tools."""
 import codecs
 import os.path
-import warnings
 from base64 import urlsafe_b64encode
 from pathlib import Path
 from uuid import uuid4
 import yaml
-from gammapy.utils.check import add_checksum, verify_checksum
 
 __all__ = [
     "get_images_paths",
@@ -34,7 +32,7 @@ def get_images_paths(folder=PATH_DOCS):
             yield i.resolve()
 
 
-def read_yaml(filename, logger=None, checksum=False):
+def read_yaml(filename, logger=None):
     """Read YAML file.
 
     Parameters
@@ -43,8 +41,6 @@ def read_yaml(filename, logger=None, checksum=False):
         Filename.
     logger : `~logging.Logger`
         Logger.
-    checksum : bool
-        Whether to perform checksum verification. Default is False.
 
     Returns
     -------
@@ -56,18 +52,10 @@ def read_yaml(filename, logger=None, checksum=False):
         logger.info(f"Reading {path}")
 
     text = path.read_text()
+    return yaml.safe_load(text)
 
-    data = yaml.safe_load(text)
-    checksum_str = data.pop("checksum", None)
-    if checksum:
-        index = text.find("checksum")
-        if not verify_checksum(text[:index], checksum_str):
-            warnings.warn(f"Checksum verification failed for {filename}.", UserWarning)
 
-    return data
-
-
-def write_yaml(dictionary, filename, logger=None, sort_keys=True, checksum=False):
+def write_yaml(dictionary, filename, logger=None, sort_keys=True):
     """Write YAML file.
 
     Parameters
@@ -80,14 +68,9 @@ def write_yaml(dictionary, filename, logger=None, sort_keys=True, checksum=False
         Logger. Default is None.
     sort_keys : bool, optional
         Whether to sort keys. Default is True.
-    checksum : bool, optional
-        Whether to add checksum keyword. Default is False.
     """
     text = yaml.safe_dump(dictionary, default_flow_style=False, sort_keys=sort_keys)
 
-    if checksum:
-        text = add_checksum(text, sort_keys=sort_keys)
-
     path = make_path(filename)
     path.parent.mkdir(exist_ok=True)
     if logger is not None:


Test patch:
diff --git a/gammapy/data/tests/test_metadata.py b/gammapy/data/tests/test_metadata.py
index a0c521143..5cba8a16b 100644
--- a/gammapy/data/tests/test_metadata.py
+++ b/gammapy/data/tests/test_metadata.py
@@ -79,7 +79,6 @@ def test_observation_metadata_from_header(hess_eventlist_header):
 
 @requires_data()
 def test_observation_metadata_bad(hess_eventlist_header):
-    # TODO: adapt with proper format handling
     with pytest.raises(ValueError):
         ObservationMetaData.from_header(hess_eventlist_header, format="bad")
 
diff --git a/gammapy/datasets/tests/test_io.py b/gammapy/datasets/tests/test_io.py
index dba24f114..763621f5c 100644
--- a/gammapy/datasets/tests/test_io.py
+++ b/gammapy/datasets/tests/test_io.py
@@ -1,12 +1,9 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import warnings
-import pytest
 from numpy.testing import assert_allclose
 import astropy.units as u
 from regions import CircleSkyRegion
 from gammapy.datasets import Datasets, SpectrumDataset, SpectrumDatasetOnOff
 from gammapy.modeling.models import DatasetModels
-from gammapy.utils.scripts import make_path
 from gammapy.utils.testing import requires_data
 
 
@@ -124,47 +121,3 @@ def test_ogip_writer(tmp_path):
     new_datasets = datasets.read(tmp_path / "written_datasets.yaml")
 
     assert new_datasets[0].counts_off is None
-
-
-@requires_data()
-def test_datasets_write_checksum(tmp_path):
-    filedata = "$GAMMAPY_DATA/tests/models/gc_example_datasets.yaml"
-    filemodel = "$GAMMAPY_DATA/tests/models/gc_example_models.yaml"
-
-    datasets = Datasets.read(
-        filename=filedata,
-        filename_models=filemodel,
-    )
-
-    filename = tmp_path / "written_datasets.yaml"
-    filename_models = tmp_path / "written_models.yaml"
-    datasets.write(
-        filename=filename,
-        filename_models=filename_models,
-        checksum=True,
-    )
-
-    assert "checksum" in filename.read_text()
-    assert "checksum" in filename_models.read_text()
-
-    with warnings.catch_warnings():
-        warnings.simplefilter("error")
-        Datasets.read(filename=filename, filename_models=filename_models, checksum=True)
-
-    # Remove checksum from datasets.yaml file
-    yaml_content = filename.read_text()
-    index = yaml_content.find("checksum")
-    bad = make_path(tmp_path) / "bad_checksum.yaml"
-    bad.write_text(yaml_content[:index])
-
-    with pytest.warns(UserWarning):
-        Datasets.read(filename=bad, filename_models=filename_models, checksum=True)
-
-    # Modify models yaml file
-    yaml_content = filename_models.read_text()
-    yaml_content = yaml_content.replace("name: gc", "name: bad")
-    bad = make_path(tmp_path) / "bad_models.yaml"
-    bad.write_text(yaml_content)
-
-    with pytest.warns(UserWarning):
-        Datasets.read(filename=filename, filename_models=bad, checksum=True)
diff --git a/gammapy/datasets/tests/test_map.py b/gammapy/datasets/tests/test_map.py
index 0780ace83..03fa702fa 100644
--- a/gammapy/datasets/tests/test_map.py
+++ b/gammapy/datasets/tests/test_map.py
@@ -1,6 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import json
-import warnings
 import pytest
 import numpy as np
 from numpy.testing import assert_allclose, assert_equal
@@ -1353,10 +1352,6 @@ def test_map_datasets_on_off_checksum(images, tmp_path):
         assert "CHECKSUM" in hdu.header
         assert "DATASUM" in hdu.header
 
-    with warnings.catch_warnings():
-        warnings.simplefilter("error")
-        Datasets.read(tmp_path / "test.yaml", lazy=False)
-
 
 def test_create_onoff(geom):
     # tests empty datasets created
diff --git a/gammapy/estimators/tests/test_metadata.py b/gammapy/estimators/tests/test_metadata.py
deleted file mode 100644
index 90d140053..000000000
--- a/gammapy/estimators/tests/test_metadata.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-import pytest
-from astropy import units as u
-from astropy.coordinates import SkyCoord
-from pydantic import ValidationError
-from gammapy.utils.metadata import CreatorMetaData, TargetMetaData
-from gammapy.utils.testing import requires_data
-from gammapy.version import version
-from ..metadata import FluxMetaData
-
-
-@pytest.fixture()
-def default():
-    default = FluxMetaData(
-        sed_type="likelihood",
-        creation=CreatorMetaData(
-            date="2022-01-01", creator="gammapy test", origin="CTA"
-        ),
-        target=TargetMetaData(
-            name="PKS2155-304",
-            position=SkyCoord(
-                "21 58 52.06 -30 13 32.11", frame="icrs", unit=(u.hourangle, u.deg)
-            ),
-        ),
-        n_sigma=2.0,
-        n_sigma_ul=None,
-    )
-    return default
-
-
-@requires_data()
-def test_creator(default):
-
-    assert default.creation.creator == "gammapy test"
-    assert default.sed_type == "likelihood"
-    assert default.sed_type_init is None
-    assert default.target.name == "PKS2155-304"
-    assert default.target.position is not None
-
-    with pytest.raises(ValidationError):
-        default.target.position = 2.0
-
-    with pytest.raises(ValidationError):
-        default.sed_type = "test"
-
-    default = FluxMetaData()
-    assert default.creation.creator == f"Gammapy {version}"
-
-
-def test_from_header():
-    tdict = {
-        "SED_TYPE": "dnde",
-        "N_SIGMA": "4.3",
-        "OBJECT": "RXJ",
-        "RA_OBJ": "123.5",
-        "DEC_OBJ": "-4.8",
-        "OBS_IDS": ["1", "2", "3", "6"],
-        "DATASETS": "myanalysis",
-        "INSTRU": "HAWC",
-        "CREATED": "2023-10-27 16:05:09.795",
-        "ORIGIN": "Gammapy v2.3",
-        "CREATOR": "mynotebook",
-        "EPHEM": "fermi23.1",
-    }
-
-    meta = FluxMetaData.from_header(tdict)
-    assert meta.n_sigma == 4.3
-    assert meta.creation.origin == "Gammapy v2.3"
-    assert meta.target.position == SkyCoord(123.5 * u.deg, -4.8 * u.deg, frame="icrs")
-    assert meta.optional is None
-    # TODO: add for support: assert meta.optional["EPHEM"] == "fermi23.1"
-
-
-@requires_data()
-def test_to_header(default):
-    hdr = default.to_header()
-    assert hdr["OBJECT"] == "PKS2155-304"
-    assert hdr["ORIGIN"] == "CTA"
-    assert hdr["RA_OBJ"] == 329.7169166666666
-    assert hdr["N_SIGMA"] == 2.0
-    assert hdr["CREATOR"] == "gammapy test"
diff --git a/gammapy/modeling/models/tests/test_io.py b/gammapy/modeling/models/tests/test_io.py
index fbc293616..1eaf28110 100644
--- a/gammapy/modeling/models/tests/test_io.py
+++ b/gammapy/modeling/models/tests/test_io.py
@@ -26,7 +26,7 @@ from gammapy.modeling.models import (
     TemplateNPredModel,
 )
 from gammapy.utils.deprecation import GammapyDeprecationWarning
-from gammapy.utils.scripts import make_path, read_yaml, write_yaml
+from gammapy.utils.scripts import read_yaml, write_yaml
 from gammapy.utils.testing import requires_data, requires_dependency
 
 
@@ -123,31 +123,6 @@ def test_sky_models_io(tmpdir, models):
     # or check serialised dict content
 
 
-@requires_data()
-def test_sky_models_checksum(tmpdir, models):
-    import yaml
-
-    models.write(
-        tmpdir / "tmp.yaml", full_output=True, overwrite_templates=False, checksum=True
-    )
-    file = open(tmpdir / "tmp.yaml", "rb")
-    yaml_content = file.read()
-    file.close()
-
-    assert "checksum: " in str(yaml_content)
-
-    data = yaml.safe_load(yaml_content)
-    data["checksum"] = "bad"
-    yaml_str = yaml.dump(
-        data, sort_keys=False, indent=4, width=80, default_flow_style=False
-    )
-    path = make_path(tmpdir) / "bad_checksum.yaml"
-    path.write_text(yaml_str)
-
-    with pytest.warns(UserWarning):
-        Models.read(tmpdir / "bad_checksum.yaml", checksum=True)
-
-
 @requires_data()
 def test_sky_models_io_auto_write(tmp_path, models):
     models_new = models.copy()
diff --git a/gammapy/modeling/models/tests/test_spatial.py b/gammapy/modeling/models/tests/test_spatial.py
index 15f884996..5b7d662ef 100644
--- a/gammapy/modeling/models/tests/test_spatial.py
+++ b/gammapy/modeling/models/tests/test_spatial.py
@@ -15,8 +15,10 @@ from regions import (
 )
 from gammapy.maps import Map, MapAxis, MapCoord, RegionGeom, WcsGeom, WcsNDMap
 from gammapy.modeling.models import (
+    SPATIAL_MODEL_REGISTRY,
     ConstantSpatialModel,
     DiskSpatialModel,
+    FoVBackgroundModel,
     GaussianSpatialModel,
     GeneralizedGaussianSpatialModel,
     PiecewiseNormSpatialModel,
@@ -480,6 +482,32 @@ def test_sky_diffuse_map_empty(caplog):
         assert np.all(np.isfinite(model.map.data))
 
 
+@pytest.mark.parametrize("model_cls", SPATIAL_MODEL_REGISTRY)
+def test_model_from_dict(tmpdir, model_cls):
+    if model_cls in [TemplateSpatialModel, TemplateNDSpatialModel]:
+        default_map = Map.create(map_type="wcs", width=(1, 1), binsz=0.5, unit="sr-1")
+        filename = str(tmpdir / "template.fits")
+        model = model_cls(default_map, filename=filename)
+        model.write()
+    elif model_cls is PiecewiseNormSpatialModel:
+        geom = WcsGeom.create(skydir=(0, 0), npix=(2, 2), binsz=0.3, frame="galactic")
+        default_coords = MapCoord.create(geom.footprint)
+        default_coords["lon"] *= u.deg
+        default_coords["lat"] *= u.deg
+        model = model_cls(default_coords, frame="galactic")
+    else:
+        model = model_cls()
+
+    data = model.to_dict()
+    model_from_dict = model_cls.from_dict(data)
+    assert model_from_dict.tag == model_from_dict.tag
+
+    bkg_model = FoVBackgroundModel(spatial_model=model, dataset_name="test")
+    bkg_model_dict = bkg_model.to_dict()
+    bkg_model_from_dict = FoVBackgroundModel.from_dict(bkg_model_dict)
+    assert bkg_model_from_dict.spatial_model is not None
+
+
 def test_evaluate_on_fk5_map():
     # Check if spatial model can be evaluated on a map with FK5 frame
     # Regression test for GH-2402
diff --git a/gammapy/utils/tests/test_check.py b/gammapy/utils/tests/test_check.py
deleted file mode 100644
index 0963cbc43..000000000
--- a/gammapy/utils/tests/test_check.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-import yaml
-from gammapy.utils.check import add_checksum, verify_checksum, yaml_checksum
-
-
-def test_yaml_checksum():
-    data = {
-        "a": 50,
-        "b": 3.14e-12,
-    }
-    yaml_str = yaml.dump(
-        data, sort_keys=False, indent=4, width=80, default_flow_style=False
-    )
-    checksum = yaml_checksum(yaml_str)
-
-    assert checksum == "47fd166725c49519c7c31c19f53b53dd"
-    assert verify_checksum(yaml_str, "47fd166725c49519c7c31c19f53b53dd")
-
-    yaml_with_checksum = add_checksum(yaml_str)
-    assert "checksum: 47fd166725c49519c7c31c19f53b53dd" in yaml_with_checksum
diff --git a/gammapy/utils/tests/test_metadata.py b/gammapy/utils/tests/test_metadata.py
index 9d2bb8d56..c9f8a1afc 100644
--- a/gammapy/utils/tests/test_metadata.py
+++ b/gammapy/utils/tests/test_metadata.py
@@ -94,9 +94,6 @@ def test_obs_info():
     obs_info.obs_id = 23523
     assert obs_info.obs_id == 23523
 
-    with pytest.raises(ValidationError):
-        obs_info.obs_id = "ab"
-
     obs_info.instrument = "CTA-North"
     assert obs_info.instrument == "CTA-North"
 
diff --git a/gammapy/utils/tests/test_scripts.py b/gammapy/utils/tests/test_scripts.py
index 3d9851ba5..1fabf31b3 100644
--- a/gammapy/utils/tests/test_scripts.py
+++ b/gammapy/utils/tests/test_scripts.py
@@ -1,14 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import warnings
 import pytest
-import yaml
-from gammapy.utils.scripts import (
-    get_images_paths,
-    make_path,
-    read_yaml,
-    recursive_merge_dicts,
-    write_yaml,
-)
+from gammapy.utils.scripts import get_images_paths, recursive_merge_dicts
 
 
 @pytest.mark.xfail
@@ -26,30 +18,3 @@ def test_recursive_merge_dicts():
     assert new["b"]["g"] == 98
     assert new["a"] == 42
     assert new["d"] == 99
-
-
-def test_read_write_yaml_checksum(tmp_path):
-    data = {"b": 1234, "a": "other"}
-    path = make_path(tmp_path / "test.yaml")
-    write_yaml(data, path, sort_keys=False, checksum=True)
-
-    yaml_content = path.read_text()
-    assert "checksum: " in yaml_content
-
-    with warnings.catch_warnings():
-        warnings.simplefilter("error")
-        res = read_yaml(path, checksum=True)
-    assert "checksum" not in res.keys()
-
-    yaml_content = yaml_content.replace("1234", "12345")
-    bad = make_path(tmp_path) / "bad_checksum.yaml"
-    bad.write_text(yaml_content)
-    with pytest.warns(UserWarning):
-        read_yaml(bad, checksum=True)
-
-    res["checksum"] = "bad"
-    yaml_str = yaml.dump(data, sort_keys=True, default_flow_style=False)
-    path.write_text(yaml_str)
-
-    with pytest.warns(UserWarning):
-        read_yaml(bad, checksum=True)