Commit 5cf58fba17bde67b4bfb64077d78d1fc67ed2a64
1 parent
2b504733
Add "calutils" module to cscripts (#1830)
There was a code duplication in csroot2caldb and csobs2caldb, which is now avoided by exporting the code in the "calutils" module.
Showing
5 changed files
with
124 additions
and
117 deletions
cscripts/Makefile.am
| ... | ... | @@ -44,6 +44,7 @@ pythonscripts = $(builddir)/__init__.py \ |
| 44 | 44 | $(srcdir)/csiactobs.py \ |
| 45 | 45 | $(srcdir)/obsutils.py \ |
| 46 | 46 | $(srcdir)/modutils.py \ |
| 47 | + $(srcdir)/calutils.py \ | |
| 47 | 48 | $(srcdir)/ioutils.py |
| 48 | 49 | |
| 49 | 50 | # Parfiles to be distributed in $(prefix)/syspfiles |
| ... | ... | @@ -206,6 +207,7 @@ clean-local: |
| 206 | 207 | rm -rf $(builddir)/cs*.py; \ |
| 207 | 208 | rm -rf $(builddir)/obsutils.py; \ |
| 208 | 209 | rm -rf $(builddir)/modutils.py; \ |
| 210 | + rm -rf $(builddir)/calutils.py; \ | |
| 209 | 211 | rm -rf $(builddir)/ioutils.py; \ |
| 210 | 212 | rm -rf $(builddir)/*.par; \ |
| 211 | 213 | fi | ... | ... |
cscripts/__init__.py.in
| ... | ... | @@ -43,6 +43,7 @@ __all__ = [ |
| 43 | 43 | "csworkflow", |
| 44 | 44 | "obsutils", |
| 45 | 45 | "modutils", |
| 46 | + "calutils", | |
| 46 | 47 | "ioutils" |
| 47 | 48 | ] |
| 48 | 49 | if cur_version > req_version: |
| ... | ... | @@ -71,6 +72,7 @@ from cscripts.cstsmapsplit import cstsmapsplit |
| 71 | 72 | from cscripts.csworkflow import csworkflow |
| 72 | 73 | from cscripts import obsutils |
| 73 | 74 | from cscripts import modutils |
| 75 | +from cscripts import calutils | |
| 74 | 76 | from cscripts import ioutils |
| 75 | 77 | if cur_version > req_version: |
| 76 | 78 | from cscripts.csfindobs import csfindobs | ... | ... |
cscripts/calutils.py
0 → 100755
| 1 | +# ========================================================================== | |
| 2 | +# Utility functions for calibration handling | |
| 3 | +# | |
| 4 | +# Copyright (C) 2016 Juergen Knoedlseder | |
| 5 | +# | |
| 6 | +# This program is free software: you can redistribute it and/or modify | |
| 7 | +# it under the terms of the GNU General Public License as published by | |
| 8 | +# the Free Software Foundation, either version 3 of the License, or | |
| 9 | +# (at your option) any later version. | |
| 10 | +# | |
| 11 | +# This program is distributed in the hope that it will be useful, | |
| 12 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | +# GNU General Public License for more details. | |
| 15 | +# | |
| 16 | +# You should have received a copy of the GNU General Public License | |
| 17 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 18 | +# | |
| 19 | +# ========================================================================== | |
| 20 | +import gammalib | |
| 21 | + | |
| 22 | + | |
| 23 | +# ======================= # | |
| 24 | +# Create CIF binary table # | |
| 25 | +# ======================= # | |
| 26 | +def create_cif_table(): | |
| 27 | + """ | |
| 28 | + Create Calibration Database Index File binary table | |
| 29 | + """ | |
| 30 | + # Create binary table | |
| 31 | + table = gammalib.GFitsBinTable() | |
| 32 | + | |
| 33 | + # Append columns. Reference: CAL/GEN/92-008 | |
| 34 | + table.append(gammalib.GFitsTableStringCol("TELESCOP", 0, 10)) | |
| 35 | + table.append(gammalib.GFitsTableStringCol("INSTRUME", 0, 10)) | |
| 36 | + table.append(gammalib.GFitsTableStringCol("DETNAM", 0, 20)) | |
| 37 | + table.append(gammalib.GFitsTableStringCol("FILTER", 0, 10)) | |
| 38 | + table.append(gammalib.GFitsTableStringCol("CAL_DEV", 0, 20)) | |
| 39 | + table.append(gammalib.GFitsTableStringCol("CAL_DIR", 0, 70)) | |
| 40 | + table.append(gammalib.GFitsTableStringCol("CAL_FILE", 0, 40)) | |
| 41 | + table.append(gammalib.GFitsTableStringCol("CAL_CLAS", 0, 3)) | |
| 42 | + table.append(gammalib.GFitsTableStringCol("CAL_DTYP", 0, 4)) | |
| 43 | + table.append(gammalib.GFitsTableStringCol("CAL_CNAM", 0, 20)) | |
| 44 | + table.append(gammalib.GFitsTableStringCol("CAL_CBD", 0, 70, 9)) | |
| 45 | + table.append(gammalib.GFitsTableShortCol("CAL_XNO", 0)) | |
| 46 | + table.append(gammalib.GFitsTableStringCol("CAL_VSD", 0, 10)) | |
| 47 | + table.append(gammalib.GFitsTableStringCol("CAL_VST", 0, 8)) | |
| 48 | + table.append(gammalib.GFitsTableDoubleCol("REF_TIME", 0)) | |
| 49 | + table.append(gammalib.GFitsTableShortCol("CAL_QUAL", 0)) | |
| 50 | + table.append(gammalib.GFitsTableStringCol("CAL_DATE", 0, 8)) | |
| 51 | + table.append(gammalib.GFitsTableStringCol("CAL_DESC", 0, 70)) | |
| 52 | + | |
| 53 | + # Set keywords. Reference: CAL/GEN/92-008 | |
| 54 | + table.extname("CIF") | |
| 55 | + table.card("CIFVERSN", "1992a", "Version of CIF format") | |
| 56 | + | |
| 57 | + # Return table | |
| 58 | + return table | ... | ... |
cscripts/csobs2caldb.py
| ... | ... | @@ -23,6 +23,7 @@ import os |
| 23 | 23 | import datetime |
| 24 | 24 | import gammalib |
| 25 | 25 | import ctools |
| 26 | +from cscripts import calutils | |
| 26 | 27 | |
| 27 | 28 | |
| 28 | 29 | # ================= # |
| ... | ... | @@ -43,11 +44,11 @@ class csobs2caldb(ctools.cscript): |
| 43 | 44 | # Constructor |
| 44 | 45 | def __init__(self, *argv): |
| 45 | 46 | """ |
| 46 | - Constructor. | |
| 47 | + Constructor | |
| 47 | 48 | """ |
| 48 | 49 | # Set name and version |
| 49 | 50 | self._name = 'csobs2caldb' |
| 50 | - self._version = '1.1.0' | |
| 51 | + self._version = '1.2.0' | |
| 51 | 52 | |
| 52 | 53 | # Initialise members |
| 53 | 54 | self._observation = gammalib.GCTAObservation() |
| ... | ... | @@ -137,7 +138,7 @@ class csobs2caldb(ctools.cscript): |
| 137 | 138 | |
| 138 | 139 | def _make_irf_file(self): |
| 139 | 140 | """ |
| 140 | - Creates an IRF FITS file. | |
| 141 | + Creates an IRF FITS file | |
| 141 | 142 | """ |
| 142 | 143 | # Write header into logger |
| 143 | 144 | if self._logTerse(): |
| ... | ... | @@ -209,59 +210,33 @@ class csobs2caldb(ctools.cscript): |
| 209 | 210 | |
| 210 | 211 | def _make_caldb_index(self): |
| 211 | 212 | """ |
| 212 | - Creates an IRF index FITS file. | |
| 213 | + Creates an IRF index FITS file | |
| 213 | 214 | """ |
| 214 | 215 | # Write header into logger |
| 215 | 216 | if self._logTerse(): |
| 216 | - self._log("\n") | |
| 217 | - self._log.header2("Creating database index file") | |
| 217 | + self._log('\n') | |
| 218 | + self._log.header2('Creating database index file') | |
| 218 | 219 | |
| 219 | 220 | # Open calibration database index |
| 220 | - indx_file = self._base_dir+"/caldb.indx" | |
| 221 | + indx_file = self._base_dir + '/caldb.indx' | |
| 221 | 222 | |
| 222 | 223 | # Open index file (or create one if it does not exist) |
| 223 | 224 | cif = gammalib.GFits(indx_file, True) |
| 224 | 225 | |
| 225 | 226 | # Retrieve "CIF" table |
| 226 | - if cif.contains("CIF"): | |
| 227 | - table = cif.table("CIF") | |
| 227 | + if cif.contains('CIF'): | |
| 228 | + table = cif.table('CIF') | |
| 228 | 229 | |
| 229 | - # ... or create binary table if no "CIF" table exists yet | |
| 230 | + # ... or create binary table if no "CIF" table exists yet, append | |
| 231 | + # binary table to CIF file and return a reference to the appended | |
| 232 | + # table to work with | |
| 230 | 233 | else: |
| 231 | - | |
| 232 | - # Create empty binary table | |
| 233 | - bintable = gammalib.GFitsBinTable() | |
| 234 | - bintable.extname("CIF") | |
| 235 | - bintable.card("CIFVERSN", "1992a", "Version of CIF format") | |
| 236 | - | |
| 237 | - # Append table to FITS file and recover a reference to the | |
| 238 | - # appended table | |
| 239 | - table = cif.append(bintable) | |
| 240 | - | |
| 241 | - # Attach columns. Reference: CAL/GEN/92-008 | |
| 242 | - table.append(gammalib.GFitsTableStringCol("TELESCOP", 0, 10)) | |
| 243 | - table.append(gammalib.GFitsTableStringCol("INSTRUME", 0, 10)) | |
| 244 | - table.append(gammalib.GFitsTableStringCol("DETNAM", 0, 20)) | |
| 245 | - table.append(gammalib.GFitsTableStringCol("FILTER", 0, 10)) | |
| 246 | - table.append(gammalib.GFitsTableStringCol("CAL_DEV", 0, 20)) | |
| 247 | - table.append(gammalib.GFitsTableStringCol("CAL_DIR", 0, 70)) | |
| 248 | - table.append(gammalib.GFitsTableStringCol("CAL_FILE", 0, 40)) | |
| 249 | - table.append(gammalib.GFitsTableStringCol("CAL_CLAS", 0, 3)) | |
| 250 | - table.append(gammalib.GFitsTableStringCol("CAL_DTYP", 0, 4)) | |
| 251 | - table.append(gammalib.GFitsTableStringCol("CAL_CNAM", 0, 20)) | |
| 252 | - table.append(gammalib.GFitsTableStringCol("CAL_CBD", 0, 70, 9)) | |
| 253 | - table.append(gammalib.GFitsTableShortCol("CAL_XNO", 0)) | |
| 254 | - table.append(gammalib.GFitsTableStringCol("CAL_VSD", 0, 10)) | |
| 255 | - table.append(gammalib.GFitsTableStringCol("CAL_VST", 0, 8)) | |
| 256 | - table.append(gammalib.GFitsTableDoubleCol("REF_TIME", 0)) | |
| 257 | - table.append(gammalib.GFitsTableShortCol("CAL_QUAL", 0)) | |
| 258 | - table.append(gammalib.GFitsTableStringCol("CAL_DATE", 0, 8)) | |
| 259 | - table.append(gammalib.GFitsTableStringCol("CAL_DESC", 0, 70)) | |
| 234 | + table = cif.append(calutils.create_cif_table()) | |
| 260 | 235 | |
| 261 | 236 | # Check if output config already exist |
| 262 | 237 | has_config = False |
| 263 | 238 | row_index = -1 |
| 264 | - for caldir in table["CAL_DIR"]: | |
| 239 | + for caldir in table['CAL_DIR']: | |
| 265 | 240 | row_index += 1 |
| 266 | 241 | if caldir == self._cal_dir: |
| 267 | 242 | has_config = True |
| ... | ... | @@ -286,56 +261,56 @@ class csobs2caldb(ctools.cscript): |
| 286 | 261 | today, time = now.split() |
| 287 | 262 | |
| 288 | 263 | # Set element |
| 289 | - table["TELESCOP"][row] = self._mission | |
| 290 | - table["INSTRUME"][row] = self._caldb | |
| 291 | - table["DETNAM"][row] = "NONE" | |
| 292 | - table["FILTER"][row] = "NONE" | |
| 293 | - table["CAL_DEV"][row] = "ONLINE" | |
| 294 | - table["CAL_CLAS"][row] = "BCF" | |
| 295 | - table["CAL_DTYP"][row] = "DATA" | |
| 296 | - table["CAL_VSD"][row] = today | |
| 297 | - table["CAL_VST"][row] = time.split(".")[0] | |
| 298 | - table["REF_TIME"][row] = 51544.0 | |
| 299 | - table["CAL_QUAL"][row] = 0 | |
| 300 | - table["CAL_CBD"][row] = "NAME("+self["irf"].string()+")" | |
| 301 | - table["CAL_DATE"][row] = today.replace("-","/")[2:] | |
| 302 | - table["CAL_DIR"][row] = self._cal_dir | |
| 303 | - table["CAL_FILE"][row] = self._outfile | |
| 264 | + table['TELESCOP'][row] = self._mission | |
| 265 | + table['INSTRUME'][row] = self._caldb | |
| 266 | + table['DETNAM'][row] = 'NONE' | |
| 267 | + table['FILTER'][row] = 'NONE' | |
| 268 | + table['CAL_DEV'][row] = 'ONLINE' | |
| 269 | + table['CAL_CLAS'][row] = 'BCF' | |
| 270 | + table['CAL_DTYP'][row] = 'DATA' | |
| 271 | + table['CAL_VSD'][row] = today | |
| 272 | + table['CAL_VST'][row] = time.split('.')[0] | |
| 273 | + table['REF_TIME'][row] = 51544.0 | |
| 274 | + table['CAL_QUAL'][row] = 0 | |
| 275 | + table['CAL_CBD'][row] = 'NAME('+self['irf'].string()+')' | |
| 276 | + table['CAL_DATE'][row] = today.replace('-','/')[2:] | |
| 277 | + table['CAL_DIR'][row] = self._cal_dir | |
| 278 | + table['CAL_FILE'][row] = self._outfile | |
| 304 | 279 | |
| 305 | 280 | # Add effective area information |
| 306 | 281 | row = row_index-4 |
| 307 | - table["CAL_CNAM"][row] = "EFF_AREA" | |
| 308 | - table["CAL_DESC"][row] = self._caldb+" effective area" | |
| 282 | + table['CAL_CNAM'][row] = 'EFF_AREA' | |
| 283 | + table['CAL_DESC'][row] = self._caldb+' effective area' | |
| 309 | 284 | |
| 310 | 285 | # Add point spread function information |
| 311 | 286 | row = row_index-3 |
| 312 | - table["CAL_CNAM"][row] = "RPSF" | |
| 313 | - table["CAL_DESC"][row] = self._caldb+" point spread function" | |
| 287 | + table['CAL_CNAM'][row] = 'RPSF' | |
| 288 | + table['CAL_DESC'][row] = self._caldb+' point spread function' | |
| 314 | 289 | |
| 315 | 290 | # Add energy dispersion information |
| 316 | 291 | row = row_index-2 |
| 317 | - table["CAL_CNAM"][row] = "EDISP" | |
| 318 | - table["CAL_DESC"][row] = self._caldb+" energy dispersion" | |
| 292 | + table['CAL_CNAM'][row] = 'EDISP' | |
| 293 | + table['CAL_DESC'][row] = self._caldb+' energy dispersion' | |
| 319 | 294 | |
| 320 | 295 | # Add background information |
| 321 | 296 | row = row_index-1 |
| 322 | - table["CAL_CNAM"][row] = "BGD" | |
| 323 | - table["CAL_DESC"][row] = self._caldb+" background" | |
| 297 | + table['CAL_CNAM'][row] = 'BGD' | |
| 298 | + table['CAL_DESC'][row] = self._caldb+' background' | |
| 324 | 299 | |
| 325 | 300 | # Log resulting FITS table and header |
| 326 | 301 | if self._logNormal(): |
| 327 | 302 | self._log(str(table)) |
| 328 | - self._log("\n") | |
| 303 | + self._log('\n') | |
| 329 | 304 | if self._logExplicit(): |
| 330 | 305 | self._log(str(table.header())) |
| 331 | - self._log("\n") | |
| 306 | + self._log('\n') | |
| 332 | 307 | |
| 333 | 308 | # Return CIF FITS file |
| 334 | 309 | return cif |
| 335 | 310 | |
| 336 | 311 | def _make_dirs(self): |
| 337 | 312 | """ |
| 338 | - Make CALDB directories. | |
| 313 | + Make CALDB directories | |
| 339 | 314 | """ |
| 340 | 315 | # Write header into logger |
| 341 | 316 | if self._logTerse(): |
| ... | ... | @@ -385,7 +360,7 @@ class csobs2caldb(ctools.cscript): |
| 385 | 360 | # Public methods |
| 386 | 361 | def run(self): |
| 387 | 362 | """ |
| 388 | - Run the script. | |
| 363 | + Run the script | |
| 389 | 364 | """ |
| 390 | 365 | # Switch screen logging on in debug mode |
| 391 | 366 | if self._logDebug(): |
| ... | ... | @@ -396,8 +371,8 @@ class csobs2caldb(ctools.cscript): |
| 396 | 371 | |
| 397 | 372 | # Write input parameters and header into logger |
| 398 | 373 | if self._logTerse(): |
| 399 | - self._log("\n") | |
| 400 | - self._log.header1("Creating CALDB entry") | |
| 374 | + self._log('\n') | |
| 375 | + self._log.header1('Creating CALDB entry') | |
| 401 | 376 | |
| 402 | 377 | # Create directory structure |
| 403 | 378 | self._make_dirs() |
| ... | ... | @@ -413,24 +388,24 @@ class csobs2caldb(ctools.cscript): |
| 413 | 388 | |
| 414 | 389 | def save(self): |
| 415 | 390 | """ |
| 416 | - Save calibration database FITS file. | |
| 391 | + Save calibration database FITS file | |
| 417 | 392 | """ |
| 418 | 393 | # Write header into logger |
| 419 | 394 | if self._logTerse(): |
| 420 | - self._log("\n") | |
| 421 | - self._log.header1("Save calibration database") | |
| 395 | + self._log('\n') | |
| 396 | + self._log.header1('Save calibration database') | |
| 422 | 397 | |
| 423 | 398 | # Set response filename |
| 424 | - filename = self._rsp_dir + "/" + self._outfile | |
| 399 | + filename = self._rsp_dir + '/' + self._outfile | |
| 425 | 400 | |
| 426 | 401 | # Write filenames into logger |
| 427 | 402 | if self._logNormal(): |
| 428 | - self._log(gammalib.parformat("CALDB index file")) | |
| 403 | + self._log(gammalib.parformat('CALDB index file')) | |
| 429 | 404 | self._log(self._caldb_inx.filename().url()) |
| 430 | - self._log("\n") | |
| 431 | - self._log(gammalib.parformat("Response file")) | |
| 405 | + self._log('\n') | |
| 406 | + self._log(gammalib.parformat('Response file')) | |
| 432 | 407 | self._log(filename) |
| 433 | - self._log("\n") | |
| 408 | + self._log('\n') | |
| 434 | 409 | |
| 435 | 410 | # Save caldb index file |
| 436 | 411 | self._caldb_inx.save(self._clobber()) |
| ... | ... | @@ -443,7 +418,7 @@ class csobs2caldb(ctools.cscript): |
| 443 | 418 | |
| 444 | 419 | def execute(self): |
| 445 | 420 | """ |
| 446 | - Execute the script. | |
| 421 | + Execute the script | |
| 447 | 422 | """ |
| 448 | 423 | # Open logfile |
| 449 | 424 | self.logFileOpen() | ... | ... |
cscripts/csroot2caldb.py
| ... | ... | @@ -24,6 +24,7 @@ import math |
| 24 | 24 | from datetime import datetime |
| 25 | 25 | import gammalib |
| 26 | 26 | import ctools |
| 27 | +from cscripts import calutils | |
| 27 | 28 | |
| 28 | 29 | # Optional ROOT import |
| 29 | 30 | try: |
| ... | ... | @@ -44,12 +45,11 @@ class csroot2caldb(ctools.cscript): |
| 44 | 45 | # Constructor |
| 45 | 46 | def __init__(self, *argv): |
| 46 | 47 | """ |
| 47 | - CALDB constructor. | |
| 48 | - | |
| 48 | + Constructor | |
| 49 | 49 | """ |
| 50 | 50 | # Set name |
| 51 | - self._name = "csroot2caldb" | |
| 52 | - self._version = "1.0.0" | |
| 51 | + self._name = 'csroot2caldb' | |
| 52 | + self._version = '1.2.0' | |
| 53 | 53 | |
| 54 | 54 | # Initialise some members |
| 55 | 55 | self._cif = None |
| ... | ... | @@ -70,7 +70,7 @@ class csroot2caldb(ctools.cscript): |
| 70 | 70 | # Private methods |
| 71 | 71 | def _get_parameters(self): |
| 72 | 72 | """ |
| 73 | - Get parameters from parfile. | |
| 73 | + Get parameters from parfile | |
| 74 | 74 | """ |
| 75 | 75 | # Query parameters |
| 76 | 76 | self['infile'].filename() |
| ... | ... | @@ -377,40 +377,10 @@ class csroot2caldb(ctools.cscript): |
| 377 | 377 | Keywords: |
| 378 | 378 | None |
| 379 | 379 | """ |
| 380 | - # Create binary table | |
| 381 | - table = gammalib.GFitsBinTable() | |
| 382 | - | |
| 383 | - # Set boundary | |
| 384 | - | |
| 385 | - # Attach columns. Reference: CAL/GEN/92-008 | |
| 386 | - table.append(gammalib.GFitsTableStringCol("TELESCOP", 0, 10)) | |
| 387 | - table.append(gammalib.GFitsTableStringCol("INSTRUME", 0, 10)) | |
| 388 | - table.append(gammalib.GFitsTableStringCol("DETNAM", 0, 20)) | |
| 389 | - table.append(gammalib.GFitsTableStringCol("FILTER", 0, 10)) | |
| 390 | - table.append(gammalib.GFitsTableStringCol("CAL_DEV", 0, 20)) | |
| 391 | - table.append(gammalib.GFitsTableStringCol("CAL_DIR", 0, 70)) | |
| 392 | - table.append(gammalib.GFitsTableStringCol("CAL_FILE", 0, 40)) | |
| 393 | - table.append(gammalib.GFitsTableStringCol("CAL_CLAS", 0, 3)) | |
| 394 | - table.append(gammalib.GFitsTableStringCol("CAL_DTYP", 0, 4)) | |
| 395 | - table.append(gammalib.GFitsTableStringCol("CAL_CNAM", 0, 20)) | |
| 396 | - table.append(gammalib.GFitsTableStringCol("CAL_CBD", 0, 70, 9)) | |
| 397 | - table.append(gammalib.GFitsTableShortCol("CAL_XNO", 0)) | |
| 398 | - table.append(gammalib.GFitsTableStringCol("CAL_VSD", 0, 10)) | |
| 399 | - table.append(gammalib.GFitsTableStringCol("CAL_VST", 0, 8)) | |
| 400 | - table.append(gammalib.GFitsTableDoubleCol("REF_TIME", 0)) | |
| 401 | - table.append(gammalib.GFitsTableShortCol("CAL_QUAL", 0)) | |
| 402 | - table.append(gammalib.GFitsTableStringCol("CAL_DATE", 0, 8)) | |
| 403 | - table.append(gammalib.GFitsTableStringCol("CAL_DESC", 0, 70)) | |
| 404 | - | |
| 405 | - # Set keywords. Reference: CAL/GEN/92-008 | |
| 406 | - table.extname("CIF") | |
| 407 | - table.card("CIFVERSN", "1992a", "Version of CIF format") | |
| 408 | - | |
| 409 | - # Attach table to FITS file. Note that at this moment the | |
| 410 | - # FITS table gets connected to the FITS file. Yet since | |
| 411 | - # nothing was yet written to the FITS file, we cannot read | |
| 412 | - # anything from it. | |
| 413 | - self._cif.append(table) | |
| 380 | + # Attach table to FITS file. Note that at this moment the FITS table | |
| 381 | + # gets connected to the FITS file. Yet since nothing was yet written | |
| 382 | + # to the FITS file, we cannot read anything from it. | |
| 383 | + self._cif.append(calutils.create_cif_table()) | |
| 414 | 384 | |
| 415 | 385 | # Return |
| 416 | 386 | return | ... | ... |