From 67dd1921af0bb05ad071067b0e79795681564d53 Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Mon, 14 Sep 2026 12:48:15 -0400 Subject: [PATCH 1/7] Update ObsErrorFactorSnowElevationDiff implementation with filtered_orography --- .../ObsErrorFactorSnowElevationDiff.cc | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc new file mode 100644 index 000000000..ff594e262 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h" + +#include + +#include "eckit/exception/Exceptions.h" +#include "ioda/ObsDataVector.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/ObsFilterData.h" + +namespace ufo { + +static ObsFunctionMaker + makerSnowElevDiff_("ObsErrorFactorSnowElevationDiff"); + +// ----------------------------------------------------------------------------- + +ObsErrorFactorSnowElevationDiff::ObsErrorFactorSnowElevationDiff( + const eckit::Configuration &config) + : invars_() { + oops::Log::trace() << "ObsErrorFactorSnowElevationDiff constructor" << std::endl; + oops::Log::debug() << "ObsErrorFactorSnowElevationDiff: config = " << config << std::endl; + + // Initialize options + options_.reset(new ObsErrorFactorSnowElevationDiffParameters()); + options_->deserialize(config); + + // Include required observation elevation variable + const std::string obs_elev_var = options_->obs_elevation_var.value(); + invars_ += Variable(obs_elev_var); + + // Include required model elevation variable + const std::string model_elev_var = options_->model_elevation_var.value(); + invars_ += Variable(model_elev_var); +} + +// ----------------------------------------------------------------------------- + +ObsErrorFactorSnowElevationDiff::~ObsErrorFactorSnowElevationDiff() { + oops::Log::trace() << "ObsErrorFactorSnowElevationDiff destructor" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsErrorFactorSnowElevationDiff::compute( + const ObsFilterData & data, + ioda::ObsDataVector & obserr) const { + oops::Log::trace() << "ObsErrorFactorSnowElevationDiff compute start" << std::endl; + + const float missing = util::missingValue(); + const float h_scale = options_->elevation_scale_h.value(); + + // If no observations on this processor then nothing to do + if (data.nlocs() == 0) return; + + // Ensure that only one output variable is expected + ASSERT(obserr.nvars() == 1); + + // Get dimensions + size_t nlocs = data.nlocs(); + + // Get observation elevation variable + std::vector ob_elevation(nlocs); + const std::string obs_elev_var = options_->obs_elevation_var.value(); + data.get(Variable(obs_elev_var), ob_elevation); + + // Get model surface elevation variable + std::vector model_elevation(nlocs); + const std::string model_elev_var = options_->model_elevation_var.value(); + data.get(Variable(model_elev_var), model_elevation); + + // Compute inflation factor for each observation + float dz, inflation_factor_1, inflation_factor; + int iv = 0; + + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + // If missing observation or model elevation, set factor to 1.0 (no inflation) + if (ob_elevation[iloc] == missing || model_elevation[iloc] == missing) { + obserr[iv][iloc] = 1.0f; + } else { + // Compute elevation difference + dz = std::abs(model_elevation[iloc] - ob_elevation[iloc]); + + // Compute inflation_factor_1 = exp(-1 * dz^2 / (h^2)) + inflation_factor_1 = std::exp(-1.0f * dz * dz / (h_scale * h_scale)); + + // Compute output inflation_factor = 1 / inflation_factor_1 + inflation_factor = 1.0f / inflation_factor_1; + + // Return the inflation factor (ratio) + obserr[iv][iloc] = inflation_factor; + } + } + + oops::Log::trace() << "ObsErrorFactorSnowElevationDiff compute complete" << std::endl; +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & ObsErrorFactorSnowElevationDiff::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo From 5a62615ecad73dc6233db9d6d979c0194d812997 Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Mon, 14 Sep 2026 12:53:18 -0400 Subject: [PATCH 2/7] Add ObsErrorFactorSnowElevationDiff header file --- .../ObsErrorFactorSnowElevationDiff.h | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h new file mode 100644 index 000000000..59c4f0fe3 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORSNOWELEVATIONDIFF_H_ +#define UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORSNOWELEVATIONDIFF_H_ + +#include +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// \brief Options controlling ObsErrorFactorSnowElevationDiff ObsFunction +class ObsErrorFactorSnowElevationDiffParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsErrorFactorSnowElevationDiffParameters, Parameters) + + public: + oops::RequiredParameter obs_elevation_var{"obs_elevation_var", this}; + oops::RequiredParameter model_elevation_var{"model_elevation_var", this}; + oops::RequiredParameter elevation_scale_h{"elevation_scale_h", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Inflate the observation error based on elevation difference between model and observation. +/// +/// This routine computes an observation error inflation factor based on the elevation difference +/// between the model surface elevation and the observed station elevation. +/// The inflation factor is computed as: 1 / exp(-1 * dz^2 / (h^2)) +/// where dz = |model_elevation - obs_elevation| and h is the elevation_scale_h parameter. +/// +/// ~~~~ +/// +/// ### example configurations for a FilterBase derived class: ### +/// +/// - filter: BlackList +/// filter variables: +/// - name: snowDepth +/// action: +/// name: inflate error +/// inflation variable: +/// name: ObsFunction/ObsErrorFactorSnowElevationDiff +/// options: +/// obs_elevation_var: MetaData/stationElevation +/// model_elevation_var: GeoVaLs/filtered_orography +/// elevation_scale_h: 100.0 +/// +class ObsErrorFactorSnowElevationDiff : public ObsFunctionBase { + public: + static const std::string classname() {return "ObsErrorFactorSnowElevationDiff";} + + explicit ObsErrorFactorSnowElevationDiff(const eckit::Configuration &config); + ~ObsErrorFactorSnowElevationDiff(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + std::unique_ptr options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORSNOWELEVATIONDIFF_H_ From 774f4a97a5ee21112c05d1c84ea1d7513e64cf64 Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Mon, 14 Sep 2026 14:25:53 -0400 Subject: [PATCH 3/7] Add default filter variables --- .../ObsErrorFactorSnowElevationDiff.h | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h index 59c4f0fe3..a5eb893a6 100644 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h @@ -26,35 +26,38 @@ class ObsErrorFactorSnowElevationDiffParameters : public oops::Parameters { OOPS_CONCRETE_PARAMETERS(ObsErrorFactorSnowElevationDiffParameters, Parameters) public: - oops::RequiredParameter obs_elevation_var{"obs_elevation_var", this}; - oops::RequiredParameter model_elevation_var{"model_elevation_var", this}; - oops::RequiredParameter elevation_scale_h{"elevation_scale_h", this}; + oops::Parameter obs_elevation_var{"observation_elevation", "MetaData/stationElevation", this}; + oops::Parameter model_elevation_var{"model_elevation", "GeoVaLs/filtered_orography", this}; + oops::Parameter elevation_scale_h{"elevation_scale_m", 800., this}; }; // ----------------------------------------------------------------------------- -/// \brief Inflate the observation error based on elevation difference between model and observation. +/// \brief Inflate observation error based on elevation difference between model and observation. /// /// This routine computes an observation error inflation factor based on the elevation difference /// between the model surface elevation and the observed station elevation. /// The inflation factor is computed as: 1 / exp(-1 * dz^2 / (h^2)) -/// where dz = |model_elevation - obs_elevation| and h is the elevation_scale_h parameter. +/// where dz = |model_elevation - obs_elevation| +/// and h is the elevation_scale parameter in m (Elevation difference at which the obs-error will be inflated by a factor of exp(1)). /// -/// ~~~~ +/// Authors +/// First draft: Github Copilot. +/// Revisiewd and tested, in accordance with NOAA's use of AI tools, by Tseganeh Z. Gichamo +/// +/// ### example configurations for application of this filter: ### /// -/// ### example configurations for a FilterBase derived class: ### -/// -/// - filter: BlackList +/// - filter: Perform Action /// filter variables: -/// - name: snowDepth +/// - name: totalSnowDepth /// action: /// name: inflate error /// inflation variable: /// name: ObsFunction/ObsErrorFactorSnowElevationDiff /// options: -/// obs_elevation_var: MetaData/stationElevation -/// model_elevation_var: GeoVaLs/filtered_orography -/// elevation_scale_h: 100.0 +/// observation_elevation: MetaData/stationElevation +/// model_elevation: GeoVaLs/filtered_orography +/// elevation_scale_m: 800.0 // (Unit m) /// class ObsErrorFactorSnowElevationDiff : public ObsFunctionBase { public: From 4243656f3454cfcfb7cca9dbe2efd1a170a0e047 Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Mon, 14 Sep 2026 15:02:54 -0400 Subject: [PATCH 4/7] Set data with missing elev to fill value --- .../ObsErrorFactorSnowElevationDiff.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc index ff594e262..cc414858b 100644 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.cc @@ -18,7 +18,7 @@ namespace ufo { static ObsFunctionMaker - makerSnowElevDiff_("ObsErrorFactorSnowElevationDiff"); + makerObsErrorFactorSnowElevationDiff_("ObsErrorFactorSnowElevationDiff"); // ----------------------------------------------------------------------------- @@ -32,11 +32,11 @@ ObsErrorFactorSnowElevationDiff::ObsErrorFactorSnowElevationDiff( options_.reset(new ObsErrorFactorSnowElevationDiffParameters()); options_->deserialize(config); - // Include required observation elevation variable + // Include observation elevation variable const std::string obs_elev_var = options_->obs_elevation_var.value(); invars_ += Variable(obs_elev_var); - // Include required model elevation variable + // Include model elevation variable const std::string model_elev_var = options_->model_elevation_var.value(); invars_ += Variable(model_elev_var); } @@ -55,7 +55,7 @@ void ObsErrorFactorSnowElevationDiff::compute( oops::Log::trace() << "ObsErrorFactorSnowElevationDiff compute start" << std::endl; const float missing = util::missingValue(); - const float h_scale = options_->elevation_scale_h.value(); + const float h_scale = options_->elevation_scale_m.value(); // If no observations on this processor then nothing to do if (data.nlocs() == 0) return; @@ -81,9 +81,9 @@ void ObsErrorFactorSnowElevationDiff::compute( int iv = 0; for (size_t iloc = 0; iloc < nlocs; ++iloc) { - // If missing observation or model elevation, set factor to 1.0 (no inflation) + // If missing observation or model elevation, set obserror to missing if (ob_elevation[iloc] == missing || model_elevation[iloc] == missing) { - obserr[iv][iloc] = 1.0f; + obserr[iv][iloc] = missing; } else { // Compute elevation difference dz = std::abs(model_elevation[iloc] - ob_elevation[iloc]); @@ -94,8 +94,8 @@ void ObsErrorFactorSnowElevationDiff::compute( // Compute output inflation_factor = 1 / inflation_factor_1 inflation_factor = 1.0f / inflation_factor_1; - // Return the inflation factor (ratio) - obserr[iv][iloc] = inflation_factor; + // Inflate error by the inflation factor + obserr[iv][iloc] *= inflation_factor; } } From 579be53afa4c1430952e7de8ebeb4fcb71d83e3a Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Mon, 14 Sep 2026 16:19:14 -0400 Subject: [PATCH 5/7] add to cmake file --- src/ufo/filters/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ufo/filters/CMakeLists.txt b/src/ufo/filters/CMakeLists.txt index 044728843..c91ebde6f 100644 --- a/src/ufo/filters/CMakeLists.txt +++ b/src/ufo/filters/CMakeLists.txt @@ -323,6 +323,8 @@ set ( filters_files obsfunctions/ObsErrorFactorSfcPressure.h obsfunctions/ObsErrorFactorConventional.cc obsfunctions/ObsErrorFactorConventional.h + obsfunctions/ObsErrorFactorSnowElevationDiff.cc + obsfunctions/ObsErrorFactorSnowElevationDiff.h obsfunctions/PotentialTemperatureFromTemperature.cc obsfunctions/PotentialTemperatureFromTemperature.h obsfunctions/NearSSTRetCheckIR.cc From 2fb1bb289a0c5e7f21422e2f0514573685433e99 Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Mon, 14 Sep 2026 16:42:47 -0400 Subject: [PATCH 6/7] Fix var name --- src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h index a5eb893a6..d5480aed5 100644 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h @@ -28,7 +28,7 @@ class ObsErrorFactorSnowElevationDiffParameters : public oops::Parameters { public: oops::Parameter obs_elevation_var{"observation_elevation", "MetaData/stationElevation", this}; oops::Parameter model_elevation_var{"model_elevation", "GeoVaLs/filtered_orography", this}; - oops::Parameter elevation_scale_h{"elevation_scale_m", 800., this}; + oops::Parameter elevation_scale_m{"elevation_scale_m", 800., this}; }; // ----------------------------------------------------------------------------- From 93ca511c039ccb6ff5dfe57908a1be1ae54a6b1b Mon Sep 17 00:00:00 2001 From: "Tseganeh Z. Gichamo" Date: Tue, 15 Sep 2026 08:00:36 -0400 Subject: [PATCH 7/7] Revise authorship and testing notes in header Updated authorship information and clarified revision details. --- .../filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h index d5480aed5..e2f38bede 100644 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSnowElevationDiff.h @@ -41,9 +41,7 @@ class ObsErrorFactorSnowElevationDiffParameters : public oops::Parameters { /// where dz = |model_elevation - obs_elevation| /// and h is the elevation_scale parameter in m (Elevation difference at which the obs-error will be inflated by a factor of exp(1)). /// -/// Authors -/// First draft: Github Copilot. -/// Revisiewd and tested, in accordance with NOAA's use of AI tools, by Tseganeh Z. Gichamo +/// Authors: Tseganeh Z Gichamo and Gihub Copilot. All the ML generated code sections have been revised and tested by Tseganeh Z. Gichamo /// /// ### example configurations for application of this filter: ### ///