Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions dump/mapping/bufr_gsrcsr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
import os
import numpy as np

import bufr
from bufr.obs_builder import ObsBuilder, add_main_functions, map_path

MAPPING_PATH = map_path('bufr_gsrcsr.yaml')


class BufrGsrcsrObsBuilder(ObsBuilder):

def __init__(self):
super().__init__(MAPPING_PATH, log_name=os.path.basename(__file__))

def compute_sensor_channel_number(self, nlocs, nchannels):
"""
Creates a 2D array of sensor channel numbers with shape (nlocs, nchannels),
where each row is [7, 8, ..., nchannels].

Args:
nlocs (int): Number of observation locations.
nchannels (int): Number of channels.

Returns:
np.ndarray: 2D array with shape (nlocs, nchannels).
"""
return np.tile(np.arange(7, nchannels + 1, dtype=np.int32), (nlocs, 1))

def make_obs(self, comm, input_path):
# Get container from mapping file
self.log.info('Get container from bufr')
container = super().make_obs(comm, input_path)

self.log.debug(f'Container list (original): {container.list()}')
self.log.debug(f'All_sub_categories = {container.all_sub_categories()}')
self.log.debug(f'Category map = {container.get_category_map()}')

# Add a new derived variable, sensorChannelNumber into container
for cat in container.all_sub_categories():
self.log.debug(f'category: {cat}')

satId = container.get('satelliteId', cat)
sccf_paths = container.get_paths('sensorCentralFrequency', cat)
nlocs = satId.shape[0] # Number of locations
nchannels = 16 # Number of channels
# Add Ten Channels from 7 to 16
sensor_channel_number = self.compute_sensor_channel_number(nlocs, nchannels)
self.log.debug(f'Adding derived variable: sensorChannelNumber for {nlocs} locations')
container.add('sensorChannelNumber', sensor_channel_number, sccf_paths, cat)

if not np.any(satId):
self.log.warning(f'Category {cat[0]} does not exist in input file')
continue # Skip invalid category

# Final container state
self.log.debug(f'Container list (updated): {container.list()}')

return container

def _make_description(self):
description = super()._make_description()

description.add_variables([
{
'name': 'MetaData/sensorChannelNumber',
'source': 'sensorChannelNumber',
'units': '',
'longName': 'Sensor Channel Number',
},
])

return description


add_main_functions(BufrGsrcsrObsBuilder)
215 changes: 215 additions & 0 deletions dump/mapping/bufr_gsrcsr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# (C) Copyright 2025 NOAA/NWS/NCEP/EMC
#
# This software is licensed under the terms of the Apache License Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0.

bufr:
exports:
group_by_variable: geopotentialHeight
subsets:
- NC021046

variables:
# MetaData
timestamp:
datetime:
year: "*/YEAR"
month: "*/MNTH"
day: "*/DAYS"
hour: "*/HOUR"
minute: "*/MINU"
second: "*/SECO"

latitude:
query: "*/CLATH"

longitude:
query: "*/CLONH"

satelliteId:
query: "*/SAID"

satelliteZenithAngle:
query: "*/SAZA"

solarZenithAngle:
query: "*/SOZA"

solarAzimuthAngle:
query: "*/SOLAZI"

sensorAzimuthAngle:
query: "*/BEARAZ"

landOrSeaQualifier:
query: "*/LSQL"

geopotentialHeight:
query: "*/HITE"

sensorCentralFrequency:
query: "[*/CLFRASEQ{2}/SCCF, */CSRADSEQ/SCCF]"

sensorBandWidth:
query: "[*/CLFRASEQ{2}/SCBW, */CSRADSEQ/SCBW]"

dataProviderOrigin:
query: "*/GCLONG"

brightnessTemperaturePercentConfidence:
query: "*/RPSEQ4{1}/PCCF"

# ObsValue
brightnessTemperature:
query: "*/CSRADSEQ/TMBRST"

brightnessTemperatureStandardDeviation:
query: "*/SDRADSQ{1}/SDTB"

cloudAmount:
query: "*/CLFRASEQ{2}/CLDMNT"

cloudFree:
query: "*/CLFRASEQ{2}/NCLDMNT"

splits:
satId:
category:
variable: satelliteId
map:
_270: g16 #GOES-16
_271: g17 #GOES-17
_272: g18 #GOES-18

encoder:
dimensions:
- name: Channel
path: "*/CSRADSEQ"

globals:
- name: "platformCommonName"
type: string
value: "GOES-16"

- name: "platformLongDescription"
type: string
value: "Geostationary Operational Environmental Satellite-R Series (GOES-R)"

- name: "sensor"
type: string
value: "Advanced Baseline Imager (ABI)"

- name: "providerFullName"
type: string
value: "National Oceanic and Atmospheric Administration (NOAA)"

- name: "source"
type: string
value: "U.S. National Weather Service, National Centers for Environmental Prediction (NCEP)"

- name: "sourceFiles"
type: string
value: "NCEP BUFR Dump containing NC021046 GOES-16 Clear Sky Radiances (CSR)"

- name: "processingLevel"
type: string
value: "Level-2"

- name: "converter"
type: string
value: "bufr-query"

variables:

# MetaData
- name: "MetaData/dateTime"
source: variables/timestamp
longName: "Datetime"
units: "seconds since 1970-01-01T00:00:00Z"

- name: "MetaData/latitude"
source: variables/latitude
longName: "Latitude"
units: "degrees_north"
range: [-90, 90]

- name: "MetaData/longitude"
source: variables/longitude
longName: "Longitude"
units: "degrees_east"
range: [-180, 180]

- name: "MetaData/satelliteIdentifier"
source: variables/satelliteId
longName: "Satellite Identifier"

- name: "MetaData/satelliteZenithAngle"
source: variables/satelliteZenithAngle
longName: "Satellite Zenith Angle"
units: "degree"
range: [0, 90]

- name: "MetaData/solarZenithAngle"
source: variables/solarZenithAngle
longName: "Solar Zenith Angle"
units: "degree"
range: [0, 180]

- name: "MetaData/sensorAzimuthAngle"
source: variables/sensorAzimuthAngle
longName: "Sensor Azimuth Angle"
units: "degree"

- name: "MetaData/solarAzimuthAngle"
source: variables/solarAzimuthAngle
longName: "Solar Azimuth Angle"
units: "degree"

- name: "MetaData/landOrSeaQualifier"
source: variables/landOrSeaQualifier
longName: "Land/Sea Qualifier"

- name: "MetaData/geopotentialHeight"
source: variables/geopotentialHeight
longName: "Geopotential Height"
units: "m"

- name: "MetaData/sensorCentralFrequency"
source: variables/sensorCentralFrequency
longName: "Satellite Channel Center Frequency"
units: "Hz"

- name: "MetaData/sensorBandWidth"
source: variables/sensorBandWidth
longName: "Satellite Channel Bandwidth"
units: "Hz"

- name: "MetaData/dataProviderOrigin"
source: variables/dataProviderOrigin
longName: "Data Provider Origin"

- name: "MetaData/brightnessTemperaturePercentConfidence"
source: variables/brightnessTemperaturePercentConfidence
longName: "Brightness Temperature Percent Confidence"
units: "%"

# ObsValue
- name: "ObsValue/brightnessTemperature"
source: variables/brightnessTemperature
longName: "Brightness Temperature"
units: "K"

- name: "ObsValue/brightnessTemperatureStandardDeviation"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if this is an ObsValue? Thinking this would be better in MetaData but I'm open to discussing it.

source: variables/brightnessTemperatureStandardDeviation
longName: "Brightness Temperature Standard Deviation"
units: "K"

- name: "ObsValue/cloudAmount"
source: variables/cloudAmount
longName: "Cloud Amount in Segment"
units: "1"

- name: "ObsValue/cloudFree"
source: variables/cloudFree
longName: "Segment Amount Cloud Free"
units: "1"
61 changes: 61 additions & 0 deletions ush/test/config/bufr_bufr4backend_gsrcsr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
time window:
begin: "2021-07-31T21:00:00Z"
end: "2021-08-01T03:00:00Z"
bound to include: begin

observations:
- obs space:
name: "gsrcsr-g16"
simulated variables: [brightnessTemperature]
obsdatain:
engine:
type: bufr
obsfile: "./testinput/2021080100/gdas.t00z.gsrcsr.tm00.bufr_d"
mapping file: "./bufr_gsrcsr.yaml"
category: ["g16"]
cache categories: # optional
- ["g16"]
- ["g17"]
- ["g18"]
obsdataout:
engine:
type: H5File
obsfile: "./testoutput/2021080100/bufr4backend/gdas.t00z.gsrcsr_g16.tm00.nc"


- obs space:
name: "gsrcsr-g17"
simulated variables: [brightnessTemperature]
obsdatain:
engine:
type: bufr
obsfile: "./testinput/2021080100/gdas.t00z.gsrcsr.tm00.bufr_d"
mapping file: "./bufr_gsrcsr.yaml"
category: ["g17"]
cache categories: # optional
- ["g16"]
- ["g17"]
- ["g18"]
obsdataout:
engine:
type: H5File
obsfile: "./testoutput/2021080100/bufr4backend/gdas.t00z.gsrcsr_g17.tm00.nc"


- obs space:
name: "gsrcsr-g18"
simulated variables: [brightnessTemperature]
obsdatain:
engine:
type: bufr
obsfile: "./testinput/2021080100/gdas.t00z.gsrcsr.tm00.bufr_d"
mapping file: "./bufr_gsrcsr.yaml"
category: ["g18"]
cache categories: # optional
- ["g16"]
- ["g17"]
- ["g18"]
obsdataout:
engine:
type: H5File
obsfile: "./testoutput/2021080100/bufr4backend/gdas.t00z.gsrcsr_g18.tm00.nc"
Loading