Coverage for python/lsst/sims/catUtils/utils/ObservationMetaDataGenerator.py : 9%

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
1from builtins import object
2import numpy as np
3import os
4import numbers
5from lsst.sims.catalogs.db import DBObject
6from lsst.sims.utils import ObservationMetaData
8__all__ = ["ObservationMetaDataGenerator"]
11class ObservationMetaDataGenerator(object):
12 """
13 A class that allows the user to generate instantiations of
14 `lsst.sims.utils.ObservationMetaData` corresponding to OpSim pointings.
15 The functionality includes:
16 - getOpSimRecords : obtain OpSim records matching the intersection of user
17 specified ranges on each column in the OpSim output database. The
18 records are in the form of a `numpy.recarray`
19 - ObservationMetaDataFromPointing : convert an OpSim record for a single
20 OpSim Pointing to an instance of ObservationMetaData usable by catsim
21 and PhoSim Instance Catalogs.
22 - getObservationMetaData : Obtain a list of ObservationMetaData instances
23 corresponding to OpSim pointings matching the intersection of user
24 specified ranges on each column in the OpSim output database.
26 The major method is ObservationMetaDataGenerator.getObservationMetaData()
27 which accepts bounds on columns of the opsim summary table and returns
28 a list of ObservationMetaData instantiations that fall within those
29 bounds.
30 """
32 @property
33 def table_name(self):
34 """
35 Return the name of the table in the OpSim database that we are querying
36 """
37 return 'Summary'
39 def _make_opsim_v3_interface(self):
40 # a dict keyed on the user interface names of the ObservationMetaData columns
41 # (i.e. the args to getObservationMetaData). Returns a tuple that is the
42 # (name of data column in OpSim, transformation to go from user interface to OpSim units,
43 # dtype in OpSim)
44 #
45 # Note: this dict will contain entries for every column (except propID) in the OpSim
46 # summary table, not just those the ObservationMetaDataGenerator is designed to query
47 # on. The idea is that ObservationMetaData generated by this class will carry around
48 # records of the values of all of the associated OpSim Summary columns so that users
49 # can pass those values on to PhoSim/other tools and thier own discretion.
51 interface_dict = {'obsHistID': ('obsHistID', None, np.int64),
52 'expDate': ('expDate', None, int),
53 'fieldRA': ('fieldRA', np.radians, float),
54 'fieldDec': ('fieldDec', np.radians, float),
55 'moonRA': ('moonRA', np.radians, float),
56 'moonDec': ('moonDec', np.radians, float),
57 'rotSkyPos': ('rotSkyPos', np.radians, float),
58 'telescopeFilter':
59 ('filter', lambda x: '\'{}\''.format(x), (str, 1)),
60 'rawSeeing': ('rawSeeing', None, float),
61 'sunAlt': ('sunAlt', np.radians, float),
62 'moonAlt': ('moonAlt', np.radians, float),
63 'dist2Moon': ('dist2Moon', np.radians, float),
64 'moonPhase': ('moonPhase', None, float),
65 'expMJD': ('expMJD', None, float),
66 'altitude': ('altitude', np.radians, float),
67 'azimuth': ('azimuth', np.radians, float),
68 'visitExpTime': ('visitExpTime', None, float),
69 'airmass': ('airmass', None, float),
70 'm5': ('fiveSigmaDepth', None, float),
71 'skyBrightness': ('filtSkyBrightness', None, float),
72 'sessionID': ('sessionID', None, int),
73 'fieldID': ('fieldID', None, int),
74 'night': ('night', None, int),
75 'visitTime': ('visitTime', None, float),
76 'finRank': ('finRank', None, float),
77 'FWHMgeom': ('FWHMgeom', None, float),
78 # do not include FWHMeff; that is detected by
79 # self._set_seeing_column()
80 'transparency': ('transparency', None, float),
81 'vSkyBright': ('vSkyBright', None, float),
82 'rotTelPos': ('rotTelPos', None, float),
83 'lst': ('lst', None, float),
84 'solarElong': ('solarElong', None, float),
85 'moonAz': ('moonAz', None, float),
86 'sunAz': ('sunAz', None, float),
87 'phaseAngle': ('phaseAngle', None, float),
88 'rScatter': ('rScatter', None, float),
89 'mieScatter': ('mieScatter', None, float),
90 'moonBright': ('moonBright', None, float),
91 'darkBright': ('darkBright', None, float),
92 'wind': ('wind', None, float),
93 'humidity': ('humidity', None, float),
94 'slewDist': ('slewDist', None, float),
95 'slewTime': ('slewTime', None, float),
96 'ditheredRA': ('ditheredRA', None, float),
97 'ditheredDec': ('ditheredDec', None, float)}
99 return interface_dict
101 def _make_opsim_v4_interface(self):
102 interface_dict = {'obsHistID': ('observationId', None, np.int64),
103 'fieldRA': ('fieldRA', None, float),
104 'fieldDec': ('fieldDec', None, float),
105 'moonRA': ('moonRA', None, float),
106 'moonDec': ('moonDec', None, float),
107 'rotSkyPos': ('rotSkyPos', None, float),
108 'telescopeFilter':
109 ('filter', lambda x: '\'{}\''.format(x), (str, 1)),
110 'sunAlt': ('sunAlt', None, float),
111 'moonAlt': ('moonAlt', None, float),
112 'moonPhase': ('moonPhase', None, float),
113 'expMJD': ('observationStartMJD', None, float),
114 'altitude': ('altitude', None, float),
115 'azimuth': ('azimuth', None, float),
116 'visitExpTime': ('visitExposureTime', None, float),
117 'airmass': ('airmass', None, float),
118 'm5': ('fiveSigmaDepth', None, float),
119 'skyBrightness': ('skyBrightness', None, float),
120 'fieldID': ('fieldId', None, int),
121 'night': ('night', None, int),
122 'visitTime': ('visitTime', None, float),
123 'FWHMgeom': ('seeingFWHMgeom', None, float),
124 # do not include FWHMeff; that is detected by
125 # self._set_seeing_column()
126 'rotTelPos': ('rotTelPos', None, float),
127 'lst': ('observationStartLST', None, float),
128 'solarElong': ('solarElong', None, float),
129 'moonAz': ('moonAz', None, float),
130 'sunAz': ('sunAz', None, float),
131 'slewDist': ('slewDistance', None, float),
132 'slewTime': ('slewTime', None, float),
133 'note': ('note', None, str)}
135 return interface_dict
137 def _set_seeing_column(self, input_summary_columns):
138 """
139 input_summary_columns is a list of columns in the OpSim database schema
141 This method sets the member variable self._seeing_column to a string
142 denoting the name of the seeing column in the OpSimDatabase. It also
143 sets self.user_interface_to_opsim['seeing'] to the correct value.
144 """
146 if 'FWHMeff' in input_summary_columns:
147 self._seeing_column = 'FWHMeff'
148 elif 'seeingFwhmEff' in input_summary_columns:
149 self._seeing_column = 'seeingFwhmEff'
150 else:
151 self._seeing_column = 'finSeeing'
153 self.user_interface_to_opsim['seeing'] = (self._seeing_column, None, float)
155 @property
156 def opsim_version(self):
157 return self._opsim_version
159 @property
160 def user_interface_to_opsim(self):
161 if not hasattr(self, '_user_interface_to_opsim'):
162 if self.opsim_version == 3:
163 self._user_interface_to_opsim = self._make_opsim_v3_interface()
164 elif self.opsim_version == 4:
165 self._user_interface_to_opsim = self._make_opsim_v4_interface()
166 else:
167 raise RuntimeError("Unsure how to handle opsim_version ",self.opsim_version)
168 return self._user_interface_to_opsim
170 @property
171 def table_name(self):
172 if self.opsim_version == 3:
173 return 'Summary'
174 elif self.opsim_version == 4:
175 return 'SummaryAllProps'
176 raise RuntimeError("Unsure how to handle opsim_version ",self.opsim_version)
178 def __init__(self, database=None, driver='sqlite', host=None, port=None):
179 """
180 Constructor for the class
182 Parameters
183 ----------
184 database : string
185 absolute path to the output of the OpSim database
186 driver : string, optional, defaults to 'sqlite'
187 driver/dialect for the SQL database
188 host : hostName, optional, defaults to None,
189 hostName, None is good for a local database
190 port : hostName, optional, defaults to None,
191 port, None is good for a local database
193 Returns
194 ------
195 Instance of the ObserverMetaDataGenerator class
197 ..notes : For testing purposes a small OpSim database is available at
198 `os.path.join(getPackageDir('sims_data'), 'OpSimData/opsimblitz1_1133_sqlite.db')`
199 """
200 self._opsim_version = None
201 self.driver = driver
202 self.host = host
203 self.port = port
204 self.database = database
205 self._seeing_column = 'FWHMeff'
207 if self.database is None:
208 return
210 if not os.path.isfile(self.database):
211 raise RuntimeError('%s is not a file' % self.database)
213 self.opsimdb = DBObject(driver=self.driver, database=self.database,
214 host=self.host, port=self.port)
216 # 27 January 2016
217 # Detect whether the OpSim db you are connecting to uses 'finSeeing'
218 # as its seeing column (deprecated), or FWHMeff, which is the modern
219 # standard
221 list_of_tables = self.opsimdb.get_table_names()
222 if 'Summary' in list_of_tables:
223 self._opsim_version = 3
224 else:
225 self._opsim_version = 4
227 self._summary_columns = self.opsimdb.get_column_names(self.table_name)
228 self._set_seeing_column(self._summary_columns)
230 # Set up self.dtype containg the dtype of the recarray we expect back from the SQL query.
231 # Also setup baseQuery which is just the SELECT clause of the SQL query
232 #
233 # self.active_columns will be a list containing the subset of OpSim database columns
234 # (specified in self.user_interface_to_opsim) that actually exist in this opsim database
235 dtypeList = []
236 self.baseQuery = 'SELECT'
237 self.active_columns = []
239 self._queried_columns = [] # This will be a list of all of the
240 # OpSim columns queried
241 # Note: here we will refer to the
242 # columns by their names in OpSim
244 for column in self.user_interface_to_opsim:
245 rec = self.user_interface_to_opsim[column]
246 if rec[0] in self._summary_columns:
247 self.active_columns.append(column)
248 dtypeList.append((rec[0], rec[2]))
249 if self.baseQuery != 'SELECT':
250 self.baseQuery += ','
251 self.baseQuery += ' ' + rec[0]
252 self._queried_columns.append(rec[0])
254 # Now loop over self._summary_columns, adding any columns
255 # to the query that have not already been included therein.
256 # Since we do not have explicit information about the
257 # data types of these columns, we will assume they are floats.
258 for column in self._summary_columns:
259 if column not in self._queried_columns:
260 self.baseQuery += ', ' + column
261 dtypeList.append((column, float))
263 self.dtype = np.dtype(dtypeList)
265 def getOpSimRecords(self, obsHistID=None, expDate=None, night=None, fieldRA=None,
266 fieldDec=None, moonRA=None, moonDec=None,
267 rotSkyPos=None, telescopeFilter=None, rawSeeing=None,
268 seeing=None, sunAlt=None, moonAlt=None, dist2Moon=None,
269 moonPhase=None, expMJD=None, altitude=None,
270 azimuth=None, visitExpTime=None, airmass=None,
271 skyBrightness=None, m5=None, boundType='circle',
272 boundLength=1.75, limit=None):
273 """
274 This method will query the summary table in the `self.opsimdb` database
275 according to constraints specified in the input ranges and return a
276 `numpy.recarray` containing the records that match those constraints. If limit
277 is used, the first N records will be returned in the list.
279 Parameters
280 ----------
281 obsHistID, expDate, night, fieldRA, fieldDec, moonRa, moonDec, rotSkyPos,
282 telescopeFilter, rawSeeing, seeing, sunAlt, moonAlt, dist2Moon,
283 moonPhase, expMJD, altitude, azimuth, visitExpTime, airmass,
284 skyBrightness, m5 : tuples of length 2, optional, defaults to None
285 each of these variables represent a single column (perhaps through
286 an alias) in the OpSim database, and potentially in a different unit.
287 if not None, the variable self.columnMapping is used to constrain
288 the corresponding column in the OpSim database to the ranges (inclusive)
289 specified in the tuples, after a unit transformation if necessary.
291 The ranges must be specified in the tuple in degrees for all angles in this
292 (moonRa, moonDec, rotSkyPos, sunAlt, moonAlt, dist2Moon, altitude,
293 azimuth). The times in (expMJD, are in units of MJD). visitExpTime has
294 units of seconds since the start of the survey. moonPhase is a number
295 from 0., to 100.
296 boundType : `sims.utils.ObservationMetaData.boundType`, optional, defaults to 'circle'
297 {'circle', 'box'} denoting the shape of the pointing. Further
298 documentation `sims.catalogs.generation.db.spatialBounds.py``
299 boundLength : float, optional, defaults to 0.1
300 sets `sims.utils.ObservationMetaData.boundLenght`
301 limit : integer, optional, defaults to None
302 if not None, denotes max number of records returned by the query
304 Returns
305 -------
306 `numpy.recarray` with OpSim records. The column names may be obtained as
307 res.dtype.names
309 .. notes:: The `limit` argument should only be used if a small example
310 is required. The angle ranges in the argument should be specified in degrees.
311 """
313 self._set_seeing_column(self._summary_columns)
315 query = self.baseQuery + ' FROM %s' % self.table_name
317 nConstraints = 0 # the number of constraints in this query
319 for column in self.user_interface_to_opsim:
320 transform = self.user_interface_to_opsim[column]
322 # this try/except block is because there will be columns in the OpSim Summary
323 # table (and thus in self.user_interface_to_opsim) which the
324 # ObservationMetaDataGenerator is not designed to query on
325 try:
326 value = eval(column)
327 except:
328 value = None
330 if value is not None:
331 if column not in self.active_columns:
332 raise RuntimeError("You have asked ObservationMetaDataGenerator to SELECT pointings on "
333 "%s; that column does not exist in your OpSim database" % column)
334 if nConstraints > 0:
335 query += ' AND'
336 else:
337 query += ' WHERE '
339 if isinstance(value, tuple):
340 if len(value) > 2:
341 raise RuntimeError('Cannot pass a tuple longer than 2 elements ' +
342 'to getObservationMetaData: %s is len %d'
343 % (column, len(value)))
345 # perform any necessary coordinate transformations
346 if transform[1] is not None:
347 vmin = transform[1](value[0])
348 vmax = transform[1](value[1])
349 else:
350 vmin = value[0]
351 vmax = value[1]
353 query += ' %s >= %s AND %s <= %s' % \
354 (transform[0], vmin, transform[0], vmax)
355 else:
356 # perform any necessary coordinate transformations
357 if transform[1] is not None:
358 vv = transform[1](value)
359 else:
360 vv = value
362 if isinstance(vv, numbers.Number):
363 tol = np.abs(vv)*1.0e-10
364 if tol == 0.0:
365 tol = 1.0e-10
366 query += ' %s < %.12e AND %s > %.12e' % (transform[0],
367 vv+tol,
368 transform[0],
369 vv-tol)
370 else:
371 query += ' %s == %s' % (transform[0], vv)
373 nConstraints += 1
375 mjd_name = self.user_interface_to_opsim['expMJD'][0]
376 query += ' GROUP BY %s ORDER BY %s' % (mjd_name, mjd_name)
378 if limit is not None:
379 query += ' LIMIT %d' % limit
381 if nConstraints == 0 and limit is None:
382 raise RuntimeError('You did not specify any contraints on your query;' +
383 ' you will just return ObservationMetaData for all poitnings')
385 results = self.opsimdb.execute_arbitrary(query, dtype=self.dtype)
386 return results
388 def ObservationMetaDataFromPointing(self, OpSimPointingRecord, OpSimColumns=None,
389 boundLength=1.75, boundType='circle'):
390 """
391 Return instance of ObservationMetaData for an OpSim Pointing record
392 from OpSim.
394 Parameters
395 ----------
396 OpSimPointingRecord : Dictionary, mandatory
397 Dictionary of values with keys corresponding to certain columns of
398 the Summary table in the OpSim database. The minimal list of keys
399 required for catsim to work is 'fiveSigmaDepth',
400 'filtSkyBrightness', and at least one of ('finSeeing', 'FWHMeff').
401 More keys defined in columnMap may be necessary for PhoSim to work.
402 OpSimColumns : tuple of strings, optional, defaults to None
403 The columns corresponding to the OpSim records. If None, attempts
404 to obtain these from the OpSimRecord as OpSimRecord.dtype.names
405 boundType : {'circle', 'box'}, optional, defaults to 'circle'
406 Shape of the observation
407 boundLength : scalar float, optional, defaults to 1.75
408 'characteristic size' of observation field, in units of degrees.
409 For boundType='circle', this is a radius, for boundType='box', this
410 is a size of the box
411 """
413 pointing = OpSimPointingRecord
414 pointing_column_names = pointing.dtype.names
415 # Decide what is the name of the column in the OpSim database
416 # corresponding to the Seeing. For older OpSim outputs, this is
417 # 'finSeeing'. For later OpSim outputs this is 'FWHMeff'
418 if OpSimColumns is None:
419 OpSimColumns = pointing_column_names
421 self._set_seeing_column(OpSimColumns)
423 # get the names of the columns that contain the minimal schema
424 # for an ObservationMetaData
425 mjd_name = self.user_interface_to_opsim['expMJD'][0]
426 ra_name = self.user_interface_to_opsim['fieldRA'][0]
427 dec_name = self.user_interface_to_opsim['fieldDec'][0]
428 filter_name = self.user_interface_to_opsim['telescopeFilter'][0]
430 # check to see if angles are in degrees or radians
431 if self.user_interface_to_opsim['fieldRA'][1] is None:
432 in_degrees = True
433 else:
434 in_degrees = False
436 # check to make sure the OpSim pointings being supplied contain
437 # the minimum required information
439 for required_column in (ra_name, dec_name, mjd_name, filter_name):
440 if required_column not in OpSimColumns:
441 raise RuntimeError("ObservationMetaDataGenerator requires that the database of "
442 "pointings include data for:\nfieldRA"
443 "\nfieldDec\nexpMJD\nfilter")
445 # construct a raw dict of all of the OpSim columns associated with this pointing
446 raw_dict = dict([(col, pointing[col]) for col in pointing_column_names])
447 raw_dict['opsim_version'] = self.opsim_version
449 if in_degrees:
450 ra_val = pointing[ra_name]
451 dec_val = pointing[dec_name]
452 else:
453 ra_val = np.degrees(pointing[ra_name])
454 dec_val = np.degrees(pointing[dec_name])
455 mjd_val = pointing[mjd_name]
456 filter_val = pointing[filter_name]
458 obs = ObservationMetaData(pointingRA=ra_val,
459 pointingDec=dec_val,
460 mjd=mjd_val,
461 bandpassName=filter_val,
462 boundType=boundType,
463 boundLength=boundLength)
465 m5_name = self.user_interface_to_opsim['m5'][0]
466 rotSky_name = self.user_interface_to_opsim['rotSkyPos'][0]
468 if m5_name in pointing_column_names:
469 obs.m5 = pointing[m5_name]
470 if 'filtSkyBrightness' in pointing_column_names:
471 obs.skyBrightness = pointing['filtSkyBrightness']
472 if self._seeing_column in pointing_column_names:
473 obs.seeing = pointing[self._seeing_column]
474 if rotSky_name in pointing_column_names:
475 if in_degrees:
476 obs.rotSkyPos = pointing[rotSky_name]
477 else:
478 obs.rotSkyPos = np.degrees(pointing[rotSky_name])
480 obs.OpsimMetaData = raw_dict
482 return obs
484 def ObservationMetaDataFromPointingArray(self, OpSimPointingRecords,
485 OpSimColumns=None,
486 boundLength=1.75,
487 boundType='circle'):
488 """
489 Static method to get a list of instances of ObservationMetaData
490 corresponding to the records in `numpy.recarray`, where it uses
491 the dtypes of the recArray for ObservationMetaData attributes that
492 require the dtype.
494 Parameters
495 ----------
496 OpSimPointingRecords : `numpy.recarray` of OpSim Records
497 OpSimColumns : a tuple of strings, optional, defaults to None
498 tuple of column Names of the data in the `numpy.recarray`. If
499 None, these names are extracted from the recarray.
500 boundType : {'circle' or 'box'}
501 denotes the shape of the pointing
502 boundLength : float, optional, defaults to 1.75
503 the bound length of the Pointing in units of degrees. For boundType
504 'box', this is the length of the side of the square box. For boundType
505 'circle' this is the radius.
506 """
508 if OpSimColumns is None:
509 OpSimColumns = OpSimPointingRecords.dtype.names
511 if self.opsim_version is None:
512 if 'obsHistID' in OpSimColumns:
513 self._opsim_version = 3
514 elif 'observationId' in OpSimColumns:
515 self._opsim_version = 4
516 else:
517 raise RuntimeError("Unable to determine which OpSim version your "
518 "OpSimPointingRecords correspond to; make sure "
519 "obsHistID (v3) or observationId (v4) are in the "
520 "records.")
522 out = list(self.ObservationMetaDataFromPointing(OpSimPointingRecord,
523 OpSimColumns=OpSimColumns,
524 boundLength=boundLength,
525 boundType=boundType)
526 for OpSimPointingRecord in OpSimPointingRecords)
528 return out
530 def getObservationMetaData(self, obsHistID=None, expDate=None, night=None, fieldRA=None, fieldDec=None,
531 moonRA=None, moonDec=None, rotSkyPos=None, telescopeFilter=None,
532 rawSeeing=None, seeing=None, sunAlt=None, moonAlt=None, dist2Moon=None,
533 moonPhase=None, expMJD=None, altitude=None, azimuth=None,
534 visitExpTime=None, airmass=None, skyBrightness=None,
535 m5=None, boundType='circle', boundLength=1.75, limit=None):
537 """
538 This method will query the OpSim database summary table according to user-specified
539 constraints and return a list of of ObservationMetaData instantiations consistent
540 with those constraints.
542 @param [in] limit is an integer denoting the maximum number of ObservationMetaData to
543 be returned
545 @param [in] boundType is the boundType of the ObservationMetaData to be returned
546 (see documentation in sims_catalogs_generation/../db/spatialBounds.py for more
547 details)
549 @param [in] boundLength is the boundLength of the ObservationMetaData to be
550 returned (in degrees; see documentation in
551 sims_catalogs_generation/../db/spatialBounds.py for more details)
553 All other input parameters are constraints to be placed on the SQL query of the
554 opsim output db. These contraints can either be tuples of the form (min, max)
555 or an exact value the user wants returned. Note: min and max are inclusive
556 bounds.
558 Parameters that can be constrained are:
560 @param [in] fieldRA in degrees
561 @param [in] fieldDec in degrees
562 @param [in] altitude in degrees
563 @param [in] azimuth in degrees
565 @param [in] moonRA in degrees
566 @param [in] moonDec in degrees
567 @param [in] moonAlt in degrees
568 @param [in] moonPhase (a value from 1 to 100 indicating how much of the moon is illuminated)
569 @param [in] dist2Moon the distance between the telescope pointing and the moon in degrees
571 @param [in] sunAlt in degrees
573 @param [in[ rotSkyPos (the angle of the sky with respect to the camera coordinate system) in degrees
574 @param [in] telescopeFilter a string that is one of u,g,r,i,z,y
576 @param [in] airmass
577 @param [in] rawSeeing (this is an idealized seeing at zenith at 500nm in arcseconds)
578 @param [in] seeing (this is the OpSim column 'FWHMeff' or 'finSeeing' [deprecated] in arcseconds)
580 @param [in] visitExpTime the exposure time in seconds
581 @param [in] obsHistID the integer used by OpSim to label pointings
582 @param [in] expDate is the date of the exposure (units????)
583 @param [in] expMJD is the MJD of the exposure
584 @param [in] night is the night (an int starting at zero) on which the observation took place
585 @param [in] m5 is the five sigma depth of the observation
586 @param [in] skyBrightness
587 """
589 OpSimPointingRecords = self.getOpSimRecords(obsHistID=obsHistID,
590 expDate=expDate,
591 night=night,
592 fieldRA=fieldRA,
593 fieldDec=fieldDec,
594 moonRA=moonRA,
595 moonDec=moonDec,
596 rotSkyPos=rotSkyPos,
597 telescopeFilter=telescopeFilter,
598 rawSeeing=rawSeeing,
599 seeing=seeing,
600 sunAlt=sunAlt,
601 moonAlt=moonAlt,
602 dist2Moon=dist2Moon,
603 moonPhase=moonPhase,
604 expMJD=expMJD,
605 altitude=altitude,
606 azimuth=azimuth,
607 visitExpTime=visitExpTime,
608 airmass=airmass,
609 skyBrightness=skyBrightness,
610 m5=m5, boundType=boundType,
611 boundLength=boundLength,
612 limit=limit)
614 output = self.ObservationMetaDataFromPointingArray(OpSimPointingRecords,
615 OpSimColumns=None,
616 boundType=boundType,
617 boundLength=boundLength)
618 return output