17 #include <boost/lexical_cast.hpp>
18 #include <boost/math/special_functions/round.hpp>
20 #include "ioda/ObsDataVector.h"
21 #include "ioda/ObsSpace.h"
23 #include "oops/util/IntSetParser.h"
24 #include "oops/util/Logger.h"
25 #include "oops/util/missingValues.h"
39 int safeCast(
float x,
float missingIn,
int missingOut) {
43 return boost::math::iround(x);
47 float safeCast(
int x,
int missingIn,
float missingOut) {
55 template <
typename VariableType>
57 const std::vector<bool> &apply,
59 VariableType newValue;
60 if (!boost::conversion::try_lexical_convert(valueAsString, newValue))
61 throw eckit::BadCast(
"Value '" + valueAsString +
62 "' could not be converted to the required type", Here());
64 for (
size_t ival = 0; ival < values.nvars(); ++ival) {
65 std::vector<VariableType> ¤tValues = values[ival];
66 for (
size_t iloc = 0; iloc < apply.size(); ++iloc)
68 currentValues[iloc] = newValue;
78 template <
typename SourceVariableType,
typename DestinationVariableType>
82 ASSERT(source.nvars() == destination.nvars());
84 const SourceVariableType missingSource = util::missingValue(SourceVariableType());
85 const DestinationVariableType missingDestination = util::missingValue(DestinationVariableType());
87 for (
size_t ival = 0; ival < source.nvars(); ++ival) {
88 const ioda::ObsDataRow<SourceVariableType> ¤tSource = source[ival];
89 ioda::ObsDataRow<DestinationVariableType> ¤tDestination = destination[ival];
90 for (
size_t iloc = 0; iloc < apply.size(); ++iloc) {
92 currentDestination[iloc] =
safeCast(currentSource[iloc], missingSource, missingDestination);
103 template <
typename VariableType>
107 ASSERT(source.nvars() == destination.nvars());
109 for (
size_t ival = 0; ival < source.nvars(); ++ival) {
110 const ioda::ObsDataRow<VariableType> ¤tSource = source[ival];
111 ioda::ObsDataRow<VariableType> ¤tDestination = destination[ival];
112 for (
size_t iloc = 0; iloc < apply.size(); ++iloc) {
114 currentDestination[iloc] = currentSource[iloc];
123 template <
typename SourceVariableType,
typename DestinationVariableType>
125 const std::vector<bool> &apply,
129 data.
get(variable, newValues);
135 template <
typename FunctionValueType,
typename VariableType>
138 const std::vector<bool> &apply,
142 data.
get(
function, newValues);
147 template <
typename VariableType>
150 const std::vector<bool> &apply,
153 if (params.
value_.value() != boost::none) {
157 case ioda::ObsDtype::Float:
158 assignVariable<float>(*params.
sourceVariable.value(), apply, data, values);
160 case ioda::ObsDtype::Integer:
161 assignVariable<int>(*params.
sourceVariable.value(), apply, data, values);
164 throw eckit::BadParameter(params.
sourceVariable.value()->fullName() +
165 " is not a numeric variable", Here());
168 ASSERT(params.
function.value() != boost::none);
170 assignFunction<float>(*params.
function.value(), variable, apply, data, values);
172 assignFunction<int>(*params.
function.value(), variable, apply, data, values);
174 throw eckit::BadParameter(params.
function.value()->fullName() +
175 " is not a function producing numeric values", Here());
180 template <
typename VariableType>
183 const std::vector<bool> &apply,
186 if (params.
value_.value() != boost::none) {
189 assignVariable<VariableType>(*params.
sourceVariable.value(), apply, data, values);
191 ASSERT(params.
function.value() != boost::none);
192 assignFunction<VariableType>(*params.
function.value(), variable, apply, data, values);
199 template <
typename VariableType>
201 ioda::ObsSpace &obsdb) {
203 for (
size_t ich = 0; ich < variable.
size(); ++ich) {
204 const std::string variableWithChannel = variable.
variable(ich);
205 if (obsdb.has(variable.
group(), variableWithChannel)) {
207 obsdb.get_db(variable.
group(), variableWithChannel, values[ich]);
210 values[ich].assign(obsdb.nlocs(), util::missingValue(VariableType()));
217 template <
typename VariableType>
220 ioda::ObsSpace &obsdb) {
221 for (
size_t ich = 0; ich < variable.
size(); ++ich)
222 obsdb.put_db(variable.
group(), variable.
variable(ich), values[ich]);
228 const float missing = util::missingValue(
float());
230 for (
size_t ivar = 0; ivar < obsvalues.nvars(); ++ivar) {
231 if (qcflags.varnames().has(obsvalues.varnames()[ivar])) {
232 const ioda::ObsDataRow<float> ¤tValues = obsvalues[ivar];
233 ioda::ObsDataRow<int> ¤tFlags = qcflags[obsvalues.varnames()[ivar]];
234 for (
size_t iloc = 0; iloc < obsvalues.nlocs(); ++iloc) {
249 const std::vector<bool> &apply,
251 ioda::ObsSpace &obsdb) {
263 const std::vector<bool> &apply,
265 ioda::ObsSpace &obsdb,
270 if (variable.
group() ==
"ObsValue" || variable.
group() ==
"DerivedObsValue")
277 template <
typename VariableType>
280 const std::vector<bool> &apply,
282 ioda::ObsSpace &obsdb) {
291 ioda::ObsDtype dtype,
293 const std::vector<bool> &apply,
295 ioda::ObsSpace &obsdb,
298 case ioda::ObsDtype::Float:
301 case ioda::ObsDtype::Integer:
304 case ioda::ObsDtype::String:
305 assignToNonnumericVariable<std::string>(variable, params, apply, data, obsdb);
307 case ioda::ObsDtype::DateTime:
308 assignToNonnumericVariable<util::DateTime>(variable, params, apply, data, obsdb);
311 ASSERT_MSG(
false,
"Unrecognized data type");
317 const std::set<int> setChannels = oops::parseIntSet(params.
channels);
318 std::vector<int> vecChannels(setChannels.begin(), setChannels.end());
320 if (variable.
group() ==
"ObsValue")
321 throw eckit::BadValue(
"Assignment to variables from the ObsValue group is not allowed",
327 ioda::ObsDtype
getDataType(boost::optional<ioda::ObsDtype> dtypeParam,
329 const ioda::ObsSpace &obsdb) {
330 if (dtypeParam != boost::none) {
336 for (
size_t ich = 0; ich < variable.
size(); ++ich) {
337 const std::string variableWithChannel = variable.
variable(ich);
338 if (obsdb.has(variable.
group(), variableWithChannel))
339 return obsdb.dtype(variable.
group(), variableWithChannel);
342 throw eckit::BadParameter(
"You need to specify the type of the variable to be created "
343 "by setting the 'type' option of the filter to 'float', 'int', "
344 "'string' or 'datetime'.");
352 const eckit::Configuration &config) {
353 oops::Parameters::deserialize(path, config);
357 const int numOptionsSet =
static_cast<int>(
value_.value() != boost::none) +
359 static_cast<int>(
function.value() != boost::none);
360 if (numOptionsSet != 1)
361 throw eckit::UserError(path.path() +
362 ": Exactly one of the 'value', 'source variable' and 'function' options "
370 :
ObsProcessorBase(obsdb, parameters.deferToPost, std::move(flags), std::move(obserr)),
371 parameters_(parameters)
377 if (assignment.sourceVariable.value() != boost::none) {
378 allvars_ += *assignment.sourceVariable.value();
380 if (assignment.function.value() != boost::none) {
381 allvars_ += *assignment.function.value();
387 oops::Log::trace() <<
"VariableAssignment doFilter begin" << std::endl;
399 oops::Log::trace() <<
"VariableAssignment doFilter end" << std::endl;
403 os <<
"VariableAssignment: config = " <<
parameters_ << std::endl;
Parameters controlling assignment of new values to a variable.
oops::OptionalParameter< std::string > value_
void deserialize(util::CompositePath &path, const eckit::Configuration &config) override
oops::OptionalParameter< ufo::Variable > sourceVariable
oops::OptionalParameter< ufo::Variable > function
oops::RequiredParameter< std::string > name
Name of the variable to which new values should be assigned.
oops::Parameter< std::string > channels
Set of channels to which new values should be assigned.
ObsFilterData provides access to all data related to an ObsFilter.
ioda::ObsSpace & obsspace() const
Returns reference to ObsSpace associated with ObsFilterData.
ioda::ObsDtype dtype(const Variable &) const
Determines dtype of the provided variable.
void get(const Variable &varname, std::vector< float > &values) const
Fills a std::vector with values of the specified variable.
Base class for UFO observation processors (including QC filters).
std::shared_ptr< ioda::ObsDataVector< int > > flags_
void doFilter() const override
VariableAssignment(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr< ioda::ObsDataVector< int > > flags, std::shared_ptr< ioda::ObsDataVector< float > > obserr)
void print(std::ostream &) const override
Parameters controlling the VariableAssignment filter.
oops::Parameter< std::vector< WhereParameters > > where
oops::Parameter< std::vector< AssignmentParameters > > assignments
One or more sets of options controlling the values assigned to a particular variable.
const std::string & variable() const
const std::string & group() const
oops::Variables toOopsVariables() const
ufo::Variable getVariable(const AssignmentParameters ¶ms)
Return the variable to which new values will be assigned.
float safeCast(int x, int missingIn, float missingOut)
Cast an int x to a float. If is equal to missingIn, return missingOut.
ioda::ObsDtype getDataType(boost::optional< ioda::ObsDtype > dtypeParam, const ufo::Variable &variable, const ioda::ObsSpace &obsdb)
Return the data type of the variable to which new values will be assigned.
void assignNumericValues(const AssignmentParameters ¶ms, const ufo::Variable &variable, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsDataVector< VariableType > &values)
Assign values to a numeric variable (of type float or int).
void assignToIntVariable(const ufo::Variable &variable, const AssignmentParameters ¶ms, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsSpace &obsdb)
void assignVariable(const ufo::Variable &variable, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsDataVector< DestinationVariableType > &values)
void assignToFloatVariable(const ufo::Variable &variable, const AssignmentParameters ¶ms, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsSpace &obsdb, ioda::ObsDataVector< int > &qcflags)
void assignToVariable(const ufo::Variable &variable, ioda::ObsDtype dtype, const AssignmentParameters ¶ms, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsSpace &obsdb, ioda::ObsDataVector< int > &qcflags)
void assignObsDataVector(const std::vector< bool > &apply, const ioda::ObsDataVector< VariableType > &source, ioda::ObsDataVector< VariableType > &destination)
void assignToNonnumericVariable(const ufo::Variable &variable, const AssignmentParameters ¶ms, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsSpace &obsdb)
void updateQCFlags(const ioda::ObsDataVector< float > &obsvalues, ioda::ObsDataVector< int > &qcflags)
void assignNonnumericValues(const AssignmentParameters ¶ms, const ufo::Variable &variable, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsDataVector< VariableType > &values)
Assign values to a non-numeric variable (of type string or DateTime).
void assignFunction(const ufo::Variable &function, const ufo::Variable &variable, const std::vector< bool > &apply, const ObsFilterData &data, ioda::ObsDataVector< VariableType > &values)
void assignValue(const std::string &valueAsString, const std::vector< bool > &apply, ioda::ObsDataVector< VariableType > &values)
void saveValues(const ufo::Variable &variable, const ioda::ObsDataVector< VariableType > &values, ioda::ObsSpace &obsdb)
Save the values values of variable variable to obsdb.
ioda::ObsDataVector< VariableType > getCurrentValues(const ufo::Variable &variable, ioda::ObsSpace &obsdb)
ufo::Variables getAllWhereVariables(const std::vector< WhereParameters > ¶ms)
std::vector< bool > processWhere(const std::vector< WhereParameters > ¶ms, const ObsFilterData &filterdata)
Common properties of ObsFunctions producing values of type FunctionValue.