diff --git a/src/TiledArray/expressions/blk_tsr_expr.h b/src/TiledArray/expressions/blk_tsr_expr.h index 661e2ff666..f35863dc81 100644 --- a/src/TiledArray/expressions/blk_tsr_expr.h +++ b/src/TiledArray/expressions/blk_tsr_expr.h @@ -32,6 +32,9 @@ #include #include "blk_tsr_engine.h" +#include +#include + #include namespace TiledArray { @@ -313,8 +316,18 @@ class BlkTsrExprBase : public Expr { /// Sets result trange lobound such that the tile lobounds are not changed Derived& preserve_lobound() { - return set_trange_lobound( - array_.trange().make_tile_range(lower_bound()).lobound()); + // only set lobound if *all* dimensions have non-zero extents + const bool empty = ranges::any_of( + ranges::views::zip(lower_bound_, upper_bound_), [](auto&& p) { + auto [lb, ub] = p; + return lb >= ub; + }); + + if (!empty) { + return set_trange_lobound( + array_.trange().make_tile_range(lower_bound()).lobound()); + } + return static_cast(*this); } /// @return optional to result trange lobound; if null, the result trange diff --git a/src/TiledArray/expressions/expr.h b/src/TiledArray/expressions/expr.h index 3b1e9f43be..2df1709e02 100644 --- a/src/TiledArray/expressions/expr.h +++ b/src/TiledArray/expressions/expr.h @@ -518,9 +518,10 @@ class Expr { // Move the data from dist_eval into the sub-block of result array. // This step may involve communication when the tiles are moved from the // sub-block distribution to the array distribution. - // N.B. handle the corner case of zero-volume host array, then no data needs - // to be moved - if (tsr.array().trange().tiles_range().volume() != 0) { + // N.B. handle the corner cases of zero-volume host array and zero-volume + // block, then no data needs to be moved + if (tsr.array().trange().tiles_range().volume() != 0 && + blk_range.volume() != 0) { // N.B. must deep copy TA_ASSERT(tsr.array().trange().tiles_range().includes(tsr.lower_bound())); // N.B. this expression's range, diff --git a/tests/expressions_impl.h b/tests/expressions_impl.h index e7c781ccc6..6544b61e79 100644 --- a/tests/expressions_impl.h +++ b/tests/expressions_impl.h @@ -3075,6 +3075,53 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(empty_trange1, F, Fixtures, F) { } } +// corner case: expressions involving empty blocks +BOOST_FIXTURE_TEST_CASE_TEMPLATE(empty_block_expressions, F, Fixtures, F) { + auto& a = F::a; + auto& c = F::c; + + // one of the dimensions has zero extent + const auto lb1 = std::array{3, 2, 0}; + const auto ub1 = std::array{4, 2, 2}; + + const int ntiles = F::ntiles; + const auto lb2 = std::array{ntiles, 0, 0}; + const auto ub2 = std::array{ntiles, ntiles, ntiles}; + + // preserve_lobound should go through without issues + { + std::decay_t result; + BOOST_CHECK_NO_THROW(result("a,b,c") = + a("a,b,c").block(lb1, ub1, preserve_lobound)); + + BOOST_CHECK_EQUAL(result.trange().tiles_range().volume(), 0); + } + { + std::decay_t result; + BOOST_CHECK_NO_THROW(result("a,b,c") = + a("a,b,c").block(lb2, ub2, preserve_lobound)); + BOOST_CHECK_EQUAL(result.trange().tiles_range().volume(), 0); + } + + // assignment of empty (zero volume) block + { + BOOST_CHECK_NO_THROW(c("a,b,c").block(lb1, ub1) = + a("a,b,c").block(lb1, ub1)); + } + { + BOOST_CHECK_NO_THROW(c("a,b,c").block(lb2, ub2) = + a("a,b,c").block(lb2, ub2)); + } + + // assignment from empty array + { + std::decay_t z; + z("a,b,c") = a("a,b,c").block(lb2, ub2); // z is a full empty array + BOOST_CHECK_NO_THROW(c("a,b,c").block(lb2, ub2, preserve_lobound) = + z("a,b,c")); + } +} + BOOST_AUTO_TEST_SUITE_END() #endif // TILEDARRAY_TEST_EXPRESSIONS_IMPL_H