roman-snpit-snappl 0.6.0__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of roman-snpit-snappl might be problematic. Click here for more details.

Files changed (75) hide show
  1. {roman_snpit_snappl-0.6.0/roman_snpit_snappl.egg-info → roman_snpit_snappl-0.7.0}/PKG-INFO +1 -1
  2. roman_snpit_snappl-0.7.0/changes/35.snappl.rst +1 -0
  3. roman_snpit_snappl-0.7.0/changes/36.snappl.rst +1 -0
  4. roman_snpit_snappl-0.7.0/changes/37.snappl.rst +1 -0
  5. roman_snpit_snappl-0.7.0/changes/40.snappl.rst +1 -0
  6. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0/roman_snpit_snappl.egg-info}/PKG-INFO +1 -1
  7. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/roman_snpit_snappl.egg-info/SOURCES.txt +5 -0
  8. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/_version.py +2 -2
  9. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/image.py +60 -0
  10. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/psf.py +126 -6
  11. roman_snpit_snappl-0.7.0/snappl/sed.py +168 -0
  12. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.cruft.json +0 -0
  13. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/CODEOWNERS +0 -0
  14. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +0 -0
  15. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md +0 -0
  16. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/ISSUE_TEMPLATE/PR_TEMPLATE.md +0 -0
  17. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/dependabot.yml +0 -0
  18. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/labeler.yml +0 -0
  19. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/workflows/changelog.yml +0 -0
  20. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/workflows/run_labeler.yml +0 -0
  21. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/workflows/run_snappl_tests.yml +0 -0
  22. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/workflows/sphinx-deploy.yml +0 -0
  23. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.github/workflows/sub_package_update.yml +0 -0
  24. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.gitignore +0 -0
  25. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/.pre-commit-config.yaml +0 -0
  26. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/CHANGES.rst +0 -0
  27. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/CITATION.cff +0 -0
  28. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/CODE_OF_CONDUCT.md +0 -0
  29. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/CONTRIBUTING.md +0 -0
  30. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/LICENSE +0 -0
  31. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/MANIFEST.in +0 -0
  32. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/README.rst +0 -0
  33. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/.gitkeep +0 -0
  34. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/10.snappl.rst +0 -0
  35. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/13.bugfix.rst +0 -0
  36. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/14.snappl.rst +0 -0
  37. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/15.feature.rst +0 -0
  38. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/16.feature.rst +0 -0
  39. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/18.feature.rst +0 -0
  40. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/20.bugfix.rst +0 -0
  41. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/23.snappl.rst +0 -0
  42. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/26.feature.rst +0 -0
  43. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/3.snappl.rst +0 -0
  44. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/31.feature.rst +0 -0
  45. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/5.snappl.rst +0 -0
  46. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/8.snappl.rst +0 -0
  47. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/changes/9.snappl.rst +0 -0
  48. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/codespell-ignore.txt +0 -0
  49. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/Makefile +0 -0
  50. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/_static/logo_black_filled.png +0 -0
  51. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/changes.rst +0 -0
  52. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/conf.py +0 -0
  53. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/index.rst +0 -0
  54. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/installation.rst +0 -0
  55. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/make.bat +0 -0
  56. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/docs/usage.rst +0 -0
  57. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/experimentation/README.md +0 -0
  58. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/experimentation/play_with_photutils.py +0 -0
  59. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/licenses/.DS_Store +0 -0
  60. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/licenses/LICENSE.rst +0 -0
  61. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/licenses/README.rst +0 -0
  62. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/licenses/TEMPLATE_LICENSE.rst +0 -0
  63. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/pyproject.toml +0 -0
  64. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/roman_snpit_snappl.egg-info/dependency_links.txt +0 -0
  65. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/roman_snpit_snappl.egg-info/not-zip-safe +0 -0
  66. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/roman_snpit_snappl.egg-info/requires.txt +0 -0
  67. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/roman_snpit_snappl.egg-info/top_level.txt +0 -0
  68. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/setup.cfg +0 -0
  69. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/setup.py +0 -0
  70. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/__init__.py +0 -0
  71. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/_dev/__init__.py +0 -0
  72. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/_dev/scm_version.py +0 -0
  73. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/data/README.rst +0 -0
  74. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/snappl/wcs.py +0 -0
  75. {roman_snpit_snappl-0.6.0 → roman_snpit_snappl-0.7.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roman_snpit_snappl
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Photometry utilities for the Roman SNPIT
5
5
  Author: Roman Supernove Project Infrastructure Team
6
6
  Maintainer-email: Roman SN PIT <raknop@lbl.gov>
@@ -0,0 +1 @@
1
+ photon ops now actually turns off
@@ -0,0 +1 @@
1
+ created OU24 SED Collection object
@@ -0,0 +1 @@
1
+ Added the non-slow ou24PSF class
@@ -0,0 +1 @@
1
+ Added manual fits image and test
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roman_snpit_snappl
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Photometry utilities for the Roman SNPIT
5
5
  Author: Roman Supernove Project Infrastructure Team
6
6
  Maintainer-email: Roman SN PIT <raknop@lbl.gov>
@@ -35,6 +35,10 @@ changes/23.snappl.rst
35
35
  changes/26.feature.rst
36
36
  changes/3.snappl.rst
37
37
  changes/31.feature.rst
38
+ changes/35.snappl.rst
39
+ changes/36.snappl.rst
40
+ changes/37.snappl.rst
41
+ changes/40.snappl.rst
38
42
  changes/5.snappl.rst
39
43
  changes/8.snappl.rst
40
44
  changes/9.snappl.rst
@@ -62,6 +66,7 @@ snappl/__init__.py
62
66
  snappl/_version.py
63
67
  snappl/image.py
64
68
  snappl/psf.py
69
+ snappl/sed.py
65
70
  snappl/wcs.py
66
71
  snappl/_dev/__init__.py
67
72
  snappl/_dev/scm_version.py
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.6.0'
21
- __version_tuple__ = version_tuple = (0, 6, 0)
20
+ __version__ = version = '0.7.0'
21
+ __version_tuple__ = version_tuple = (0, 7, 0)
@@ -386,6 +386,42 @@ class FITSImage( Numpy2DImage ):
386
386
  self._wcs = GalsimWCS.from_header( hdr )
387
387
  return self._wcs
388
388
 
389
+ def get_data(self, which="all"):
390
+ if self._is_cutout:
391
+ raise RuntimeError(
392
+ "get_data called on a cutout image, this will return the ORIGINAL UNCUT image. Currently not supported."
393
+ )
394
+ if which not in Image.data_array_list:
395
+ raise ValueError(f"Unknown which {which}, must be all, data, noise, or flags")
396
+
397
+ if (which == "all"):
398
+ if (self._data is not None) and (self._noise is not None) and (self._flags is not None):
399
+ return [self._data, self._noise, self._flags]
400
+ else:
401
+ raise RuntimeError(
402
+ f"get_data called with which='all', but not all data arrays are set. "
403
+ f"Data: {self._data is not None}, Noise: {self._noise is not None},"
404
+ f" Flags: {self._flags is not None}"
405
+ )
406
+
407
+ if (which == "data"):
408
+ if (self._data is not None):
409
+ return [self._data]
410
+ else:
411
+ raise RuntimeError("get_data called with which='data', but data is not set.")
412
+
413
+ if (which == "noise"):
414
+ if (self._noise is not None):
415
+ return [self._noise]
416
+ else:
417
+ raise RuntimeError("get_data called with which='noise', but noise is not set.")
418
+
419
+ if (which == "flags"):
420
+ if (self._flags is not None):
421
+ return [self._flags]
422
+ else:
423
+ raise RuntimeError("get_data called with which='flags', but flags are not set.")
424
+
389
425
  def get_cutout(self, x, y, xsize, ysize=None):
390
426
  """Creates a new snappl image object that is a cutout of the original image, at a location in pixel-space.
391
427
 
@@ -554,3 +590,27 @@ class OpenUniverse2024FITSImage( FITSImage ):
554
590
  def _get_zeropoint( self ):
555
591
  header = self._get_header()
556
592
  return galsim.roman.getBandpasses()[self.band].zeropoint + header['ZPTMAG']
593
+
594
+
595
+ class ManualFITSImage(FITSImage):
596
+ def __init__(self, header, data, noise=None, flags=None, path = None, exposure = None, sca = None, *args, **kwargs):
597
+
598
+ self._data = data
599
+ self._noise = noise
600
+ self._flags = flags
601
+ self._header = header
602
+ self._wcs = None
603
+ self._is_cutout = False
604
+ self._image_shape = None
605
+
606
+ self.inputs = types.SimpleNamespace()
607
+ self.inputs.path = None
608
+ self.inputs.exposure = None
609
+ self.inputs.sca = None
610
+
611
+ def _get_header(self):
612
+ """Get the header of the image."""
613
+ if self._header is None:
614
+ raise RuntimeError("Header is not set for ManualFITSImage.")
615
+ return self._header
616
+
@@ -1103,6 +1103,7 @@ class ou24PSF_slow( PSF ):
1103
1103
  raise ValueError( f"PSF would be rendered at ({stampx},{stampy}), which is too far off of the "
1104
1104
  f"edge of a {self.stamp_size}-pixel stamp." )
1105
1105
 
1106
+
1106
1107
  if (x, y, stampx, stampy) not in self._stamps:
1107
1108
  rmutils = roman_utils( self.config_file, self.pointing, self.sca )
1108
1109
  if seed is not None:
@@ -1113,7 +1114,6 @@ class ou24PSF_slow( PSF ):
1113
1114
  # TODO : does rmutils.getLocalWCS want 1-indexed or 0-indexed coordinates???
1114
1115
  wcs = rmutils.getLocalWCS( x+1, y+1 )
1115
1116
  stamp = galsim.Image( self.stamp_size, self.stamp_size, wcs=wcs )
1116
-
1117
1117
  point = ( galsim.DeltaFunction() * self.sed ).withFlux( flux, rmutils.bpass )
1118
1118
  # TODO : make sure that rmutils.getPSF wants 1-indexed positions (which we assume here).
1119
1119
  # (This is not that big a deal, because the PSF is not going to vary significantly
@@ -1124,18 +1124,138 @@ class ou24PSF_slow( PSF ):
1124
1124
 
1125
1125
  # Note the +1s in galsim.PositionD below; galsim uses 1-indexed pixel positions,
1126
1126
  # whereas snappl uses 0-indexed pixel positions
1127
- point.drawImage( rmutils.bpass, method='phot', rng=rmutils.rng, photon_ops=photon_ops,
1128
- n_photons=self.n_photons, maxN=self.n_photons, poisson_flux=False,
1129
- center=galsim.PositionD(stampx+1, stampy+1), use_true_center=True, image=stamp )
1130
- self._stamps[(x, y, stampx, stampy)] = stamp.array
1127
+ center = galsim.PositionD(stampx+1, stampy+1)
1128
+ # Note: self.include_photonOps is a bool that states whether we are
1129
+ # shooting photons or not, photon_ops is the actual map (not sure
1130
+ # if that's the correct word) that describes where the photons
1131
+ # should be shot, with some randomness.
1132
+ if self.include_photonOps:
1133
+ point.drawImage( rmutils.bpass, method='phot', rng=rmutils.rng, photon_ops=photon_ops,
1134
+ n_photons=self.n_photons, maxN=self.n_photons, poisson_flux=False,
1135
+ center=center, use_true_center=True, image=stamp )
1136
+
1137
+ else:
1138
+ psf = galsim.Convolve(point, photon_ops[0])
1139
+ psf.drawImage(rmutils.bpass, method="no_pixel", center=center,
1140
+ use_true_center=True, image=stamp, wcs=wcs)
1131
1141
 
1142
+ self._stamps[(x, y, stampx, stampy)] = stamp.array
1132
1143
 
1133
1144
  return self._stamps[(x, y, stampx, stampy)]
1134
1145
 
1135
1146
 
1136
1147
  # TODO : make a ou24PSF that makes an image and caches... when things are working better
1137
1148
  class ou24PSF( ou24PSF_slow ):
1138
- pass
1149
+ def __init__( self, *args, **kwargs ):
1150
+ super().__init__(*args, **kwargs)
1151
+ self._psf = None
1152
+
1153
+ def _init_psf_object( self, x0=None, y0=None, flux=1.):
1154
+ """Create the galsim PSF object, WCS, and galsim.chromatic.SimpleChromaticTransformation
1155
+ that can be reused for multiple calls to get_stamp.
1156
+
1157
+ Parameters are as in PSF.get_stamp, plus:
1158
+
1159
+ Parameters
1160
+ ----------
1161
+ seed : int
1162
+ A random seed to pass to galsim.BaseDeviate for photonOps.
1163
+ NOTE: this is not part of the base PSF interface (at least,
1164
+ as of yet), so don't use it in production pipeline code.
1165
+ However, it will be useful in tests for purposes of testing
1166
+ reproducibility.
1167
+
1168
+
1169
+ """
1170
+ self._rmutils = roman_utils(self.config_file, self.pointing, self.sca)
1171
+ self._psf = self._rmutils.getPSF(x0+1, y0+1, pupil_bin=8)
1172
+ # TODO : does rmutils.getLocalWCS want 1-indexed or 0-indexed coordinates???
1173
+ self._wcs = self._rmutils.getLocalWCS( x0+1, y0+1 )
1174
+ self._stamp = galsim.Image( self.stamp_size, self.stamp_size, wcs=self._wcs )
1175
+ self._point = ( galsim.DeltaFunction() * self.sed ).withFlux( flux, self._rmutils.bpass )
1176
+ self._convolved_psf = galsim.Convolve(self._point, self._psf)
1177
+ # This is only used to ensure the user isn't trying to move the PSF around
1178
+ self._stored_x0 = x0
1179
+ self._stored_y0 = y0
1180
+
1181
+ def get_stamp(self, x=None, y=None, x0=None, y0=None, flux=1.0, seed=None):
1182
+ """Return a 2d numpy image of the PSF at the image resolution.
1183
+ Parameters are as in PSF.get_stamp, plus:
1184
+
1185
+ Parameters
1186
+ ----------
1187
+ seed : int
1188
+ A random seed to pass to galsim.BaseDeviate for photonOps.
1189
+ NOTE: this is not part of the base PSF interface (at least,
1190
+ as of yet), so don't use it in production pipeline code.
1191
+ However, it will be useful in tests for purposes of testing
1192
+ reproducibility.
1193
+
1194
+ """
1195
+
1196
+ # If a position is not given, assume the middle of the SCA
1197
+ # (within 1/2 pixel; by default, we want to make x and y
1198
+ # centered on a pixel).
1199
+
1200
+
1201
+ x = x if x is not None else float( self.sca_size // 2 )
1202
+ y = y if y is not None else float( self.sca_size // 2 )
1203
+
1204
+ xc = int( np.floor( x + 0.5 ) )
1205
+ yc = int( np.floor( y + 0.5 ) )
1206
+ x0 = xc if x0 is None else x0
1207
+ y0 = yc if y0 is None else y0
1208
+
1209
+ if ( not isinstance( x0, numbers.Integral ) ) or ( not isinstance( y0, numbers.Integral ) ):
1210
+ raise TypeError( f"x0 and y0 must be integers; got x0 as a {type(x0)} and y0 as a {type(y0)}" )
1211
+
1212
+ if self._psf is None:
1213
+ SNLogger.debug( "Initializing ou24PSF galsim PSF object." )
1214
+ # If we don't have a psf object, then we need to initialize it, we then re use it for multiple calls to
1215
+ # get_stamp.
1216
+ self._init_psf_object( x0=x0, y0=y0, flux=flux)
1217
+ else:
1218
+ if x0 != self._stored_x0 or y0 != self._stored_y0:
1219
+ raise ValueError("ou24PSF.get_stamp called with x0 or y0 that does not match the x0 or y0 used"
1220
+ "to initialize the PSF object. If you want to recreate the PSF object, use "
1221
+ "ou24PSF_slow instead.")
1222
+
1223
+ stampx = self.stamp_size // 2 + ( x - x0 )
1224
+ stampy = self.stamp_size // 2 + ( y - y0 )
1225
+
1226
+ if ( ( stampx < -self.stamp_size ) or ( stampx > 2.*self.stamp_size ) or
1227
+ ( stampy < -self.stamp_size ) or ( stampy > 2.*self.stamp_size ) ):
1228
+ raise ValueError( f"PSF would be rendered at ({stampx},{stampy}), which is too far off of the "
1229
+ f"edge of a {self.stamp_size}-pixel stamp." )
1230
+
1231
+ if (x, y, stampx, stampy) not in self._stamps:
1232
+
1233
+ if seed is not None:
1234
+ self._rmutils.rng = galsim.BaseDeviate( seed )
1235
+
1236
+ photon_ops = [ self._psf ]
1237
+ if self.include_photonOps:
1238
+ photon_ops += self._rmutils.photon_ops
1239
+
1240
+ # Note the +1s in galsim.PositionD below; galsim uses 1-indexed pixel positions,
1241
+ # whereas snappl uses 0-indexed pixel positions
1242
+ center = galsim.PositionD(stampx+1, stampy+1)
1243
+ # Note: self.include_photonOps is a bool that states whether we are
1244
+ # shooting photons or not, photon_ops is the actual map (not sure
1245
+ # if that's the correct word) that describes where the photons
1246
+ # should be shot, with some randomness.
1247
+ if self.include_photonOps:
1248
+ self._point.drawImage(self._rmutils.bpass, method='phot', rng=self._rmutils.rng, photon_ops=photon_ops,
1249
+ n_photons=self.n_photons, maxN=self.n_photons, poisson_flux=False,
1250
+ center=center, use_true_center=True, image=self._stamp)
1251
+
1252
+ else:
1253
+ self._convolved_psf.drawImage(self._rmutils.bpass, method="no_pixel", center=center,
1254
+ use_true_center=True, image=self._stamp, wcs=self._wcs)
1255
+
1256
+ self._stamps[(x, y, stampx, stampy)] = self._stamp.array
1257
+
1258
+ return self._stamps[(x, y, stampx, stampy)]
1139
1259
 
1140
1260
  # class ou24PSF( OversampledImagePSF ):
1141
1261
  # """An OversampledImagePSF that renders its internally stored image from a galsim roman_imsim PSF.
@@ -0,0 +1,168 @@
1
+ # Put in necessary imports
2
+ import numpy as np
3
+ import os
4
+ import h5py
5
+ import galsim
6
+ import pathlib
7
+ import pandas as pd
8
+
9
+
10
+ from snpit_utils.config import Config
11
+ from snpit_utils.logger import SNLogger
12
+
13
+
14
+ class SED_collection:
15
+ def __init__(self, *args, **kwargs):
16
+ pass
17
+
18
+ def get_sed(self, filename=None, snid=None, mjd=None):
19
+ """Return a galsim SED."""
20
+ raise NotImplementedError( f"{self.__class__.__name__} needs to implement get_sed" )
21
+
22
+
23
+ class Flat_SED( SED_collection ):
24
+ def __init__( self ):
25
+ self.sed = galsim.SED( galsim.LookupTable( [1000, 26000], [1, 1], interpolant='linear' ),
26
+ wave_type='Angstrom', flux_type='fphotons' )
27
+
28
+ def get_sed( self, **kwargs):
29
+ return self.sed
30
+
31
+ class Single_CSV_SED( SED_collection ):
32
+ def __init__( self, csv_file ):
33
+ # READ THE CSV FILE, make a galsim SED in self.sed
34
+ raise NotImplementedError( "Single_CSV_SED is not implemented yet.")
35
+
36
+ def get_sed( self, **kwargs ):
37
+ return self.sed
38
+
39
+
40
+ class OU2024_Truth_SED(SED_collection):
41
+ def __init__(self, snid=None, isstar=False):
42
+ # if (snid is None) or (sn_path is None):
43
+ # raise ValueError("Must specify all of snid, sn_path")
44
+
45
+ self.snid = snid
46
+ cfg = Config.get()
47
+ self.sn_path = cfg.value("ou24.sn_truth_dir")
48
+ self.isstar = isstar
49
+
50
+ if isstar:
51
+ self.lam_array, self.flambda_array = \
52
+ self._ou24_get_star_SED()
53
+ else:
54
+ self.lam_array, self.flambda_array, self.mjd_array = \
55
+ self._ou24_get_SN_SED()
56
+
57
+ def get_sed(self, snid=None, mjd=None):
58
+ """Return an SED for the given snid and mjd.
59
+ Inputs:
60
+ snid: the ID of the object
61
+ mjd: the MJD of the observation (only used if this is a SN)
62
+
63
+ Returns:
64
+ galsim.SED: the SED for the given snid and mjd, if a supernova, for the ID if a star.
65
+ """
66
+ assert snid == self.snid, "ID does not match the SED collection ID."
67
+
68
+ if not self.isstar:
69
+ # If this is a SN, we need to find the closest SED to the given MJD.
70
+ bestindex = np.argmin(np.abs(np.array(self.mjd_array) - mjd))
71
+ max_days_cutoff = 10
72
+ closest_days_away = np.min(np.abs(np.array(self.mjd_array) - mjd))
73
+
74
+ if closest_days_away > max_days_cutoff:
75
+ SNLogger.warning(f"WARNING: No SED data within {max_days_cutoff} days of "
76
+ + f"date. \n The closest SED is {closest_days_away} days away.")
77
+
78
+ return galsim.SED(
79
+ galsim.LookupTable(np.array(self.lam_array), (self.flambda_array[bestindex]), interpolant="linear"),
80
+ wave_type="Angstrom",
81
+ flux_type="fphotons",
82
+ )
83
+
84
+ else:
85
+ # If this is a star, we just return the SED
86
+ return galsim.SED(
87
+ galsim.LookupTable(np.array(self.lam_array), (self.flambda_array), interpolant="linear"),
88
+ wave_type="Angstrom",
89
+ flux_type="fphotons",
90
+ )
91
+
92
+ def _ou24_open_parquet(self, parq, obj_type="SN", engine="fastparquet"):
93
+ """Convenience function to open a parquet file given its number."""
94
+ path = self.sn_path
95
+ file_prefix = {"SN": "snana", "star": "pointsource"}
96
+ base_name = "{:s}_{}.parquet".format(file_prefix[obj_type], parq)
97
+ file_path = os.path.join(path, base_name)
98
+ df = pd.read_parquet(file_path, engine=engine)
99
+ return df
100
+
101
+ def _ou24_find_parquet(self, obj_type="SN"):
102
+ """Find the parquet file that contains a given supernova ID."""
103
+ path = self.sn_path
104
+ ID = self.snid
105
+ files = os.listdir(path)
106
+ SNLogger.debug(f"Looking for parquet files in {path} for {obj_type} with ID {ID}")
107
+ file_prefix = {"SN": "snana", "star": "pointsource"}
108
+ files = [f for f in files if file_prefix[obj_type] in f]
109
+ files = [f for f in files if ".parquet" in f]
110
+ files = [f for f in files if "flux" not in f]
111
+
112
+ for f in files:
113
+ pqfile = int(f.split("_")[1].split(".")[0])
114
+ df = self._ou24_open_parquet(pqfile, obj_type=obj_type)
115
+ if ID in df.id.values or str(ID) in df.id.values:
116
+ return pqfile
117
+
118
+ def _ou24_get_star_SED(self):
119
+ """Return the appropriate SED for the star.
120
+ Inputs:
121
+ SNID: the ID of the object
122
+ sn_path: the path to the supernova data
123
+
124
+ Returns:
125
+ lam: the wavelength of the SED in Angstrom (numpy array of floats)
126
+ flambda: the flux of the SED units in erg/s/cm^2/Angstrom
127
+ (numpy array of floats)
128
+ """
129
+
130
+ filenum = self._ou24_find_parquet(obj_type="star")
131
+ pqfile = self._ou24_open_parquet(filenum, obj_type="star")
132
+ file_name = pqfile[pqfile["id"] == str(self.snid)]["sed_filepath"].values[0]
133
+ fullpath = pathlib.Path(Config.get().value("ou24.sims_sed_library")) / file_name
134
+ sed_table = pd.read_csv(fullpath, compression="gzip", sep=r"\s+", comment="#")
135
+ lam = sed_table.iloc[:, 0]
136
+ flambda = sed_table.iloc[:, 1]
137
+ return np.array(lam), np.array(flambda)
138
+
139
+ def _ou24_get_SN_SED(self):
140
+ """Return the appropriate SED for the supernova on the given day.
141
+
142
+ Inputs:
143
+ SNID: the ID of the object
144
+ sn_path: the path to the supernova data
145
+
146
+ Returns:
147
+ lam: the wavelength of the SED in Angstrom
148
+ flambda: the flux of the SED units in erg/s/cm^2/Angstrom
149
+ """
150
+ filenum = self._ou24_find_parquet(obj_type="SN")
151
+ file_name = "snana" + "_" + str(filenum) + ".hdf5"
152
+ fullpath = os.path.join(self.sn_path, file_name)
153
+ # Setting locking=False on the next line becasue it seems that you can't
154
+ # open an h5py file unless you have write access to... something.
155
+ # Not sure what. The directory where it exists? We won't
156
+ # always have that. It's scary to set locking to false, because it
157
+ # subverts all kinds of safety stuff that hdf5 does. However,
158
+ # because these files were created once in this case, it's not actually
159
+ # scary, and we expect them to be static. Locking only matters if you
160
+ # think somebody else might change the file
161
+ # while you're in the middle of reading bits of it.
162
+ sed_table = h5py.File(fullpath, "r", locking=False)
163
+ sed_table = sed_table[str(self.snid)]
164
+ flambda = sed_table["flambda"]
165
+ lam = sed_table["lambda"]
166
+ mjd = sed_table["mjd"]
167
+
168
+ return np.array(lam), np.array(flambda), np.array(mjd)