cmrlifecycle

Small package for auditing the update/revision lifecycle of NASA CMR granules -- i.e. figuring out when a collection's files actually change, as distinct from CMR metadata churn, so that a downstream mirror (e.g. a COG conversion published to source.coop) knows when it needs to re-derive a file. Originally built against MUR-JPL-L4-GLOB-v4.1; the core is now collection-agnostic, with a companion layer for discovering and sanity-checking collections before you audit them.

Start here

The rest of this README is a compact reference; the vignettes are where the depth and worked examples live.

Installation

From this repo:

# remotes::install_github("hypertidy/cmrlifecycle")

Usage

library(cmrlifecycle)

# don't have a short_name yet?
cmr_find_collections(keyword = "sea surface temperature", provider = "POCLOUD")

# sanity check before auditing: is it alive, and what does it cover?
cmr_collection_liveness("MUR-JPL-L4-GLOB-v4.1")
cmr_find_collections(short_name = "MUR-JPL-L4-GLOB-v4.1")

# audit the granule-level revision/production lifecycle
rev <- cmr_audit_revisions("MUR-JPL-L4-GLOB-v4.1", from = as.Date("2024-01-01"))
plot_cmr_lifecycle(rev)

See vignette("cmr-quickstart") for the full walkthrough, including opaque-GranuleUR collections (e.g. LAADS swath products), spatial subsetting for high-volume swaths (bounding_box/polygon), and cloud/S3 access.

What cmr_audit_revisions() gives you

One row per granule:

column meaning
granule_ur, concept_id CMR identifiers
data_url first GET DATA (https) download URL from UMM RelatedUrls
s3_url first GET DATA VIA DIRECT ACCESS (s3://) URL -- NA if the collection doesn't publish direct S3 access
n_rev CMR meta revision-id -- how many times the metadata record was touched (often bulk churn, not a data-change signal)
insert_date / update_date UMM ProviderDates Insert/Update
rev_date CMR meta revision-date (latest revision)
production DataGranule.ProductionDateTime -- when the science file itself was generated. This is the churn-immune signal.
temporal_start UMM TemporalExtent Beginning/SingleDateTime -- the granule's acquisition start
*_lag each date above minus gran_date (days)

gran_date is parsed from a leading YYYYMMDD in granule_ur by default (GHRSST-style naming); pass gran_date_fun for other conventions -- see vignette("cmr-quickstart")'s "when your collection doesn't play by MUR's rules" section for the opaque- GranuleUR and swath-volume cases this covers.

Key finding this was built to support

n_rev is metadata churn, not a change-of-data-file signal; production is the reliable one. The staleness rule for a mirror is:

stale <- mirror_mtime < as.Date(production)

joined against e.g. rclone lsjson mtimes of the mirror; nc_bytes is a free secondary check (a regenerated file rarely keeps its exact size).

Notes / limits

Files