From 539ab023654bd9832e00b5cf7426cf2bff2a5884 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Apr 2021 21:05:11 -0400 Subject: [PATCH] provide more generic implementation of Comm::read_lines_from_file() in utils --- src/utils.cpp | 30 ++++++++++++++++++++ src/utils.h | 25 ++++++++++++++++- unittest/formats/test_file_operations.cpp | 34 ++++++++++++++++++++++- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index 0ff1d65633..9aa8e2f7ee 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -225,6 +225,36 @@ void utils::sfread(const char *srcname, int srcline, void *s, size_t size, /* ------------------------------------------------------------------ */ +/* read N lines and broadcast */ +int utils::read_lines_from_file(FILE *fp, int nlines, int maxline, + char *buffer, int me, MPI_Comm comm) +{ + char *ptr = buffer; + *ptr = '\0'; + + if (me == 0) { + if (fp) { + for (int i = 0; i < nlines; i++) { + ptr = fgets(ptr,maxline,fp); + if (!ptr) break; // EOF? + // advance ptr to end of string and append newline char if needed. + ptr += strlen(ptr); + if (*(--ptr) != '\n') *(++ptr) = '\n'; + // ensure buffer is null terminated. null char is start of next line. + *(++ptr) = '\0'; + } + } + } + + int n = strlen(buffer); + MPI_Bcast(&n,1,MPI_INT,0,comm); + if (n == 0) return 1; + MPI_Bcast(buffer,n+1,MPI_CHAR,0,comm); + return 0; +} + +/* ------------------------------------------------------------------ */ + std::string utils::check_packages_for_style(const std::string &style, const std::string &name, LAMMPS *lmp) diff --git a/src/utils.h b/src/utils.h index c87f0b28a9..b393f89ffa 100644 --- a/src/utils.h +++ b/src/utils.h @@ -17,9 +17,12 @@ /*! \file utils.h */ #include "lmptype.h" + +#include + +#include #include #include -#include namespace LAMMPS_NS { @@ -89,6 +92,26 @@ namespace LAMMPS_NS { void sfread(const char *srcname, int srcline, void *s, size_t size, size_t num, FILE *fp, const char *filename, Error *error); + /** Read N lines of text from file into buffer and broadcast them + * + * This function uses repeated calls to fread() to fill a buffer with + * newline terminated text. If a line does not end in a newline (e.g. + * at the end of a file), it is added. The caller has to allocate an + * nlines by maxline sized buffer for storing the text data. + * Reading is done by MPI rank 0 of the given communicator only, and + * thus only MPI rank 0 needs to provide a valid file pointer. + * + * \param fp file pointer used by fread + * \param nlines number of lines to be read + * \param maxline maximum length of a single line + * \param buffer buffer for storing the data. + * \param me MPI rank of calling process in MPI communicator + * \param comm MPI communicator for broadcast + * \return 1 if the read was short, 0 if read was succesful */ + + int read_lines_from_file(FILE *fp, int nlines, int maxline, + char *buffer, int me, MPI_Comm comm); + /** Report if a requested style is in a package or may have a typo * * \param style type of style that is to be checked for diff --git a/unittest/formats/test_file_operations.cpp b/unittest/formats/test_file_operations.cpp index 700990fb72..9209e67585 100644 --- a/unittest/formats/test_file_operations.cpp +++ b/unittest/formats/test_file_operations.cpp @@ -11,13 +11,13 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ +#include "../testing/core.h" #include "info.h" #include "input.h" #include "lammps.h" #include "utils.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "../testing/core.h" #include #include @@ -28,6 +28,7 @@ using namespace LAMMPS_NS; using testing::MatchesRegex; using testing::StrEq; +using utils::read_lines_from_file; using utils::sfgets; using utils::sfread; using utils::split_words; @@ -124,6 +125,37 @@ TEST_F(FileOperationsTest, safe_fread) fclose(fp); } +TEST_F(FileOperationsTest, read_lines_from_file) +{ + char *buf = new char[MAX_BUF_SIZE]; + FILE *fp = nullptr; + MPI_Comm world = MPI_COMM_WORLD; + int me, rv; + memset(buf, 0, MAX_BUF_SIZE); + + rv = utils::read_lines_from_file(nullptr, 1, MAX_BUF_SIZE, buf, me, world); + ASSERT_EQ(rv, 1); + + MPI_Comm_rank(world, &me); + if (me == 0) { + fp = fopen("safe_file_read_test.txt", "r"); + ASSERT_NE(fp, nullptr); + } else + ASSERT_EQ(fp, nullptr); + + rv = utils::read_lines_from_file(fp, 2, MAX_BUF_SIZE / 2, buf, me, world); + ASSERT_EQ(rv, 0); + ASSERT_THAT(buf, StrEq("one line\ntwo_lines\n")); + + rv = utils::read_lines_from_file(fp, 2, MAX_BUF_SIZE / 2, buf, me, world); + ASSERT_EQ(rv, 0); + ASSERT_THAT(buf, StrEq("\nno newline\n")); + + rv = utils::read_lines_from_file(fp, 2, MAX_BUF_SIZE / 2, buf, me, world); + ASSERT_EQ(rv, 1); + delete[] buf; +} + TEST_F(FileOperationsTest, logmesg) { char buf[8];