Skip to content

Commit 076e091

Browse files
authored
Merge branch 'develop' into feature/obsfilters_ufo
2 parents 333ae34 + 0055788 commit 076e091

32 files changed

+777
-24
lines changed

.github/workflows/ci.yml

+16-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ on:
55
branches: [develop]
66
pull_request:
77
branches: [develop]
8+
workflow_dispatch:
89
env:
910
REGISTRY: ghcr.io
10-
IMAGE_NAME: twsearle/orca-jedi/ci-almalinux9:v1.3.0
11+
IMAGE_NAME: twsearle/orca-jedi/ci-almalinux9:v1.4.0
1112
jobs:
1213
build:
1314
runs-on: ubuntu-latest
@@ -32,6 +33,20 @@ jobs:
3233
repository: JCSDA-internal/oops
3334
token: ${{ secrets.GHCR_PAT }}
3435

36+
- name: checkout vader
37+
uses: actions/checkout@v3
38+
with:
39+
path: ci/vader
40+
repository: JCSDA-internal/vader
41+
token: ${{ secrets.GHCR_PAT }}
42+
43+
- name: checkout saber
44+
uses: actions/checkout@v3
45+
with:
46+
path: ci/saber
47+
repository: JCSDA-internal/saber
48+
token: ${{ secrets.GHCR_PAT }}
49+
3550
- name: checkout ioda
3651
uses: actions/checkout@v3
3752
with:

CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ ecbuild_debug( " atlas_orca_FEATURES: [${atlas_orca_FEATURES}]" )
3636
find_package( oops REQUIRED )
3737
ecbuild_debug( " oops_FEATURES : [${oops_FEATURES}]" )
3838

39+
find_package( saber REQUIRED)
40+
ecbuild_debug( " saber_FEATURES : [${saber_FEATURES}]" )
41+
3942
find_package( ufo REQUIRED)
4043
ecbuild_debug( " ufo_FEATURES : [${ufo_FEATURES}]" )
4144

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.3.0
1+
1.4.0

ci/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ if(NOT DEFINED jedicmake_DIR)
1818
endif()
1919

2020
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/oops" EXCLUDE_FROM_ALL)
21+
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/vader" EXCLUDE_FROM_ALL)
22+
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/saber" EXCLUDE_FROM_ALL)
2123
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/ioda" EXCLUDE_FROM_ALL)
2224
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/ufo" EXCLUDE_FROM_ALL)
2325
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/orca-jedi")

ci/hpccm_recipe_almalinux9.py

+8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def gitlab_url(repo, vn):
5050
odc_vn = USERARG.get('odc_vn', '1.5.2')
5151
openmpi_vn = USERARG.get('openmpi_vn', '4.1.5')
5252
pycodestyle_vn = USERARG.get('pycodestyle_vn', '2.10')
53+
qhull_vn = USERARG.get('qhull_vn', '8.0.2') # no qhull-devel rpm
5354
udunits_vn = USERARG.get('udunits_vn', '2.2.28')
5455
yaxt_vn = USERARG.get('yaxt_vn', '528-0.10.0') # URL has a number and a version
5556

@@ -203,6 +204,13 @@ def gitlab_url(repo, vn):
203204
cmake_opts=['-DCMAKE_BUILD_TYPE=Release', '-DJSON_BuildTests=OFF'],
204205
)
205206

207+
Stage0 += generic_cmake(
208+
prefix='/usr/local',
209+
url=github_url('qhull/qhull', f'v{qhull_vn}'),
210+
directory=f'qhull-{qhull_vn}',
211+
cmake_opts=['-DCMAKE_BUILD_TYPE=Release'],
212+
)
213+
206214
Stage0 += generic_cmake(
207215
prefix='/usr/local',
208216
url=github_url('NOAA-EMC/NCEPLIBS-bufr', f'v{nceplibs_bufr_vn}'),

src/mains/CMakeLists.txt

+21
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ if( ${nemo-feedback_FOUND} )
1111
LIBS orcamodel nemo_feedback
1212
DEFINITIONS NEMO_FEEDBACK_EXISTS
1313
)
14+
15+
ecbuild_add_executable( TARGET orcamodel_3DVar.x
16+
SOURCES orcamodel3DVar.cc
17+
LIBS orcamodel nemo_feedback
18+
DEFINITIONS NEMO_FEEDBACK_EXISTS
19+
)
1420
else()
1521
message(" compiling without feedback file support")
1622
ecbuild_add_executable( TARGET orcamodel_hofx.x
@@ -22,6 +28,21 @@ else()
2228
SOURCES orcamodelHofX3D.cc
2329
LIBS orcamodel
2430
)
31+
32+
ecbuild_add_executable( TARGET orcamodel_3DVar.x
33+
SOURCES orcamodel3DVar.cc
34+
LIBS orcamodel
35+
)
2536
endif()
37+
38+
ecbuild_add_executable( TARGET orcamodel_ErrorCovarianceToolbox.x
39+
SOURCES orcamodelErrorCovarianceToolbox.cc
40+
LIBS orcamodel
41+
)
42+
2643
oops_output_json_schema( "orcamodel_hofx.x" )
2744
oops_output_json_schema( "orcamodel_hofx3D.x" )
45+
# The applications below throw an error generating json schema.
46+
# This appears to be a generic issue see also oops/qg/mains/CMakeLists.txt
47+
#oops_output_json_schema( "orcamodel_3DVar.x" )
48+
#oops_output_json_schema( "orcamodel_ErrorCovarianceToolbox.x" )

src/mains/orcamodel3DVar.cc

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* (C) British Crown Copyright 2024 MetOffice
3+
*
4+
* This software is licensed under the terms of the Apache Licence Version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
#include "atlas/library/Library.h"
9+
#include "oops/generic/instantiateModelFactory.h"
10+
11+
#include "oops/runs/Run.h"
12+
#include "oops/runs/Variational.h"
13+
#include "saber/oops/instantiateCovarFactory.h"
14+
15+
#include "ufo/ObsTraits.h"
16+
#include "ufo/instantiateObsFilterFactory.h"
17+
#if defined(NEMO_FEEDBACK_EXISTS)
18+
#include "nemo-feedback/instantiateObsFilterFactory.h"
19+
#endif
20+
21+
#include "orca-jedi/utilities/OrcaModelTraits.h"
22+
23+
int main(int argc, char ** argv) {
24+
oops::Run run(argc, argv);
25+
oops::instantiateModelFactory<orcamodel::OrcaModelTraits>();
26+
atlas::Library::instance().initialise();
27+
saber::instantiateCovarFactory<orcamodel::OrcaModelTraits>();
28+
ufo::instantiateObsFilterFactory();
29+
#if defined(NEMO_FEEDBACK_EXISTS)
30+
nemo_feedback::instantiateObsFilterFactory<ufo::ObsTraits>();
31+
#endif
32+
oops::Variational<orcamodel::OrcaModelTraits , ufo::ObsTraits> var;
33+
int i = run.execute(var);
34+
atlas::Library::instance().finalise();
35+
return i;
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* (C) British Crown Copyright 2024 MetOffice
3+
*
4+
* This software is licensed under the terms of the Apache Licence Version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
#include "atlas/library/Library.h"
9+
10+
#include "saber/oops/ErrorCovarianceToolbox.h"
11+
#include "saber/oops/instantiateCovarFactory.h"
12+
13+
#include "oops/runs/Run.h"
14+
#include "orca-jedi/utilities/OrcaModelTraits.h"
15+
16+
int main(int argc, char ** argv) {
17+
oops::Run run(argc, argv);
18+
atlas::Library::instance().initialise();
19+
saber::instantiateCovarFactory<orcamodel::OrcaModelTraits>();
20+
21+
saber::ErrorCovarianceToolbox<orcamodel::OrcaModelTraits> var;
22+
int i = run.execute(var);
23+
atlas::Library::instance().finalise();
24+
return i;
25+
}

src/orca-jedi/CMakeLists.txt

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,20 @@ utilities/Types.h
3535
utilities/Types.cc
3636
utilities/IOUtils.h
3737
utilities/IOUtils.cc
38+
utilities/ModelData.h
39+
utilities/ModelData.cc
3840
variablechanges/VariableChangeParameters.h
3941
variablechanges/VariableChange.h
42+
variablechanges/LinearVariableChange.h
43+
variablechanges/LinearVariableChange.cc
44+
variablechanges/LinearVariableChangeParameters.h
4045
)
4146

4247

4348

4449
ecbuild_add_library( TARGET orcamodel
4550
SOURCES ${oops_orcamodel_src_files}
46-
PUBLIC_LIBS oops ufo atlas atlas-orca eckit netcdf
51+
PUBLIC_LIBS oops saber ufo atlas atlas-orca eckit netcdf
4752
INSTALL_HEADERS LISTED
4853
LINKER_LANGUAGE ${OOPS_LINKER_LANGUAGE}
4954
)

src/orca-jedi/increment/Increment.cc

+20-5
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ Increment & Increment::operator*=(const double & zz) {
207207
return *this;
208208
}
209209

210-
/// \brief Create increment from the different of two state objects.
210+
/// \brief Create increment from the difference of two state objects.
211211
/// \param x1 State object.
212212
/// \param x2 State object subtracted.
213213
void Increment::diff(const State & x1, const State & x2) {
@@ -222,6 +222,12 @@ void Increment::diff(const State & x1, const State & x2) {
222222
atlas::Field field2 = x2.getField(i);
223223
atlas::Field fieldi = incrementFields_[i];
224224

225+
atlas::field::MissingValue mv1(field1);
226+
bool has_mv1 = static_cast<bool>(mv1);
227+
atlas::field::MissingValue mv2(field2);
228+
bool has_mv2 = static_cast<bool>(mv2);
229+
bool has_mv = has_mv1 || has_mv2;
230+
225231
std::string fieldName1 = field1.name();
226232
std::string fieldName2 = field2.name();
227233
std::string fieldNamei = fieldi.name();
@@ -234,8 +240,15 @@ void Increment::diff(const State & x1, const State & x2) {
234240
auto field_viewi = atlas::array::make_view<double, 2>(fieldi);
235241
for (atlas::idx_t j = 0; j < field_viewi.shape(0); ++j) {
236242
for (atlas::idx_t k = 0; k < field_viewi.shape(1); ++k) {
237-
if (!ghost(j)) { field_viewi(j, k) = field_view1(j, k) - field_view2(j, k);
238-
} else { field_viewi(j, k) = 0; }
243+
if (!ghost(j)) {
244+
if (!has_mv1 || (has_mv1 && !mv1(field_view1(j, k)))) {
245+
if (!has_mv2 || (has_mv2 && !mv2(field_view2(j, k)))) {
246+
field_viewi(j, k) = field_view1(j, k) - field_view2(j, k);
247+
}
248+
}
249+
} else {
250+
field_viewi(j, k) = 0;
251+
}
239252
}
240253
}
241254
}
@@ -532,7 +545,7 @@ void Increment::read(const eckit::Configuration & conf) {
532545
throw eckit::NotImplemented(err_message, Here());
533546
}
534547

535-
/// \brief write out increments fields to a file using params specified filename.
548+
/// \brief Write out increments fields to a file using params specified filename.
536549
void Increment::write(const OrcaIncrementParameters & params) const {
537550
oops::Log::debug() << "orcamodel::increment::write" << std::endl;
538551

@@ -545,10 +558,12 @@ void Increment::write(const OrcaIncrementParameters & params) const {
545558
oops::Log::debug() << "Increment::write to filename "
546559
<< nemo_field_path << std::endl;
547560

561+
incrementFields_.haloExchange();
562+
548563
writeFieldsToFile(nemo_field_path, *geom_, time_, incrementFields_);
549564
}
550565

551-
/// \brief write out increments fields to a file using config specified filename.
566+
/// \brief Write out increments fields to a file using config specified filename.
552567
void Increment::write(const eckit::Configuration & config) const {
553568
write(oops::validateAndDeserialize<OrcaIncrementParameters>(config));
554569
}

src/orca-jedi/interpolator/Interpolator.cc

+123
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "orca-jedi/geometry/Geometry.h"
2929
#include "orca-jedi/state/State.h"
30+
#include "orca-jedi/increment/Increment.h"
3031

3132
#include "orca-jedi/interpolator/Interpolator.h"
3233

@@ -177,6 +178,128 @@ template void Interpolator::executeInterpolation<float>(
177178
const std::vector<bool> & mask,
178179
std::vector<double>::iterator& iter) const;
179180

181+
/// \brief Interpolate from model space to observation space
182+
/// \param vars Oops variables
183+
/// \param inc Increment object (input)
184+
/// \param mask Mask vector in observation space
185+
/// \param result Result (output) vector in observation space
186+
void Interpolator::apply(const oops::Variables& vars, const Increment& inc,
187+
const std::vector<bool> & mask,
188+
std::vector<double>& result) const {
189+
// input is inc output is result
190+
const size_t nvars = vars.size();
191+
192+
for (size_t j=0; j < nvars; ++j) {
193+
if (!inc.variables().has(vars[j])) {
194+
std::stringstream err_stream;
195+
err_stream << "orcamodel::Interpolator::apply varname \" "
196+
<< "\" " << vars[j]
197+
<< " not found in the model increment." << std::endl;
198+
err_stream << " add the variable to the increment variables and "
199+
<< "add a mapping from the geometry to that variable."
200+
<< std::endl;
201+
throw eckit::BadParameter(err_stream.str(), Here());
202+
}
203+
}
204+
205+
const std::vector<size_t> varSizes =
206+
inc.geometry()->variableSizes(vars);
207+
size_t nvals = 0;
208+
for (size_t jvar=0; jvar < nvars; ++jvar) nvals += nlocs_ * varSizes[jvar];
209+
result.resize(nvals);
210+
211+
std::size_t out_idx = 0;
212+
for (size_t jvar=0; jvar < nvars; ++jvar) {
213+
auto gv_varname = vars[jvar].name();
214+
atlas::Field tgt_field = atlasObsFuncSpace_.createField<double>(
215+
atlas::option::name(gv_varname) |
216+
atlas::option::levels(varSizes[jvar]));
217+
interpolator_.execute(inc.incrementFields()[gv_varname], tgt_field);
218+
auto field_view = atlas::array::make_view<double, 2>(tgt_field);
219+
atlas::field::MissingValue mv(inc.incrementFields()[gv_varname]);
220+
bool has_mv = static_cast<bool>(mv);
221+
for (std::size_t klev=0; klev < varSizes[jvar]; ++klev) {
222+
for (std::size_t iloc=0; iloc < nlocs_; iloc++) {
223+
if (has_mv && mv(field_view(iloc, klev))) {
224+
result[out_idx] = util::missingValue<double>();
225+
} else {
226+
result[out_idx] = field_view(iloc, klev);
227+
}
228+
++out_idx;
229+
}
230+
}
231+
}
232+
}
233+
234+
/// \brief Interpolate from observation space to model space
235+
/// \param vars Oops variables
236+
/// \param inc Increment object (output)
237+
/// \param mask Mask (observation space) vector
238+
/// \param resultin Values (observation space) vector (input)
239+
void Interpolator::applyAD(const oops::Variables& vars, Increment& inc,
240+
const std::vector<bool> & mask,
241+
const std::vector<double> & resultin) const
242+
{
243+
// input is resultin output is inc
244+
245+
oops::Log::trace() << "orcamodel::Interpolator::applyAD start "
246+
<< std::endl;
247+
248+
const size_t nvars = vars.size();
249+
250+
for (size_t j=0; j < nvars; ++j) {
251+
if (!inc.variables().has(vars[j])) {
252+
std::stringstream err_stream;
253+
err_stream << "orcamodel::Interpolator::apply varname \" "
254+
<< "\" " << vars[j]
255+
<< " not found in the model increment." << std::endl;
256+
err_stream << " add the variable to the increment variables and "
257+
<< "add a mapping from the geometry to that variable."
258+
<< std::endl;
259+
throw eckit::BadParameter(err_stream.str(), Here());
260+
}
261+
}
262+
263+
const std::vector<size_t> varSizes =
264+
inc.geometry()->variableSizes(vars);
265+
size_t nvals = 0;
266+
267+
for (size_t jvar=0; jvar < nvars; ++jvar) nvals += nlocs_ * varSizes[jvar];
268+
269+
std::size_t out_idx = 0;
270+
for (size_t jvar=0; jvar < nvars; ++jvar) {
271+
auto gv_varname = vars[jvar].name();
272+
auto tgt_field = atlasObsFuncSpace_.createField<double>(
273+
atlas::option::name(gv_varname) |
274+
atlas::option::levels(varSizes[jvar]));
275+
276+
// Copying observation array vector to an atlas observation field (tgt_field)
277+
auto field_view = atlas::array::make_view<double, 2>(tgt_field);
278+
atlas::field::MissingValue mv(inc.incrementFields()[gv_varname]);
279+
bool has_mv = static_cast<bool>(mv);
280+
281+
for (std::size_t klev=0; klev < varSizes[jvar]; ++klev) {
282+
for (std::size_t iloc=0; iloc < nlocs_; iloc++) {
283+
if (has_mv && mv(field_view(iloc, klev))) {
284+
field_view(iloc, klev) = util::missingValue<double>();
285+
} else {
286+
field_view(iloc, klev) = resultin[out_idx];
287+
}
288+
++out_idx;
289+
}
290+
}
291+
292+
// halo exchange update ghost points
293+
std::shared_ptr<const Geometry> geom = inc.geometry();
294+
geom->functionSpace().haloExchange(inc.incrementFields()[gv_varname]);
295+
296+
interpolator_.execute_adjoint(inc.incrementFields()[gv_varname], tgt_field);
297+
} // jvar
298+
299+
oops::Log::trace() << "orcamodel::Interpolator::applyAD done "
300+
<< std::endl;
301+
}
302+
180303
void Interpolator::print(std::ostream & os) const {
181304
os << "orcamodel::Interpolator: " << std::endl;
182305
os << " Obs function space " << atlasObsFuncSpace_ << std::endl;

0 commit comments

Comments
 (0)