Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21 

22from __future__ import annotations 

23 

24__all__ = ( 

25 "SkyPixDimension", 

26 "SkyPixSystem", 

27) 

28 

29from types import MappingProxyType 

30from typing import ( 

31 AbstractSet, 

32 Dict, 

33 Mapping, 

34 Optional, 

35 Type, 

36 TYPE_CHECKING, 

37) 

38 

39import sqlalchemy 

40 

41from lsst.sphgeom import Pixelization 

42from lsst.utils import doImport 

43from .. import ddl 

44from .._topology import TopologicalFamily, TopologicalRelationshipEndpoint, TopologicalSpace 

45 

46from ..named import NamedValueAbstractSet, NamedValueSet 

47from ._elements import Dimension 

48from .construction import DimensionConstructionBuilder, DimensionConstructionVisitor 

49 

50if TYPE_CHECKING: 50 ↛ 51line 50 didn't jump to line 51, because the condition on line 50 was never true

51 from ...registry.interfaces import SkyPixDimensionRecordStorage 

52 

53 

54class SkyPixSystem(TopologicalFamily): 

55 """A `TopologicalFamily` that represents a hierarchical pixelization of the 

56 sky. 

57 

58 Parameters 

59 ---------- 

60 name : `str` 

61 Name of the system. 

62 maxLevel : `int` 

63 Maximum level (inclusive) of the hierarchy. 

64 PixelizationClass : `type` (`lsst.sphgeom.Pixelization` subclass) 

65 Class whose instances represent a particular level of this 

66 pixelization. 

67 """ 

68 def __init__( 

69 self, 

70 name: str, *, 

71 maxLevel: int, 

72 PixelizationClass: Type[Pixelization], 

73 ): 

74 super().__init__(name, TopologicalSpace.SPATIAL) 

75 self.maxLevel = maxLevel 

76 self.PixelizationClass = PixelizationClass 

77 self._members: Dict[int, SkyPixDimension] = {} 

78 for level in range(maxLevel + 1): 

79 self._members[level] = SkyPixDimension(self, level) 

80 

81 def choose(self, endpoints: NamedValueAbstractSet[TopologicalRelationshipEndpoint]) -> SkyPixDimension: 

82 # Docstring inherited from TopologicalFamily. 

83 best: Optional[SkyPixDimension] = None 

84 for endpoint in endpoints: 

85 if endpoint not in self: 

86 continue 

87 assert isinstance(endpoint, SkyPixDimension) 

88 if best is None or best.level < endpoint.level: 

89 best = endpoint 

90 if best is None: 

91 raise RuntimeError(f"No recognized endpoints for {self.name} in {endpoints}.") 

92 return best 

93 

94 def __getitem__(self, level: int) -> SkyPixDimension: 

95 return self._members[level] 

96 

97 

98class SkyPixDimension(Dimension): 

99 """A special `Dimension` subclass for hierarchical pixelizations of the 

100 sky at a particular level. 

101 

102 Unlike most other dimensions, skypix dimension records are not stored in 

103 the database, as these records only contain an integer pixel ID and a 

104 region on the sky, and each of these can be computed directly from the 

105 other. 

106 

107 Parameters 

108 ---------- 

109 system : `SkyPixSystem` 

110 Pixelization system this dimension belongs to. 

111 level : `int` 

112 Integer level of this pixelization (smaller numbers are coarser grids). 

113 """ 

114 def __init__(self, system: SkyPixSystem, level: int): 

115 self.system = system 

116 self.level = level 

117 self.pixelization = system.PixelizationClass(level) 

118 

119 @property 

120 def name(self) -> str: 

121 return f"{self.system.name}{self.level}" 

122 

123 @property 

124 def required(self) -> NamedValueAbstractSet[Dimension]: 

125 # Docstring inherited from DimensionElement. 

126 return NamedValueSet({self}).freeze() 

127 

128 @property 

129 def implied(self) -> NamedValueAbstractSet[Dimension]: 

130 # Docstring inherited from DimensionElement. 

131 return NamedValueSet().freeze() 

132 

133 @property 

134 def topology(self) -> Mapping[TopologicalSpace, TopologicalFamily]: 

135 # Docstring inherited from TopologicalRelationshipEndpoint 

136 return MappingProxyType({TopologicalSpace.SPATIAL: self.system}) 

137 

138 @property 

139 def metadata(self) -> NamedValueAbstractSet[ddl.FieldSpec]: 

140 # Docstring inherited from DimensionElement. 

141 return NamedValueSet().freeze() 

142 

143 def hasTable(self) -> bool: 

144 # Docstring inherited from DimensionElement.hasTable. 

145 return False 

146 

147 def makeStorage(self) -> SkyPixDimensionRecordStorage: 

148 """Construct the `DimensionRecordStorage` instance that should 

149 be used to back this element in a registry. 

150 

151 Returns 

152 ------- 

153 storage : `SkyPixDimensionRecordStorage` 

154 Storage object that should back this element in a registry. 

155 """ 

156 from ...registry.dimensions.skypix import BasicSkyPixDimensionRecordStorage 

157 return BasicSkyPixDimensionRecordStorage(self) 

158 

159 @property 

160 def uniqueKeys(self) -> NamedValueAbstractSet[ddl.FieldSpec]: 

161 # Docstring inherited from DimensionElement. 

162 return NamedValueSet({ 

163 ddl.FieldSpec( 

164 name="id", 

165 dtype=sqlalchemy.BigInteger, 

166 primaryKey=True, 

167 nullable=False, 

168 ) 

169 }).freeze() 

170 

171 # Class attributes below are shadowed by instance attributes, and are 

172 # present just to hold the docstrings for those instance attributes. 

173 

174 system: SkyPixSystem 

175 """Pixelization system this dimension belongs to (`SkyPixSystem`). 

176 """ 

177 

178 level: int 

179 """Integer level of this pixelization (smaller numbers are coarser grids). 

180 """ 

181 

182 pixelization: Pixelization 

183 """Pixelization instance that can compute regions from IDs and IDs from 

184 points (`sphgeom.Pixelization`). 

185 """ 

186 

187 

188class SkyPixConstructionVisitor(DimensionConstructionVisitor): 

189 """Builder visitor for a single `SkyPixSystem` and its dimensions. 

190 

191 Parameters 

192 ---------- 

193 name : `str` 

194 Name of the `SkyPixSystem` to be constructed. 

195 pixelizationClassName : `str` 

196 Fully-qualified name of the class whose instances represent a 

197 particular level of this pixelization. 

198 maxLevel : `int`, optional 

199 Maximum level (inclusive) of the hierarchy. If not provided, 

200 an attempt will be made to obtain it from a ``MAX_LEVEL`` attribute 

201 of the pixelization class. 

202 

203 Notes 

204 ----- 

205 At present, this class adds both a new `SkyPixSystem` instance all possible 

206 `SkyPixDimension` to the builder that invokes it. In the future, it may 

207 add only the `SkyPixSystem`, with dimension instances created on-the-fly 

208 by the `DimensionUniverse`; this depends on `DimensionGraph.encode` going 

209 away or otherwise eliminating assumptions about the set of dimensions in a 

210 universe being static. 

211 """ 

212 def __init__(self, name: str, pixelizationClassName: str, maxLevel: Optional[int] = None): 

213 super().__init__(name) 

214 self._pixelizationClassName = pixelizationClassName 

215 self._maxLevel = maxLevel 

216 

217 def hasDependenciesIn(self, others: AbstractSet[str]) -> bool: 

218 # Docstring inherited from DimensionConstructionVisitor. 

219 return False 

220 

221 def visit(self, builder: DimensionConstructionBuilder) -> None: 

222 # Docstring inherited from DimensionConstructionVisitor. 

223 PixelizationClass = doImport(self._pixelizationClassName) 

224 maxLevel = self._maxLevel if self._maxLevel is not None else PixelizationClass.MAX_LEVEL 

225 system = SkyPixSystem( 

226 self.name, 

227 maxLevel=maxLevel, 

228 PixelizationClass=PixelizationClass, 

229 ) 

230 builder.topology[TopologicalSpace.SPATIAL].add(system) 

231 for level in range(maxLevel + 1): 

232 dimension = system[level] 

233 builder.dimensions.add(dimension) 

234 builder.elements.add(dimension)