Repository: pydap/pydap

Issue:
dimensions are not being defined at the Group/subgroup level
<!-- Thank you very much for your input! -->

### Description

`pydap`'s`GroupType` does not have dimensions as a property/method. This is,

```python
Group.dimensions
```

yields a `AttributeError: '<class 'pydap.model.GroupType'>' object has no attribute 'dimensions'`

`pydap`  correctly defines dimensions at the root level (i.e. `dataset.dimension`), and for each individual array (`BaseType.dimension`)
### Minimal example
Consider the test [SimpleGroup](https://github.com/pydap/pydap/blob/main/src/pydap/tests/data/dmrs/SimpleGroup.dmr).
```python
from pydap.parsers.dmr import dmr_to_dataset
abs_path = '.../SimpleGroup.dmr'
dmr = open(abs_path, 'r').read()
Dataset = dmr_to_dataset(dmr)
Dataset.tree()
```

```python
.example dataset
├──SimpleGroup
│  ├──Temperature
│  ├──Salinity
│  ├──Y
│  └──X
├──time
└──time_bnds
```
The dimensions within this dataset are: `/time, /nv, /SimpleGroup/X, /SimpleGroup/Y`. However, only `/time` and `/nv` are appropriately defined so that `Dataset.dimensions` returns `(('time', 1), ('nv', 2)`.  Similarly, for each array we can inspect the dimensions. For example,`Dataset['SimpleGroup/Temperature'].dimensions` returns `['time', 'Y', 'X']`

In the snippet below, the variable `named_dimensions` captures all the dimension with their `FQN`. However, only the dimensions at the root level are being assigned within the dataset.

https://github.com/pydap/pydap/blob/a83954d1f1feae9cd4d70edd2066c379c51a3926/src/pydap/parsers/dmr.py#L163-L171

### What I would expect
Use the information from  `named_dimensions` to define dimensions at the Groups so that `Dataset['Group'].dimensions` returns `(('Y', size_of_Y), ('X', size_of_X))`

Discussion:

Patch:
diff --git a/src/pydap/model.py b/src/pydap/model.py
index 649d68e..11bc0ba 100644
--- a/src/pydap/model.py
+++ b/src/pydap/model.py
@@ -891,13 +891,13 @@ class GroupType(StructureType):
 
     """
 
-    def __setitem__(self, key, item, dimensions=None):
+    def __setitem__(self, key, item, dimensions=dict()):
         StructureType.__setitem__(self, key, item)
 
         # The Group name does (not) go into the children ids.
 
         item.id = item.name
-        self.dimesions = dimensions or ()
+        self.dimensions = dimensions or dict()
         # self._dict[key] = item
 
         # # # By default added keys are visible:
diff --git a/src/pydap/parsers/dmr.py b/src/pydap/parsers/dmr.py
index 9729bce..2ceb5ff 100644
--- a/src/pydap/parsers/dmr.py
+++ b/src/pydap/parsers/dmr.py
@@ -168,7 +168,7 @@ def dmr_to_dataset(dmr):
         if len(name.split("/")) == 1:
             global_dimensions.append([name, size])
 
-    dataset.dimensions = tuple(tuple(item) for item in global_dimensions)
+    dataset.dimensions = {k: v for k, v in global_dimensions}
 
     # Add size entry for dimension variables
     for name, size in named_dimensions.items():
@@ -200,18 +200,18 @@ def dmr_to_dataset(dmr):
             parts = var_name.split("/")
             var_name = parts[-1]
             path = ("/").join(parts[:-1])
-            # need to do the same with dimensions
-            for i in range(len(variable["dims"])):
-                dim = variable["dims"][i].split("/")[-1]
-                variable["dims"][i] = dim
             variable["attributes"]["path"] = path
 
         data = DummyData(dtype=variable["dtype"], shape=variable["shape"], path=path)
-        array = pydap.model.BaseType(
-            name=var_name,
-            data=data,
-            dimensions=variable["dims"],
-        )
+        # make sure all dimensions have qualifying name
+        Dims = []
+        for dim in variable["dims"]:
+            if len(dim.split("/")) == 1:
+                Dims.append("/" + dim)
+            else:
+                Dims.append(dim)
+
+        array = pydap.model.BaseType(name=var_name, data=data, dimensions=Dims)
         # pass along maps
         if "maps" in variable.keys():
             array.Maps = variable["maps"]
@@ -235,6 +235,14 @@ def dmr_to_dataset(dmr):
         else:
             dataset[name] = var
 
+    group_dims = [
+        dim for dim in named_dimensions if dim not in dataset.dimensions.keys()
+    ]
+    for fqn in group_dims:
+        path = ("/").join(fqn.split("/")[:-1])
+        dim_name = fqn.split("/")[-1]
+        dataset[path].dimensions.update({dim_name: named_dimensions[fqn]})
+
     return dataset
 
 
diff --git a/src/pydap/responses/dmr.py b/src/pydap/responses/dmr.py
index b19d957..ae40129 100644
--- a/src/pydap/responses/dmr.py
+++ b/src/pydap/responses/dmr.py
@@ -54,7 +54,7 @@ def _(var, level=0):
     str2 = ' dapVersion="4.0" dmrVersion="1.0"'
     str3 = ' name="{name}">\n'.format(name=var.name)
     yield "<{indent}".format(indent=level * INDENT) + str0 + str1 + str2 + str3
-    for name, size in var.dimensions:
+    for name, size in var.dimensions.items():
         yield '{indent}<Dimension name="{name}" size="{size}"/>\n'.format(
             indent=(level + 1) * INDENT, name=name, size=size
         )
@@ -77,7 +77,7 @@ def _sequencetype(var, level=0, sequence=0):
 @dmr.register(GroupType)
 def _grouptype(var, level=0):
     yield '{indent}<Group name="{name}">\n'.format(indent=level * INDENT, name=var.name)
-    for dim, size in var.dimensions:
+    for dim, size in var.attributes["dimensions"].items():
         yield '{indent}<Dimension name="{name}" size="{size}"/>\n'.format(
             indent=(level + 1) * INDENT, name=dim, size=size
         )


Test patch:
diff --git a/src/pydap/tests/datasets.py b/src/pydap/tests/datasets.py
index 116b595..a225116 100644
--- a/src/pydap/tests/datasets.py
+++ b/src/pydap/tests/datasets.py
@@ -227,9 +227,9 @@ SimpleGrid["y"] = SimpleGrid["SimpleGrid"]["y"] = BaseType(
 SimpleGroup = DatasetType(
     "example dataset",
     description="A simple group for testing.",
-    dimensions=(("time", 1), ("nv", 2)),
+    dimensions={"time": 1, "nv": 2},
 )
-SimpleGroup["SimpleGroup"] = GroupType("SimpleGroup", dimensions=(("Y", 4), ("X", 4)))
+SimpleGroup["SimpleGroup"] = GroupType("SimpleGroup", dimensions={"Y": 4, "X": 4})
 SimpleGroup["/SimpleGroup/Temperature"] = BaseType(
     "Temperature",
     np.arange(10, 26, 1, dtype="f4").reshape(1, 4, 4),
diff --git a/src/pydap/tests/test_parsers_dmr.py b/src/pydap/tests/test_parsers_dmr.py
index 6216174..b17b41c 100644
--- a/src/pydap/tests/test_parsers_dmr.py
+++ b/src/pydap/tests/test_parsers_dmr.py
@@ -78,7 +78,9 @@ class DMRParser(unittest.TestCase):
         dataset = load_dmr_file(
             "data/dmrs/20220102090000-JPL-L4_GHRSST-SSTfnd-MUR-GLOB-v02.0-fv04.1.dmr"
         )
-        self.assertEqual(dataset["sea_ice_fraction"].dimensions, ["time", "lat", "lon"])
+        self.assertEqual(
+            dataset["sea_ice_fraction"].dimensions, ["/time", "/lat", "/lon"]
+        )
 
     def test_mod05(self):
         dataset = load_dmr_file(
@@ -87,8 +89,8 @@ class DMRParser(unittest.TestCase):
         self.assertEqual(
             dataset["Water_Vapor_Infrared"].dimensions,
             [
-                "Cell_Along_Swath_5km",
-                "Cell_Across_Swath_5km",
+                "/Cell_Along_Swath_5km",
+                "/Cell_Across_Swath_5km",
             ],
         )
 
@@ -101,15 +103,15 @@ class DMRParser(unittest.TestCase):
     def tests_global_dimensions(self):
         dataset = load_dmr_file("data/dmrs/SimpleGroup.dmr")
         # pick a single variable Maps
-        names = tuple([item[0] for item in dataset.dimensions])
-        sizes = tuple([item[1] for item in dataset.dimensions])
-        self.assertEqual(names, ("time", "nv"))
-        self.assertEqual(sizes, (1, 2))
+        names = [key for key in dataset.dimensions.keys()]
+        sizes = [v[1] for v in dataset.dimensions.items()]
+        self.assertEqual(names, ["time", "nv"])
+        self.assertEqual(sizes, [1, 2])
 
     def tests_named_dimension(self):
         dataset = load_dmr_file("data/dmrs/SimpleGroup.dmr")
         # get only names of dimensions
-        names = tuple([item[0] for item in dataset.dimensions])
+        names = [key for key in dataset.dimensions.keys()]
         # get all variables/arrays
         variables = []
         for var in walk(dataset, BaseType):