Skip to content

Commit

Permalink
RCAL-911 & 932: remove units from MOS and ELP pipelines. (#405)
Browse files Browse the repository at this point in the history
Co-authored-by: Eddie Schlafly <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Paul Huwe <[email protected]>
  • Loading branch information
4 people authored Oct 17, 2024
1 parent 065b958 commit a4542f1
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 151 deletions.
1 change: 1 addition & 0 deletions changes/405.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove units from roman_datamodels.
31 changes: 7 additions & 24 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ name = "roman_datamodels"
description = "data models supporting calibration of the Nancy Grace Roman Space Telescope"
readme = "README.md"
requires-python = ">=3.10"
authors = [
{ name = "STScI", email = "[email protected]" },
]
authors = [{ name = "STScI", email = "[email protected]" }]
classifiers = [
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering :: Astronomy",
Expand All @@ -22,9 +20,7 @@ dependencies = [
"rad @ git+https://github.com/spacetelescope/rad.git",
"asdf-standard >=1.1.0",
]
dynamic = [
"version",
]
dynamic = ["version"]

[project.license]
file = "LICENSE"
Expand All @@ -36,9 +32,7 @@ test = [
"pytest-doctestplus >=0.10.0",
"pytest-env >= 0.8",
]
aws = [
"stsci-aws-utils >= 0.1.2",
]
aws = ["stsci-aws-utils >= 0.1.2"]
docs = [
"sphinx",
"sphinx-automodapi",
Expand All @@ -55,39 +49,28 @@ repository = "https://github.com/spacetelescope/roman_datamodels"
roman_datamodels = "roman_datamodels.stnode._integration:get_extensions"

[build-system]
requires = [
"setuptools >=61",
"setuptools_scm[toml] >=3.4",
"wheel",
]
requires = ["setuptools >=61", "setuptools_scm[toml] >=3.4", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
write_to = "src/roman_datamodels/_version.py"

[tool.setuptools.packages.find]
where = [
"src",
]
where = ["src"]

[tool.pytest.ini_options]
minversion = 4.6
doctest_plus = true
doctest_rst = true
text_file_format = "rst"
addopts = "--color=yes --doctest-rst"
testpaths = [
"tests",
]
testpaths = ["tests"]
filterwarnings = [
"error",
"ignore:numpy.ndarray size changed:RuntimeWarning",
"ignore:The `hash` argument is deprecated in favor of `unsafe_hash` and will be removed in or after August 2025:DeprecationWarning",
]
env = [
"ROMAN_VALIDATE=true",
"ROMAN_STRICT_VALIDATION=true",
]
env = ["ROMAN_VALIDATE=true", "ROMAN_STRICT_VALIDATION=true"]

[tool.coverage.report]
exclude_lines = [
Expand Down
2 changes: 1 addition & 1 deletion src/roman_datamodels/maker_utils/_common_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def mk_sky_background(**kwargs):
roman_datamodels.stnode.SkyBackground
"""
sb = stnode.SkyBackground()
sb["level"] = kwargs.get("level", NONUM * (u.MJy / u.sr))
sb["level"] = kwargs.get("level", NONUM)
sb["method"] = kwargs.get("method", "None")
sb["subtracted"] = kwargs.get("subtracted", False)

Expand Down
100 changes: 32 additions & 68 deletions src/roman_datamodels/maker_utils/_datamodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,13 @@ def mk_level1_science_raw(*, shape=(8, 4096, 4096), dq=False, filepath=None, **k

n_groups = shape[0]

wfi_science_raw["data"] = kwargs.get("data", u.Quantity(np.zeros(shape, dtype=np.uint16), u.DN, dtype=np.uint16))
wfi_science_raw["data"] = kwargs.get("data", np.zeros(shape, dtype=np.uint16))

if dq:
wfi_science_raw["resultantdq"] = kwargs.get("resultantdq", np.zeros(shape, dtype=np.uint8))

# add amp 33 ref pix
wfi_science_raw["amp33"] = kwargs.get(
"amp33", u.Quantity(np.zeros((n_groups, 4096, 128), dtype=np.uint16), u.DN, dtype=np.uint16)
)
wfi_science_raw["amp33"] = kwargs.get("amp33", np.zeros((n_groups, 4096, 128), dtype=np.uint16))

return save_node(wfi_science_raw, filepath=filepath)

Expand Down Expand Up @@ -110,17 +108,13 @@ def mk_level2_image(*, shape=(4088, 4088), n_groups=8, filepath=None, **kwargs):
wfi_image["meta"] = mk_photometry_meta(**kwargs.get("meta", {}))

# add border reference pixel arrays
wfi_image["border_ref_pix_left"] = kwargs.get(
"border_ref_pix_left", u.Quantity(np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32), u.DN, dtype=np.float32)
)
wfi_image["border_ref_pix_left"] = kwargs.get("border_ref_pix_left", np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32))
wfi_image["border_ref_pix_right"] = kwargs.get(
"border_ref_pix_right", u.Quantity(np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32), u.DN, dtype=np.float32)
)
wfi_image["border_ref_pix_top"] = kwargs.get(
"border_ref_pix_top", u.Quantity(np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32), u.DN, dtype=np.float32)
"border_ref_pix_right", np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32)
)
wfi_image["border_ref_pix_top"] = kwargs.get("border_ref_pix_top", np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32))
wfi_image["border_ref_pix_bottom"] = kwargs.get(
"border_ref_pix_bottom", u.Quantity(np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32), u.DN, dtype=np.float32)
"border_ref_pix_bottom", np.zeros((n_groups, shape[0] + 8, 4), dtype=np.float32)
)

# and their dq arrays
Expand All @@ -131,20 +125,14 @@ def mk_level2_image(*, shape=(4088, 4088), n_groups=8, filepath=None, **kwargs):

# add amp 33 ref pixel array
amp33_size = (n_groups, 4096, 128)
wfi_image["amp33"] = kwargs.get("amp33", u.Quantity(np.zeros(amp33_size, dtype=np.uint16), u.DN, dtype=np.uint16))
wfi_image["data"] = kwargs.get("data", u.Quantity(np.zeros(shape, dtype=np.float32), u.DN / u.s, dtype=np.float32))
wfi_image["amp33"] = kwargs.get("amp33", np.zeros(amp33_size, dtype=np.uint16))
wfi_image["data"] = kwargs.get("data", np.zeros(shape, dtype=np.float32))
wfi_image["dq"] = kwargs.get("dq", np.zeros(shape, dtype=np.uint32))
wfi_image["err"] = kwargs.get("err", u.Quantity(np.zeros(shape, dtype=np.float32), u.DN / u.s, dtype=np.float32))
wfi_image["err"] = kwargs.get("err", np.zeros(shape, dtype=np.float32))

wfi_image["var_poisson"] = kwargs.get(
"var_poisson", u.Quantity(np.zeros(shape, dtype=np.float32), u.DN**2 / u.s**2, dtype=np.float32)
)
wfi_image["var_rnoise"] = kwargs.get(
"var_rnoise", u.Quantity(np.zeros(shape, dtype=np.float32), u.DN**2 / u.s**2, dtype=np.float32)
)
wfi_image["var_flat"] = kwargs.get(
"var_flat", u.Quantity(np.zeros(shape, dtype=np.float32), u.DN**2 / u.s**2, dtype=np.float32)
)
wfi_image["var_poisson"] = kwargs.get("var_poisson", np.zeros(shape, dtype=np.float32))
wfi_image["var_rnoise"] = kwargs.get("var_rnoise", np.zeros(shape, dtype=np.float32))
wfi_image["var_flat"] = kwargs.get("var_flat", np.zeros(shape, dtype=np.float32))
wfi_image["cal_logs"] = mk_cal_logs(**kwargs)

wfi_image["meta"]["wcs"] = mk_wcs()
Expand Down Expand Up @@ -185,20 +173,14 @@ def mk_level3_mosaic(*, shape=(4088, 4088), n_images=2, filepath=None, **kwargs)

wfi_mosaic = stnode.WfiMosaic()
wfi_mosaic["meta"] = mk_mosaic_meta(**kwargs.get("meta", {}))
wfi_mosaic["data"] = kwargs.get("data", u.Quantity(np.zeros(shape, dtype=np.float32), u.MJy / u.sr, dtype=np.float32))
wfi_mosaic["err"] = kwargs.get("err", u.Quantity(np.zeros(shape, dtype=np.float32), u.MJy / u.sr, dtype=np.float32))
wfi_mosaic["data"] = kwargs.get("data", np.zeros(shape, dtype=np.float32))
wfi_mosaic["err"] = kwargs.get("err", np.zeros(shape, dtype=np.float32))
wfi_mosaic["context"] = kwargs.get("context", np.zeros((n_images,) + shape, dtype=np.uint32))
wfi_mosaic["weight"] = kwargs.get("weight", np.zeros(shape, dtype=np.float32))

wfi_mosaic["var_poisson"] = kwargs.get(
"var_poisson", u.Quantity(np.zeros(shape, dtype=np.float32), u.MJy**2 / u.sr**2, dtype=np.float32)
)
wfi_mosaic["var_rnoise"] = kwargs.get(
"var_rnoise", u.Quantity(np.zeros(shape, dtype=np.float32), u.MJy**2 / u.sr**2, dtype=np.float32)
)
wfi_mosaic["var_flat"] = kwargs.get(
"var_flat", u.Quantity(np.zeros(shape, dtype=np.float32), u.MJy**2 / u.sr**2, dtype=np.float32)
)
wfi_mosaic["var_poisson"] = kwargs.get("var_poisson", np.zeros(shape, dtype=np.float32))
wfi_mosaic["var_rnoise"] = kwargs.get("var_rnoise", np.zeros(shape, dtype=np.float32))
wfi_mosaic["var_flat"] = kwargs.get("var_flat", np.zeros(shape, dtype=np.float32))
wfi_mosaic["cal_logs"] = mk_cal_logs(**kwargs)

wfi_mosaic["meta"]["wcs"] = mk_wcs()
Expand Down Expand Up @@ -267,18 +249,10 @@ def mk_ramp(*, shape=(8, 4096, 4096), filepath=None, **kwargs):
ramp["meta"] = mk_common_meta(**kwargs.get("meta", {}))

# add border reference pixel arrays
ramp["border_ref_pix_left"] = kwargs.get(
"border_ref_pix_left", u.Quantity(np.zeros((shape[0], shape[1], 4), dtype=np.float32), u.DN, dtype=np.float32)
)
ramp["border_ref_pix_right"] = kwargs.get(
"border_ref_pix_right", u.Quantity(np.zeros((shape[0], shape[1], 4), dtype=np.float32), u.DN, dtype=np.float32)
)
ramp["border_ref_pix_top"] = kwargs.get(
"border_ref_pix_top", u.Quantity(np.zeros((shape[0], 4, shape[2]), dtype=np.float32), u.DN, dtype=np.float32)
)
ramp["border_ref_pix_bottom"] = kwargs.get(
"border_ref_pix_bottom", u.Quantity(np.zeros((shape[0], 4, shape[2]), dtype=np.float32), u.DN, dtype=np.float32)
)
ramp["border_ref_pix_left"] = kwargs.get("border_ref_pix_left", np.zeros((shape[0], shape[1], 4), dtype=np.float32))
ramp["border_ref_pix_right"] = kwargs.get("border_ref_pix_right", np.zeros((shape[0], shape[1], 4), dtype=np.float32))
ramp["border_ref_pix_top"] = kwargs.get("border_ref_pix_top", np.zeros((shape[0], 4, shape[2]), dtype=np.float32))
ramp["border_ref_pix_bottom"] = kwargs.get("border_ref_pix_bottom", np.zeros((shape[0], 4, shape[2]), dtype=np.float32))

# and their dq arrays
ramp["dq_border_ref_pix_left"] = kwargs.get("dq_border_ref_pix_left", np.zeros((shape[1], 4), dtype=np.uint32))
Expand All @@ -287,12 +261,12 @@ def mk_ramp(*, shape=(8, 4096, 4096), filepath=None, **kwargs):
ramp["dq_border_ref_pix_bottom"] = kwargs.get("dq_border_ref_pix_bottom", np.zeros((4, shape[2]), dtype=np.uint32))

# add amp 33 ref pixel array
ramp["amp33"] = kwargs.get("amp33", u.Quantity(np.zeros((shape[0], shape[1], 128), dtype=np.uint16), u.DN, dtype=np.uint16))
ramp["amp33"] = kwargs.get("amp33", np.zeros((shape[0], shape[1], 128), dtype=np.uint16))

ramp["data"] = kwargs.get("data", u.Quantity(np.full(shape, 1.0, dtype=np.float32), u.DN, dtype=np.float32))
ramp["data"] = kwargs.get("data", np.full(shape, 1.0, dtype=np.float32))
ramp["pixeldq"] = kwargs.get("pixeldq", np.zeros(shape[1:], dtype=np.uint32))
ramp["groupdq"] = kwargs.get("groupdq", np.zeros(shape, dtype=np.uint8))
ramp["err"] = kwargs.get("err", u.Quantity(np.zeros(shape, dtype=np.float32), u.DN, dtype=np.float32))
ramp["err"] = kwargs.get("err", np.zeros(shape, dtype=np.float32))

return save_node(ramp, filepath=filepath)

Expand Down Expand Up @@ -321,25 +295,15 @@ def mk_ramp_fit_output(*, shape=(8, 4096, 4096), filepath=None, **kwargs):
rampfitoutput = stnode.RampFitOutput()
rampfitoutput["meta"] = mk_common_meta(**kwargs.get("meta", {}))

rampfitoutput["slope"] = kwargs.get(
"slope", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron / u.s, dtype=np.float32)
)
rampfitoutput["sigslope"] = kwargs.get(
"sigslope", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron / u.s, dtype=np.float32)
)
rampfitoutput["yint"] = kwargs.get("yint", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron, dtype=np.float32))
rampfitoutput["sigyint"] = kwargs.get("sigyint", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron, dtype=np.float32))
rampfitoutput["pedestal"] = kwargs.get(
"pedestal", u.Quantity(np.zeros(shape[1:], dtype=np.float32), u.electron, dtype=np.float32)
)
rampfitoutput["slope"] = kwargs.get("slope", np.zeros(shape, dtype=np.float32))
rampfitoutput["sigslope"] = kwargs.get("sigslope", np.zeros(shape, dtype=np.float32))
rampfitoutput["yint"] = kwargs.get("yint", np.zeros(shape, dtype=np.float32))
rampfitoutput["sigyint"] = kwargs.get("sigyint", np.zeros(shape, dtype=np.float32))
rampfitoutput["pedestal"] = kwargs.get("pedestal", np.zeros(shape[1:], dtype=np.float32))
rampfitoutput["weights"] = kwargs.get("weights", np.zeros(shape, dtype=np.float32))
rampfitoutput["crmag"] = kwargs.get("crmag", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron, dtype=np.float32))
rampfitoutput["var_poisson"] = kwargs.get(
"var_poisson", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron**2 / u.s**2, dtype=np.float32)
)
rampfitoutput["var_rnoise"] = kwargs.get(
"var_rnoise", u.Quantity(np.zeros(shape, dtype=np.float32), u.electron**2 / u.s**2, dtype=np.float32)
)
rampfitoutput["crmag"] = kwargs.get("crmag", np.zeros(shape, dtype=np.float32))
rampfitoutput["var_poisson"] = kwargs.get("var_poisson", np.zeros(shape, dtype=np.float32))
rampfitoutput["var_rnoise"] = kwargs.get("var_rnoise", np.zeros(shape, dtype=np.float32))

return save_node(rampfitoutput, filepath=filepath)

Expand Down
14 changes: 6 additions & 8 deletions src/roman_datamodels/maker_utils/_tagged_nodes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from astropy import units as u

from roman_datamodels import stnode

from ._base import NONUM
Expand All @@ -15,12 +13,12 @@ def mk_photometry(**kwargs):
roman_datamodels.stnode.Photometry
"""
phot = stnode.Photometry()
phot["conversion_microjanskys"] = kwargs.get("conversion_microjanskys", NONUM * u.uJy / u.arcsec**2)
phot["conversion_megajanskys"] = kwargs.get("conversion_megajanskys", NONUM * u.MJy / u.sr)
phot["pixelarea_steradians"] = kwargs.get("pixelarea_steradians", NONUM * u.sr)
phot["pixelarea_arcsecsq"] = kwargs.get("pixelarea_arcsecsq", NONUM * u.arcsec**2)
phot["conversion_microjanskys_uncertainty"] = kwargs.get("conversion_microjanskys_uncertainty", NONUM * u.uJy / u.arcsec**2)
phot["conversion_megajanskys_uncertainty"] = kwargs.get("conversion_megajanskys_uncertainty", NONUM * u.MJy / u.sr)
phot["conversion_microjanskys"] = kwargs.get("conversion_microjanskys", float(NONUM))
phot["conversion_megajanskys"] = kwargs.get("conversion_megajanskys", float(NONUM))
phot["pixelarea_steradians"] = kwargs.get("pixelarea_steradians", float(NONUM))
phot["pixelarea_arcsecsq"] = kwargs.get("pixelarea_arcsecsq", float(NONUM))
phot["conversion_microjanskys_uncertainty"] = kwargs.get("conversion_microjanskys_uncertainty", float(NONUM))
phot["conversion_megajanskys_uncertainty"] = kwargs.get("conversion_megajanskys_uncertainty", float(NONUM))

return phot

Expand Down
33 changes: 3 additions & 30 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,11 @@ def test_make_ramp():

assert ramp.meta.exposure.type == "WFI_IMAGE"
assert ramp.data.dtype == np.float32
assert ramp.data.unit == u.DN
assert ramp.pixeldq.dtype == np.uint32
assert ramp.pixeldq.shape == (8, 8)
assert ramp.groupdq.dtype == np.uint8
assert ramp.err.dtype == np.float32
assert ramp.err.shape == (2, 8, 8)
assert ramp.err.unit == u.DN

# Test validation
ramp = datamodels.RampModel(ramp)
Expand All @@ -211,22 +209,14 @@ def test_make_ramp_fit_output():

assert rampfitoutput.meta.exposure.type == "WFI_IMAGE"
assert rampfitoutput.slope.dtype == np.float32
assert rampfitoutput.slope.unit == u.electron / u.s
assert rampfitoutput.sigslope.dtype == np.float32
assert rampfitoutput.sigslope.unit == u.electron / u.s
assert rampfitoutput.yint.dtype == np.float32
assert rampfitoutput.yint.unit == u.electron
assert rampfitoutput.sigyint.dtype == np.float32
assert rampfitoutput.sigyint.unit == u.electron
assert rampfitoutput.pedestal.dtype == np.float32
assert rampfitoutput.pedestal.unit == u.electron
assert rampfitoutput.weights.dtype == np.float32
assert rampfitoutput.crmag.dtype == np.float32
assert rampfitoutput.crmag.unit == u.electron
assert rampfitoutput.var_poisson.dtype == np.float32
assert rampfitoutput.var_poisson.unit == u.electron**2 / u.s**2
assert rampfitoutput.var_rnoise.dtype == np.float32
assert rampfitoutput.var_rnoise.unit == u.electron**2 / u.s**2
assert rampfitoutput.var_poisson.shape == (2, 8, 8)
assert rampfitoutput.pedestal.shape == (8, 8)

Expand Down Expand Up @@ -655,7 +645,6 @@ def test_make_level1_science_raw():
wfi_science_raw = utils.mk_level1_science_raw(shape=shape, dq=True)

assert wfi_science_raw.data.dtype == np.uint16
assert wfi_science_raw.data.unit == u.DN

# Test validation
wfi_science_raw_model = datamodels.ScienceRawModel(wfi_science_raw)
Expand All @@ -667,29 +656,17 @@ def test_make_level2_image():
wfi_image = utils.mk_level2_image(shape=(8, 8))

assert wfi_image.data.dtype == np.float32
assert wfi_image.data.unit == u.DN / u.s
assert wfi_image.dq.dtype == np.uint32
assert wfi_image.err.dtype == np.float32
assert wfi_image.err.unit == u.DN / u.s
assert wfi_image.var_poisson.dtype == np.float32
assert wfi_image.var_poisson.unit == u.DN**2 / u.s**2
assert wfi_image.var_rnoise.dtype == np.float32
assert wfi_image.var_rnoise.unit == u.DN**2 / u.s**2
assert wfi_image.var_flat.dtype == np.float32
assert wfi_image.var_flat.unit == u.DN**2 / u.s**2
assert isinstance(wfi_image.cal_logs[0], str)

# Test validation
wfi_image_model = datamodels.ImageModel(wfi_image)
assert wfi_image_model.validate() is None

# Test Physical units
wfi_image_model.data = wfi_image_model.data.value * (u.MJy / u.sr)
wfi_image_model.err = wfi_image_model.err.value * (u.MJy / u.sr)
wfi_image_model.var_poisson = wfi_image_model.var_poisson.value * (u.MJy**2 / u.sr**2)
wfi_image_model.var_rnoise = wfi_image_model.var_rnoise.value * (u.MJy**2 / u.sr**2)
wfi_image_model.var_flat = wfi_image_model.var_flat.value * (u.MJy**2 / u.sr**2)

# Test validation
assert wfi_image_model.validate() is None

Expand Down Expand Up @@ -723,16 +700,12 @@ def test_make_level3_mosaic():
wfi_mosaic = utils.mk_level3_mosaic(shape=(8, 8))

assert wfi_mosaic.data.dtype == np.float32
assert wfi_mosaic.data.unit == u.MJy / u.sr

assert wfi_mosaic.err.dtype == np.float32
assert wfi_mosaic.err.unit == u.MJy / u.sr
assert wfi_mosaic.context.dtype == np.uint32
assert wfi_mosaic.weight.dtype == np.float32
assert wfi_mosaic.var_poisson.dtype == np.float32
assert wfi_mosaic.var_poisson.unit == u.MJy**2 / u.sr**2
assert wfi_mosaic.var_rnoise.dtype == np.float32
assert wfi_mosaic.var_rnoise.unit == u.MJy**2 / u.sr**2
assert wfi_mosaic.var_flat.dtype == np.float32
assert isinstance(wfi_mosaic.cal_logs[0], str)

Expand Down Expand Up @@ -1027,10 +1000,10 @@ def test_datamodel_save_filename(tmp_path):
@pytest.mark.parametrize(
"model_class, expect_success",
[
(datamodels.FpsModel, True),
(datamodels.FpsModel, False), # will no longer succeed until we implement a wrapper to remove units
(datamodels.RampModel, True),
(datamodels.ScienceRawModel, True),
(datamodels.TvacModel, True),
(datamodels.TvacModel, False), # will no longer succeed until we implement a wrapper to remove units
(datamodels.MosaicModel, False),
],
)
Expand All @@ -1052,7 +1025,7 @@ def test_rampmodel_from_science_raw(tmp_path, model_class, expect_success):
assert new_ramp.meta.calibration_software_version == model.meta.calibration_software_version

else:
with pytest.raises(ValueError):
with pytest.raises((ValueError, ValidationError)):
datamodels.RampModel.from_science_raw(model)


Expand Down
Loading

0 comments on commit a4542f1

Please sign in to comment.