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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

# This file is part of ap_association. 

# 

# Developed for the LSST Data Management System. 

# This product includes software developed by the LSST Project 

# (https://www.lsst.org). 

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

# for details of code ownership. 

# 

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

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

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

# (at your option) any later version. 

# 

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

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

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

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

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

 

"""Defines afw schemas and conversions for use in ap_association tasks. 

 

Until a more finalized interface between the prompt products database (PPD) can 

be established, we put many utility functions for converting `lsst.afw.table` and 

`lsst.afw.image` objects to a PPD. This includes both mapping schemas between 

different catalogs and the DB. 

""" 

 

__all__ = ["make_minimal_dia_object_schema", 

"make_minimal_dia_source_schema", 

"getCcdVisitSchemaSql"] 

 

from collections import OrderedDict as oDict 

 

import lsst.afw.table as afwTable 

import lsst.afw.image as afwImage 

from lsst.daf.base import DateTime 

 

 

def make_minimal_dia_object_schema(filter_names=None): 

"""Define and create the minimal schema required for a DIAObject. 

 

Parameters 

---------- 

filter_names : `list` of `str` 

Names of the filters expect and compute means for. 

 

Returns 

------- 

schema : `lsst.afw.table.Schema` 

Minimal schema for DIAObjects. 

""" 

schema = afwTable.SourceTable.makeMinimalSchema() 

# For the MVP/S we currently only care about the position though 

# in the future we will add summary computations for fluxes etc. 

# as well as their errors. 

 

# TODO: In the future we would like to store a covariance of the coordinate. 

# This functionality is not defined currently in the stack, so we will hold 

# off until it is implemented. This is to be addressed in DM-7101. 

schema.addField("pixelId", type='L', 

doc='Unique spherical pixelization identifier.') 

schema.addField("nDiaSources", type='L') 

if filter_names is None: 

filter_names = [] 

for filter_name in filter_names: 

schema.addField("psFluxMean_%s" % filter_name, type='D', 

doc='Calibrated mean flux in %s band.' % filter_name) 

schema.addField("psFluxMeanErr_%s" % filter_name, type='D', 

doc='Calibrated error on mean flux in %s band.' % 

filter_name) 

schema.addField("psFluxSigma_%s" % filter_name, type='D', 

doc='Calibrated scatter in flux in %s band.' % 

filter_name) 

return schema 

 

 

def make_minimal_dia_source_schema(): 

""" Define and create the minimal schema required for a DIASource. 

 

Returns 

------- 

schema : `lsst.afw.table.Schema` 

Minimal schema for DIAObjects. 

""" 

schema = afwTable.SourceTable.makeMinimalSchema() 

schema.addField("diaObjectId", type='L', 

doc='Unique identifier of the DIAObject this source is ' 

'associated to.') 

schema.addField("ccdVisitId", type='L', 

doc='Id of the exposure and ccd this object was detected ' 

'in.') 

schema.addField("midPointTai", type="D", 

doc="Time of mid-exposure for this DIASource") 

schema.addField("psFlux", type='D', 

doc='Calibrated PSF flux of this source.') 

schema.addField("psFluxErr", type='D', 

doc='Calibrated PSF flux err of this source.') 

schema.addField("filterName", type='String', size=10, 

doc='String name of the filter this source was observed ' 

'in.') 

schema.addField("filterId", type='L', 

doc='Obs package id of the filter this source was ' 

'observed in.') 

schema.addField("flags", type='L', 

doc='Quality flags for this DIASource.') 

schema.addField("pixelId", type='L', 

doc='Unique spherical pixelization identifier.') 

return schema 

 

 

def getCcdVisitSchemaSql(): 

"""Define the schema for the CcdVisit table. 

 

Returns 

------- 

ccdVisitNames : `collections.OrderedDict` 

Names of columns in the ccdVisit table. 

""" 

return oDict([("ccdVisitId", "INTEGER PRIMARY KEY"), 

("ccdNum", "INTEGER"), 

("filterName", "TEXT"), 

("filterId", "INTEGER"), 

("ra", "REAL"), 

("decl", "REAL"), 

("expTime", "REAL"), 

("expMidptMJD", "REAL"), 

("fluxZeroPoint", "REAL"), 

("fluxZeroPointErr", "REAL")]) 

 

 

def get_ccd_visit_info_from_exposure(exposure): 

""" 

Extract info on the ccd and visit from the exposure. 

 

Parameters 

---------- 

exposure : `lsst.afw.image.Exposure` 

Exposure to store information from. 

 

Returns 

------- 

values : `dict` of ``values`` 

List values representing info taken from the exposure. 

""" 

visit_info = exposure.getInfo().getVisitInfo() 

date = visit_info.getDate() 

sphPoint = exposure.getWcs().getSkyOrigin() 

# TODO: Calib is going away and being replaced with photoCalib as in 

# DM-10153. 

flux0, flux0_err = exposure.getCalib().getFluxMag0() 

filter_obj = exposure.getFilter() 

# Values list is: 

# [CcdVisitId ``int``, 

# ccdNum ``int``, 

# filterName ``str``, 

# RA WCS center ``degrees``, 

# DEC WCS center ``degrees``, 

# exposure time ``seconds``, 

# dateTimeMJD ``days``, 

# flux zero point ``counts``, 

# flux zero point error ``counts``] 

values = {'ccdVisitId': visit_info.getExposureId(), 

'ccdNum': exposure.getDetector().getId(), 

'filterName': filter_obj.getName(), 

'filterId': filter_obj.getId(), 

'ra': sphPoint.getRa().asDegrees(), 

'decl': sphPoint.getDec().asDegrees(), 

'expTime': visit_info.getExposureTime(), 

'expMidptMJD': date.get(system=DateTime.MJD), 

'fluxZeroPoint': flux0, 

'fluxZeroPointErr': flux0_err, 

'photoCalib': afwImage.PhotoCalib(1 / flux0, 

flux0_err / flux0 ** 2)} 

return values 

 

 

def add_dia_source_aliases_to_catalog(source_catalog): 

"""Create the needed aliases on fluxes and other columns if the input 

catalog does not already have them. 

 

Parameters 

---------- 

source_catalog : `lsst.afw.table.SourceCatalog` 

Input source catalog to add alias columns to. 

""" 

if not source_catalog.getSchema().contains(make_minimal_dia_source_schema()): 

# Create aliases to appropriate flux fields if they exist. 

schema_names = source_catalog.getSchema().getNames() 

if 'base_PsfFlux_instFlux' in schema_names and \ 

'base_PsfFlux_instFluxErr' in schema_names and \ 

'psFlux' not in schema_names and \ 

'psFluxErr' not in schema_names: 

alias_map = source_catalog.getSchema().getAliasMap() 

alias_map.set('psFlux', 'base_PsfFlux_instFlux') 

alias_map.set('psFluxErr', 'base_PsfFlux_instFluxErr') 

 

 

def convert_dia_source_to_asssoc_schema(dia_sources, 

obj_ids=None, 

exposure=None): 

"""Create aliases in for the input source catalog schema and overwrite 

values if requested. 

 

Parameters 

---------- 

dia_sources : `lsst.afw.table.SourceCatalog` 

Input source catalog to alias and overwrite values of. 

obj_ids : array-like of `int` 

DIAObject ids to associate with the input DIASources. Overwrites 

the current value in diaObjectId column. If None the obj_id no action 

is taken. The array should be the same length as dia_sources. 

exposure : `lsst.afw.image.Exposure` 

Exposure to copy flux and filter information from. 

""" 

 

output_dia_sources = afwTable.SourceCatalog( 

make_minimal_dia_source_schema()) 

output_dia_sources.reserve(len(dia_sources)) 

add_dia_source_aliases_to_catalog(dia_sources) 

dia_source_schema = dia_sources.getSchema() 

 

if exposure is None: 

exp_dict = None 

else: 

exp_dict = get_ccd_visit_info_from_exposure(exposure) 

 

for src_idx, source_record in enumerate(dia_sources): 

if obj_ids is None: 

obj_id = None 

else: 

obj_id = obj_ids[src_idx] 

overwrite_dict = make_overwrite_dict(source_record, obj_id, exp_dict) 

output_record = output_dia_sources.addNew() 

copy_source_record(source_record, 

output_record, 

dia_source_schema, 

overwrite_dict) 

return output_dia_sources 

 

 

def make_overwrite_dict(source_record, obj_id=None, exp_dict=None): 

"""Create a dictionary of values to overwrite the values stored in an 

afw.table or when storing values in a DB. 

 

Parameters 

---------- 

source_record : `lsst.afw.table.SourceRecord` 

Input source catalog to modify values of obj_id or exp_dict are not 

None. In the case they the values are either copied from the input 

record or the afw.table default is used. 

obj_id : `int` (optional) 

DIAObject id to overwrite. 

exp_dict : `dict` 

Dictionary of exposure properties for this source_catalog. 

 

Returns 

------- 

overwrite_dict : `dict` 

Dictionary of keys and values to overwrite for this source record. 

""" 

overwrite_dict = {} 

if obj_id is not None: 

overwrite_dict['diaObjectId'] = int(obj_id) 

if exp_dict is not None: 

psFlux = source_record['psFlux'] 

psFluxErr = source_record['psFluxErr'] 

 

meas = exp_dict['photoCalib'].instFluxToMaggies(psFlux, psFluxErr) 

overwrite_dict['psFlux'] = meas.value 

overwrite_dict['psFluxErr'] = meas.err 

overwrite_dict['ccdVisitId'] = exp_dict['ccdVisitId'] 

overwrite_dict['midPointTai'] = exp_dict["expMidptMJD"] 

overwrite_dict['filterName'] = exp_dict['filterName'] 

overwrite_dict['filterId'] = exp_dict['filterId'] 

 

return overwrite_dict 

 

 

def copy_source_record(source_record, 

output_record, 

source_record_schema, 

overwrite_dict): 

"""Copy values from an input source record to a record with the proper 

schema for ap_association. 

 

Parameters 

---------- 

source_record : `lsst.afw.table.SourceRecord` 

Input source record to copy values from. 

output_record : `lsst.afw.table.SourceRecord` 

Output source record to copy values into and edit values of. 

source_record_schema : `lsst.afw.table.Schema` 

Schema of the input source record. 

overwrite_dict : `dict` of ``values`` 

Dictionary with column names and values to overwrite into the 

output source record. 

""" 

for sub_schema in make_minimal_dia_source_schema(): 

field_name = sub_schema.getField().getName() 

if field_name in overwrite_dict: 

output_record.set(sub_schema.getKey(), 

overwrite_dict[field_name]) 

elif field_name in source_record_schema.getNames(): 

output_record.set( 

sub_schema.getKey(), 

source_record.get( 

source_record_schema.find(field_name).getKey()))