8 #ifndef UFO_OBSLOCALIZATION_OBSLOCALIZATION_H_
9 #define UFO_OBSLOCALIZATION_OBSLOCALIZATION_H_
18 #include "atlas/util/Earth.h"
20 #include "eckit/config/Configuration.h"
21 #include "eckit/container/KDTree.h"
22 #include "eckit/geometry/Point2.h"
23 #include "eckit/geometry/Point3.h"
24 #include "eckit/geometry/UnitSphere.h"
26 #include "ioda/ObsSpace.h"
27 #include "ioda/ObsVector.h"
29 #include "oops/base/ObsLocalizationBase.h"
30 #include "oops/util/missingValues.h"
44 typedef eckit::geometry::Point3
Point;
47 typedef eckit::KDTreeMemory<TreeTrait>
KDTree;
53 ioda::ObsVector & locvector)
const override;
64 void print(std::ostream &)
const override;
67 std::unique_ptr<KDTree>
kd_;
81 template<
typename MODEL>
83 const ioda::ObsSpace & obsspace)
84 : lats_(obsspace.
nlocs()), lons_(obsspace.
nlocs())
90 distName_ = obsspace.distribution()->name();
92 const size_t nlocs = obsspace.nlocs();
94 obsspace.get_db(
"MetaData",
"longitude",
lons_);
95 obsspace.get_db(
"MetaData",
"latitude",
lats_);
98 kd_ = std::unique_ptr<KDTree> (
new KDTree() );
100 typedef typename KDTree::PointType
Point;
101 std::vector<typename KDTree::Value> points;
102 for (
unsigned int i = 0; i <
nlocs; i++) {
103 eckit::geometry::Point2 lonlat(
lons_[i],
lats_[i]);
106 atlas::util::Earth::convertSphericalToCartesian(lonlat, xyz);
107 double index =
static_cast<double>(i);
108 typename KDTree::Value v(xyz, index);
112 kd_->build(points.begin(), points.end());
118 template<
typename MODEL>
120 ioda::ObsVector & locvector)
const {
121 oops::Log::trace() <<
"ObsLocalization::computeLocalization" << std::endl;
126 if ( distName_ !=
"Halo" && distName_ !=
"InefficientDistribution" ) {
127 std::string
message =
"Can not use ObsLocalization with distribution=" + distName_;
128 throw eckit::BadParameter(
message);
135 eckit::geometry::Point2 refPoint = *i;
136 size_t nlocs = lons_.size();
138 oops::Log::trace() <<
"Local obs searching via brute force." << std::endl;
140 for (
unsigned int jj = 0; jj <
nlocs; ++jj) {
141 eckit::geometry::Point2 searchPoint(lons_[jj], lats_[jj]);
142 double localDist = options_.distance(refPoint, searchPoint);
143 if ( localDist < options_.lengthscale ) {
144 localobs_.push_back(jj);
145 obsdist_.push_back(localDist);
148 const boost::optional<int> & maxnobs = options_.maxnobs;
149 if ( (maxnobs != boost::none) && (localobs_.size() > *maxnobs ) ) {
150 for (
unsigned int jj = 0; jj < localobs_.size(); ++jj) {
152 <<
" , " << obsdist_[jj] << std::endl;
155 std::vector<std::pair<std::size_t, double>> localObsIndDistPair;
156 for (
unsigned int jj = 0; jj < obsdist_.size(); ++jj) {
157 localObsIndDistPair.push_back(std::make_pair(localobs_[jj], obsdist_[jj]));
161 sort(localObsIndDistPair.begin(), localObsIndDistPair.end(),
162 [](
const std::pair<std::size_t, double> & p1,
163 const std::pair<std::size_t, double> & p2){
164 return(p1.second < p2.second);
168 for (
unsigned int jj = 0; jj < obsdist_.size(); ++jj) {
169 localobs_[jj] = localObsIndDistPair[jj].first;
170 obsdist_[jj] = localObsIndDistPair[jj].second;
174 localobs_.resize(*maxnobs);
175 obsdist_.resize(*maxnobs);
177 }
else if (
nlocs > 0) {
181 oops::Log::trace() <<
"Local obs searching via KDTree" << std::endl;
184 ABORT(
"ObsLocalization:: search method must be 'brute_force' when using 'cartesian' distance");
187 eckit::geometry::Point3 refPoint3D;
188 atlas::util::Earth::convertSphericalToCartesian(refPoint, refPoint3D);
189 double alpha = (options_.lengthscale / options_.radius_earth)/ 2.0;
190 double chordLength = 2.0*options_.radius_earth * sin(
alpha);
192 auto closePoints = kd_->findInSphere(refPoint3D, chordLength);
195 for (
unsigned int jloc = 0; jloc < closePoints.size(); ++jloc) {
196 localobs_.push_back(closePoints[jloc].payload());
197 obsdist_.push_back(closePoints[jloc].
distance());
201 const boost::optional<int> & maxnobs = options_.maxnobs;
202 if ( (maxnobs != boost::none) && (localobs_.size() > *maxnobs ) ) {
204 localobs_.resize(*maxnobs);
205 obsdist_.resize(*maxnobs);
210 const double missing = util::missingValue(
double());
211 for (
size_t jj = 0; jj < locvector.size(); ++jj) {
216 const size_t nvars = locvector.nvars();
217 const size_t nlocal = localobs_.size();
218 for (
size_t jlocal = 0; jlocal < nlocal; ++jlocal) {
220 for (
size_t jvar = 0; jvar < nvars; ++jvar) {
221 locvector[jvar + localobs_[jlocal] * nvars] = 1.0;
228 template<
typename MODEL>
230 os <<
"ObsLocalization (box car) horizontal localization with " << options_.lengthscale
231 <<
" lengthscale" << std::endl;
Options controlling local observations subsetting.
oops::Parameter< SearchMethod > searchMethod
Method for searching for nearest points: brute force or KD-tree.
Horizontal Box car observation space localization.
std::unique_ptr< KDTree > kd_
KD-tree for searching for local obs.
void print(std::ostream &) const override
const std::vector< int > & localobs() const
MODEL::GeometryIterator GeometryIterator_
std::vector< float > lons_
void computeLocalization(const GeometryIterator_ &, ioda::ObsVector &locvector) const override
std::string distName_
TODO(travis) distribution name is needed for temporary fix, should be removed eventually.
const ObsLocParameters & localizationOptions() const
ObsLocParameters options_
const std::vector< double > & horizontalObsdist() const
ObsLocalization(const eckit::Configuration &, const ioda::ObsSpace &)
std::vector< double > obsdist_
eckit::KDTreeMemory< TreeTrait > KDTree
std::vector< float > lats_
std::vector< int > localobs_
float distance(const Point &a, const Point &b)
Returns the distance between the two cartesian-mapped Point arguments
std::array< float, 3 > Point
real(kind_real), parameter, public alpha
integer function nlocs(this)
Return the number of observational locations in this Locations object.
character(len=max_string) message
eckit::geometry::Point3 Point