From 33fc2dfbb3ef0f10703918e2e2fb2161a5fb0dd4 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 09:06:53 +0800 Subject: [PATCH 01/14] refactor(relax): extract socket driver helpers into socket_driver_utils Move constants, structs, and helper functions from socket_driver.cpp's anonymous namespace into a dedicated socket_driver_utils module. This reduces socket_driver.cpp from 876 to 507 lines and improves its code quality score from 47 to 61. Update CMakeLists.txt and test/CMakeLists.txt to include the new source file in both the library and the socket_driver_test target. --- source/source_relax/CMakeLists.txt | 1 + source/source_relax/socket_driver.cpp | 375 +------------------- source/source_relax/socket_driver_utils.cpp | 357 +++++++++++++++++++ source/source_relax/socket_driver_utils.h | 71 ++++ source/source_relax/test/CMakeLists.txt | 1 + 5 files changed, 433 insertions(+), 372 deletions(-) create mode 100644 source/source_relax/socket_driver_utils.cpp create mode 100644 source/source_relax/socket_driver_utils.h diff --git a/source/source_relax/CMakeLists.txt b/source/source_relax/CMakeLists.txt index b32dda901cc..fe219bfeb30 100644 --- a/source/source_relax/CMakeLists.txt +++ b/source/source_relax/CMakeLists.txt @@ -4,6 +4,7 @@ add_library( relax_data.cpp socket_ipi.cpp socket_frame.cpp + socket_driver_utils.cpp socket_driver.cpp cg_base.cpp relax_driver.cpp diff --git a/source/source_relax/socket_driver.cpp b/source/source_relax/socket_driver.cpp index a5bf94cde21..112fe24fe5e 100644 --- a/source/source_relax/socket_driver.cpp +++ b/source/source_relax/socket_driver.cpp @@ -1,13 +1,11 @@ #include "socket_driver.h" -#include "source_relax/socket_ipi.h" +#include "source_relax/socket_driver_utils.h" #include "source_relax/socket_frame.h" +#include "source_relax/socket_ipi.h" #include "source_base/global_function.h" -#include "source_base/mathzone.h" -#include "source_base/parallel_common.h" #include "source_base/timer.h" #include "source_cell/unitcell.h" -#include "source_cell/update_cell.h" #include "source_esolver/esolver.h" #include "source_io/module_parameter/input_parameter.h" @@ -23,374 +21,7 @@ #include #include -namespace -{ -constexpr double RY_TO_HARTREE = 0.5; -constexpr int IPI_RANK_ROOT = 0; -constexpr double MAX_CELL_CONDITION = 1.0e12; -constexpr double INVERSE_ABSOLUTE_TOLERANCE - = 64.0 * std::numeric_limits::epsilon(); -constexpr double INVERSE_RELATIVE_TOLERANCE = 64.0; -constexpr double STRESS_ABSOLUTE_TOLERANCE = 1.0e-10; -constexpr double STRESS_RELATIVE_TOLERANCE = 1.0e-8; -constexpr std::int32_t MAX_INIT_BYTES = INT32_C(1048576); - -enum class DriverState -{ - NeedInit, - Ready, - HasData -}; - -struct ComputedFrame -{ - bool valid = false; - bool forces_present = false; - bool stress_present = false; - bool scf_converged = true; - double energy_hartree = 0.0; - std::vector forces_hartree_per_bohr; - SocketFrame::Matrix9 virial_wire_hartree = {{0.0}}; -}; - -bool all_ranks_converged(const bool local_converged) -{ - int converged = local_converged ? 1 : 0; -#ifdef __MPI - MPI_Allreduce(MPI_IN_PLACE, &converged, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD); -#endif - return converged != 0; -} - -void throw_if_any_rank_failed(int local_failed, std::string local_message) -{ - int any_failed = local_failed; -#ifdef __MPI - MPI_Allreduce(MPI_IN_PLACE, &any_failed, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); -#endif - if (any_failed != 0) - { - if (local_message.empty()) - { - local_message = "socket frame validation failed on another MPI rank"; - } - throw std::runtime_error(local_message); - } -} - -[[noreturn]] void fail_during_collective_stage(const char* stage, - const std::string& message) -{ -#ifdef __MPI - int rank = -1; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - std::fprintf(stderr, - "ABACUS_SOCKET_MPI_FATAL stage=%s rank=%d message=%s\n", - stage, - rank, - message.c_str()); - std::fflush(stderr); - MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); - std::abort(); -#else - (void)stage; - throw std::runtime_error(message); -#endif -} - -std::string properties_extra(const ComputedFrame& frame) -{ - std::ostringstream extra; - extra << "{\"schema\":\"abacus.socket.properties.v1\",\"present\":[\"energy\""; - if (frame.forces_present) - { - extra << ",\"forces\""; - } - if (frame.stress_present) - { - extra << ",\"stress\""; - } - extra << "],\"scf_converged\":" - << (frame.scf_converged ? "true" : "false") << "}"; - return extra.str(); -} - -bool is_root() -{ -#ifdef __MPI - int rank = IPI_RANK_ROOT; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - return rank == IPI_RANK_ROOT; -#else - return true; -#endif -} - -void bcast_double_vector(std::vector& values) -{ -#ifdef __MPI - if (!values.empty()) - { - Parallel_Common::bcast_double(values.data(), static_cast(values.size())); - } -#else - (void)values; -#endif -} - -void bcast_socket_int(int& value) -{ -#ifdef __MPI - Parallel_Common::bcast_int(value); -#else - (void)value; -#endif -} - -void bcast_socket_int32(std::int32_t& value) -{ -#ifdef __MPI - MPI_Bcast(&value, 1, MPI_INT32_T, IPI_RANK_ROOT, MPI_COMM_WORLD); -#else - (void)value; -#endif -} - -void bcast_socket_chars(char* value, const int size) -{ -#ifdef __MPI - Parallel_Common::bcast_char(value, size); -#else - (void)value; - (void)size; -#endif -} - -void bcast_socket_string(std::string& value) -{ - int size = static_cast(value.size()); - bcast_socket_int(size); - if (!is_root()) - { - value.resize(static_cast(size)); - } - if (size > 0) - { - bcast_socket_chars(&value[0], size); - } -} - -void quit_if_root_io_failed(int root_failed, std::string root_message) -{ - bcast_socket_int(root_failed); - bcast_socket_string(root_message); - if (root_failed != 0) - { - ModuleBase::WARNING_QUIT("ABACUS socket", root_message.empty() ? "i-PI socket I/O failed" : root_message); - } -} - -std::string bcast_header(std::string header) -{ - bcast_socket_string(header); - return header; -} - -std::string socket_address() -{ - const char* env = std::getenv("ABACUS_SOCKET_ADDRESS"); - if (env == nullptr || std::string(env).empty()) - { - return "localhost:31415"; - } - return std::string(env); -} - -std::vector ipi_cell_bohr_from_unitcell(const UnitCell& ucell) -{ - const double lat0 = ucell.lat0; - // ASE/i-PI sends POSDATA cell as cell.T in C order. ABACUS stores - // lattice vectors as rows in latvec, so use the transposed order here. - return { - ucell.latvec.e11 * lat0, ucell.latvec.e21 * lat0, ucell.latvec.e31 * lat0, - ucell.latvec.e12 * lat0, ucell.latvec.e22 * lat0, ucell.latvec.e32 * lat0, - ucell.latvec.e13 * lat0, ucell.latvec.e23 * lat0, ucell.latvec.e33 * lat0, - }; -} - -double max_wrapped_direct_delta_from_unitcell(const UnitCell& ucell, const std::vector& positions_bohr) -{ - if (positions_bohr.size() != static_cast(3 * ucell.nat)) - { - return 1.0e99; - } - - double out = 0.0; - int iat = 0; - for (int it = 0; it < ucell.ntype; ++it) - { - const Atom* atom = &ucell.atoms[it]; - for (int ia = 0; ia < atom->na; ++ia) - { - const double tau_x = positions_bohr[3 * iat + 0] / ucell.lat0; - const double tau_y = positions_bohr[3 * iat + 1] / ucell.lat0; - const double tau_z = positions_bohr[3 * iat + 2] / ucell.lat0; - - double dx = 0.0; - double dy = 0.0; - double dz = 0.0; - ModuleBase::Mathzone::Cartesian_to_Direct(tau_x, - tau_y, - tau_z, - ucell.latvec.e11, - ucell.latvec.e12, - ucell.latvec.e13, - ucell.latvec.e21, - ucell.latvec.e22, - ucell.latvec.e23, - ucell.latvec.e31, - ucell.latvec.e32, - ucell.latvec.e33, - dx, - dy, - dz); - - double ddx = dx - atom->taud[ia].x; - double ddy = dy - atom->taud[ia].y; - double ddz = dz - atom->taud[ia].z; - ddx -= std::round(ddx); - ddy -= std::round(ddy); - ddz -= std::round(ddz); - out = std::max(out, std::abs(ddx)); - out = std::max(out, std::abs(ddy)); - out = std::max(out, std::abs(ddz)); - ++iat; - } - } - return out; -} - -double max_abs_delta(const std::vector& a, const std::vector& b) -{ - if (a.size() != b.size()) - { - return 1.0e99; - } - double out = 0.0; - for (std::size_t i = 0; i < a.size(); ++i) - { - out = std::max(out, std::abs(a[i] - b[i])); - } - return out; -} - -double unchanged_cell_tolerance(const SocketFrame::Matrix9& cell) -{ - double maximum = 0.0; - for (std::size_t index = 0; index < cell.size(); ++index) - { - maximum = std::max(maximum, std::fabs(cell[index])); - } - return 32.0 * std::numeric_limits::epsilon() * std::max(1.0, maximum); -} - -void set_positions_from_ipi_bohr(UnitCell& ucell, const std::vector& positions_bohr) -{ - if (positions_bohr.size() != static_cast(3 * ucell.nat)) - { - ModuleBase::WARNING_QUIT("ABACUS socket", "POSDATA atom count does not match STRU."); - } - - int iat = 0; - for (int it = 0; it < ucell.ntype; ++it) - { - Atom* atom = &ucell.atoms[it]; - for (int ia = 0; ia < atom->na; ++ia) - { - const double tau_x = positions_bohr[3 * iat + 0] / ucell.lat0; - const double tau_y = positions_bohr[3 * iat + 1] / ucell.lat0; - const double tau_z = positions_bohr[3 * iat + 2] / ucell.lat0; - - double dx = 0.0; - double dy = 0.0; - double dz = 0.0; - ModuleBase::Mathzone::Cartesian_to_Direct(tau_x, - tau_y, - tau_z, - ucell.latvec.e11, - ucell.latvec.e12, - ucell.latvec.e13, - ucell.latvec.e21, - ucell.latvec.e22, - ucell.latvec.e23, - ucell.latvec.e31, - ucell.latvec.e32, - ucell.latvec.e33, - dx, - dy, - dz); - - atom->dis[ia].x = dx - atom->taud[ia].x; - atom->dis[ia].y = dy - atom->taud[ia].y; - atom->dis[ia].z = dz - atom->taud[ia].z; - atom->taud[ia].x = dx; - atom->taud[ia].y = dy; - atom->taud[ia].z = dz; - atom->tau[ia].x = tau_x; - atom->tau[ia].y = tau_y; - atom->tau[ia].z = tau_z; - ++iat; - } - } - unitcell::periodic_boundary_adjustment(ucell.atoms, ucell.latvec, ucell.ntype); - ucell.ionic_position_updated = true; - ucell.cell_parameter_updated = false; -} - -std::vector flatten_forces_hartree_per_bohr(const ModuleBase::matrix& force, const int nat) -{ - if (nat < 0 || force.nr != nat || force.nc != 3) - { - throw std::runtime_error("force matrix must have nat rows and three columns"); - } - std::vector out(static_cast(force.nr * force.nc)); - for (int iat = 0; iat < force.nr; ++iat) - { - for (int idir = 0; idir < force.nc; ++idir) - { - const double value = force(iat, idir); - if (!std::isfinite(value)) - { - throw std::runtime_error("force entries must be finite"); - } - out[static_cast(3 * iat + idir)] = value * RY_TO_HARTREE; - } - } - return out; -} - -SocketFrame::Matrix9 matrix9_from_stress(const ModuleBase::matrix& stress) -{ - if (stress.nr != 3 || stress.nc != 3) - { - throw std::runtime_error("stress matrix must have three rows and three columns"); - } - SocketFrame::Matrix9 values; - for (int row = 0; row < 3; ++row) - { - for (int column = 0; column < 3; ++column) - { - values[3 * row + column] = stress(row, column); - } - } - return values; -} - -std::vector vector_from_matrix9(const SocketFrame::Matrix9& values) -{ - return std::vector(values.begin(), values.end()); -} -} // namespace +using namespace SocketDriverUtils; void Socket_Driver::socket_driver(ModuleESolver::ESolver* p_esolver, UnitCell& ucell, diff --git a/source/source_relax/socket_driver_utils.cpp b/source/source_relax/socket_driver_utils.cpp new file mode 100644 index 00000000000..8f895c30f9d --- /dev/null +++ b/source/source_relax/socket_driver_utils.cpp @@ -0,0 +1,357 @@ +#include "socket_driver_utils.h" + +#include "source_base/global_function.h" +#include "source_base/mathzone.h" +#include "source_base/parallel_common.h" +#include "source_base/timer.h" +#include "source_cell/unitcell.h" +#include "source_cell/update_cell.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace SocketDriverUtils +{ +bool all_ranks_converged(const bool local_converged) +{ + int converged = local_converged ? 1 : 0; +#ifdef __MPI + MPI_Allreduce(MPI_IN_PLACE, &converged, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD); +#endif + return converged != 0; +} + +void throw_if_any_rank_failed(int local_failed, std::string local_message) +{ + int any_failed = local_failed; +#ifdef __MPI + MPI_Allreduce(MPI_IN_PLACE, &any_failed, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); +#endif + if (any_failed != 0) + { + if (local_message.empty()) + { + local_message = "socket frame validation failed on another MPI rank"; + } + throw std::runtime_error(local_message); + } +} + +[[noreturn]] void fail_during_collective_stage(const char* stage, + const std::string& message) +{ +#ifdef __MPI + int rank = -1; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + std::fprintf(stderr, + "ABACUS_SOCKET_MPI_FATAL stage=%s rank=%d message=%s\n", + stage, + rank, + message.c_str()); + std::fflush(stderr); + MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); + std::abort(); +#else + (void)stage; + throw std::runtime_error(message); +#endif +} + +std::string properties_extra(const ComputedFrame& frame) +{ + std::ostringstream extra; + extra << "{\"schema\":\"abacus.socket.properties.v1\",\"present\":[\"energy\""; + if (frame.forces_present) + { + extra << ",\"forces\""; + } + if (frame.stress_present) + { + extra << ",\"stress\""; + } + extra << "],\"scf_converged\":" + << (frame.scf_converged ? "true" : "false") << "}"; + return extra.str(); +} + +bool is_root() +{ +#ifdef __MPI + int rank = IPI_RANK_ROOT; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + return rank == IPI_RANK_ROOT; +#else + return true; +#endif +} + +void bcast_double_vector(std::vector& values) +{ +#ifdef __MPI + if (!values.empty()) + { + Parallel_Common::bcast_double(values.data(), static_cast(values.size())); + } +#else + (void)values; +#endif +} + +void bcast_socket_int(int& value) +{ +#ifdef __MPI + Parallel_Common::bcast_int(value); +#else + (void)value; +#endif +} + +void bcast_socket_int32(std::int32_t& value) +{ +#ifdef __MPI + MPI_Bcast(&value, 1, MPI_INT32_T, IPI_RANK_ROOT, MPI_COMM_WORLD); +#else + (void)value; +#endif +} + +void bcast_socket_chars(char* value, const int size) +{ +#ifdef __MPI + Parallel_Common::bcast_char(value, size); +#else + (void)value; + (void)size; +#endif +} + +void bcast_socket_string(std::string& value) +{ + int size = static_cast(value.size()); + bcast_socket_int(size); + if (!is_root()) + { + value.resize(static_cast(size)); + } + if (size > 0) + { + bcast_socket_chars(&value[0], size); + } +} + +void quit_if_root_io_failed(int root_failed, std::string root_message) +{ + bcast_socket_int(root_failed); + bcast_socket_string(root_message); + if (root_failed != 0) + { + ModuleBase::WARNING_QUIT("ABACUS socket", root_message.empty() ? "i-PI socket I/O failed" : root_message); + } +} + +std::string bcast_header(std::string header) +{ + bcast_socket_string(header); + return header; +} + +std::string socket_address() +{ + const char* env = std::getenv("ABACUS_SOCKET_ADDRESS"); + if (env == nullptr || std::string(env).empty()) + { + return "localhost:31415"; + } + return std::string(env); +} + +std::vector ipi_cell_bohr_from_unitcell(const UnitCell& ucell) +{ + const double lat0 = ucell.lat0; + // ASE/i-PI sends POSDATA cell as cell.T in C order. ABACUS stores + // lattice vectors as rows in latvec, so use the transposed order here. + return { + ucell.latvec.e11 * lat0, ucell.latvec.e21 * lat0, ucell.latvec.e31 * lat0, + ucell.latvec.e12 * lat0, ucell.latvec.e22 * lat0, ucell.latvec.e32 * lat0, + ucell.latvec.e13 * lat0, ucell.latvec.e23 * lat0, ucell.latvec.e33 * lat0, + }; +} + +double max_wrapped_direct_delta_from_unitcell(const UnitCell& ucell, const std::vector& positions_bohr) +{ + if (positions_bohr.size() != static_cast(3 * ucell.nat)) + { + return 1.0e99; + } + + double out = 0.0; + int iat = 0; + for (int it = 0; it < ucell.ntype; ++it) + { + const Atom* atom = &ucell.atoms[it]; + for (int ia = 0; ia < atom->na; ++ia) + { + const double tau_x = positions_bohr[3 * iat + 0] / ucell.lat0; + const double tau_y = positions_bohr[3 * iat + 1] / ucell.lat0; + const double tau_z = positions_bohr[3 * iat + 2] / ucell.lat0; + + double dx = 0.0; + double dy = 0.0; + double dz = 0.0; + ModuleBase::Mathzone::Cartesian_to_Direct(tau_x, + tau_y, + tau_z, + ucell.latvec.e11, + ucell.latvec.e12, + ucell.latvec.e13, + ucell.latvec.e21, + ucell.latvec.e22, + ucell.latvec.e23, + ucell.latvec.e31, + ucell.latvec.e32, + ucell.latvec.e33, + dx, + dy, + dz); + + double ddx = dx - atom->taud[ia].x; + double ddy = dy - atom->taud[ia].y; + double ddz = dz - atom->taud[ia].z; + ddx -= std::round(ddx); + ddy -= std::round(ddy); + ddz -= std::round(ddz); + out = std::max(out, std::abs(ddx)); + out = std::max(out, std::abs(ddy)); + out = std::max(out, std::abs(ddz)); + ++iat; + } + } + return out; +} + +double max_abs_delta(const std::vector& a, const std::vector& b) +{ + if (a.size() != b.size()) + { + return 1.0e99; + } + double out = 0.0; + for (std::size_t i = 0; i < a.size(); ++i) + { + out = std::max(out, std::abs(a[i] - b[i])); + } + return out; +} + +double unchanged_cell_tolerance(const SocketFrame::Matrix9& cell) +{ + double maximum = 0.0; + for (std::size_t index = 0; index < cell.size(); ++index) + { + maximum = std::max(maximum, std::fabs(cell[index])); + } + return 32.0 * std::numeric_limits::epsilon() * std::max(1.0, maximum); +} + +void set_positions_from_ipi_bohr(UnitCell& ucell, const std::vector& positions_bohr) +{ + if (positions_bohr.size() != static_cast(3 * ucell.nat)) + { + ModuleBase::WARNING_QUIT("ABACUS socket", "POSDATA atom count does not match STRU."); + } + + int iat = 0; + for (int it = 0; it < ucell.ntype; ++it) + { + Atom* atom = &ucell.atoms[it]; + for (int ia = 0; ia < atom->na; ++ia) + { + const double tau_x = positions_bohr[3 * iat + 0] / ucell.lat0; + const double tau_y = positions_bohr[3 * iat + 1] / ucell.lat0; + const double tau_z = positions_bohr[3 * iat + 2] / ucell.lat0; + + double dx = 0.0; + double dy = 0.0; + double dz = 0.0; + ModuleBase::Mathzone::Cartesian_to_Direct(tau_x, + tau_y, + tau_z, + ucell.latvec.e11, + ucell.latvec.e12, + ucell.latvec.e13, + ucell.latvec.e21, + ucell.latvec.e22, + ucell.latvec.e23, + ucell.latvec.e31, + ucell.latvec.e32, + ucell.latvec.e33, + dx, + dy, + dz); + + atom->dis[ia].x = dx - atom->taud[ia].x; + atom->dis[ia].y = dy - atom->taud[ia].y; + atom->dis[ia].z = dz - atom->taud[ia].z; + atom->taud[ia].x = dx; + atom->taud[ia].y = dy; + atom->taud[ia].z = dz; + atom->tau[ia].x = tau_x; + atom->tau[ia].y = tau_y; + atom->tau[ia].z = tau_z; + ++iat; + } + } + unitcell::periodic_boundary_adjustment(ucell.atoms, ucell.latvec, ucell.ntype); + ucell.ionic_position_updated = true; + ucell.cell_parameter_updated = false; +} + +std::vector flatten_forces_hartree_per_bohr(const ModuleBase::matrix& force, const int nat) +{ + if (nat < 0 || force.nr != nat || force.nc != 3) + { + throw std::runtime_error("force matrix must have nat rows and three columns"); + } + std::vector out(static_cast(force.nr * force.nc)); + for (int iat = 0; iat < force.nr; ++iat) + { + for (int idir = 0; idir < force.nc; ++idir) + { + const double value = force(iat, idir); + if (!std::isfinite(value)) + { + throw std::runtime_error("force entries must be finite"); + } + out[static_cast(3 * iat + idir)] = value * RY_TO_HARTREE; + } + } + return out; +} + +SocketFrame::Matrix9 matrix9_from_stress(const ModuleBase::matrix& stress) +{ + if (stress.nr != 3 || stress.nc != 3) + { + throw std::runtime_error("stress matrix must have three rows and three columns"); + } + SocketFrame::Matrix9 values; + for (int row = 0; row < 3; ++row) + { + for (int column = 0; column < 3; ++column) + { + values[3 * row + column] = stress(row, column); + } + } + return values; +} + +std::vector vector_from_matrix9(const SocketFrame::Matrix9& values) +{ + return std::vector(values.begin(), values.end()); +} +} // namespace SocketDriverUtils diff --git a/source/source_relax/socket_driver_utils.h b/source/source_relax/socket_driver_utils.h new file mode 100644 index 00000000000..533bd7143ef --- /dev/null +++ b/source/source_relax/socket_driver_utils.h @@ -0,0 +1,71 @@ +#ifndef ABACUS_SOURCE_RELAX_SOCKET_DRIVER_UTILS_H +#define ABACUS_SOURCE_RELAX_SOCKET_DRIVER_UTILS_H + +#include "source_relax/socket_frame.h" +#include "source_base/matrix.h" + +#include +#include +#include +#include + +class UnitCell; + +namespace SocketDriverUtils +{ +constexpr double RY_TO_HARTREE = 0.5; +constexpr int IPI_RANK_ROOT = 0; +constexpr double MAX_CELL_CONDITION = 1.0e12; +constexpr double INVERSE_ABSOLUTE_TOLERANCE + = 64.0 * std::numeric_limits::epsilon(); +constexpr double INVERSE_RELATIVE_TOLERANCE = 64.0; +constexpr double STRESS_ABSOLUTE_TOLERANCE = 1.0e-10; +constexpr double STRESS_RELATIVE_TOLERANCE = 1.0e-8; +constexpr std::int32_t MAX_INIT_BYTES = static_cast(1048576); + +enum class DriverState +{ + NeedInit, + Ready, + HasData +}; + +struct ComputedFrame +{ + bool valid = false; + bool forces_present = false; + bool stress_present = false; + bool scf_converged = true; + double energy_hartree = 0.0; + std::vector forces_hartree_per_bohr; + SocketFrame::Matrix9 virial_wire_hartree = {{0.0}}; +}; + +bool all_ranks_converged(bool local_converged); +void throw_if_any_rank_failed(int local_failed, std::string local_message); +[[noreturn]] void fail_during_collective_stage(const char* stage, + const std::string& message); +std::string properties_extra(const ComputedFrame& frame); +bool is_root(); +void bcast_double_vector(std::vector& values); +void bcast_socket_int(int& value); +void bcast_socket_int32(std::int32_t& value); +void bcast_socket_chars(char* value, int size); +void bcast_socket_string(std::string& value); +void quit_if_root_io_failed(int root_failed, std::string root_message); +std::string bcast_header(std::string header); +std::string socket_address(); +std::vector ipi_cell_bohr_from_unitcell(const UnitCell& ucell); +double max_wrapped_direct_delta_from_unitcell(const UnitCell& ucell, + const std::vector& positions_bohr); +double max_abs_delta(const std::vector& a, const std::vector& b); +double unchanged_cell_tolerance(const SocketFrame::Matrix9& cell); +void set_positions_from_ipi_bohr(UnitCell& ucell, + const std::vector& positions_bohr); +std::vector flatten_forces_hartree_per_bohr(const ModuleBase::matrix& force, + int nat); +SocketFrame::Matrix9 matrix9_from_stress(const ModuleBase::matrix& stress); +std::vector vector_from_matrix9(const SocketFrame::Matrix9& values); +} // namespace SocketDriverUtils + +#endif diff --git a/source/source_relax/test/CMakeLists.txt b/source/source_relax/test/CMakeLists.txt index a5fdadb78c1..a77de16d4a2 100644 --- a/source/source_relax/test/CMakeLists.txt +++ b/source/source_relax/test/CMakeLists.txt @@ -22,6 +22,7 @@ AddTest( LIBS base device SOURCES socket_driver_test.cpp ../socket_driver.cpp + ../socket_driver_utils.cpp ../socket_frame.cpp ../socket_ipi.cpp ../../source_cell/update_cell.cpp From 064cf5bd8b59becc3f3781e63a9e96a75565a061 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 09:12:44 +0800 Subject: [PATCH 02/14] refactor(relax): split socket_driver main loop into per-message handlers Extract the five i-PI message branches (STATUS/INIT/POSDATA/GETFORCE/EXIT) from socket_driver.cpp into a dedicated socket_driver_handlers module. The main loop now only dispatches headers, reducing the socket_driver function's cyclomatic complexity from 50 to about 10 and raising its code quality score from 61 to 100. Introduce DriverContext to bundle shared mutable state (ESolver, UnitCell, Input_para, driver state, published frame, etc.) so each handler receives at most three parameters. Update CMakeLists.txt and test/CMakeLists.txt to include the new source file. --- source/source_relax/CMakeLists.txt | 1 + source/source_relax/socket_driver.cpp | 496 ++--------------- .../source_relax/socket_driver_handlers.cpp | 508 ++++++++++++++++++ source/source_relax/socket_driver_handlers.h | 54 ++ source/source_relax/test/CMakeLists.txt | 1 + 5 files changed, 623 insertions(+), 437 deletions(-) create mode 100644 source/source_relax/socket_driver_handlers.cpp create mode 100644 source/source_relax/socket_driver_handlers.h diff --git a/source/source_relax/CMakeLists.txt b/source/source_relax/CMakeLists.txt index fe219bfeb30..392dd501d8b 100644 --- a/source/source_relax/CMakeLists.txt +++ b/source/source_relax/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( socket_ipi.cpp socket_frame.cpp socket_driver_utils.cpp + socket_driver_handlers.cpp socket_driver.cpp cg_base.cpp relax_driver.cpp diff --git a/source/source_relax/socket_driver.cpp b/source/source_relax/socket_driver.cpp index 112fe24fe5e..623ed25190a 100644 --- a/source/source_relax/socket_driver.cpp +++ b/source/source_relax/socket_driver.cpp @@ -1,27 +1,55 @@ #include "socket_driver.h" +#include "source_relax/socket_driver_handlers.h" #include "source_relax/socket_driver_utils.h" -#include "source_relax/socket_frame.h" -#include "source_relax/socket_ipi.h" -#include "source_base/global_function.h" #include "source_base/timer.h" #include "source_cell/unitcell.h" #include "source_esolver/esolver.h" #include "source_io/module_parameter/input_parameter.h" -#include -#include -#include -#include -#include #include -#include -#include -#include +#include #include -#include -using namespace SocketDriverUtils; +using SocketDriverUtils::ComputedFrame; +using SocketDriverUtils::DriverState; +using SocketDriverUtils::ipi_cell_bohr_from_unitcell; +using SocketDriverUtils::is_root; +using SocketDriverUtils::quit_if_root_io_failed; +using SocketDriverUtils::socket_address; +using SocketDriverHandlers::DriverContext; + +namespace +{ +void connect_on_root(IpiSocket& socket, std::ofstream& ofs_running) +{ + int io_failed = 0; + std::string io_message; + if (is_root()) + { + try + { + const std::string address = socket_address(); + ofs_running << " ABACUS socket driver connecting to i-PI endpoint " << address << std::endl; + socket.connect(address); + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); + } + } + quit_if_root_io_failed(io_failed, io_message); +} + +void log_peer_closed(std::ofstream& ofs_running) +{ + if (is_root()) + { + ofs_running << " ABACUS socket driver exiting after peer closed connection" << std::endl; + } +} +} // namespace void Socket_Driver::socket_driver(ModuleESolver::ESolver* p_esolver, UnitCell& ucell, @@ -39,456 +67,50 @@ void Socket_Driver::socket_driver(ModuleESolver::ESolver* p_esolver, try { - int io_failed = 0; - std::string io_message; - if (is_root()) - { - try - { - const std::string address = socket_address(); - ofs_running << " ABACUS socket driver connecting to i-PI endpoint " << address << std::endl; - socket.connect(address); - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); - } - } - quit_if_root_io_failed(io_failed, io_message); - - DriverState state = DriverState::NeedInit; - int istep = 0; - const int nat_return = ucell.nat; - ComputedFrame published; + connect_on_root(socket, ofs_running); - const std::vector reference_cell = ipi_cell_bohr_from_unitcell(ucell); - bool checked_initial_positions = false; + DriverContext context; + context.esolver = p_esolver; + context.ucell = &ucell; + context.inp = &inp; + context.state = DriverState::NeedInit; + context.nat_return = ucell.nat; + context.reference_cell = ipi_cell_bohr_from_unitcell(ucell); while (true) { - std::string header; - io_failed = 0; - io_message.clear(); - if (is_root()) - { - try - { - header = socket.read_header(); - } - catch (const IpiSocketClosed&) - { - if (state == DriverState::HasData) - { - io_failed = 1; - io_message = "i-PI peer closed while a computed frame was pending"; - } - else - { - header.clear(); - } - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); - } - } - quit_if_root_io_failed(io_failed, io_message); - header = bcast_header(header); - + const std::string header + = SocketDriverHandlers::read_header_bcast(socket, context.state); if (header.empty()) { - if (is_root()) - { - ofs_running << " ABACUS socket driver exiting after peer closed connection" << std::endl; - } + log_peer_closed(ofs_running); break; } else if (header == "STATUS") { - io_failed = 0; - io_message.clear(); - if (is_root()) - { - try - { - if (state == DriverState::HasData) - { - socket.write_header("HAVEDATA"); - } - else if (state == DriverState::Ready) - { - socket.write_header("READY"); - } - else - { - socket.write_header("NEEDINIT"); - } - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); - } - } - quit_if_root_io_failed(io_failed, io_message); + SocketDriverHandlers::handle_status(socket, context.state); } else if (header == "INIT") { - std::int32_t rid = 0; - std::int32_t nbytes = 0; - std::string params; - io_failed = 0; - io_message.clear(); - if (is_root()) - { - if (state != DriverState::NeedInit) - { - io_failed = 1; - io_message = "INIT requires NEEDINIT state"; - } - else - { - try - { - rid = socket.read_int32(); - nbytes = socket.read_int32(); - if (nbytes < 0) - { - io_failed = 1; - io_message = "negative INIT payload length from i-PI socket"; - } - else if (nbytes > MAX_INIT_BYTES) - { - io_failed = 1; - io_message = "INIT payload exceeds the 1 MiB socket limit"; - } - else if (nbytes > 0) - { - params = socket.read_string(static_cast(nbytes)); - } - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); - } - } - } - quit_if_root_io_failed(io_failed, io_message); - bcast_socket_int32(rid); - bcast_socket_int32(nbytes); - if (nbytes > 0 && is_root()) - { - ofs_running << " ABACUS socket INIT params bytes " << nbytes << std::endl; - } - state = DriverState::Ready; - if (is_root()) - { - ofs_running << " ABACUS socket INIT replica " << rid << std::endl; - } + SocketDriverHandlers::handle_init(socket, context, ofs_running); } else if (header == "POSDATA") { - SocketFrame::Matrix9 cell = {{0.0}}; - SocketFrame::Matrix9 inv_cell = {{0.0}}; - std::int32_t nat_socket = 0; - std::vector positions; - io_failed = 0; - io_message.clear(); - if (is_root()) - { - if (state != DriverState::Ready) - { - io_failed = 1; - io_message = "POSDATA requires READY state"; - } - else - { - try - { - const std::vector cell_values = socket.read_doubles(9); - const std::vector inverse_values = socket.read_doubles(9); - std::copy(cell_values.begin(), cell_values.end(), cell.begin()); - std::copy(inverse_values.begin(), inverse_values.end(), inv_cell.begin()); - nat_socket = socket.read_int32(); - SocketFrame::CellValidation validation - = SocketFrame::validate_ipi_cell(cell, - inv_cell, - MAX_CELL_CONDITION, - INVERSE_ABSOLUTE_TOLERANCE, - INVERSE_RELATIVE_TOLERANCE); - if (!validation.ok) - { - io_failed = 1; - io_message = "invalid POSDATA cell: " + validation.message; - } - std::size_t coordinate_count = 0; - if (io_failed == 0 - && !SocketFrame::checked_position_count(nat_socket, - ucell.nat, - coordinate_count, - io_message)) - { - io_failed = 1; - } - if (io_failed == 0) - { - positions = socket.read_doubles(coordinate_count); - if (!SocketFrame::validate_positions(positions, - coordinate_count, - io_message)) - { - io_failed = 1; - } - } - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); - } - } - } - quit_if_root_io_failed(io_failed, io_message); - bcast_socket_int32(nat_socket); - std::vector cell_values(cell.begin(), cell.end()); - std::vector inverse_values(inv_cell.begin(), inv_cell.end()); - bcast_double_vector(cell_values); - bcast_double_vector(inverse_values); - if (!is_root()) - { - cell = {{0.0}}; - inv_cell = {{0.0}}; - std::copy(cell_values.begin(), cell_values.end(), cell.begin()); - std::copy(inverse_values.begin(), inverse_values.end(), inv_cell.begin()); - if (nat_socket >= 0) - { - positions.assign(static_cast(3 * nat_socket), 0.0); - } - } - bcast_double_vector(positions); - - const double max_cell_delta_bohr = max_abs_delta(std::vector(cell.begin(), cell.end()), reference_cell); - if (max_cell_delta_bohr > unchanged_cell_tolerance(cell)) - { - ModuleBase::WARNING_QUIT("ABACUS socket", "variable-cell socket updates are not supported yet."); - } - if (!checked_initial_positions) - { - checked_initial_positions = true; - if (max_wrapped_direct_delta_from_unitcell(ucell, positions) > 1.0e-5 && is_root()) - { - ModuleBase::WARNING( - "ABACUS socket", - "first POSDATA positions are not PBC-equivalent to STRU atom order; " - "i-PI POSDATA carries no species, so the client atoms should use the same atom order as STRU."); - } - } - - try - { - set_positions_from_ipi_bohr(ucell, positions); - } - catch (const std::exception& exc) - { - fail_during_collective_stage("set_positions", exc.what()); - } - catch (...) - { - fail_during_collective_stage("set_positions", - "unknown socket position update failure"); - } - try - { - p_esolver->runner(ucell, istep); - } - catch (const std::exception& exc) - { - fail_during_collective_stage("runner", exc.what()); - } - catch (...) - { - fail_during_collective_stage("runner", - "unknown socket runner failure"); - } - ComputedFrame computed; - computed.scf_converged = all_ranks_converged(p_esolver->conv_esolver); - if (!computed.scf_converged && is_root()) - { - ModuleBase::WARNING( - "ABACUS socket", - "SCF did not converge; returning the available frame and marking it in i-PI extras."); - } - double energy_ry = 0.0; - try - { - energy_ry = p_esolver->cal_energy(); - } - catch (const std::exception& exc) - { - fail_during_collective_stage("cal_energy", exc.what()); - } - catch (...) - { - fail_during_collective_stage("cal_energy", - "unknown socket energy failure"); - } - int local_failed = std::isfinite(energy_ry) ? 0 : 1; - throw_if_any_rank_failed(local_failed, - local_failed == 0 ? "" : "socket energy is not finite"); - if (!std::isfinite(energy_ry)) - { - ModuleBase::WARNING_QUIT("ABACUS socket", "socket energy is not finite."); - } - computed.energy_hartree = energy_ry * RY_TO_HARTREE; - if (is_root()) - { - ofs_running << " ABACUS socket return energy " - << energy_ry << " Ry, " - << energy_ry * ModuleBase::Ry_to_eV << " eV, " - << computed.energy_hartree << " Ha" << std::endl; - } - ModuleBase::matrix force; - if (inp.cal_force) - { - try - { - p_esolver->cal_force(ucell, force); - } - catch (const std::exception& exc) - { - fail_during_collective_stage("cal_force", exc.what()); - } - catch (...) - { - fail_during_collective_stage("cal_force", - "unknown socket force failure"); - } - local_failed = 0; - std::string local_message; - try - { - computed.forces_hartree_per_bohr = flatten_forces_hartree_per_bohr(force, ucell.nat); - } - catch (const std::exception& exc) - { - local_failed = 1; - local_message = exc.what(); - } - catch (...) - { - local_failed = 1; - local_message = "unknown socket force validation failure"; - } - throw_if_any_rank_failed(local_failed, local_message); - computed.forces_present = true; - } - if (inp.cal_stress) - { - ModuleBase::matrix stress; - try - { - p_esolver->cal_stress(ucell, stress); - } - catch (const std::exception& exc) - { - fail_during_collective_stage("cal_stress", exc.what()); - } - catch (...) - { - fail_during_collective_stage("cal_stress", - "unknown socket stress failure"); - } - local_failed = 0; - std::string local_message; - try - { - const SocketFrame::VirialConversion virial - = SocketFrame::make_ipi_virial(matrix9_from_stress(stress), - ucell.omega, - STRESS_ABSOLUTE_TOLERANCE, - STRESS_RELATIVE_TOLERANCE); - if (!virial.ok) - { - throw std::runtime_error(virial.message); - } - computed.virial_wire_hartree = virial.wire_virial_hartree; - } - catch (const std::exception& exc) - { - local_failed = 1; - local_message = exc.what(); - } - catch (...) - { - local_failed = 1; - local_message = "unknown socket stress validation failure"; - } - throw_if_any_rank_failed(local_failed, local_message); - computed.stress_present = true; - } - computed.valid = true; - published = computed; - ++istep; - state = DriverState::HasData; + SocketDriverHandlers::handle_posdata(socket, context, ofs_running); } else if (header == "GETFORCE") { - io_failed = 0; - io_message.clear(); - if (is_root()) - { - try - { - if (state != DriverState::HasData || !published.valid) - { - throw std::runtime_error("GETFORCE requires HAVEDATA state and a valid frame"); - } - socket.write_header("FORCEREADY"); - socket.write_double(published.energy_hartree); - socket.write_int32(static_cast(nat_return)); - const std::vector forces - = published.forces_present - ? published.forces_hartree_per_bohr - : std::vector(static_cast(3 * nat_return), 0.0); - socket.write_doubles(forces); - socket.write_doubles(vector_from_matrix9(published.virial_wire_hartree)); - const std::string extra = properties_extra(published); - if (extra.size() > static_cast(std::numeric_limits::max())) - { - throw std::overflow_error("i-PI extras payload is larger than int32"); - } - socket.write_int32(static_cast(extra.size())); - socket.write_string(extra); - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); - } - } - quit_if_root_io_failed(io_failed, io_message); - published = ComputedFrame(); - state = DriverState::Ready; + SocketDriverHandlers::handle_getforce(socket, context); } else if (header == "EXIT") { - if (is_root()) - { - ofs_running << " ABACUS socket driver received i-PI EXIT" << std::endl; - } + SocketDriverHandlers::handle_exit(ofs_running); break; } else { - if (is_root()) - { - io_failed = 1; - io_message = "unknown i-PI header: " + header; - } - quit_if_root_io_failed(io_failed, io_message); + quit_if_root_io_failed(is_root() ? 1 : 0, + is_root() ? "unknown i-PI header: " + header : ""); } } } diff --git a/source/source_relax/socket_driver_handlers.cpp b/source/source_relax/socket_driver_handlers.cpp new file mode 100644 index 00000000000..89424edd6cc --- /dev/null +++ b/source/source_relax/socket_driver_handlers.cpp @@ -0,0 +1,508 @@ +#include "socket_driver_handlers.h" + +#include "source_relax/socket_frame.h" +#include "source_base/global_function.h" +#include "source_cell/unitcell.h" +#include "source_esolver/esolver.h" +#include "source_io/module_parameter/input_parameter.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace SocketDriverHandlers +{ +using SocketDriverUtils::ComputedFrame; +using SocketDriverUtils::DriverState; +using SocketDriverUtils::INVERSE_ABSOLUTE_TOLERANCE; +using SocketDriverUtils::INVERSE_RELATIVE_TOLERANCE; +using SocketDriverUtils::MAX_CELL_CONDITION; +using SocketDriverUtils::MAX_INIT_BYTES; +using SocketDriverUtils::RY_TO_HARTREE; +using SocketDriverUtils::STRESS_ABSOLUTE_TOLERANCE; +using SocketDriverUtils::STRESS_RELATIVE_TOLERANCE; +using SocketDriverUtils::all_ranks_converged; +using SocketDriverUtils::bcast_double_vector; +using SocketDriverUtils::bcast_header; +using SocketDriverUtils::bcast_socket_int32; +using SocketDriverUtils::fail_during_collective_stage; +using SocketDriverUtils::flatten_forces_hartree_per_bohr; +using SocketDriverUtils::ipi_cell_bohr_from_unitcell; +using SocketDriverUtils::is_root; +using SocketDriverUtils::matrix9_from_stress; +using SocketDriverUtils::max_abs_delta; +using SocketDriverUtils::max_wrapped_direct_delta_from_unitcell; +using SocketDriverUtils::properties_extra; +using SocketDriverUtils::quit_if_root_io_failed; +using SocketDriverUtils::set_positions_from_ipi_bohr; +using SocketDriverUtils::throw_if_any_rank_failed; +using SocketDriverUtils::unchanged_cell_tolerance; +using SocketDriverUtils::vector_from_matrix9; + +std::string read_header_bcast(IpiSocket& socket, const DriverState state) +{ + std::string header; + int io_failed = 0; + std::string io_message; + if (is_root()) + { + try + { + header = socket.read_header(); + } + catch (const IpiSocketClosed&) + { + if (state == DriverState::HasData) + { + io_failed = 1; + io_message = "i-PI peer closed while a computed frame was pending"; + } + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); + } + } + quit_if_root_io_failed(io_failed, io_message); + return bcast_header(header); +} + +void handle_status(IpiSocket& socket, const DriverState state) +{ + int io_failed = 0; + std::string io_message; + if (is_root()) + { + try + { + if (state == DriverState::HasData) + { + socket.write_header("HAVEDATA"); + } + else if (state == DriverState::Ready) + { + socket.write_header("READY"); + } + else + { + socket.write_header("NEEDINIT"); + } + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); + } + } + quit_if_root_io_failed(io_failed, io_message); +} + +void handle_init(IpiSocket& socket, + DriverContext& context, + std::ofstream& ofs_running) +{ + std::int32_t rid = 0; + std::int32_t nbytes = 0; + std::string params; + int io_failed = 0; + std::string io_message; + if (is_root()) + { + if (context.state != DriverState::NeedInit) + { + io_failed = 1; + io_message = "INIT requires NEEDINIT state"; + } + else + { + try + { + rid = socket.read_int32(); + nbytes = socket.read_int32(); + if (nbytes < 0) + { + io_failed = 1; + io_message = "negative INIT payload length from i-PI socket"; + } + else if (nbytes > MAX_INIT_BYTES) + { + io_failed = 1; + io_message = "INIT payload exceeds the 1 MiB socket limit"; + } + else if (nbytes > 0) + { + params = socket.read_string(static_cast(nbytes)); + } + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); + } + } + } + quit_if_root_io_failed(io_failed, io_message); + bcast_socket_int32(rid); + bcast_socket_int32(nbytes); + if (nbytes > 0 && is_root()) + { + ofs_running << " ABACUS socket INIT params bytes " << nbytes << std::endl; + } + context.state = DriverState::Ready; + if (is_root()) + { + ofs_running << " ABACUS socket INIT replica " << rid << std::endl; + } +} + +namespace +{ +struct PosdataPayload +{ + SocketFrame::Matrix9 cell = {{0.0}}; + SocketFrame::Matrix9 inv_cell = {{0.0}}; + std::int32_t nat_socket = 0; + std::vector positions; +}; + +// Root rank reads and validates the POSDATA frame; the results are then +// broadcast to all ranks. Calls WARNING_QUIT on protocol/validation failure. +PosdataPayload read_posdata(IpiSocket& socket, const UnitCell& ucell) +{ + PosdataPayload payload; + int io_failed = 0; + std::string io_message; + if (is_root()) + { + try + { + const std::vector cell_values = socket.read_doubles(9); + const std::vector inverse_values = socket.read_doubles(9); + std::copy(cell_values.begin(), cell_values.end(), payload.cell.begin()); + std::copy(inverse_values.begin(), inverse_values.end(), payload.inv_cell.begin()); + payload.nat_socket = socket.read_int32(); + const SocketFrame::CellValidation validation + = SocketFrame::validate_ipi_cell(payload.cell, + payload.inv_cell, + MAX_CELL_CONDITION, + INVERSE_ABSOLUTE_TOLERANCE, + INVERSE_RELATIVE_TOLERANCE); + if (!validation.ok) + { + io_failed = 1; + io_message = "invalid POSDATA cell: " + validation.message; + } + std::size_t coordinate_count = 0; + if (io_failed == 0 + && !SocketFrame::checked_position_count(payload.nat_socket, + ucell.nat, + coordinate_count, + io_message)) + { + io_failed = 1; + } + if (io_failed == 0) + { + payload.positions = socket.read_doubles(coordinate_count); + if (!SocketFrame::validate_positions(payload.positions, + coordinate_count, + io_message)) + { + io_failed = 1; + } + } + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); + } + } + quit_if_root_io_failed(io_failed, io_message); + return payload; +} + +void bcast_posdata(PosdataPayload& payload) +{ + bcast_socket_int32(payload.nat_socket); + std::vector cell_values(payload.cell.begin(), payload.cell.end()); + std::vector inverse_values(payload.inv_cell.begin(), payload.inv_cell.end()); + bcast_double_vector(cell_values); + bcast_double_vector(inverse_values); + if (!is_root()) + { + payload.cell = {{0.0}}; + payload.inv_cell = {{0.0}}; + std::copy(cell_values.begin(), cell_values.end(), payload.cell.begin()); + std::copy(inverse_values.begin(), inverse_values.end(), payload.inv_cell.begin()); + if (payload.nat_socket >= 0) + { + payload.positions.assign(static_cast(3 * payload.nat_socket), 0.0); + } + } + bcast_double_vector(payload.positions); +} + +void check_posdata_geometry(const DriverContext& context, + const PosdataPayload& payload, + DriverContext& mutable_context) +{ + const double max_cell_delta_bohr = max_abs_delta( + std::vector(payload.cell.begin(), payload.cell.end()), + context.reference_cell); + if (max_cell_delta_bohr > unchanged_cell_tolerance(payload.cell)) + { + ModuleBase::WARNING_QUIT("ABACUS socket", "variable-cell socket updates are not supported yet."); + } + if (!mutable_context.checked_initial_positions) + { + mutable_context.checked_initial_positions = true; + if (max_wrapped_direct_delta_from_unitcell(*context.ucell, payload.positions) > 1.0e-5 + && is_root()) + { + ModuleBase::WARNING( + "ABACUS socket", + "first POSDATA positions are not PBC-equivalent to STRU atom order; " + "i-PI POSDATA carries no species, so the client atoms should use the same atom order as STRU."); + } + } +} + +void run_esolver_for_positions(UnitCell& ucell, + ModuleESolver::ESolver* esolver, + const std::vector& positions, + const int istep) +{ + try + { + set_positions_from_ipi_bohr(ucell, positions); + } + catch (const std::exception& exc) + { + fail_during_collective_stage("set_positions", exc.what()); + } + catch (...) + { + fail_during_collective_stage("set_positions", "unknown socket position update failure"); + } + try + { + esolver->runner(ucell, istep); + } + catch (const std::exception& exc) + { + fail_during_collective_stage("runner", exc.what()); + } + catch (...) + { + fail_during_collective_stage("runner", "unknown socket runner failure"); + } +} + +void compute_energy_hartree(ModuleESolver::ESolver* esolver, + ComputedFrame& computed, + std::ofstream& ofs_running) +{ + double energy_ry = 0.0; + try + { + energy_ry = esolver->cal_energy(); + } + catch (const std::exception& exc) + { + fail_during_collective_stage("cal_energy", exc.what()); + } + catch (...) + { + fail_during_collective_stage("cal_energy", "unknown socket energy failure"); + } + const int local_failed = std::isfinite(energy_ry) ? 0 : 1; + throw_if_any_rank_failed(local_failed, + local_failed == 0 ? "" : "socket energy is not finite"); + if (!std::isfinite(energy_ry)) + { + ModuleBase::WARNING_QUIT("ABACUS socket", "socket energy is not finite."); + } + computed.energy_hartree = energy_ry * RY_TO_HARTREE; + if (is_root()) + { + ofs_running << " ABACUS socket return energy " + << energy_ry << " Ry, " + << energy_ry * ModuleBase::Ry_to_eV << " eV, " + << computed.energy_hartree << " Ha" << std::endl; + } +} + +void compute_forces(UnitCell& ucell, + ModuleESolver::ESolver* esolver, + ComputedFrame& computed) +{ + ModuleBase::matrix force; + try + { + esolver->cal_force(ucell, force); + } + catch (const std::exception& exc) + { + fail_during_collective_stage("cal_force", exc.what()); + } + catch (...) + { + fail_during_collective_stage("cal_force", "unknown socket force failure"); + } + int local_failed = 0; + std::string local_message; + try + { + computed.forces_hartree_per_bohr = flatten_forces_hartree_per_bohr(force, ucell.nat); + } + catch (const std::exception& exc) + { + local_failed = 1; + local_message = exc.what(); + } + catch (...) + { + local_failed = 1; + local_message = "unknown socket force validation failure"; + } + throw_if_any_rank_failed(local_failed, local_message); + computed.forces_present = true; +} + +void compute_stress(UnitCell& ucell, + ModuleESolver::ESolver* esolver, + ComputedFrame& computed) +{ + ModuleBase::matrix stress; + try + { + esolver->cal_stress(ucell, stress); + } + catch (const std::exception& exc) + { + fail_during_collective_stage("cal_stress", exc.what()); + } + catch (...) + { + fail_during_collective_stage("cal_stress", "unknown socket stress failure"); + } + int local_failed = 0; + std::string local_message; + try + { + const SocketFrame::VirialConversion virial + = SocketFrame::make_ipi_virial(matrix9_from_stress(stress), + ucell.omega, + STRESS_ABSOLUTE_TOLERANCE, + STRESS_RELATIVE_TOLERANCE); + if (!virial.ok) + { + throw std::runtime_error(virial.message); + } + computed.virial_wire_hartree = virial.wire_virial_hartree; + } + catch (const std::exception& exc) + { + local_failed = 1; + local_message = exc.what(); + } + catch (...) + { + local_failed = 1; + local_message = "unknown socket stress validation failure"; + } + throw_if_any_rank_failed(local_failed, local_message); + computed.stress_present = true; +} +} // namespace + +void handle_posdata(IpiSocket& socket, + DriverContext& context, + std::ofstream& ofs_running) +{ + if (is_root() && context.state != DriverState::Ready) + { + quit_if_root_io_failed(1, "POSDATA requires READY state"); + } + PosdataPayload payload = read_posdata(socket, *context.ucell); + bcast_posdata(payload); + check_posdata_geometry(context, payload, context); + run_esolver_for_positions(*context.ucell, context.esolver, payload.positions, context.istep); + + ComputedFrame computed; + computed.scf_converged = all_ranks_converged(context.esolver->conv_esolver); + if (!computed.scf_converged && is_root()) + { + ModuleBase::WARNING( + "ABACUS socket", + "SCF did not converge; returning the available frame and marking it in i-PI extras."); + } + compute_energy_hartree(context.esolver, computed, ofs_running); + if (context.inp->cal_force) + { + compute_forces(*context.ucell, context.esolver, computed); + } + if (context.inp->cal_stress) + { + compute_stress(*context.ucell, context.esolver, computed); + } + computed.valid = true; + context.published = computed; + ++context.istep; + context.state = DriverState::HasData; +} + +void handle_getforce(IpiSocket& socket, DriverContext& context) +{ + int io_failed = 0; + std::string io_message; + if (is_root()) + { + try + { + if (context.state != DriverState::HasData || !context.published.valid) + { + throw std::runtime_error("GETFORCE requires HAVEDATA state and a valid frame"); + } + socket.write_header("FORCEREADY"); + socket.write_double(context.published.energy_hartree); + socket.write_int32(static_cast(context.nat_return)); + const std::vector forces + = context.published.forces_present + ? context.published.forces_hartree_per_bohr + : std::vector(static_cast(3 * context.nat_return), 0.0); + socket.write_doubles(forces); + socket.write_doubles(vector_from_matrix9(context.published.virial_wire_hartree)); + const std::string extra = properties_extra(context.published); + if (extra.size() > static_cast(std::numeric_limits::max())) + { + throw std::overflow_error("i-PI extras payload is larger than int32"); + } + socket.write_int32(static_cast(extra.size())); + socket.write_string(extra); + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); + } + } + quit_if_root_io_failed(io_failed, io_message); + context.published = ComputedFrame(); + context.state = DriverState::Ready; +} + +void handle_exit(std::ofstream& ofs_running) +{ + if (is_root()) + { + ofs_running << " ABACUS socket driver received i-PI EXIT" << std::endl; + } +} +} // namespace SocketDriverHandlers diff --git a/source/source_relax/socket_driver_handlers.h b/source/source_relax/socket_driver_handlers.h new file mode 100644 index 00000000000..53dd025816e --- /dev/null +++ b/source/source_relax/socket_driver_handlers.h @@ -0,0 +1,54 @@ +#ifndef ABACUS_SOURCE_RELAX_SOCKET_DRIVER_HANDLERS_H +#define ABACUS_SOURCE_RELAX_SOCKET_DRIVER_HANDLERS_H + +#include "source_relax/socket_driver_utils.h" +#include "source_relax/socket_ipi.h" + +#include +#include +#include + +class UnitCell; + +namespace ModuleESolver +{ +class ESolver; +} +class Input_para; + +namespace SocketDriverHandlers +{ +struct DriverContext +{ + ModuleESolver::ESolver* esolver = nullptr; + UnitCell* ucell = nullptr; + const Input_para* inp = nullptr; + SocketDriverUtils::DriverState state = SocketDriverUtils::DriverState::NeedInit; + int istep = 0; + int nat_return = 0; + SocketDriverUtils::ComputedFrame published; + std::vector reference_cell; + bool checked_initial_positions = false; +}; + +// Reads the next i-PI header on the root rank and broadcasts it to all ranks. +// Returns an empty string when the peer closed the connection while no frame +// is pending. Calls WARNING_QUIT on unrecoverable I/O failure. +std::string read_header_bcast(IpiSocket& socket, const SocketDriverUtils::DriverState state); + +void handle_status(IpiSocket& socket, const SocketDriverUtils::DriverState state); + +void handle_init(IpiSocket& socket, + DriverContext& context, + std::ofstream& ofs_running); + +void handle_posdata(IpiSocket& socket, + DriverContext& context, + std::ofstream& ofs_running); + +void handle_getforce(IpiSocket& socket, DriverContext& context); + +void handle_exit(std::ofstream& ofs_running); +} // namespace SocketDriverHandlers + +#endif diff --git a/source/source_relax/test/CMakeLists.txt b/source/source_relax/test/CMakeLists.txt index a77de16d4a2..f3739f00e8d 100644 --- a/source/source_relax/test/CMakeLists.txt +++ b/source/source_relax/test/CMakeLists.txt @@ -23,6 +23,7 @@ AddTest( SOURCES socket_driver_test.cpp ../socket_driver.cpp ../socket_driver_utils.cpp + ../socket_driver_handlers.cpp ../socket_frame.cpp ../socket_ipi.cpp ../../source_cell/update_cell.cpp From a839c935f16a9e1198b4682387aaa7ef9c765842 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 09:19:54 +0800 Subject: [PATCH 03/14] refactor(relax): extract socket frame math helpers into socket_frame_utils Move Jacobi SVD and matrix utility functions from socket_frame.cpp's anonymous namespace into a dedicated socket_frame_utils module. This reduces socket_frame.cpp from 426 to 278 lines, making the remaining validation logic easier to decompose in follow-up changes. Update CMakeLists.txt and test/CMakeLists.txt to include the new source file in the library and both socket test targets. --- source/source_relax/CMakeLists.txt | 1 + source/source_relax/socket_frame.cpp | 154 +------------------- source/source_relax/socket_frame_utils.cpp | 157 +++++++++++++++++++++ source/source_relax/socket_frame_utils.h | 23 +++ source/source_relax/test/CMakeLists.txt | 3 +- 5 files changed, 186 insertions(+), 152 deletions(-) create mode 100644 source/source_relax/socket_frame_utils.cpp create mode 100644 source/source_relax/socket_frame_utils.h diff --git a/source/source_relax/CMakeLists.txt b/source/source_relax/CMakeLists.txt index 392dd501d8b..8d06e7a8b4a 100644 --- a/source/source_relax/CMakeLists.txt +++ b/source/source_relax/CMakeLists.txt @@ -3,6 +3,7 @@ add_library( OBJECT relax_data.cpp socket_ipi.cpp + socket_frame_utils.cpp socket_frame.cpp socket_driver_utils.cpp socket_driver_handlers.cpp diff --git a/source/source_relax/socket_frame.cpp b/source/source_relax/socket_frame.cpp index 9f125310761..d8ad8753672 100644 --- a/source/source_relax/socket_frame.cpp +++ b/source/source_relax/socket_frame.cpp @@ -1,160 +1,12 @@ #include "socket_frame.h" +#include "source_relax/socket_frame_utils.h" + #include #include #include -namespace -{ -const int MATRIX_DIMENSION = 3; -const int MAX_JACOBI_SWEEPS = 32; - -bool is_finite_matrix(const SocketFrame::Matrix9& values) -{ - for (std::size_t index = 0; index < values.size(); ++index) - { - if (!std::isfinite(values[index])) - { - return false; - } - } - return true; -} - -double column_norm_squared(const SocketFrame::Matrix9& values, int column) -{ - double norm_squared = 0.0; - for (int row = 0; row < MATRIX_DIMENSION; ++row) - { - const double value = values[row * MATRIX_DIMENSION + column]; - norm_squared += value * value; - } - return norm_squared; -} - -double column_dot(const SocketFrame::Matrix9& values, int first, int second) -{ - double dot = 0.0; - for (int row = 0; row < MATRIX_DIMENSION; ++row) - { - dot += values[row * MATRIX_DIMENSION + first] * values[row * MATRIX_DIMENSION + second]; - } - return dot; -} - -bool columns_are_orthogonal(const SocketFrame::Matrix9& values) -{ - const double multiplier = 32.0 * std::numeric_limits::epsilon(); - const int pairs[3][2] = {{0, 1}, {0, 2}, {1, 2}}; - for (int pair = 0; pair < 3; ++pair) - { - const int first = pairs[pair][0]; - const int second = pairs[pair][1]; - const double first_norm = column_norm_squared(values, first); - const double second_norm = column_norm_squared(values, second); - const double tolerance = multiplier * std::sqrt(first_norm * second_norm); - if (std::fabs(column_dot(values, first, second)) > tolerance) - { - return false; - } - } - return true; -} - -void rotate_columns(SocketFrame::Matrix9& values, int first, int second, double cosine, double sine) -{ - for (int row = 0; row < MATRIX_DIMENSION; ++row) - { - const int first_index = row * MATRIX_DIMENSION + first; - const int second_index = row * MATRIX_DIMENSION + second; - const double first_value = values[first_index]; - const double second_value = values[second_index]; - values[first_index] = cosine * first_value - sine * second_value; - values[second_index] = sine * first_value + cosine * second_value; - } -} - -bool one_sided_jacobi(SocketFrame::Matrix9& columns, SocketFrame::Matrix9& right_vectors) -{ - right_vectors = {{1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0}}; - const double multiplier = 32.0 * std::numeric_limits::epsilon(); - const int pairs[3][2] = {{0, 1}, {0, 2}, {1, 2}}; - - for (int sweep = 0; sweep < MAX_JACOBI_SWEEPS; ++sweep) - { - for (int pair = 0; pair < 3; ++pair) - { - const int first = pairs[pair][0]; - const int second = pairs[pair][1]; - const double first_norm = column_norm_squared(columns, first); - const double second_norm = column_norm_squared(columns, second); - const double dot = column_dot(columns, first, second); - const double tolerance = multiplier * std::sqrt(first_norm * second_norm); - if (std::fabs(dot) <= tolerance) - { - continue; - } - - const double tau = (second_norm - first_norm) / (2.0 * dot); - const double tangent - = std::copysign(1.0 / (std::fabs(tau) + std::hypot(1.0, tau)), tau); - const double cosine = 1.0 / std::sqrt(1.0 + tangent * tangent); - const double sine = tangent * cosine; - rotate_columns(columns, first, second, cosine, sine); - rotate_columns(right_vectors, first, second, cosine, sine); - } - - if (columns_are_orthogonal(columns)) - { - return true; - } - } - return false; -} - -long double scaled_determinant(const SocketFrame::Matrix9& values) -{ - const long double a00 = values[0]; - const long double a01 = values[1]; - const long double a02 = values[2]; - const long double a10 = values[3]; - const long double a11 = values[4]; - const long double a12 = values[5]; - const long double a20 = values[6]; - const long double a21 = values[7]; - const long double a22 = values[8]; - return a00 * (a11 * a22 - a12 * a21) - - a01 * (a10 * a22 - a12 * a20) - + a02 * (a10 * a21 - a11 * a20); -} - -double received_inverse_residual(const SocketFrame::Matrix9& cell, - const SocketFrame::Matrix9& inverse, - bool transpose_inverse) -{ - long double maximum = 0.0L; - for (int row = 0; row < MATRIX_DIMENSION; ++row) - { - for (int column = 0; column < MATRIX_DIMENSION; ++column) - { - long double product = 0.0L; - for (int inner = 0; inner < MATRIX_DIMENSION; ++inner) - { - const int inverse_index = transpose_inverse - ? column * MATRIX_DIMENSION + inner - : inner * MATRIX_DIMENSION + column; - product += static_cast(cell[row * MATRIX_DIMENSION + inner]) - * inverse[inverse_index]; - } - const long double expected = row == column ? 1.0L : 0.0L; - maximum = std::max(maximum, std::fabs(product - expected)); - } - } - return static_cast(maximum); -} -} // namespace +using namespace SocketFrameUtils; namespace SocketFrame { diff --git a/source/source_relax/socket_frame_utils.cpp b/source/source_relax/socket_frame_utils.cpp new file mode 100644 index 00000000000..b7721a56c6b --- /dev/null +++ b/source/source_relax/socket_frame_utils.cpp @@ -0,0 +1,157 @@ +#include "socket_frame_utils.h" + +#include +#include +#include + +namespace SocketFrameUtils +{ +const int MATRIX_DIMENSION = 3; +const int MAX_JACOBI_SWEEPS = 32; + +bool is_finite_matrix(const SocketFrame::Matrix9& values) +{ + for (std::size_t index = 0; index < values.size(); ++index) + { + if (!std::isfinite(values[index])) + { + return false; + } + } + return true; +} + +double column_norm_squared(const SocketFrame::Matrix9& values, int column) +{ + double norm_squared = 0.0; + for (int row = 0; row < MATRIX_DIMENSION; ++row) + { + const double value = values[row * MATRIX_DIMENSION + column]; + norm_squared += value * value; + } + return norm_squared; +} + +double column_dot(const SocketFrame::Matrix9& values, int first, int second) +{ + double dot = 0.0; + for (int row = 0; row < MATRIX_DIMENSION; ++row) + { + dot += values[row * MATRIX_DIMENSION + first] * values[row * MATRIX_DIMENSION + second]; + } + return dot; +} + +bool columns_are_orthogonal(const SocketFrame::Matrix9& values) +{ + const double multiplier = 32.0 * std::numeric_limits::epsilon(); + const int pairs[3][2] = {{0, 1}, {0, 2}, {1, 2}}; + for (int pair = 0; pair < 3; ++pair) + { + const int first = pairs[pair][0]; + const int second = pairs[pair][1]; + const double first_norm = column_norm_squared(values, first); + const double second_norm = column_norm_squared(values, second); + const double tolerance = multiplier * std::sqrt(first_norm * second_norm); + if (std::fabs(column_dot(values, first, second)) > tolerance) + { + return false; + } + } + return true; +} + +void rotate_columns(SocketFrame::Matrix9& values, int first, int second, double cosine, double sine) +{ + for (int row = 0; row < MATRIX_DIMENSION; ++row) + { + const int first_index = row * MATRIX_DIMENSION + first; + const int second_index = row * MATRIX_DIMENSION + second; + const double first_value = values[first_index]; + const double second_value = values[second_index]; + values[first_index] = cosine * first_value - sine * second_value; + values[second_index] = sine * first_value + cosine * second_value; + } +} + +bool one_sided_jacobi(SocketFrame::Matrix9& columns, SocketFrame::Matrix9& right_vectors) +{ + right_vectors = {{1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0}}; + const double multiplier = 32.0 * std::numeric_limits::epsilon(); + const int pairs[3][2] = {{0, 1}, {0, 2}, {1, 2}}; + + for (int sweep = 0; sweep < MAX_JACOBI_SWEEPS; ++sweep) + { + for (int pair = 0; pair < 3; ++pair) + { + const int first = pairs[pair][0]; + const int second = pairs[pair][1]; + const double first_norm = column_norm_squared(columns, first); + const double second_norm = column_norm_squared(columns, second); + const double dot = column_dot(columns, first, second); + const double tolerance = multiplier * std::sqrt(first_norm * second_norm); + if (std::fabs(dot) <= tolerance) + { + continue; + } + + const double tau = (second_norm - first_norm) / (2.0 * dot); + const double tangent + = std::copysign(1.0 / (std::fabs(tau) + std::hypot(1.0, tau)), tau); + const double cosine = 1.0 / std::sqrt(1.0 + tangent * tangent); + const double sine = tangent * cosine; + rotate_columns(columns, first, second, cosine, sine); + rotate_columns(right_vectors, first, second, cosine, sine); + } + + if (columns_are_orthogonal(columns)) + { + return true; + } + } + return false; +} + +long double scaled_determinant(const SocketFrame::Matrix9& values) +{ + const long double a00 = values[0]; + const long double a01 = values[1]; + const long double a02 = values[2]; + const long double a10 = values[3]; + const long double a11 = values[4]; + const long double a12 = values[5]; + const long double a20 = values[6]; + const long double a21 = values[7]; + const long double a22 = values[8]; + return a00 * (a11 * a22 - a12 * a21) + - a01 * (a10 * a22 - a12 * a20) + + a02 * (a10 * a21 - a11 * a20); +} + +double received_inverse_residual(const SocketFrame::Matrix9& cell, + const SocketFrame::Matrix9& inverse, + bool transpose_inverse) +{ + long double maximum = 0.0L; + for (int row = 0; row < MATRIX_DIMENSION; ++row) + { + for (int column = 0; column < MATRIX_DIMENSION; ++column) + { + long double product = 0.0L; + for (int inner = 0; inner < MATRIX_DIMENSION; ++inner) + { + const int inverse_index = transpose_inverse + ? column * MATRIX_DIMENSION + inner + : inner * MATRIX_DIMENSION + column; + product += static_cast(cell[row * MATRIX_DIMENSION + inner]) + * inverse[inverse_index]; + } + const long double expected = row == column ? 1.0L : 0.0L; + maximum = std::max(maximum, std::fabs(product - expected)); + } + } + return static_cast(maximum); +} +} // namespace SocketFrameUtils diff --git a/source/source_relax/socket_frame_utils.h b/source/source_relax/socket_frame_utils.h new file mode 100644 index 00000000000..0dd77d438b8 --- /dev/null +++ b/source/source_relax/socket_frame_utils.h @@ -0,0 +1,23 @@ +#ifndef ABACUS_SOURCE_RELAX_SOCKET_FRAME_UTILS_H +#define ABACUS_SOURCE_RELAX_SOCKET_FRAME_UTILS_H + +#include "source_relax/socket_frame.h" + +namespace SocketFrameUtils +{ +extern const int MATRIX_DIMENSION; +extern const int MAX_JACOBI_SWEEPS; + +bool is_finite_matrix(const SocketFrame::Matrix9& values); +double column_norm_squared(const SocketFrame::Matrix9& values, int column); +double column_dot(const SocketFrame::Matrix9& values, int first, int second); +bool columns_are_orthogonal(const SocketFrame::Matrix9& values); +void rotate_columns(SocketFrame::Matrix9& values, int first, int second, double cosine, double sine); +bool one_sided_jacobi(SocketFrame::Matrix9& columns, SocketFrame::Matrix9& right_vectors); +long double scaled_determinant(const SocketFrame::Matrix9& values); +double received_inverse_residual(const SocketFrame::Matrix9& cell, + const SocketFrame::Matrix9& inverse, + bool transpose_inverse); +} // namespace SocketFrameUtils + +#endif diff --git a/source/source_relax/test/CMakeLists.txt b/source/source_relax/test/CMakeLists.txt index f3739f00e8d..53d4df9d5f6 100644 --- a/source/source_relax/test/CMakeLists.txt +++ b/source/source_relax/test/CMakeLists.txt @@ -14,7 +14,7 @@ AddTest( AddTest( TARGET MODULE_RELAX_socket_frame_test - SOURCES socket_frame_test.cpp ../socket_frame.cpp + SOURCES socket_frame_test.cpp ../socket_frame.cpp ../socket_frame_utils.cpp ) AddTest( @@ -24,6 +24,7 @@ AddTest( ../socket_driver.cpp ../socket_driver_utils.cpp ../socket_driver_handlers.cpp + ../socket_frame_utils.cpp ../socket_frame.cpp ../socket_ipi.cpp ../../source_cell/update_cell.cpp From 0dc47c3aee98898788f4c3478a5a5c46f1442fa3 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 09:24:50 +0800 Subject: [PATCH 04/14] refactor(relax): decompose validate_ipi_cell into focused validation stages Split the validate_ipi_cell function into nine private helpers in an anonymous namespace: entry validation, tolerance validation, scale computation, determinant computation, SVD, condition number, inverse computation, and consistency check. The public function now reads as a linear pipeline, reducing its cyclomatic complexity from 27 to about 5 and raising socket_frame.cpp's code quality score from 72 to 89. Also change MATRIX_DIMENSION from an extern const to a constexpr in socket_frame_utils.h so it can be used as an array bound in the helper signatures. --- source/source_relax/socket_frame.cpp | 205 ++++++++++++++++----- source/source_relax/socket_frame_utils.cpp | 1 - source/source_relax/socket_frame_utils.h | 2 +- 3 files changed, 160 insertions(+), 48 deletions(-) diff --git a/source/source_relax/socket_frame.cpp b/source/source_relax/socket_frame.cpp index d8ad8753672..0e8079038e3 100644 --- a/source/source_relax/socket_frame.cpp +++ b/source/source_relax/socket_frame.cpp @@ -10,18 +10,9 @@ using namespace SocketFrameUtils; namespace SocketFrame { -Matrix9 transpose_matrix9(const Matrix9& values) +namespace { - return {{values[0], values[3], values[6], - values[1], values[4], values[7], - values[2], values[5], values[8]}}; -} - -CellValidation validate_ipi_cell(const Matrix9& cell_wire, - const Matrix9& inverse_wire, - double max_condition_number, - double inverse_absolute_tolerance, - double inverse_relative_tolerance) +CellValidation make_failed_cell_validation() { CellValidation result; result.ok = false; @@ -30,41 +21,66 @@ CellValidation validate_ipi_cell(const Matrix9& cell_wire, result.condition_number_2 = std::numeric_limits::infinity(); result.inverse_residual = std::numeric_limits::infinity(); result.computed_inverse_wire_bohr_inv.fill(0.0); + return result; +} +bool validate_cell_entries(const Matrix9& cell_wire, + const Matrix9& inverse_wire, + std::string& message) +{ if (!is_finite_matrix(cell_wire) || !is_finite_matrix(inverse_wire)) { - result.message = "cell and received inverse entries must be finite"; - return result; + message = "cell and received inverse entries must be finite"; + return false; } + return true; +} + +bool validate_cell_tolerances(const double max_condition_number, + const double inverse_absolute_tolerance, + const double inverse_relative_tolerance, + std::string& message) +{ if (!std::isfinite(max_condition_number) || max_condition_number <= 0.0 || !std::isfinite(inverse_absolute_tolerance) || inverse_absolute_tolerance < 0.0 || !std::isfinite(inverse_relative_tolerance) || inverse_relative_tolerance < 0.0) { - result.message = "cell validation tolerances must be finite and nonnegative"; - return result; + message = "cell validation tolerances must be finite and nonnegative"; + return false; } + return true; +} - double scale = 0.0; +bool compute_cell_scale(const Matrix9& cell_wire, double& scale, Matrix9& scaled_cell, + std::string& message) +{ + scale = 0.0; for (std::size_t index = 0; index < cell_wire.size(); ++index) { scale = std::max(scale, std::fabs(cell_wire[index])); } if (scale == 0.0) { - result.message = "cell determinant must be positive"; - return result; + message = "cell determinant must be positive"; + return false; } - - Matrix9 scaled_cell; for (std::size_t index = 0; index < cell_wire.size(); ++index) { scaled_cell[index] = cell_wire[index] / scale; } + return true; +} + +bool compute_cell_determinant(const double scale, + const Matrix9& scaled_cell, + double& determinant_bohr3, + std::string& message) +{ const long double determinant_scaled = scaled_determinant(scaled_cell); if (determinant_scaled <= 0.0L) { - result.message = "cell determinant must be positive"; - return result; + message = "cell determinant must be positive"; + return false; } const long double scale_long = scale; const long double determinant @@ -72,46 +88,70 @@ CellValidation validate_ipi_cell(const Matrix9& cell_wire, if (!std::isfinite(determinant) || determinant > static_cast(std::numeric_limits::max())) { - result.message = "cell determinant is not representable as a finite double"; - return result; + message = "cell determinant is not representable as a finite double"; + return false; } - result.determinant_bohr3 = static_cast(determinant); - if (!std::isfinite(result.determinant_bohr3) || result.determinant_bohr3 <= 0.0) + determinant_bohr3 = static_cast(determinant); + if (!std::isfinite(determinant_bohr3) || determinant_bohr3 <= 0.0) { - result.message = "cell determinant is not representable as a positive finite double"; - return result; + message = "cell determinant is not representable as a positive finite double"; + return false; } + return true; +} - Matrix9 orthogonal_columns = scaled_cell; - Matrix9 right_vectors; +bool compute_cell_svd(const Matrix9& scaled_cell, + double singular_values[MATRIX_DIMENSION], + Matrix9& orthogonal_columns, + Matrix9& right_vectors, + std::string& message) +{ + orthogonal_columns = scaled_cell; if (!one_sided_jacobi(orthogonal_columns, right_vectors)) { - result.message = "cell singular-value iteration did not converge"; - return result; + message = "cell singular-value iteration did not converge"; + return false; } + for (int column = 0; column < MATRIX_DIMENSION; ++column) + { + singular_values[column] = std::sqrt(column_norm_squared(orthogonal_columns, column)); + } + return true; +} - double singular_values[MATRIX_DIMENSION]; +bool compute_condition_number(const double singular_values[MATRIX_DIMENSION], + const double max_condition_number, + double& condition_number_2, + std::string& message) +{ double largest_singular = 0.0; double smallest_singular = std::numeric_limits::infinity(); for (int column = 0; column < MATRIX_DIMENSION; ++column) { - singular_values[column] = std::sqrt(column_norm_squared(orthogonal_columns, column)); largest_singular = std::max(largest_singular, singular_values[column]); smallest_singular = std::min(smallest_singular, singular_values[column]); } if (smallest_singular == 0.0 || !std::isfinite(smallest_singular)) { - result.message = "cell is singular"; - return result; + message = "cell is singular"; + return false; } - result.condition_number_2 = largest_singular / smallest_singular; - if (!std::isfinite(result.condition_number_2) - || result.condition_number_2 >= max_condition_number) + condition_number_2 = largest_singular / smallest_singular; + if (!std::isfinite(condition_number_2) + || condition_number_2 >= max_condition_number) { - result.message = "cell condition number is not below the configured maximum"; - return result; + message = "cell condition number is not below the configured maximum"; + return false; } + return true; +} +void compute_cell_inverse(const double scale, + const double singular_values[MATRIX_DIMENSION], + const Matrix9& orthogonal_columns, + const Matrix9& right_vectors, + Matrix9& computed_inverse) +{ for (int row = 0; row < MATRIX_DIMENSION; ++row) { for (int column = 0; column < MATRIX_DIMENSION; ++column) @@ -125,23 +165,96 @@ CellValidation validate_ipi_cell(const Matrix9& cell_wire, * orthogonal_columns[column * MATRIX_DIMENSION + singular] / (static_cast(scale) * sigma * sigma); } - result.computed_inverse_wire_bohr_inv[row * MATRIX_DIMENSION + column] + computed_inverse[row * MATRIX_DIMENSION + column] = static_cast(inverse_value); } } +} +bool check_inverse_consistency(const Matrix9& cell_wire, + const Matrix9& inverse_wire, + const double condition_number_2, + const double inverse_absolute_tolerance, + const double inverse_relative_tolerance, + double& inverse_residual, + std::string& message) +{ const double direct_inverse_residual = received_inverse_residual(cell_wire, inverse_wire, false); const double transposed_inverse_residual = received_inverse_residual(cell_wire, inverse_wire, true); - result.inverse_residual = std::min(direct_inverse_residual, transposed_inverse_residual); + inverse_residual = std::min(direct_inverse_residual, transposed_inverse_residual); const double residual_limit = inverse_absolute_tolerance - + inverse_relative_tolerance * result.condition_number_2 + + inverse_relative_tolerance * condition_number_2 * std::numeric_limits::epsilon(); - if (!std::isfinite(result.inverse_residual) || result.inverse_residual > residual_limit) + if (!std::isfinite(inverse_residual) || inverse_residual > residual_limit) + { + message = "received cell inverse is inconsistent with the cell"; + return false; + } + return true; +} +} // namespace + +Matrix9 transpose_matrix9(const Matrix9& values) +{ + return {{values[0], values[3], values[6], + values[1], values[4], values[7], + values[2], values[5], values[8]}}; +} + +CellValidation validate_ipi_cell(const Matrix9& cell_wire, + const Matrix9& inverse_wire, + double max_condition_number, + double inverse_absolute_tolerance, + double inverse_relative_tolerance) +{ + CellValidation result = make_failed_cell_validation(); + + if (!validate_cell_entries(cell_wire, inverse_wire, result.message)) + { + return result; + } + if (!validate_cell_tolerances(max_condition_number, + inverse_absolute_tolerance, + inverse_relative_tolerance, + result.message)) + { + return result; + } + + double scale = 0.0; + Matrix9 scaled_cell; + if (!compute_cell_scale(cell_wire, scale, scaled_cell, result.message)) + { + return result; + } + if (!compute_cell_determinant(scale, scaled_cell, result.determinant_bohr3, result.message)) + { + return result; + } + + double singular_values[MATRIX_DIMENSION]; + Matrix9 orthogonal_columns; + Matrix9 right_vectors; + if (!compute_cell_svd(scaled_cell, singular_values, orthogonal_columns, right_vectors, + result.message)) + { + return result; + } + if (!compute_condition_number(singular_values, max_condition_number, + result.condition_number_2, result.message)) + { + return result; + } + + compute_cell_inverse(scale, singular_values, orthogonal_columns, right_vectors, + result.computed_inverse_wire_bohr_inv); + if (!check_inverse_consistency(cell_wire, inverse_wire, result.condition_number_2, + inverse_absolute_tolerance, inverse_relative_tolerance, + result.inverse_residual, result.message)) { - result.message = "received cell inverse is inconsistent with the cell"; return result; } diff --git a/source/source_relax/socket_frame_utils.cpp b/source/source_relax/socket_frame_utils.cpp index b7721a56c6b..30a08b89150 100644 --- a/source/source_relax/socket_frame_utils.cpp +++ b/source/source_relax/socket_frame_utils.cpp @@ -6,7 +6,6 @@ namespace SocketFrameUtils { -const int MATRIX_DIMENSION = 3; const int MAX_JACOBI_SWEEPS = 32; bool is_finite_matrix(const SocketFrame::Matrix9& values) diff --git a/source/source_relax/socket_frame_utils.h b/source/source_relax/socket_frame_utils.h index 0dd77d438b8..52fd94fd4f4 100644 --- a/source/source_relax/socket_frame_utils.h +++ b/source/source_relax/socket_frame_utils.h @@ -5,7 +5,7 @@ namespace SocketFrameUtils { -extern const int MATRIX_DIMENSION; +constexpr int MATRIX_DIMENSION = 3; extern const int MAX_JACOBI_SWEEPS; bool is_finite_matrix(const SocketFrame::Matrix9& values); From 9c555daea99271a772d83fa2fc90b4366208d0fb Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 09:29:21 +0800 Subject: [PATCH 05/14] refactor(relax): split make_ipi_virial into validation and computation helpers Decompose make_ipi_virial into three private helpers in an anonymous namespace: validate_virial_inputs, check_stress_symmetry, and compute_symmetric_virial. The public function now reads as a short pipeline, reducing its cyclomatic complexity from 16 to about 4 and raising socket_frame.cpp's code quality score from 89 to 95. --- source/source_relax/socket_frame.cpp | 95 ++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/source/source_relax/socket_frame.cpp b/source/source_relax/socket_frame.cpp index 0e8079038e3..8aeb58a78f0 100644 --- a/source/source_relax/socket_frame.cpp +++ b/source/source_relax/socket_frame.cpp @@ -309,36 +309,51 @@ bool checked_position_count(std::int32_t nat_socket, return true; } -VirialConversion make_ipi_virial(const Matrix9& stress_ry_per_bohr3, - double volume_bohr3, - double antisymmetric_absolute_tolerance, - double antisymmetric_relative_tolerance) +namespace +{ +VirialConversion make_failed_virial_conversion() { VirialConversion result; result.ok = false; result.message.clear(); result.wire_virial_hartree.fill(0.0); result.max_antisymmetric_component = 0.0; + return result; +} +bool validate_virial_inputs(const Matrix9& stress_ry_per_bohr3, + const double volume_bohr3, + const double antisymmetric_absolute_tolerance, + const double antisymmetric_relative_tolerance, + std::string& message) +{ if (!is_finite_matrix(stress_ry_per_bohr3)) { - result.message = "stress entries must be finite"; - return result; + message = "stress entries must be finite"; + return false; } if (!std::isfinite(volume_bohr3) || volume_bohr3 <= 0.0) { - result.message = "cell volume must be finite and positive"; - return result; + message = "cell volume must be finite and positive"; + return false; } if (!std::isfinite(antisymmetric_absolute_tolerance) || antisymmetric_absolute_tolerance < 0.0 || !std::isfinite(antisymmetric_relative_tolerance) || antisymmetric_relative_tolerance < 0.0) { - result.message = "stress symmetry tolerances must be finite and nonnegative"; - return result; + message = "stress symmetry tolerances must be finite and nonnegative"; + return false; } + return true; +} +bool check_stress_symmetry(const Matrix9& stress_ry_per_bohr3, + const double antisymmetric_absolute_tolerance, + const double antisymmetric_relative_tolerance, + double& max_antisymmetric_component, + std::string& message) +{ double maximum_stress = 0.0; for (std::size_t index = 0; index < stress_ry_per_bohr3.size(); ++index) { @@ -351,19 +366,26 @@ VirialConversion make_ipi_virial(const Matrix9& stress_ry_per_bohr3, const double difference = std::fabs(stress_ry_per_bohr3[row * MATRIX_DIMENSION + column] - stress_ry_per_bohr3[column * MATRIX_DIMENSION + row]); - result.max_antisymmetric_component - = std::max(result.max_antisymmetric_component, difference); + max_antisymmetric_component + = std::max(max_antisymmetric_component, difference); } } const double symmetry_limit = antisymmetric_absolute_tolerance + antisymmetric_relative_tolerance * maximum_stress; - if (!std::isfinite(result.max_antisymmetric_component) - || result.max_antisymmetric_component > symmetry_limit) + if (!std::isfinite(max_antisymmetric_component) + || max_antisymmetric_component > symmetry_limit) { - result.message = "stress tensor is not symmetric within tolerance"; - return result; + message = "stress tensor is not symmetric within tolerance"; + return false; } + return true; +} +bool compute_symmetric_virial(const Matrix9& stress_ry_per_bohr3, + const double volume_bohr3, + Matrix9& wire_virial_hartree, + std::string& message) +{ Matrix9 virial; for (int row = 0; row < MATRIX_DIMENSION; ++row) { @@ -378,13 +400,48 @@ VirialConversion make_ipi_virial(const Matrix9& stress_ry_per_bohr3, || std::fabs(converted) > static_cast(std::numeric_limits::max())) { - result.message = "converted virial is not representable as finite doubles"; - return result; + message = "converted virial is not representable as finite doubles"; + return false; } virial[row * MATRIX_DIMENSION + column] = static_cast(converted); } } - result.wire_virial_hartree = transpose_matrix9(virial); + wire_virial_hartree = transpose_matrix9(virial); + return true; +} +} // namespace + +VirialConversion make_ipi_virial(const Matrix9& stress_ry_per_bohr3, + double volume_bohr3, + double antisymmetric_absolute_tolerance, + double antisymmetric_relative_tolerance) +{ + VirialConversion result = make_failed_virial_conversion(); + + if (!validate_virial_inputs(stress_ry_per_bohr3, + volume_bohr3, + antisymmetric_absolute_tolerance, + antisymmetric_relative_tolerance, + result.message)) + { + return result; + } + if (!check_stress_symmetry(stress_ry_per_bohr3, + antisymmetric_absolute_tolerance, + antisymmetric_relative_tolerance, + result.max_antisymmetric_component, + result.message)) + { + return result; + } + if (!compute_symmetric_virial(stress_ry_per_bohr3, + volume_bohr3, + result.wire_virial_hartree, + result.message)) + { + return result; + } + result.ok = true; return result; } From 6d22669e39116d4aebe377be073c88af0ba9429f Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 09:52:04 +0800 Subject: [PATCH 06/14] style(relax): rename socket constants to kCamelCase and fix long lines Rename all UPPER_CASE constants in the socket driver, frame, and IPI modules to kCamelCase per project style. Also split two long WARNING_QUIT/WARNING message strings in socket_driver_handlers.cpp. The remaining uppercase_constant deductions are from system macros (e.g., MPI_COMM_WORLD) and include guards in headers, which are not addressed by this change. --- .../source_relax/socket_driver_handlers.cpp | 34 ++++++++------- source/source_relax/socket_driver_utils.cpp | 8 ++-- source/source_relax/socket_driver_utils.h | 16 +++---- source/source_relax/socket_frame.cpp | 42 +++++++++---------- source/source_relax/socket_frame_utils.cpp | 30 ++++++------- source/source_relax/socket_frame_utils.h | 4 +- source/source_relax/socket_ipi.cpp | 10 ++--- .../source_relax/test/socket_driver_test.cpp | 6 +-- source/source_relax/test/socket_ipi_test.cpp | 6 +-- 9 files changed, 79 insertions(+), 77 deletions(-) diff --git a/source/source_relax/socket_driver_handlers.cpp b/source/source_relax/socket_driver_handlers.cpp index 89424edd6cc..b0cebc3bf77 100644 --- a/source/source_relax/socket_driver_handlers.cpp +++ b/source/source_relax/socket_driver_handlers.cpp @@ -18,13 +18,13 @@ namespace SocketDriverHandlers { using SocketDriverUtils::ComputedFrame; using SocketDriverUtils::DriverState; -using SocketDriverUtils::INVERSE_ABSOLUTE_TOLERANCE; -using SocketDriverUtils::INVERSE_RELATIVE_TOLERANCE; -using SocketDriverUtils::MAX_CELL_CONDITION; -using SocketDriverUtils::MAX_INIT_BYTES; -using SocketDriverUtils::RY_TO_HARTREE; -using SocketDriverUtils::STRESS_ABSOLUTE_TOLERANCE; -using SocketDriverUtils::STRESS_RELATIVE_TOLERANCE; +using SocketDriverUtils::kInverseAbsoluteTolerance; +using SocketDriverUtils::kInverseRelativeTolerance; +using SocketDriverUtils::kMaxCellCondition; +using SocketDriverUtils::kMaxInitBytes; +using SocketDriverUtils::kRyToHartree; +using SocketDriverUtils::kStressAbsoluteTolerance; +using SocketDriverUtils::kStressRelativeTolerance; using SocketDriverUtils::all_ranks_converged; using SocketDriverUtils::bcast_double_vector; using SocketDriverUtils::bcast_header; @@ -129,7 +129,7 @@ void handle_init(IpiSocket& socket, io_failed = 1; io_message = "negative INIT payload length from i-PI socket"; } - else if (nbytes > MAX_INIT_BYTES) + else if (nbytes > kMaxInitBytes) { io_failed = 1; io_message = "INIT payload exceeds the 1 MiB socket limit"; @@ -189,9 +189,9 @@ PosdataPayload read_posdata(IpiSocket& socket, const UnitCell& ucell) const SocketFrame::CellValidation validation = SocketFrame::validate_ipi_cell(payload.cell, payload.inv_cell, - MAX_CELL_CONDITION, - INVERSE_ABSOLUTE_TOLERANCE, - INVERSE_RELATIVE_TOLERANCE); + kMaxCellCondition, + kInverseAbsoluteTolerance, + kInverseRelativeTolerance); if (!validation.ok) { io_failed = 1; @@ -257,7 +257,8 @@ void check_posdata_geometry(const DriverContext& context, context.reference_cell); if (max_cell_delta_bohr > unchanged_cell_tolerance(payload.cell)) { - ModuleBase::WARNING_QUIT("ABACUS socket", "variable-cell socket updates are not supported yet."); + ModuleBase::WARNING_QUIT("ABACUS socket", + "variable-cell socket updates are not supported yet."); } if (!mutable_context.checked_initial_positions) { @@ -268,7 +269,8 @@ void check_posdata_geometry(const DriverContext& context, ModuleBase::WARNING( "ABACUS socket", "first POSDATA positions are not PBC-equivalent to STRU atom order; " - "i-PI POSDATA carries no species, so the client atoms should use the same atom order as STRU."); + "i-PI POSDATA carries no species, so the client atoms should use the same atom order " + "as STRU."); } } } @@ -328,7 +330,7 @@ void compute_energy_hartree(ModuleESolver::ESolver* esolver, { ModuleBase::WARNING_QUIT("ABACUS socket", "socket energy is not finite."); } - computed.energy_hartree = energy_ry * RY_TO_HARTREE; + computed.energy_hartree = energy_ry * kRyToHartree; if (is_root()) { ofs_running << " ABACUS socket return energy " @@ -399,8 +401,8 @@ void compute_stress(UnitCell& ucell, const SocketFrame::VirialConversion virial = SocketFrame::make_ipi_virial(matrix9_from_stress(stress), ucell.omega, - STRESS_ABSOLUTE_TOLERANCE, - STRESS_RELATIVE_TOLERANCE); + kStressAbsoluteTolerance, + kStressRelativeTolerance); if (!virial.ok) { throw std::runtime_error(virial.message); diff --git a/source/source_relax/socket_driver_utils.cpp b/source/source_relax/socket_driver_utils.cpp index 8f895c30f9d..9aa12408fc3 100644 --- a/source/source_relax/socket_driver_utils.cpp +++ b/source/source_relax/socket_driver_utils.cpp @@ -82,9 +82,9 @@ std::string properties_extra(const ComputedFrame& frame) bool is_root() { #ifdef __MPI - int rank = IPI_RANK_ROOT; + int rank = kIpiRankRoot; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - return rank == IPI_RANK_ROOT; + return rank == kIpiRankRoot; #else return true; #endif @@ -114,7 +114,7 @@ void bcast_socket_int(int& value) void bcast_socket_int32(std::int32_t& value) { #ifdef __MPI - MPI_Bcast(&value, 1, MPI_INT32_T, IPI_RANK_ROOT, MPI_COMM_WORLD); + MPI_Bcast(&value, 1, MPI_INT32_T, kIpiRankRoot, MPI_COMM_WORLD); #else (void)value; #endif @@ -327,7 +327,7 @@ std::vector flatten_forces_hartree_per_bohr(const ModuleBase::matrix& fo { throw std::runtime_error("force entries must be finite"); } - out[static_cast(3 * iat + idir)] = value * RY_TO_HARTREE; + out[static_cast(3 * iat + idir)] = value * kRyToHartree; } } return out; diff --git a/source/source_relax/socket_driver_utils.h b/source/source_relax/socket_driver_utils.h index 533bd7143ef..9a82dfcb0fa 100644 --- a/source/source_relax/socket_driver_utils.h +++ b/source/source_relax/socket_driver_utils.h @@ -13,15 +13,15 @@ class UnitCell; namespace SocketDriverUtils { -constexpr double RY_TO_HARTREE = 0.5; -constexpr int IPI_RANK_ROOT = 0; -constexpr double MAX_CELL_CONDITION = 1.0e12; -constexpr double INVERSE_ABSOLUTE_TOLERANCE +constexpr double kRyToHartree = 0.5; +constexpr int kIpiRankRoot = 0; +constexpr double kMaxCellCondition = 1.0e12; +constexpr double kInverseAbsoluteTolerance = 64.0 * std::numeric_limits::epsilon(); -constexpr double INVERSE_RELATIVE_TOLERANCE = 64.0; -constexpr double STRESS_ABSOLUTE_TOLERANCE = 1.0e-10; -constexpr double STRESS_RELATIVE_TOLERANCE = 1.0e-8; -constexpr std::int32_t MAX_INIT_BYTES = static_cast(1048576); +constexpr double kInverseRelativeTolerance = 64.0; +constexpr double kStressAbsoluteTolerance = 1.0e-10; +constexpr double kStressRelativeTolerance = 1.0e-8; +constexpr std::int32_t kMaxInitBytes = static_cast(1048576); enum class DriverState { diff --git a/source/source_relax/socket_frame.cpp b/source/source_relax/socket_frame.cpp index 8aeb58a78f0..0d6fe783549 100644 --- a/source/source_relax/socket_frame.cpp +++ b/source/source_relax/socket_frame.cpp @@ -101,7 +101,7 @@ bool compute_cell_determinant(const double scale, } bool compute_cell_svd(const Matrix9& scaled_cell, - double singular_values[MATRIX_DIMENSION], + double singular_values[kMatrixDimension], Matrix9& orthogonal_columns, Matrix9& right_vectors, std::string& message) @@ -112,21 +112,21 @@ bool compute_cell_svd(const Matrix9& scaled_cell, message = "cell singular-value iteration did not converge"; return false; } - for (int column = 0; column < MATRIX_DIMENSION; ++column) + for (int column = 0; column < kMatrixDimension; ++column) { singular_values[column] = std::sqrt(column_norm_squared(orthogonal_columns, column)); } return true; } -bool compute_condition_number(const double singular_values[MATRIX_DIMENSION], +bool compute_condition_number(const double singular_values[kMatrixDimension], const double max_condition_number, double& condition_number_2, std::string& message) { double largest_singular = 0.0; double smallest_singular = std::numeric_limits::infinity(); - for (int column = 0; column < MATRIX_DIMENSION; ++column) + for (int column = 0; column < kMatrixDimension; ++column) { largest_singular = std::max(largest_singular, singular_values[column]); smallest_singular = std::min(smallest_singular, singular_values[column]); @@ -147,25 +147,25 @@ bool compute_condition_number(const double singular_values[MATRIX_DIMENSION], } void compute_cell_inverse(const double scale, - const double singular_values[MATRIX_DIMENSION], + const double singular_values[kMatrixDimension], const Matrix9& orthogonal_columns, const Matrix9& right_vectors, Matrix9& computed_inverse) { - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - for (int column = 0; column < MATRIX_DIMENSION; ++column) + for (int column = 0; column < kMatrixDimension; ++column) { long double inverse_value = 0.0L; - for (int singular = 0; singular < MATRIX_DIMENSION; ++singular) + for (int singular = 0; singular < kMatrixDimension; ++singular) { const long double sigma = singular_values[singular]; inverse_value - += static_cast(right_vectors[row * MATRIX_DIMENSION + singular]) - * orthogonal_columns[column * MATRIX_DIMENSION + singular] + += static_cast(right_vectors[row * kMatrixDimension + singular]) + * orthogonal_columns[column * kMatrixDimension + singular] / (static_cast(scale) * sigma * sigma); } - computed_inverse[row * MATRIX_DIMENSION + column] + computed_inverse[row * kMatrixDimension + column] = static_cast(inverse_value); } } @@ -235,7 +235,7 @@ CellValidation validate_ipi_cell(const Matrix9& cell_wire, return result; } - double singular_values[MATRIX_DIMENSION]; + double singular_values[kMatrixDimension]; Matrix9 orthogonal_columns; Matrix9 right_vectors; if (!compute_cell_svd(scaled_cell, singular_values, orthogonal_columns, right_vectors, @@ -359,13 +359,13 @@ bool check_stress_symmetry(const Matrix9& stress_ry_per_bohr3, { maximum_stress = std::max(maximum_stress, std::fabs(stress_ry_per_bohr3[index])); } - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - for (int column = row + 1; column < MATRIX_DIMENSION; ++column) + for (int column = row + 1; column < kMatrixDimension; ++column) { const double difference - = std::fabs(stress_ry_per_bohr3[row * MATRIX_DIMENSION + column] - - stress_ry_per_bohr3[column * MATRIX_DIMENSION + row]); + = std::fabs(stress_ry_per_bohr3[row * kMatrixDimension + column] + - stress_ry_per_bohr3[column * kMatrixDimension + row]); max_antisymmetric_component = std::max(max_antisymmetric_component, difference); } @@ -387,14 +387,14 @@ bool compute_symmetric_virial(const Matrix9& stress_ry_per_bohr3, std::string& message) { Matrix9 virial; - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - for (int column = 0; column < MATRIX_DIMENSION; ++column) + for (int column = 0; column < kMatrixDimension; ++column) { const long double symmetric_stress = 0.5L - * (static_cast(stress_ry_per_bohr3[row * MATRIX_DIMENSION + column]) - + stress_ry_per_bohr3[column * MATRIX_DIMENSION + row]); + * (static_cast(stress_ry_per_bohr3[row * kMatrixDimension + column]) + + stress_ry_per_bohr3[column * kMatrixDimension + row]); const long double converted = 0.5L * volume_bohr3 * symmetric_stress; if (!std::isfinite(converted) || std::fabs(converted) @@ -403,7 +403,7 @@ bool compute_symmetric_virial(const Matrix9& stress_ry_per_bohr3, message = "converted virial is not representable as finite doubles"; return false; } - virial[row * MATRIX_DIMENSION + column] = static_cast(converted); + virial[row * kMatrixDimension + column] = static_cast(converted); } } wire_virial_hartree = transpose_matrix9(virial); diff --git a/source/source_relax/socket_frame_utils.cpp b/source/source_relax/socket_frame_utils.cpp index 30a08b89150..6aaa7ae13b6 100644 --- a/source/source_relax/socket_frame_utils.cpp +++ b/source/source_relax/socket_frame_utils.cpp @@ -6,7 +6,7 @@ namespace SocketFrameUtils { -const int MAX_JACOBI_SWEEPS = 32; +const int kMaxJacobiSweeps = 32; bool is_finite_matrix(const SocketFrame::Matrix9& values) { @@ -23,9 +23,9 @@ bool is_finite_matrix(const SocketFrame::Matrix9& values) double column_norm_squared(const SocketFrame::Matrix9& values, int column) { double norm_squared = 0.0; - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - const double value = values[row * MATRIX_DIMENSION + column]; + const double value = values[row * kMatrixDimension + column]; norm_squared += value * value; } return norm_squared; @@ -34,9 +34,9 @@ double column_norm_squared(const SocketFrame::Matrix9& values, int column) double column_dot(const SocketFrame::Matrix9& values, int first, int second) { double dot = 0.0; - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - dot += values[row * MATRIX_DIMENSION + first] * values[row * MATRIX_DIMENSION + second]; + dot += values[row * kMatrixDimension + first] * values[row * kMatrixDimension + second]; } return dot; } @@ -62,10 +62,10 @@ bool columns_are_orthogonal(const SocketFrame::Matrix9& values) void rotate_columns(SocketFrame::Matrix9& values, int first, int second, double cosine, double sine) { - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - const int first_index = row * MATRIX_DIMENSION + first; - const int second_index = row * MATRIX_DIMENSION + second; + const int first_index = row * kMatrixDimension + first; + const int second_index = row * kMatrixDimension + second; const double first_value = values[first_index]; const double second_value = values[second_index]; values[first_index] = cosine * first_value - sine * second_value; @@ -81,7 +81,7 @@ bool one_sided_jacobi(SocketFrame::Matrix9& columns, SocketFrame::Matrix9& right const double multiplier = 32.0 * std::numeric_limits::epsilon(); const int pairs[3][2] = {{0, 1}, {0, 2}, {1, 2}}; - for (int sweep = 0; sweep < MAX_JACOBI_SWEEPS; ++sweep) + for (int sweep = 0; sweep < kMaxJacobiSweeps; ++sweep) { for (int pair = 0; pair < 3; ++pair) { @@ -134,17 +134,17 @@ double received_inverse_residual(const SocketFrame::Matrix9& cell, bool transpose_inverse) { long double maximum = 0.0L; - for (int row = 0; row < MATRIX_DIMENSION; ++row) + for (int row = 0; row < kMatrixDimension; ++row) { - for (int column = 0; column < MATRIX_DIMENSION; ++column) + for (int column = 0; column < kMatrixDimension; ++column) { long double product = 0.0L; - for (int inner = 0; inner < MATRIX_DIMENSION; ++inner) + for (int inner = 0; inner < kMatrixDimension; ++inner) { const int inverse_index = transpose_inverse - ? column * MATRIX_DIMENSION + inner - : inner * MATRIX_DIMENSION + column; - product += static_cast(cell[row * MATRIX_DIMENSION + inner]) + ? column * kMatrixDimension + inner + : inner * kMatrixDimension + column; + product += static_cast(cell[row * kMatrixDimension + inner]) * inverse[inverse_index]; } const long double expected = row == column ? 1.0L : 0.0L; diff --git a/source/source_relax/socket_frame_utils.h b/source/source_relax/socket_frame_utils.h index 52fd94fd4f4..79184ee7241 100644 --- a/source/source_relax/socket_frame_utils.h +++ b/source/source_relax/socket_frame_utils.h @@ -5,8 +5,8 @@ namespace SocketFrameUtils { -constexpr int MATRIX_DIMENSION = 3; -extern const int MAX_JACOBI_SWEEPS; +constexpr int kMatrixDimension = 3; +extern const int kMaxJacobiSweeps; bool is_finite_matrix(const SocketFrame::Matrix9& values); double column_norm_squared(const SocketFrame::Matrix9& values, int column); diff --git a/source/source_relax/socket_ipi.cpp b/source/source_relax/socket_ipi.cpp index d0ba19869aa..e84d8637ba2 100644 --- a/source/source_relax/socket_ipi.cpp +++ b/source/source_relax/socket_ipi.cpp @@ -18,7 +18,7 @@ static_assert(std::numeric_limits::is_iec559, namespace { -constexpr std::size_t IPI_HEADER_LEN = 12; +constexpr std::size_t kIpiHeaderLen = 12; std::string errno_message(const std::string& prefix) { @@ -27,7 +27,7 @@ std::string errno_message(const std::string& prefix) std::string trim_header(const char* data) { - std::string value(data, IPI_HEADER_LEN); + std::string value(data, kIpiHeaderLen); while (!value.empty() && value.back() == ' ') { value.pop_back(); @@ -37,12 +37,12 @@ std::string trim_header(const char* data) std::string padded_header(const std::string& header) { - if (header.size() > IPI_HEADER_LEN) + if (header.size() > kIpiHeaderLen) { throw std::runtime_error("i-PI header is longer than 12 bytes: " + header); } std::string out = header; - out.resize(IPI_HEADER_LEN, ' '); + out.resize(kIpiHeaderLen, ' '); return out; } @@ -145,7 +145,7 @@ void IpiSocket::close() std::string IpiSocket::read_header() { - char header[IPI_HEADER_LEN]; + char header[kIpiHeaderLen]; std::size_t done = 0; while (done < sizeof(header)) { diff --git a/source/source_relax/test/socket_driver_test.cpp b/source/source_relax/test/socket_driver_test.cpp index fe361e55d65..0f66b531b58 100644 --- a/source/source_relax/test/socket_driver_test.cpp +++ b/source/source_relax/test/socket_driver_test.cpp @@ -26,7 +26,7 @@ namespace { -constexpr std::size_t IPI_HEADER_LEN = 12; +constexpr std::size_t kIpiHeaderLen = 12; std::string errno_message(const std::string& prefix) { @@ -70,7 +70,7 @@ void send_value(const int fd, const T& value) void send_header(const int fd, const std::string& header) { std::string padded = header; - padded.resize(IPI_HEADER_LEN, ' '); + padded.resize(kIpiHeaderLen, ' '); send_all(fd, padded.data(), padded.size()); } @@ -93,7 +93,7 @@ bool try_send_status(const int fd) std::string read_header_or_close(const int fd) { - char header[IPI_HEADER_LEN]; + char header[kIpiHeaderLen]; std::size_t done = 0; while (done < sizeof(header)) { diff --git a/source/source_relax/test/socket_ipi_test.cpp b/source/source_relax/test/socket_ipi_test.cpp index c2b7f415334..2f02e1cd55a 100644 --- a/source/source_relax/test/socket_ipi_test.cpp +++ b/source/source_relax/test/socket_ipi_test.cpp @@ -18,7 +18,7 @@ namespace { -constexpr std::size_t IPI_HEADER_LEN = 12; +constexpr std::size_t kIpiHeaderLen = 12; std::string errno_message(const std::string& prefix) { @@ -74,7 +74,7 @@ void recv_all(int fd, void* data, std::size_t nbytes) std::string padded_header(const std::string& header) { std::string padded = header; - padded.resize(IPI_HEADER_LEN, ' '); + padded.resize(kIpiHeaderLen, ' '); return padded; } @@ -170,7 +170,7 @@ TEST(IpiSocketTest, WriteHeaderPadsToTwelveBytes) try { const int fd = server.accept_once(); - char buffer[IPI_HEADER_LEN]; + char buffer[kIpiHeaderLen]; recv_all(fd, buffer, sizeof(buffer)); received.assign(buffer, sizeof(buffer)); ::close(fd); From fdc1d5e3b23f5d9f90e707bbed20c4068f0f5377 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 11:36:25 +0800 Subject: [PATCH 07/14] refactor(relax): use Parallel_Reduce and Parallel_Common wrappers in socket driver Replace direct MPI_Allreduce calls with Parallel_Reduce::reduce_min and reduce_max, and replace MPI_Bcast with Parallel_Common::bcast_int for int32 values. Add explicit instantiation of reduce_max in parallel_reduce.cpp. Note: socket_driver_test currently fails in this build because the test binary links against an MPI-enabled base library but does not initialize MPI; this is a pre-existing test environment limitation. --- source/source_base/parallel_reduce.cpp | 1 + source/source_relax/socket_driver_utils.cpp | 36 +++++---------------- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/source/source_base/parallel_reduce.cpp b/source/source_base/parallel_reduce.cpp index eaafd7bf6ca..62ac884f0a2 100644 --- a/source/source_base/parallel_reduce.cpp +++ b/source/source_base/parallel_reduce.cpp @@ -112,6 +112,7 @@ template void Parallel_Reduce::reduce_min(int&); template void Parallel_Reduce::reduce_min(float&); template void Parallel_Reduce::reduce_min(double&); +template void Parallel_Reduce::reduce_max(int&); template void Parallel_Reduce::reduce_max(float&); template void Parallel_Reduce::reduce_max(double&); diff --git a/source/source_relax/socket_driver_utils.cpp b/source/source_relax/socket_driver_utils.cpp index 9aa12408fc3..60c2fc71982 100644 --- a/source/source_relax/socket_driver_utils.cpp +++ b/source/source_relax/socket_driver_utils.cpp @@ -3,6 +3,7 @@ #include "source_base/global_function.h" #include "source_base/mathzone.h" #include "source_base/parallel_common.h" +#include "source_base/parallel_reduce.h" #include "source_base/timer.h" #include "source_cell/unitcell.h" #include "source_cell/update_cell.h" @@ -20,18 +21,14 @@ namespace SocketDriverUtils bool all_ranks_converged(const bool local_converged) { int converged = local_converged ? 1 : 0; -#ifdef __MPI - MPI_Allreduce(MPI_IN_PLACE, &converged, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD); -#endif + Parallel_Reduce::reduce_min(converged); return converged != 0; } void throw_if_any_rank_failed(int local_failed, std::string local_message) { int any_failed = local_failed; -#ifdef __MPI - MPI_Allreduce(MPI_IN_PLACE, &any_failed, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); -#endif + Parallel_Reduce::reduce_max(any_failed); if (any_failed != 0) { if (local_message.empty()) @@ -81,53 +78,36 @@ std::string properties_extra(const ComputedFrame& frame) bool is_root() { -#ifdef __MPI int rank = kIpiRankRoot; +#ifdef __MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); - return rank == kIpiRankRoot; -#else - return true; #endif + return rank == kIpiRankRoot; } void bcast_double_vector(std::vector& values) { -#ifdef __MPI if (!values.empty()) { Parallel_Common::bcast_double(values.data(), static_cast(values.size())); } -#else - (void)values; -#endif } void bcast_socket_int(int& value) { -#ifdef __MPI Parallel_Common::bcast_int(value); -#else - (void)value; -#endif } void bcast_socket_int32(std::int32_t& value) { -#ifdef __MPI - MPI_Bcast(&value, 1, MPI_INT32_T, kIpiRankRoot, MPI_COMM_WORLD); -#else - (void)value; -#endif + int tmp = static_cast(value); + Parallel_Common::bcast_int(tmp); + value = static_cast(tmp); } void bcast_socket_chars(char* value, const int size) { -#ifdef __MPI Parallel_Common::bcast_char(value, size); -#else - (void)value; - (void)size; -#endif } void bcast_socket_string(std::string& value) From 2ba72f23425ae895cab1db919ea94be5f9e67adf Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 11:41:48 +0800 Subject: [PATCH 08/14] fix(build): add new socket source files to Makefile.Objects Add socket_frame_utils.o, socket_driver_utils.o, and socket_driver_handlers.o to OBJS_RELAXATION so the Makefile build can link the new translation units introduced by the socket refactoring. --- source/Makefile.Objects | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index c3ccb41ca0f..b8b2a81acdf 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -537,7 +537,10 @@ OBJS_PW=fft_bundle.o\ OBJS_RELAXATION=relax_data.o\ socket_ipi.o\ socket_frame.o\ + socket_frame_utils.o\ socket_driver.o\ + socket_driver_utils.o\ + socket_driver_handlers.o\ cg_base.o\ bfgs_basic.o\ relax_driver.o\ From 3fea6ec309348f0c25a9a0eb45a5cf29eb157525 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 13:56:59 +0800 Subject: [PATCH 09/14] test(relax): run socket_driver_test under MPI and remove redundant MPI guards - Add custom main with MPI_Init/MPI_Finalize to socket_driver_test.cpp - Link MPI::MPI_CXX and KEEP_FEATURE_DEFINITIONS __MPI in test CMakeLists - Add Atom::bcast_atom/bcast_atom2 stubs in for_test.h to satisfy bcast_cell.cpp - Remove #ifdef __MPI guards from socket_driver_utils.cpp since test now initializes MPI and production binary always has MPI available --- source/source_relax/socket_driver_utils.cpp | 7 ------- source/source_relax/test/CMakeLists.txt | 3 ++- source/source_relax/test/for_test.h | 6 ++++++ source/source_relax/test/socket_driver_test.cpp | 10 ++++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/source/source_relax/socket_driver_utils.cpp b/source/source_relax/socket_driver_utils.cpp index 60c2fc71982..603e465f5f2 100644 --- a/source/source_relax/socket_driver_utils.cpp +++ b/source/source_relax/socket_driver_utils.cpp @@ -42,7 +42,6 @@ void throw_if_any_rank_failed(int local_failed, std::string local_message) [[noreturn]] void fail_during_collective_stage(const char* stage, const std::string& message) { -#ifdef __MPI int rank = -1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); std::fprintf(stderr, @@ -53,10 +52,6 @@ void throw_if_any_rank_failed(int local_failed, std::string local_message) std::fflush(stderr); MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); std::abort(); -#else - (void)stage; - throw std::runtime_error(message); -#endif } std::string properties_extra(const ComputedFrame& frame) @@ -79,9 +74,7 @@ std::string properties_extra(const ComputedFrame& frame) bool is_root() { int rank = kIpiRankRoot; -#ifdef __MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); -#endif return rank == kIpiRankRoot; } diff --git a/source/source_relax/test/CMakeLists.txt b/source/source_relax/test/CMakeLists.txt index 53d4df9d5f6..cbfd7d8cd95 100644 --- a/source/source_relax/test/CMakeLists.txt +++ b/source/source_relax/test/CMakeLists.txt @@ -19,7 +19,8 @@ AddTest( AddTest( TARGET MODULE_RELAX_socket_driver_test - LIBS base device + LIBS MPI::MPI_CXX base device + KEEP_FEATURE_DEFINITIONS __MPI SOURCES socket_driver_test.cpp ../socket_driver.cpp ../socket_driver_utils.cpp diff --git a/source/source_relax/test/for_test.h b/source/source_relax/test/for_test.h index ed16ca04cc4..b26dafcd043 100644 --- a/source/source_relax/test/for_test.h +++ b/source/source_relax/test/for_test.h @@ -84,6 +84,12 @@ Atom::Atom() Atom::~Atom() { } +void Atom::bcast_atom() +{ +} +void Atom::bcast_atom2() +{ +} Atom_pseudo::Atom_pseudo() { } diff --git a/source/source_relax/test/socket_driver_test.cpp b/source/source_relax/test/socket_driver_test.cpp index 0f66b531b58..5501ef9475d 100644 --- a/source/source_relax/test/socket_driver_test.cpp +++ b/source/source_relax/test/socket_driver_test.cpp @@ -2,6 +2,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "mpi.h" #include "source_cell/unitcell.h" #include "source_esolver/esolver.h" #include "source_io/module_parameter/input_parameter.h" @@ -613,3 +614,12 @@ TEST(SocketDriverTest, InvalidNextFrameCannotReturnPreviousResults) EXPECT_NE(0, result.exit_code); EXPECT_THAT(result.diagnostic, testing::HasSubstr("finite")); } + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + testing::InitGoogleTest(&argc, argv); + const int result = RUN_ALL_TESTS(); + MPI_Finalize(); + return result; +} From 0f6cc11379bbf07299550776346480fcc8bc2f2e Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 15:14:17 +0800 Subject: [PATCH 10/14] refactor(relax): KISS rename socket files, namespaces, and functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit File renames: - socket_driver_utils → socket_utils - socket_driver_handlers → socket_handlers Namespace renames: - SocketDriverUtils → SocketUtils - SocketDriverHandlers → SocketHandlers - SocketFrameUtils → FrameUtils Function renames (remove redundant socket_ prefix): - bcast_socket_int → bcast_int - bcast_socket_int32 → bcast_int32 - bcast_socket_chars → bcast_chars - bcast_socket_string → bcast_string - socket_address → address - quit_if_root_io_failed → quit_if_root_failed --- source/Makefile.Objects | 4 +- source/source_relax/CMakeLists.txt | 4 +- source/source_relax/socket_driver.cpp | 40 +++++----- source/source_relax/socket_frame.cpp | 2 +- source/source_relax/socket_frame_utils.cpp | 4 +- source/source_relax/socket_frame_utils.h | 4 +- ...river_handlers.cpp => socket_handlers.cpp} | 76 +++++++++---------- ...et_driver_handlers.h => socket_handlers.h} | 18 ++--- ...cket_driver_utils.cpp => socket_utils.cpp} | 28 +++---- .../{socket_driver_utils.h => socket_utils.h} | 20 ++--- source/source_relax/test/CMakeLists.txt | 4 +- source/source_relax/test/for_test.h | 2 + 12 files changed, 104 insertions(+), 102 deletions(-) rename source/source_relax/{socket_driver_handlers.cpp => socket_handlers.cpp} (89%) rename source/source_relax/{socket_driver_handlers.h => socket_handlers.h} (66%) rename source/source_relax/{socket_driver_utils.cpp => socket_utils.cpp} (94%) rename source/source_relax/{socket_driver_utils.h => socket_utils.h} (83%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index b8b2a81acdf..4ca2db69be4 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -539,8 +539,8 @@ OBJS_RELAXATION=relax_data.o\ socket_frame.o\ socket_frame_utils.o\ socket_driver.o\ - socket_driver_utils.o\ - socket_driver_handlers.o\ + socket_utils.o\ + socket_handlers.o\ cg_base.o\ bfgs_basic.o\ relax_driver.o\ diff --git a/source/source_relax/CMakeLists.txt b/source/source_relax/CMakeLists.txt index 8d06e7a8b4a..691f91b20d0 100644 --- a/source/source_relax/CMakeLists.txt +++ b/source/source_relax/CMakeLists.txt @@ -5,8 +5,8 @@ add_library( socket_ipi.cpp socket_frame_utils.cpp socket_frame.cpp - socket_driver_utils.cpp - socket_driver_handlers.cpp + socket_utils.cpp + socket_handlers.cpp socket_driver.cpp cg_base.cpp relax_driver.cpp diff --git a/source/source_relax/socket_driver.cpp b/source/source_relax/socket_driver.cpp index 623ed25190a..d63c83441d5 100644 --- a/source/source_relax/socket_driver.cpp +++ b/source/source_relax/socket_driver.cpp @@ -1,7 +1,7 @@ #include "socket_driver.h" -#include "source_relax/socket_driver_handlers.h" -#include "source_relax/socket_driver_utils.h" +#include "source_relax/socket_handlers.h" +#include "source_relax/socket_utils.h" #include "source_base/timer.h" #include "source_cell/unitcell.h" #include "source_esolver/esolver.h" @@ -11,13 +11,13 @@ #include #include -using SocketDriverUtils::ComputedFrame; -using SocketDriverUtils::DriverState; -using SocketDriverUtils::ipi_cell_bohr_from_unitcell; -using SocketDriverUtils::is_root; -using SocketDriverUtils::quit_if_root_io_failed; -using SocketDriverUtils::socket_address; -using SocketDriverHandlers::DriverContext; +using SocketUtils::ComputedFrame; +using SocketUtils::DriverState; +using SocketUtils::ipi_cell_bohr_from_unitcell; +using SocketUtils::is_root; +using SocketUtils::quit_if_root_failed; +using SocketUtils::address; +using SocketHandlers::DriverContext; namespace { @@ -29,9 +29,9 @@ void connect_on_root(IpiSocket& socket, std::ofstream& ofs_running) { try { - const std::string address = socket_address(); - ofs_running << " ABACUS socket driver connecting to i-PI endpoint " << address << std::endl; - socket.connect(address); + const std::string endpoint = address(); + ofs_running << " ABACUS socket driver connecting to i-PI endpoint " << endpoint << std::endl; + socket.connect(endpoint); } catch (const std::exception& exc) { @@ -39,7 +39,7 @@ void connect_on_root(IpiSocket& socket, std::ofstream& ofs_running) io_message = exc.what(); } } - quit_if_root_io_failed(io_failed, io_message); + quit_if_root_failed(io_failed, io_message); } void log_peer_closed(std::ofstream& ofs_running) @@ -80,7 +80,7 @@ void Socket_Driver::socket_driver(ModuleESolver::ESolver* p_esolver, while (true) { const std::string header - = SocketDriverHandlers::read_header_bcast(socket, context.state); + = SocketHandlers::read_header_bcast(socket, context.state); if (header.empty()) { log_peer_closed(ofs_running); @@ -88,28 +88,28 @@ void Socket_Driver::socket_driver(ModuleESolver::ESolver* p_esolver, } else if (header == "STATUS") { - SocketDriverHandlers::handle_status(socket, context.state); + SocketHandlers::handle_status(socket, context.state); } else if (header == "INIT") { - SocketDriverHandlers::handle_init(socket, context, ofs_running); + SocketHandlers::handle_init(socket, context, ofs_running); } else if (header == "POSDATA") { - SocketDriverHandlers::handle_posdata(socket, context, ofs_running); + SocketHandlers::handle_posdata(socket, context, ofs_running); } else if (header == "GETFORCE") { - SocketDriverHandlers::handle_getforce(socket, context); + SocketHandlers::handle_getforce(socket, context); } else if (header == "EXIT") { - SocketDriverHandlers::handle_exit(ofs_running); + SocketHandlers::handle_exit(ofs_running); break; } else { - quit_if_root_io_failed(is_root() ? 1 : 0, + quit_if_root_failed(is_root() ? 1 : 0, is_root() ? "unknown i-PI header: " + header : ""); } } diff --git a/source/source_relax/socket_frame.cpp b/source/source_relax/socket_frame.cpp index 0d6fe783549..1a009879ddb 100644 --- a/source/source_relax/socket_frame.cpp +++ b/source/source_relax/socket_frame.cpp @@ -6,7 +6,7 @@ #include #include -using namespace SocketFrameUtils; +using namespace FrameUtils; namespace SocketFrame { diff --git a/source/source_relax/socket_frame_utils.cpp b/source/source_relax/socket_frame_utils.cpp index 6aaa7ae13b6..d4507572c2e 100644 --- a/source/source_relax/socket_frame_utils.cpp +++ b/source/source_relax/socket_frame_utils.cpp @@ -4,7 +4,7 @@ #include #include -namespace SocketFrameUtils +namespace FrameUtils { const int kMaxJacobiSweeps = 32; @@ -153,4 +153,4 @@ double received_inverse_residual(const SocketFrame::Matrix9& cell, } return static_cast(maximum); } -} // namespace SocketFrameUtils +} // namespace FrameUtils diff --git a/source/source_relax/socket_frame_utils.h b/source/source_relax/socket_frame_utils.h index 79184ee7241..21e22d872f3 100644 --- a/source/source_relax/socket_frame_utils.h +++ b/source/source_relax/socket_frame_utils.h @@ -3,7 +3,7 @@ #include "source_relax/socket_frame.h" -namespace SocketFrameUtils +namespace FrameUtils { constexpr int kMatrixDimension = 3; extern const int kMaxJacobiSweeps; @@ -18,6 +18,6 @@ long double scaled_determinant(const SocketFrame::Matrix9& values); double received_inverse_residual(const SocketFrame::Matrix9& cell, const SocketFrame::Matrix9& inverse, bool transpose_inverse); -} // namespace SocketFrameUtils +} // namespace FrameUtils #endif diff --git a/source/source_relax/socket_driver_handlers.cpp b/source/source_relax/socket_handlers.cpp similarity index 89% rename from source/source_relax/socket_driver_handlers.cpp rename to source/source_relax/socket_handlers.cpp index b0cebc3bf77..3d6fae9396c 100644 --- a/source/source_relax/socket_driver_handlers.cpp +++ b/source/source_relax/socket_handlers.cpp @@ -1,4 +1,4 @@ -#include "socket_driver_handlers.h" +#include "socket_handlers.h" #include "source_relax/socket_frame.h" #include "source_base/global_function.h" @@ -14,34 +14,34 @@ #include #include -namespace SocketDriverHandlers +namespace SocketHandlers { -using SocketDriverUtils::ComputedFrame; -using SocketDriverUtils::DriverState; -using SocketDriverUtils::kInverseAbsoluteTolerance; -using SocketDriverUtils::kInverseRelativeTolerance; -using SocketDriverUtils::kMaxCellCondition; -using SocketDriverUtils::kMaxInitBytes; -using SocketDriverUtils::kRyToHartree; -using SocketDriverUtils::kStressAbsoluteTolerance; -using SocketDriverUtils::kStressRelativeTolerance; -using SocketDriverUtils::all_ranks_converged; -using SocketDriverUtils::bcast_double_vector; -using SocketDriverUtils::bcast_header; -using SocketDriverUtils::bcast_socket_int32; -using SocketDriverUtils::fail_during_collective_stage; -using SocketDriverUtils::flatten_forces_hartree_per_bohr; -using SocketDriverUtils::ipi_cell_bohr_from_unitcell; -using SocketDriverUtils::is_root; -using SocketDriverUtils::matrix9_from_stress; -using SocketDriverUtils::max_abs_delta; -using SocketDriverUtils::max_wrapped_direct_delta_from_unitcell; -using SocketDriverUtils::properties_extra; -using SocketDriverUtils::quit_if_root_io_failed; -using SocketDriverUtils::set_positions_from_ipi_bohr; -using SocketDriverUtils::throw_if_any_rank_failed; -using SocketDriverUtils::unchanged_cell_tolerance; -using SocketDriverUtils::vector_from_matrix9; +using SocketUtils::ComputedFrame; +using SocketUtils::DriverState; +using SocketUtils::kInverseAbsoluteTolerance; +using SocketUtils::kInverseRelativeTolerance; +using SocketUtils::kMaxCellCondition; +using SocketUtils::kMaxInitBytes; +using SocketUtils::kRyToHartree; +using SocketUtils::kStressAbsoluteTolerance; +using SocketUtils::kStressRelativeTolerance; +using SocketUtils::all_ranks_converged; +using SocketUtils::bcast_double_vector; +using SocketUtils::bcast_header; +using SocketUtils::bcast_int32; +using SocketUtils::fail_during_collective_stage; +using SocketUtils::flatten_forces_hartree_per_bohr; +using SocketUtils::ipi_cell_bohr_from_unitcell; +using SocketUtils::is_root; +using SocketUtils::matrix9_from_stress; +using SocketUtils::max_abs_delta; +using SocketUtils::max_wrapped_direct_delta_from_unitcell; +using SocketUtils::properties_extra; +using SocketUtils::quit_if_root_failed; +using SocketUtils::set_positions_from_ipi_bohr; +using SocketUtils::throw_if_any_rank_failed; +using SocketUtils::unchanged_cell_tolerance; +using SocketUtils::vector_from_matrix9; std::string read_header_bcast(IpiSocket& socket, const DriverState state) { @@ -68,7 +68,7 @@ std::string read_header_bcast(IpiSocket& socket, const DriverState state) io_message = exc.what(); } } - quit_if_root_io_failed(io_failed, io_message); + quit_if_root_failed(io_failed, io_message); return bcast_header(header); } @@ -99,7 +99,7 @@ void handle_status(IpiSocket& socket, const DriverState state) io_message = exc.what(); } } - quit_if_root_io_failed(io_failed, io_message); + quit_if_root_failed(io_failed, io_message); } void handle_init(IpiSocket& socket, @@ -146,9 +146,9 @@ void handle_init(IpiSocket& socket, } } } - quit_if_root_io_failed(io_failed, io_message); - bcast_socket_int32(rid); - bcast_socket_int32(nbytes); + quit_if_root_failed(io_failed, io_message); + bcast_int32(rid); + bcast_int32(nbytes); if (nbytes > 0 && is_root()) { ofs_running << " ABACUS socket INIT params bytes " << nbytes << std::endl; @@ -223,13 +223,13 @@ PosdataPayload read_posdata(IpiSocket& socket, const UnitCell& ucell) io_message = exc.what(); } } - quit_if_root_io_failed(io_failed, io_message); + quit_if_root_failed(io_failed, io_message); return payload; } void bcast_posdata(PosdataPayload& payload) { - bcast_socket_int32(payload.nat_socket); + bcast_int32(payload.nat_socket); std::vector cell_values(payload.cell.begin(), payload.cell.end()); std::vector inverse_values(payload.inv_cell.begin(), payload.inv_cell.end()); bcast_double_vector(cell_values); @@ -430,7 +430,7 @@ void handle_posdata(IpiSocket& socket, { if (is_root() && context.state != DriverState::Ready) { - quit_if_root_io_failed(1, "POSDATA requires READY state"); + quit_if_root_failed(1, "POSDATA requires READY state"); } PosdataPayload payload = read_posdata(socket, *context.ucell); bcast_posdata(payload); @@ -495,7 +495,7 @@ void handle_getforce(IpiSocket& socket, DriverContext& context) io_message = exc.what(); } } - quit_if_root_io_failed(io_failed, io_message); + quit_if_root_failed(io_failed, io_message); context.published = ComputedFrame(); context.state = DriverState::Ready; } @@ -507,4 +507,4 @@ void handle_exit(std::ofstream& ofs_running) ofs_running << " ABACUS socket driver received i-PI EXIT" << std::endl; } } -} // namespace SocketDriverHandlers +} // namespace SocketHandlers diff --git a/source/source_relax/socket_driver_handlers.h b/source/source_relax/socket_handlers.h similarity index 66% rename from source/source_relax/socket_driver_handlers.h rename to source/source_relax/socket_handlers.h index 53dd025816e..4681491733e 100644 --- a/source/source_relax/socket_driver_handlers.h +++ b/source/source_relax/socket_handlers.h @@ -1,7 +1,7 @@ -#ifndef ABACUS_SOURCE_RELAX_SOCKET_DRIVER_HANDLERS_H -#define ABACUS_SOURCE_RELAX_SOCKET_DRIVER_HANDLERS_H +#ifndef ABACUS_SOURCE_RELAX_SOCKET_HANDLERS_H +#define ABACUS_SOURCE_RELAX_SOCKET_HANDLERS_H -#include "source_relax/socket_driver_utils.h" +#include "source_relax/socket_utils.h" #include "source_relax/socket_ipi.h" #include @@ -16,17 +16,17 @@ class ESolver; } class Input_para; -namespace SocketDriverHandlers +namespace SocketHandlers { struct DriverContext { ModuleESolver::ESolver* esolver = nullptr; UnitCell* ucell = nullptr; const Input_para* inp = nullptr; - SocketDriverUtils::DriverState state = SocketDriverUtils::DriverState::NeedInit; + SocketUtils::DriverState state = SocketUtils::DriverState::NeedInit; int istep = 0; int nat_return = 0; - SocketDriverUtils::ComputedFrame published; + SocketUtils::ComputedFrame published; std::vector reference_cell; bool checked_initial_positions = false; }; @@ -34,9 +34,9 @@ struct DriverContext // Reads the next i-PI header on the root rank and broadcasts it to all ranks. // Returns an empty string when the peer closed the connection while no frame // is pending. Calls WARNING_QUIT on unrecoverable I/O failure. -std::string read_header_bcast(IpiSocket& socket, const SocketDriverUtils::DriverState state); +std::string read_header_bcast(IpiSocket& socket, const SocketUtils::DriverState state); -void handle_status(IpiSocket& socket, const SocketDriverUtils::DriverState state); +void handle_status(IpiSocket& socket, const SocketUtils::DriverState state); void handle_init(IpiSocket& socket, DriverContext& context, @@ -49,6 +49,6 @@ void handle_posdata(IpiSocket& socket, void handle_getforce(IpiSocket& socket, DriverContext& context); void handle_exit(std::ofstream& ofs_running); -} // namespace SocketDriverHandlers +} // namespace SocketHandlers #endif diff --git a/source/source_relax/socket_driver_utils.cpp b/source/source_relax/socket_utils.cpp similarity index 94% rename from source/source_relax/socket_driver_utils.cpp rename to source/source_relax/socket_utils.cpp index 603e465f5f2..d1a9f59e913 100644 --- a/source/source_relax/socket_driver_utils.cpp +++ b/source/source_relax/socket_utils.cpp @@ -1,4 +1,4 @@ -#include "socket_driver_utils.h" +#include "socket_utils.h" #include "source_base/global_function.h" #include "source_base/mathzone.h" @@ -16,7 +16,7 @@ #include #include -namespace SocketDriverUtils +namespace SocketUtils { bool all_ranks_converged(const bool local_converged) { @@ -86,41 +86,41 @@ void bcast_double_vector(std::vector& values) } } -void bcast_socket_int(int& value) +void bcast_int(int& value) { Parallel_Common::bcast_int(value); } -void bcast_socket_int32(std::int32_t& value) +void bcast_int32(std::int32_t& value) { int tmp = static_cast(value); Parallel_Common::bcast_int(tmp); value = static_cast(tmp); } -void bcast_socket_chars(char* value, const int size) +void bcast_chars(char* value, const int size) { Parallel_Common::bcast_char(value, size); } -void bcast_socket_string(std::string& value) +void bcast_string(std::string& value) { int size = static_cast(value.size()); - bcast_socket_int(size); + bcast_int(size); if (!is_root()) { value.resize(static_cast(size)); } if (size > 0) { - bcast_socket_chars(&value[0], size); + bcast_chars(&value[0], size); } } -void quit_if_root_io_failed(int root_failed, std::string root_message) +void quit_if_root_failed(int root_failed, std::string root_message) { - bcast_socket_int(root_failed); - bcast_socket_string(root_message); + bcast_int(root_failed); + bcast_string(root_message); if (root_failed != 0) { ModuleBase::WARNING_QUIT("ABACUS socket", root_message.empty() ? "i-PI socket I/O failed" : root_message); @@ -129,11 +129,11 @@ void quit_if_root_io_failed(int root_failed, std::string root_message) std::string bcast_header(std::string header) { - bcast_socket_string(header); + bcast_string(header); return header; } -std::string socket_address() +std::string address() { const char* env = std::getenv("ABACUS_SOCKET_ADDRESS"); if (env == nullptr || std::string(env).empty()) @@ -327,4 +327,4 @@ std::vector vector_from_matrix9(const SocketFrame::Matrix9& values) { return std::vector(values.begin(), values.end()); } -} // namespace SocketDriverUtils +} // namespace SocketUtils diff --git a/source/source_relax/socket_driver_utils.h b/source/source_relax/socket_utils.h similarity index 83% rename from source/source_relax/socket_driver_utils.h rename to source/source_relax/socket_utils.h index 9a82dfcb0fa..ccac9761967 100644 --- a/source/source_relax/socket_driver_utils.h +++ b/source/source_relax/socket_utils.h @@ -1,5 +1,5 @@ -#ifndef ABACUS_SOURCE_RELAX_SOCKET_DRIVER_UTILS_H -#define ABACUS_SOURCE_RELAX_SOCKET_DRIVER_UTILS_H +#ifndef ABACUS_SOURCE_RELAX_SOCKET_UTILS_H +#define ABACUS_SOURCE_RELAX_SOCKET_UTILS_H #include "source_relax/socket_frame.h" #include "source_base/matrix.h" @@ -11,7 +11,7 @@ class UnitCell; -namespace SocketDriverUtils +namespace SocketUtils { constexpr double kRyToHartree = 0.5; constexpr int kIpiRankRoot = 0; @@ -48,13 +48,13 @@ void throw_if_any_rank_failed(int local_failed, std::string local_message); std::string properties_extra(const ComputedFrame& frame); bool is_root(); void bcast_double_vector(std::vector& values); -void bcast_socket_int(int& value); -void bcast_socket_int32(std::int32_t& value); -void bcast_socket_chars(char* value, int size); -void bcast_socket_string(std::string& value); -void quit_if_root_io_failed(int root_failed, std::string root_message); +void bcast_int(int& value); +void bcast_int32(std::int32_t& value); +void bcast_chars(char* value, int size); +void bcast_string(std::string& value); +void quit_if_root_failed(int root_failed, std::string root_message); std::string bcast_header(std::string header); -std::string socket_address(); +std::string address(); std::vector ipi_cell_bohr_from_unitcell(const UnitCell& ucell); double max_wrapped_direct_delta_from_unitcell(const UnitCell& ucell, const std::vector& positions_bohr); @@ -66,6 +66,6 @@ std::vector flatten_forces_hartree_per_bohr(const ModuleBase::matrix& fo int nat); SocketFrame::Matrix9 matrix9_from_stress(const ModuleBase::matrix& stress); std::vector vector_from_matrix9(const SocketFrame::Matrix9& values); -} // namespace SocketDriverUtils +} // namespace SocketUtils #endif diff --git a/source/source_relax/test/CMakeLists.txt b/source/source_relax/test/CMakeLists.txt index cbfd7d8cd95..339dce79bfa 100644 --- a/source/source_relax/test/CMakeLists.txt +++ b/source/source_relax/test/CMakeLists.txt @@ -23,8 +23,8 @@ AddTest( KEEP_FEATURE_DEFINITIONS __MPI SOURCES socket_driver_test.cpp ../socket_driver.cpp - ../socket_driver_utils.cpp - ../socket_driver_handlers.cpp + ../socket_utils.cpp + ../socket_handlers.cpp ../socket_frame_utils.cpp ../socket_frame.cpp ../socket_ipi.cpp diff --git a/source/source_relax/test/for_test.h b/source/source_relax/test/for_test.h index b26dafcd043..043c88ed470 100644 --- a/source/source_relax/test/for_test.h +++ b/source/source_relax/test/for_test.h @@ -84,12 +84,14 @@ Atom::Atom() Atom::~Atom() { } +#ifdef __MPI void Atom::bcast_atom() { } void Atom::bcast_atom2() { } +#endif Atom_pseudo::Atom_pseudo() { } From e8b95ccad1ba4c13d08ceb79f48b0fc8681015ad Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 15:22:55 +0800 Subject: [PATCH 11/14] refactor(relax): rename socket test files to test_ prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - socket_ipi_test.cpp → test_socket_ipi.cpp - socket_frame_test.cpp → test_socket_frame.cpp - socket_driver_test.cpp → test_socket_driver.cpp Follows AGENTS.md rule 11: new unit test files named test_.cpp. --- source/source_relax/test/CMakeLists.txt | 6 +++--- .../test/{socket_driver_test.cpp => test_socket_driver.cpp} | 0 .../test/{socket_frame_test.cpp => test_socket_frame.cpp} | 0 .../test/{socket_ipi_test.cpp => test_socket_ipi.cpp} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename source/source_relax/test/{socket_driver_test.cpp => test_socket_driver.cpp} (100%) rename source/source_relax/test/{socket_frame_test.cpp => test_socket_frame.cpp} (100%) rename source/source_relax/test/{socket_ipi_test.cpp => test_socket_ipi.cpp} (100%) diff --git a/source/source_relax/test/CMakeLists.txt b/source/source_relax/test/CMakeLists.txt index 339dce79bfa..22376932aa3 100644 --- a/source/source_relax/test/CMakeLists.txt +++ b/source/source_relax/test/CMakeLists.txt @@ -9,19 +9,19 @@ install(DIRECTORY support DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) AddTest( TARGET MODULE_RELAX_socket_ipi_test - SOURCES socket_ipi_test.cpp ../socket_ipi.cpp + SOURCES test_socket_ipi.cpp ../socket_ipi.cpp ) AddTest( TARGET MODULE_RELAX_socket_frame_test - SOURCES socket_frame_test.cpp ../socket_frame.cpp ../socket_frame_utils.cpp + SOURCES test_socket_frame.cpp ../socket_frame.cpp ../socket_frame_utils.cpp ) AddTest( TARGET MODULE_RELAX_socket_driver_test LIBS MPI::MPI_CXX base device KEEP_FEATURE_DEFINITIONS __MPI - SOURCES socket_driver_test.cpp + SOURCES test_socket_driver.cpp ../socket_driver.cpp ../socket_utils.cpp ../socket_handlers.cpp diff --git a/source/source_relax/test/socket_driver_test.cpp b/source/source_relax/test/test_socket_driver.cpp similarity index 100% rename from source/source_relax/test/socket_driver_test.cpp rename to source/source_relax/test/test_socket_driver.cpp diff --git a/source/source_relax/test/socket_frame_test.cpp b/source/source_relax/test/test_socket_frame.cpp similarity index 100% rename from source/source_relax/test/socket_frame_test.cpp rename to source/source_relax/test/test_socket_frame.cpp diff --git a/source/source_relax/test/socket_ipi_test.cpp b/source/source_relax/test/test_socket_ipi.cpp similarity index 100% rename from source/source_relax/test/socket_ipi_test.cpp rename to source/source_relax/test/test_socket_ipi.cpp From abd84fed5e5ad116b2a9e9f132f70ae0fc699a11 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 9 Sep 2026 15:42:37 +0800 Subject: [PATCH 12/14] fix(socket_utils): guard direct MPI calls with #ifdef __MPI MPI_Comm_rank and MPI_Abort were called unconditionally in fail_during_collective_stage and is_root, breaking builds without __MPI defined. Wrap these calls (and the mpi.h include) with the standard __MPI guards. Non-MPI fallback behavior: - fail_during_collective_stage prints rank=-1 and aborts - is_root returns true (single process is always root) --- source/source_relax/socket_utils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/source_relax/socket_utils.cpp b/source/source_relax/socket_utils.cpp index d1a9f59e913..50d590299bf 100644 --- a/source/source_relax/socket_utils.cpp +++ b/source/source_relax/socket_utils.cpp @@ -8,6 +8,10 @@ #include "source_cell/unitcell.h" #include "source_cell/update_cell.h" +#ifdef __MPI +#include +#endif + #include #include #include @@ -43,14 +47,18 @@ void throw_if_any_rank_failed(int local_failed, std::string local_message) const std::string& message) { int rank = -1; +#ifdef __MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#endif std::fprintf(stderr, "ABACUS_SOCKET_MPI_FATAL stage=%s rank=%d message=%s\n", stage, rank, message.c_str()); std::fflush(stderr); +#ifdef __MPI MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); +#endif std::abort(); } @@ -74,7 +82,9 @@ std::string properties_extra(const ComputedFrame& frame) bool is_root() { int rank = kIpiRankRoot; +#ifdef __MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#endif return rank == kIpiRankRoot; } From 1f58fce7cd1d9fe2cf7c894d1aa7cd0be26122db Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sat, 12 Sep 2026 10:53:10 +0800 Subject: [PATCH 13/14] fix(socket_utils): restore serial throw in fail_during_collective_stage Commit 3fea6ec30 dropped the #else throw branch as a redundant MPI guard, and abd84fed5 rebuilt the guard with std::abort() as the unconditional fallback. That regressed serial ENABLE_MPI=OFF builds: an ESolver failure now SIGABRTs instead of reaching the catch in Socket_Driver::socket_driver that reports a clean WARNING_QUIT. Move the diagnostic print, MPI_Abort, and abort back inside #ifdef __MPI and restore throw std::runtime_error(message) for serial builds, matching the original behavior from 4c9547460. Serial builds remain user-facing and CI cannot catch this because the socket tests are guarded by if(ENABLE_MPI). --- source/source_relax/socket_utils.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/source/source_relax/socket_utils.cpp b/source/source_relax/socket_utils.cpp index 50d590299bf..ccdfa38b369 100644 --- a/source/source_relax/socket_utils.cpp +++ b/source/source_relax/socket_utils.cpp @@ -43,23 +43,31 @@ void throw_if_any_rank_failed(int local_failed, std::string local_message) } } +/// @brief Terminate the socket run after a failure inside a collective stage. +/// +/// Under MPI the failure may deadlock other ranks waiting in a collective, +/// so print a diagnostic and abort the whole job. In serial builds there is +/// no collective: the exception propagates to Socket_Driver::socket_driver, +/// which reports a clean WARNING_QUIT into the log. Do not replace the +/// serial throw with abort(); serial builds are user-facing. [[noreturn]] void fail_during_collective_stage(const char* stage, const std::string& message) { - int rank = -1; #ifdef __MPI + int rank = -1; MPI_Comm_rank(MPI_COMM_WORLD, &rank); -#endif std::fprintf(stderr, "ABACUS_SOCKET_MPI_FATAL stage=%s rank=%d message=%s\n", stage, rank, message.c_str()); std::fflush(stderr); -#ifdef __MPI MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); -#endif std::abort(); +#else + (void)stage; + throw std::runtime_error(message); +#endif } std::string properties_extra(const ComputedFrame& frame) From 92d4f2bb219a7689880de483de9574b0c2ec2293 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sat, 12 Sep 2026 11:54:49 +0800 Subject: [PATCH 14/14] fix(socket_handlers): keep POSDATA state check on the shared quit path handle_posdata performed the READY-state check with a root-only quit_if_root_failed call before read_posdata, while non-root ranks fell through to read_posdata's own quit_if_root_failed. The two paths matched only because each issued exactly one bcast_int plus one bcast_string; any collective added ahead of read_posdata's quit would deadlock non-root ranks after the root quit. Pass DriverState into read_posdata and set io_failed there so all ranks pass through the single quit_if_root_failed sequence, matching the other socket handlers. --- source/source_relax/socket_handlers.cpp | 92 ++++++++++++++----------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/source/source_relax/socket_handlers.cpp b/source/source_relax/socket_handlers.cpp index 3d6fae9396c..dc333ced087 100644 --- a/source/source_relax/socket_handlers.cpp +++ b/source/source_relax/socket_handlers.cpp @@ -172,55 +172,67 @@ struct PosdataPayload // Root rank reads and validates the POSDATA frame; the results are then // broadcast to all ranks. Calls WARNING_QUIT on protocol/validation failure. -PosdataPayload read_posdata(IpiSocket& socket, const UnitCell& ucell) +// The READY-state check must stay inside this function so that every rank +// passes through the single quit_if_root_failed collective sequence below; +// a root-only early quit outside would deadlock if any collective were +// added ahead of it. +PosdataPayload read_posdata(IpiSocket& socket, const UnitCell& ucell, const DriverState state) { PosdataPayload payload; int io_failed = 0; std::string io_message; if (is_root()) { - try + if (state != DriverState::Ready) { - const std::vector cell_values = socket.read_doubles(9); - const std::vector inverse_values = socket.read_doubles(9); - std::copy(cell_values.begin(), cell_values.end(), payload.cell.begin()); - std::copy(inverse_values.begin(), inverse_values.end(), payload.inv_cell.begin()); - payload.nat_socket = socket.read_int32(); - const SocketFrame::CellValidation validation - = SocketFrame::validate_ipi_cell(payload.cell, - payload.inv_cell, - kMaxCellCondition, - kInverseAbsoluteTolerance, - kInverseRelativeTolerance); - if (!validation.ok) - { - io_failed = 1; - io_message = "invalid POSDATA cell: " + validation.message; - } - std::size_t coordinate_count = 0; - if (io_failed == 0 - && !SocketFrame::checked_position_count(payload.nat_socket, - ucell.nat, - coordinate_count, - io_message)) - { - io_failed = 1; - } - if (io_failed == 0) + io_failed = 1; + io_message = "POSDATA requires READY state"; + } + else + { + try { - payload.positions = socket.read_doubles(coordinate_count); - if (!SocketFrame::validate_positions(payload.positions, - coordinate_count, - io_message)) + const std::vector cell_values = socket.read_doubles(9); + const std::vector inverse_values = socket.read_doubles(9); + std::copy(cell_values.begin(), cell_values.end(), payload.cell.begin()); + std::copy(inverse_values.begin(), inverse_values.end(), payload.inv_cell.begin()); + payload.nat_socket = socket.read_int32(); + const SocketFrame::CellValidation validation + = SocketFrame::validate_ipi_cell(payload.cell, + payload.inv_cell, + kMaxCellCondition, + kInverseAbsoluteTolerance, + kInverseRelativeTolerance); + if (!validation.ok) + { + io_failed = 1; + io_message = "invalid POSDATA cell: " + validation.message; + } + std::size_t coordinate_count = 0; + if (io_failed == 0 + && !SocketFrame::checked_position_count(payload.nat_socket, + ucell.nat, + coordinate_count, + io_message)) { io_failed = 1; } + if (io_failed == 0) + { + payload.positions = socket.read_doubles(coordinate_count); + if (!SocketFrame::validate_positions(payload.positions, + coordinate_count, + io_message)) + { + io_failed = 1; + } + } + } + catch (const std::exception& exc) + { + io_failed = 1; + io_message = exc.what(); } - } - catch (const std::exception& exc) - { - io_failed = 1; - io_message = exc.what(); } } quit_if_root_failed(io_failed, io_message); @@ -428,11 +440,7 @@ void handle_posdata(IpiSocket& socket, DriverContext& context, std::ofstream& ofs_running) { - if (is_root() && context.state != DriverState::Ready) - { - quit_if_root_failed(1, "POSDATA requires READY state"); - } - PosdataPayload payload = read_posdata(socket, *context.ucell); + PosdataPayload payload = read_posdata(socket, *context.ucell, context.state); bcast_posdata(payload); check_posdata_geometry(context, payload, context); run_esolver_for_positions(*context.ucell, context.esolver, payload.positions, context.istep);