diff --git a/src/opsinputs/CxWriterParameters.h b/src/opsinputs/CxWriterParameters.h index ac51b83e..fecab46f 100644 --- a/src/opsinputs/CxWriterParameters.h +++ b/src/opsinputs/CxWriterParameters.h @@ -58,6 +58,14 @@ class CxWriterParameters : public oops::ObsFilterParametersBase { /// default is true. oops::Parameter geoValsAreTopToBottom{"geovals_are_top_to_bottom", true, this}; + /// Fill radiosonde CX columns using H(x) values rather than GeoVaLs. + /// This can be used to produce a set of CX columns (which are used as H(x) in VAR) + /// that correspond closely to the H(x) produced by JADA. + /// In other words it enables a 'JADA compatibility mode' for VAR. + /// This option only has an effect for airTemperature, potentialTemperature, windEastward, + /// windNorthward and relativeHumidity. + oops::Parameter writeHofXIntoCX{"write_hofx_into_cx", false, this}; + /// If this list of ufo::variable is defined in the yaml a subset of the flags /// will be made with just these variables present. This will allow Fortran calls such-as /// "reject_obs_with_all_variables_failing_qc" and channel numbering to work correctly. diff --git a/src/opsinputs/opsinputs_cxwriter_mod.F90 b/src/opsinputs/opsinputs_cxwriter_mod.F90 index 95726f69..e9495ac9 100644 --- a/src/opsinputs/opsinputs_cxwriter_mod.F90 +++ b/src/opsinputs/opsinputs_cxwriter_mod.F90 @@ -27,7 +27,7 @@ module opsinputs_cxwriter_mod use opsinputs_fill_mod, only: & opsinputs_fill_fillreal, & opsinputs_fill_fillrealfromgeoval, & - opsinputs_fill_fillreal2dfromgeoval + opsinputs_fill_fillreal2dfromgeovalorhofx use opsinputs_mpl_mod, only: opsinputs_mpl_allgather_integer use opsinputs_obsspace_mod, only: opsinputs_obsspace_get_db_datetime_offset_in_seconds use opsinputs_utils_mod, only: & @@ -137,6 +137,7 @@ module opsinputs_cxwriter_mod logical :: RejectObsWithAnyVariableFailingQC logical :: RejectObsWithAllVariablesFailingQC logical :: GeoVaLsAreTopToBottom + logical :: WriteHofXIntoCX character(len=100) :: latitudeName character(len=100) :: longitudeName @@ -257,6 +258,9 @@ function opsinputs_cxwriter_create(self, f_conf, geovars) call f_conf % get_or_die("geovals_are_top_to_bottom", & self % GeoVaLsAreTopToBottom) +call f_conf % get_or_die("write_hofx_into_cx", & + self % WriteHofXIntoCX) + call f_conf % get_or_die ("latitude_name", StringValue) self % latitudeName = StringValue @@ -1087,166 +1091,171 @@ subroutine opsinputs_cxwriter_populatecx(self, ReportFlags, Cx) ! Upper-air variables case (StashItem_theta) ! IndexCxtheta - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % theta, "theta", Cx % theta, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_theta, & - self % JediToOpsLayoutMapping, self % hofx, self % varnames) + self % JediToOpsLayoutMapping, self % hofx, self % varnames, & + "potentialTemperature", self % WriteHofXIntoCX) case (StashCode_rh, StashCode_rh_p) ! IndexCxrh - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % rh, "rh", Cx % rh, & self % GeoVaLs, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_rh, & - self % JediToOpsLayoutMapping, self % hofx, self % varnames) + self % JediToOpsLayoutMapping, self % hofx, self % varnames, & + "relativeHumidity", self % WriteHofXIntoCX) case (StashItem_u, StashCode_u_p_B_grid) ! IndexCxu - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % u, "u", Cx % u, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_u, & - self % JediToOpsLayoutMapping, self % hofx, self % varnames) + self % JediToOpsLayoutMapping, self % hofx, self % varnames, & + "windEastward", self % WriteHofXIntoCX) case (StashItem_v) ! IndexCxv - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % v, "v", Cx % v, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_v, & - self % JediToOpsLayoutMapping, self % hofx, self % varnames) + self % JediToOpsLayoutMapping, self % hofx, self % varnames, & + "windNorthward", self % WriteHofXIntoCX) case (StashItem_w) ! IndexCxw - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % w, "w", Cx % w, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_w, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_q) ! IndexCxq - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % q, "q", Cx % q, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_q, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_qc) ! IndexCxqc - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % qc, "qc", Cx % qc, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_qc, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_p_bar) ! IndexCxp_bar - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % p_bar, "p_bar", Cx % p_bar, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_p_bar, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) ! wsmigaj: I haven't been able to identify the stash code associated with this field ! case (?) ! IndexCxcloud - ! call opsinputs_fill_fillreal2dfromgeoval( & + ! call opsinputs_fill_fillreal2dfromgeovalorhofx( & ! Cx % Header % cloud, "cloud", Cx % cloud, & ! self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_cloud, & ! self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_ql_layer) ! IndexCxql_layer - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % ql_layer, "ql_layer", Cx % ql_layer, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_ql_layer, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_p) ! IndexCxP - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % PLevelsA, "PLevelsA", Cx % PLevelsA, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_PLevelsA, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (AncilCode_salt) ! IndexCxSalt - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % Salt, "Salt", Cx % Salt, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_Salt, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_t_p) ! IndexCxt - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % t, "t", Cx % t, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_t, & - self % JediToOpsLayoutMapping, self % hofx, self % varnames) + self % JediToOpsLayoutMapping, self % hofx, self % varnames, & + "airTemperature", self % WriteHofXIntoCX) case (StashCode_qf_layer) ! IndexCxqf_layer - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % qf_layer, "qf_layer", Cx % qf_layer, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_qf_layer, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_RainRate_layer) ! IndexCxRainRate_layer - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % RainRate_layer, "RainRate_layer", Cx % RainRate_layer, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_RainRate_layer, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_cloud_conv) ! IndexCxcloud_conv - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % cloud_conv, "cloud_conv", Cx % cloud_conv, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_cloud_conv, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_qc_conv) ! IndexCxqc_conv - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % qc_conv, "qc_conv", Cx % qc_conv, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_qc_conv, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_cloud_layer) ! IndexCxcloud_layer - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % cloud_layer, "cloud_layer", Cx % cloud_layer, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_cloud_layer, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_ozone_new) ! IndexCxOzone - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % ozone, "ozone", Cx % ozone, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_ozone, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_qcf) ! IndexCxqcf - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % qcf, "qcf", Cx % qcf, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_qcf, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_qcl) ! IndexCxqcl - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % qcl, "qcl", Cx % qcl, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_qcl, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_cloud_bulk) ! IndexCxcloud_bulk - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % cloud_bulk, "cloud_bulk", Cx % cloud_bulk, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_cloud_bulk, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) ! wsmigaj: I haven't been able to identify the stash code associated with this field ! case (?) ! if (Cx % Header % ObsGroup == ObsGroupGroundLidar) then - ! call opsinputs_fill_fillreal2dfromgeoval( & + ! call opsinputs_fill_fillreal2dfromgeovalorhofx( & ! Cx % Header % aerosol_p, "aerosol_p", Cx % aerosol_p, & ! self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_aerosol_p, & ! self % JediToOpsLayoutMapping, self % hofx, self % varnames) ! end if case (StashCode_CDNC) ! IndexCxCDNC - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % CDNC, "CDNC", Cx % CDNC, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_CDNC, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_RH_AfterMainCloud) ! IndexCxRH_AMC - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % RH_AMC, "RH_AMC", Cx % RH_AMC, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_RH_AMC, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_Cl) ! IndexCxCl - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % Cl, "Cl", Cx % Cl, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_Cl, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_Cf) ! IndexCxCf - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % Cf, "Cf", Cx % Cf, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_Cf, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_qrain) ! IndexCxqrain - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % qrain, "qrain", Cx % qrain, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_qrain, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashItem_Exner) ! IndexCxExnerA - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % ExnerA, "ExnerA", Cx % ExnerA, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_ExnerA, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_RichNumber) ! IndexCxRichNumber - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % RichNumber, "RichNumber", Cx % RichNumber, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_RichNumber, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_SoilMoisture) ! IndexCxSoilMoisture - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % SoilMoisture, "SoilMoisture", Cx % SoilMoisture, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_SoilMoisture, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) case (StashCode_SoilTemp) ! IndexCxSoilTemp - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % SoilTemp, "SoilTemp", Cx % SoilTemp, & self % GeoVals, self % GeoVaLsAreTopToBottom, opsinputs_cxfields_SoilTemp, & self % JediToOpsLayoutMapping, self % hofx, self % varnames) @@ -1254,7 +1263,7 @@ subroutine opsinputs_cxwriter_populatecx(self, ReportFlags, Cx) DustBinIndex = CxField - StashItem_dustMin + 1 if (DustBinIndex <= NDustBins) then write (DustBinIndexStr, '(i1)') DustBinIndex - call opsinputs_fill_fillreal2dfromgeoval( & + call opsinputs_fill_fillreal2dfromgeovalorhofx( & Cx % Header % dustp, "dustp", Cx % dustp(DustBinIndex) % field, & self % GeoVals, self % GeoVaLsAreTopToBottom, & opsinputs_cxfields_dustp_start // DustBinIndexStr // opsinputs_cxfields_dustp_end, & diff --git a/src/opsinputs/opsinputs_fill_mod.F90 b/src/opsinputs/opsinputs_fill_mod.F90 index be049e87..c9befbfb 100644 --- a/src/opsinputs/opsinputs_fill_mod.F90 +++ b/src/opsinputs/opsinputs_fill_mod.F90 @@ -67,10 +67,11 @@ module opsinputs_fill_mod opsinputs_fill_fillreal, & opsinputs_fill_fillreal2d, & opsinputs_fill_fillrealfromgeoval, & - opsinputs_fill_fillreal2dfromgeoval, & opsinputs_fill_fillrealfromgeovalformultilevelobs, & opsinputs_fill_fillreal2dfromgeovalformultilevelobs, & - &opsinputs_fill_fillreal2dfromgeovalforsinglelevelobs, & + opsinputs_fill_fillreal2dfromgeoval, & + opsinputs_fill_fillreal2dfromgeovalorhofx, & + opsinputs_fill_fillreal2dfromhofx, & opsinputs_fill_fillstring, & opsinputs_fill_filltimeoffsets, & opsinputs_fill_filltimeoffsets2d, & @@ -1363,7 +1364,7 @@ end subroutine opsinputs_fill_fillrealfromgeoval !> \note This function returns early (without a warning) if the specified GeoVaL is not found. !> We rely on warnings printed by the OPS code whenever data needed to output a requested varfield !> are not found. -subroutine opsinputs_fill_fillreal2dfromgeovalforsinglelevelobs( & +subroutine opsinputs_fill_fillreal2dfromgeoval( & Hdr, OpsVarName, NumObs, Real2, GeoVals, GeoVaLsAreTopToBottom, JediVarName) implicit none @@ -1397,7 +1398,7 @@ subroutine opsinputs_fill_fillreal2dfromgeovalforsinglelevelobs( & Real2 = Real2(:, GeoVal % nval:1:-1) end if end if -end subroutine opsinputs_fill_fillreal2dfromgeovalforsinglelevelobs +end subroutine opsinputs_fill_fillreal2dfromgeoval !> Populate a 2D array of real numbers and its header from either a GeoVaL or an HofX vector. !> @@ -1420,8 +1421,9 @@ end subroutine opsinputs_fill_fillreal2dfromgeovalforsinglelevelobs !> HofX data structure. !> \param[in] varnames !> List of simulated variables. -subroutine opsinputs_fill_fillreal2dfromgeoval( & - Hdr, OpsVarName, Real2, GeoVals, GeoVaLsAreTopToBottom, JediVarName, JediToOpsLayoutMapping, hofx, varnames) +subroutine opsinputs_fill_fillreal2dfromgeovalorhofx( & + Hdr, OpsVarName, Real2, GeoVals, GeoVaLsAreTopToBottom, JediVarName, JediToOpsLayoutMapping, hofx, varnames, & + JediObsName, WriteHofXIntoCX) implicit none ! Subroutine arguments: @@ -1434,24 +1436,122 @@ subroutine opsinputs_fill_fillreal2dfromgeoval( & type(opsinputs_jeditoopslayoutmapping), intent(in) :: JediToOpsLayoutMapping real(c_double), intent(in) :: hofx(:, :) type(oops_variables), intent(in) :: varnames +character(len=*), intent(in), optional :: JediObsName +logical, intent(in), optional :: WriteHofXIntoCX ! Local declarations: integer :: i integer :: hofxIndex +logical :: hofxToCx + +hofxToCx = .false. +if (present(WriteHofXIntoCX)) then + hofxToCx = WriteHofXIntoCX +end if ! Body: if (JediToOpsLayoutMapping % ConvertRecordsToMultiLevelObs) then - call opsinputs_fill_fillreal2dfromgeovalformultilevelobs( & - Hdr, OpsVarName, JediToOpsLayoutMapping % NumOpsObs, Real2, & - GeoVals, GeoVaLsAreTopToBottom, JediVarName, JediToOpsLayoutMapping) + ! Index of variable in hofx array. + hofxIndex = 0 + do i = 1, size(hofx, 1) + if (present(JediObsName)) then + if (varnames % variable(i) == JediObsName) then + hofxIndex = i + exit + end if + else + if (varnames % variable(i) == JediVarName) then + hofxIndex = i + exit + end if + end if + end do + if (hofxIndex > 0 .and. hofxToCx) then + call opsinputs_fill_fillreal2dfromhofx( & + Hdr, OpsVarName, Real2, & + JediToOpsLayoutMapping, hofx(hofxIndex,:), JediVarName, GeoVals) + else + call opsinputs_fill_fillreal2dfromgeovalformultilevelobs( & + Hdr, OpsVarName, JediToOpsLayoutMapping % NumOpsObs, Real2, & + GeoVals, GeoVaLsAreTopToBottom, JediVarName, JediToOpsLayoutMapping) + end if else - call opsinputs_fill_fillreal2dfromgeovalforsinglelevelobs( & + call opsinputs_fill_fillreal2dfromgeoval( & Hdr, OpsVarName, JediToOpsLayoutMapping % NumOpsObs, Real2, & GeoVals, GeoVaLsAreTopToBottom, JediVarName) end if -end subroutine opsinputs_fill_fillreal2dfromgeoval +end subroutine opsinputs_fill_fillreal2dfromgeovalorhofx + +! ------------------------------------------------------------------------------ + +!> Populate a 2D array of real numbers and its header from an HofX vector. +!> +!> \param[inout] Hdr +!> Header to be populated. +!> \param[in] OpsVarName +!> Name of the OB_type field to which \p Real2 corresponds. +!> \param[inout] Real2 +!> Pointer to the array to be populated. +!> \param[in] JediToOpsLayoutMapping +!> Mapping between indices of observations in the JEDI and OPS data structures. +!> \param[in] hofx +!> The HofX vector to use. +!> \param[in] GeoVals +!> A container holding the specified GeoVaL. +subroutine opsinputs_fill_fillreal2dfromhofx( & + Hdr, OpsVarName, Real2, JediToOpsLayoutMapping, hofx, JediVarName, GeoVals) +implicit none + +! Subroutine arguments: +type(ElementHeader_Type), intent(inout) :: Hdr +character(len=*), intent(in) :: OpsVarName +real(real64), pointer, intent(out) :: Real2(:,:) +type(opsinputs_jeditoopslayoutmapping), intent(in) :: JediToOpsLayoutMapping +real(c_double), intent(in) :: hofx(:) +character(len=*), intent(in) :: JediVarName +type(ufo_geovals), intent(in) :: GeoVals + +! Local declarations: +type(ufo_geoval), pointer :: GeoVal +real(kind_real) :: MissingReal +integer :: iObs, iLevel, iJediObs, numLevels + +! Body: + +if (ufo_vars_getindex(GeoVals % variables, JediVarName) > 0) then + ! Retrieve GeoVal + call ufo_geovals_get_var(GeoVals, JediVarName, GeoVal) + numLevels = GeoVal % nval +else + numLevels = JediToOpsLayoutMapping % MaxNumLevelsPerObs +end if + +MissingReal = missing_value(0.0_c_double) + +! Fill the OPS data structures +call Ops_Alloc(Hdr, OpsVarName, JediToOpsLayoutMapping % NumOpsObs, Real2, & + num_levels = int(numLevels, kind = 8)) + +do iObs = 1, JediToOpsLayoutMapping % NumOpsObs + ! Do not fill the CX column if there are no entries in the profile. + if (JediToOpsLayoutMapping % RecordStarts(iObs + 1) - JediToOpsLayoutMapping % RecordStarts(iObs) == 0) then + cycle + end if + do iLevel = 1, numLevels + ! Location of current observation in the ObsSpace. + iJediObs = JediToOpsLayoutMapping % LocationsOrderedByRecord( & + JediToOpsLayoutMapping % RecordStarts(iObs) - 1 + iLevel) + if (hofx(iJediObs) /= MissingReal) then + Real2(iObs,iLevel) = hofx(iJediObs) + end if + end do +end do + +end subroutine opsinputs_fill_fillreal2dfromhofx + + ! ------------------------------------------------------------------------------ diff --git a/test/testinput/002_UpperAirCxField_rh_Sonde.yaml b/test/testinput/002_UpperAirCxField_rh_Sonde.yaml index 3abdc1cb..66dd74e5 100644 --- a/test/testinput/002_UpperAirCxField_rh_Sonde.yaml +++ b/test/testinput/002_UpperAirCxField_rh_Sonde.yaml @@ -47,3 +47,52 @@ observations: HofX: HofX benchmarkFlag: 1000 # just to keep the ObsFilters test happy flaggedBenchmark: 0 + +# Same as above but writing H(x) into CX for relativeHumidity (as opposed to the default +# behaviour of writing GeoVaLs into CX). This is 'JADA compatibility mode'. + - obs space: + name: Sonde + obsdatain: + engine: + type: H5File + obsfile: Data/relative_humidity_Sonde.nc4 + obsgrouping: + group variables: [ "stationIdentification" ] + sort variable: "pressure" + sort order: "descending" + simulated variables: [relativeHumidity] + extension: + allocate companion records with length: 3 + geovals: + filename: Data/002_UpperAirCxFieldForMultiLevelObs_relative_humidity.nc4 + obs filters: + # Set the flag of observations with missing values to "pass": we want to check if these + # values are encoded correctly in the Cx file. + - filter: Reset Flags to Pass + flags_to_reset: [10] # missing + # Blacklist all entries in the original profiles. + # This must be run after the Reset Flags to Pass filter, + # which sets the flags of any missing values in the original profiles + # to 'pass'. Therefore this filter is run with the 'defer to post' option set to true. + - filter: BlackList + where: + - variable: + name: MetaData/extendedObsSpace + is_in: 0 + defer to post: true + - filter: Cx Writer + namelist_directory: testinput/CxWriterNamelists_002_UpperAirCxField_rh + reject_obs_with_any_variable_failing_qc: true + general_mode: debug + IC_PLevels: 5 + write_hofx_into_cx: true + - filter: Cx Checker + expected_surface_variables: [] + expected_upper_air_variables: ["2"] # IndexCxrh + expected_main_table_columns: + - # batch 1 + - ["1.25", "1.35", "1.45"] # column 1 + - ["1.55", "1.65", "1.75"] # column 2 + HofX: HofX + benchmarkFlag: 1000 # just to keep the ObsFilters test happy + flaggedBenchmark: 0