From 1f0f9973a664b8112c28447e3a86671272740bd1 Mon Sep 17 00:00:00 2001 From: Alex Kaszynski Date: Fri, 19 Jul 2024 22:37:46 -0600 Subject: [PATCH 1/3] bump to v0.3.0 (#44) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index be9b228..579fcad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ description = "Pythonic interface to MAPDL archive files." name = "mapdl-archive" readme = "README.rst" requires-python = ">=3.8" -version = "0.2.dev0" +version = "0.3.dev0" [project.urls] Repository = "https://github.com/akaszynski/mapdl-archive" From 0be7ccf07be12fea7bb10332c51f64f2775ba7c1 Mon Sep 17 00:00:00 2001 From: Alex Kaszynski Date: Sun, 21 Jul 2024 12:53:55 -0600 Subject: [PATCH 2/3] Fix windows CMBLOCK (#45) * refactor to have persistant line * linux fixes * disable debug output * remove all traces of vtk_int --- src/archive.cpp | 149 ++++++++++++++++++++---------------------- src/reader.cpp | 135 +++++++++++++++----------------------- tests/test__reader.py | 18 ++++- 3 files changed, 138 insertions(+), 164 deletions(-) diff --git a/src/archive.cpp b/src/archive.cpp index 6214d16..9e94221 100644 --- a/src/archive.cpp +++ b/src/archive.cpp @@ -16,13 +16,6 @@ #include #endif -// Standard VTK int size -#if defined(__linux__) || defined(__APPLE__) -typedef int64_t vtk_int; -#else -typedef int32_t vtk_int; -#endif - // VTK cell types #define VTK_TRIANGLE 5 #define VTK_QUAD 9 @@ -137,8 +130,8 @@ void WriteEblock( const NDArray rcon_arr, // real constant ID array const NDArray elem_nnodes_arr, // number of nodes per element const NDArray celltypes_arr, // VTK celltypes array - const NDArray offset_arr, // VTK offset array - const NDArray cells_arr, // VTK cell connectivity array + const NDArray offset_arr, // VTK offset array + const NDArray cells_arr, // VTK cell connectivity array const NDArray typenum_arr, // ANSYS type number (e.g. 187 for SOLID187) const NDArray nodenum_arr, // ANSYS node numbering std::string &mode) { @@ -151,8 +144,8 @@ void WriteEblock( const int *rcon = rcon_arr.data(); const int *elem_nnodes = elem_nnodes_arr.data(); const uint8_t *celltypes = celltypes_arr.data(); - const vtk_int *offset = offset_arr.data(); - const vtk_int *cells = cells_arr.data(); + const int64_t *offset = offset_arr.data(); + const int64_t *cells = cells_arr.data(); const int *typenum = typenum_arr.data(); const int *nodenum = nodenum_arr.data(); @@ -431,17 +424,17 @@ NDArray CmblockItems(const NDArray array) { // Resets the midside nodes of the tetrahedral starting at index c. // midside nodes between // (0,1), (1,2), (2,0), (0,3), (1,3), and (2,3) -template inline void ResetMidTet(const vtk_int *cells, T *points) { - vtk_int ind0 = cells[0] * 3; - vtk_int ind1 = cells[1] * 3; - vtk_int ind2 = cells[2] * 3; - vtk_int ind3 = cells[3] * 3; - vtk_int ind4 = cells[4] * 3; - vtk_int ind5 = cells[5] * 3; - vtk_int ind6 = cells[6] * 3; - vtk_int ind7 = cells[7] * 3; - vtk_int ind8 = cells[8] * 3; - vtk_int ind9 = cells[9] * 3; +template inline void ResetMidTet(const int64_t *cells, T *points) { + int64_t ind0 = cells[0] * 3; + int64_t ind1 = cells[1] * 3; + int64_t ind2 = cells[2] * 3; + int64_t ind3 = cells[3] * 3; + int64_t ind4 = cells[4] * 3; + int64_t ind5 = cells[5] * 3; + int64_t ind6 = cells[6] * 3; + int64_t ind7 = cells[7] * 3; + int64_t ind8 = cells[8] * 3; + int64_t ind9 = cells[9] * 3; for (size_t j = 0; j < 3; j++) { points[ind4 + j] = (points[ind0 + j] + points[ind1 + j]) * 0.5; @@ -462,20 +455,20 @@ template inline void ResetMidTet(const vtk_int *cells, T *points) { // 10(1, 4) // 11(2, 4) // 12(3, 4) -template inline void ResetMidPyr(const vtk_int *cells, T *points) { - vtk_int ind0 = cells[0] * 3; - vtk_int ind1 = cells[1] * 3; - vtk_int ind2 = cells[2] * 3; - vtk_int ind3 = cells[3] * 3; - vtk_int ind4 = cells[4] * 3; - vtk_int ind5 = cells[5] * 3; - vtk_int ind6 = cells[6] * 3; - vtk_int ind7 = cells[7] * 3; - vtk_int ind8 = cells[8] * 3; - vtk_int ind9 = cells[9] * 3; - vtk_int ind10 = cells[10] * 3; - vtk_int ind11 = cells[11] * 3; - vtk_int ind12 = cells[12] * 3; +template inline void ResetMidPyr(const int64_t *cells, T *points) { + int64_t ind0 = cells[0] * 3; + int64_t ind1 = cells[1] * 3; + int64_t ind2 = cells[2] * 3; + int64_t ind3 = cells[3] * 3; + int64_t ind4 = cells[4] * 3; + int64_t ind5 = cells[5] * 3; + int64_t ind6 = cells[6] * 3; + int64_t ind7 = cells[7] * 3; + int64_t ind8 = cells[8] * 3; + int64_t ind9 = cells[9] * 3; + int64_t ind10 = cells[10] * 3; + int64_t ind11 = cells[11] * 3; + int64_t ind12 = cells[12] * 3; for (size_t j = 0; j < 3; j++) { points[ind5 + j] = (points[ind0 + j] + points[ind1 + j]) * 0.5; @@ -489,7 +482,7 @@ template inline void ResetMidPyr(const vtk_int *cells, T *points) { } } -template inline void ResetMidWeg(const vtk_int *cells, T *points) { +template inline void ResetMidWeg(const int64_t *cells, T *points) { // Reset midside nodes of a wedge cell: // 6 (0,1) // 7 (1,2) @@ -500,21 +493,21 @@ template inline void ResetMidWeg(const vtk_int *cells, T *points) { // 12 (0,3) // 13 (1,4) // 14 (2,5) - vtk_int ind0 = cells[0] * 3; - vtk_int ind1 = cells[1] * 3; - vtk_int ind2 = cells[2] * 3; - vtk_int ind3 = cells[3] * 3; - vtk_int ind4 = cells[4] * 3; - vtk_int ind5 = cells[5] * 3; - vtk_int ind6 = cells[6] * 3; - vtk_int ind7 = cells[7] * 3; - vtk_int ind8 = cells[8] * 3; - vtk_int ind9 = cells[9] * 3; - vtk_int ind10 = cells[10] * 3; - vtk_int ind11 = cells[11] * 3; - vtk_int ind12 = cells[12] * 3; - vtk_int ind13 = cells[13] * 3; - vtk_int ind14 = cells[14] * 3; + int64_t ind0 = cells[0] * 3; + int64_t ind1 = cells[1] * 3; + int64_t ind2 = cells[2] * 3; + int64_t ind3 = cells[3] * 3; + int64_t ind4 = cells[4] * 3; + int64_t ind5 = cells[5] * 3; + int64_t ind6 = cells[6] * 3; + int64_t ind7 = cells[7] * 3; + int64_t ind8 = cells[8] * 3; + int64_t ind9 = cells[9] * 3; + int64_t ind10 = cells[10] * 3; + int64_t ind11 = cells[11] * 3; + int64_t ind12 = cells[12] * 3; + int64_t ind13 = cells[13] * 3; + int64_t ind14 = cells[14] * 3; for (size_t j = 0; j < 3; j++) { points[ind6 + j] = (points[ind0 + j] + points[ind1 + j]) * 0.5; @@ -542,27 +535,27 @@ template inline void ResetMidWeg(const vtk_int *cells, T *points) { // 17 (1,5) // 18 (2,6) // 19 (3,7) -template inline void ResetMidHex(const vtk_int *cells, T *points) { - vtk_int ind0 = cells[0] * 3; - vtk_int ind1 = cells[1] * 3; - vtk_int ind2 = cells[2] * 3; - vtk_int ind3 = cells[3] * 3; - vtk_int ind4 = cells[4] * 3; - vtk_int ind5 = cells[5] * 3; - vtk_int ind6 = cells[6] * 3; - vtk_int ind7 = cells[7] * 3; - vtk_int ind8 = cells[8] * 3; - vtk_int ind9 = cells[9] * 3; - vtk_int ind10 = cells[10] * 3; - vtk_int ind11 = cells[11] * 3; - vtk_int ind12 = cells[12] * 3; - vtk_int ind13 = cells[13] * 3; - vtk_int ind14 = cells[14] * 3; - vtk_int ind15 = cells[15] * 3; - vtk_int ind16 = cells[16] * 3; - vtk_int ind17 = cells[17] * 3; - vtk_int ind18 = cells[18] * 3; - vtk_int ind19 = cells[19] * 3; +template inline void ResetMidHex(const int64_t *cells, T *points) { + int64_t ind0 = cells[0] * 3; + int64_t ind1 = cells[1] * 3; + int64_t ind2 = cells[2] * 3; + int64_t ind3 = cells[3] * 3; + int64_t ind4 = cells[4] * 3; + int64_t ind5 = cells[5] * 3; + int64_t ind6 = cells[6] * 3; + int64_t ind7 = cells[7] * 3; + int64_t ind8 = cells[8] * 3; + int64_t ind9 = cells[9] * 3; + int64_t ind10 = cells[10] * 3; + int64_t ind11 = cells[11] * 3; + int64_t ind12 = cells[12] * 3; + int64_t ind13 = cells[13] * 3; + int64_t ind14 = cells[14] * 3; + int64_t ind15 = cells[15] * 3; + int64_t ind16 = cells[16] * 3; + int64_t ind17 = cells[17] * 3; + int64_t ind18 = cells[18] * 3; + int64_t ind19 = cells[19] * 3; for (size_t j = 0; j < 3; j++) { points[ind8 + j] = (points[ind0 + j] + points[ind1 + j]) * 0.5; @@ -583,18 +576,18 @@ template inline void ResetMidHex(const vtk_int *cells, T *points) { template void ResetMidside( NDArray celltypes_arr, - NDArray cells_arr, - NDArray offset_arr, + NDArray cells_arr, + NDArray offset_arr, NDArray points_arr) { int n_cells = celltypes_arr.size(); - const vtk_int *cells = cells_arr.data(); - const vtk_int *offset = offset_arr.data(); + const int64_t *cells = cells_arr.data(); + const int64_t *offset = offset_arr.data(); const uint8_t *celltypes = celltypes_arr.data(); T *points = points_arr.data(); for (int i = 0; i < n_cells; i++) { - vtk_int c = offset[i]; + int64_t c = offset[i]; switch (celltypes[i]) { case VTK_QUADRATIC_TETRA: diff --git a/src/reader.cpp b/src/reader.cpp index d932893..d4de5de 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -489,10 +489,14 @@ NDArray InterpretComponent(const std::vector &component) { class Archive { - public: + private: + std::string line; + bool debug; std::string filename; + + public: bool read_parameters; - bool debug; + bool read_eblock; bool eblock_is_read = false; bool nblock_is_read = false; @@ -534,8 +538,6 @@ class Archive { // https://nanobind.readthedocs.io/en/latest/faq.html#why-am-i-getting-errors-about-leaked-functions-and-types nb::set_leak_warnings(false); - std::ifstream cfile(filename); - if (!cfile.is_open()) { throw std::runtime_error("No such file or directory: '" + filename + "'"); } @@ -552,8 +554,6 @@ class Archive { std::cout << "reading ET" << std::endl; } - std::string line; - std::getline(cfile, line); std::istringstream iss(line); std::string token; std::vector et_vals; @@ -576,14 +576,12 @@ class Archive { } } - // Read ETBLOCK line + // Read ETBLOCK void ReadETBlock() { - std::string line; std::string token; std::vector values; - // start by reading in the first line, which must be an ETBLOCK - std::getline(cfile, line); + // Assumes current line is an ETBLOCK std::istringstream iss(line); // skip first item (ETBLOCK) @@ -622,17 +620,14 @@ class Archive { } } + // Read EBLOCK void ReadEBlock() { - - // start by reading in the first line, which must be an EBLOCK - std::string line; - std::getline(cfile, line); - // Sometimes, DAT files contain two EBLOCKs. Read only the first block. if (eblock_is_read) { return; } + // Assumes already start of EBLOCK std::istringstream iss(line); // Only read in SOLID eblocks @@ -667,8 +662,7 @@ class Archive { std::cout << "reading KEYOPT" << std::endl; } - std::string line; - std::getline(cfile, line); + // Assumes at KEYOPT line std::istringstream iss(line); std::string token; @@ -703,14 +697,15 @@ class Archive { } } + // Read RLBLOCK void ReadRLBLOCK() { - std::string line; - std::getline(cfile, line); if (debug) { std::cout << "reading RLBLOCK" << std::endl; } std::vector set_dat; + + // Assumes line at RLBLOCK std::istringstream iss(line); std::string token; // while (std::getline(iss, token, ',')) { @@ -797,22 +792,19 @@ class Archive { } } - void ReadNBlock() { - - // Before reading NBLOCK, save where the nblock started - nblock_start = cfile.tellg(); - - // start by reading in the first line, which must be a NBLOCK - std::string line; - std::getline(cfile, line); + void ReadNBlock(const int pos) { // Sometimes, DAT files contains multiple node blocks. Read only the first block. if (nblock_is_read) { return; } + nblock_start = pos; // Get size of NBLOCK + // Assumes line is at NBLOCK + // std::cout << "line: " << line << std::endl; try { + // Number of nodes is last item in string n_nodes = std::stoi(line.substr(line.rfind(',') + 1)); } catch (...) { std::cerr << "Failed to read number of nodes when reading:" << line << std::endl; @@ -859,18 +851,13 @@ class Archive { } } + // Read CMBLOCK void ReadCMBlock() { - std::string line; - std::getline(cfile, line); - - if (line.compare(0, 8, "CMBLOCK,") != 0 && line.compare(0, 8, "cmblock,") != 0) { - return; - } - if (debug) { std::cout << "reading CMBLOCK" << std::endl; } + // Assumes line at CMBLOCK std::istringstream iss(line); std::string token; std::vector split_line; @@ -938,11 +925,14 @@ class Archive { } else if (line_comp_type.find("ELEM") != std::string::npos) { elem_comps[comname] = InterpretComponent(component); } + + if (debug) { + std::cout << "Done reading CMBLOCK" << std::endl; + } } void Read() { int first_char, next_char; - std::string line; int position_start, position_end; while (true) { @@ -950,112 +940,84 @@ class Archive { // line. It's faster and the parsing logic is always based on the first // character first_char = cfile.peek(); - // if (debug) { - // std::cout << "Read character: " << static_cast(first_char) << - // std::endl; - // } +#ifdef DEBUG + std::cout << "Read character: " << static_cast(first_char) << std::endl; +#endif if (cfile.eof()) { +#ifdef DEBUG + std::cout << "Reached EOF" << std::endl; +#endif break; } else if (first_char == 'E' || first_char == 'e') { + std::getline(cfile, line); + // E commands (ET or ETBLOCK) if (debug) { std::cout << "Read E" << std::endl; } - // get line but do not advance - position_start = cfile.tellg(); - std::getline(cfile, line); - position_end = cfile.tellg(); - cfile.seekg(position_start); - // Record element type if (line.compare(0, 3, "ET,") == 0 || line.compare(0, 3, "et,") == 0) { ReadETLine(); } else if ( - line.compare(0, 7, "ETBLOCK") == 0 || - line.compare(0, 7, "etblock") == 0) { + line.compare(0, 6, "ETBLOC") == 0 || line.compare(0, 6, "etbloc") == 0) { ReadETBlock(); } else if ( line.compare(0, 6, "EBLOCK") == 0 || line.compare(0, 6, "eblock") == 0 && read_eblock) { ReadEBlock(); - } else { - cfile.seekg(position_end); } } else if (first_char == 'K' || first_char == 'k') { + std::getline(cfile, line); if (debug) { std::cout << "Read K" << std::endl; } - // get line but do not advance - position_start = cfile.tellg(); - std::getline(cfile, line); - position_end = cfile.tellg(); - cfile.seekg(position_start); - // Record keyopt if (line.compare(0, 5, "KEYOP") == 0 || line.compare(0, 5, "keyop") == 0) { ReadKEYOPTLine(); - } else { - cfile.seekg(position_end); } } else if (first_char == 'R' || first_char == 'r') { + std::getline(cfile, line); // test for RLBLOCK if (debug) { std::cout << "Read R" << std::endl; } - // get line but do not advance - position_start = cfile.tellg(); - std::getline(cfile, line); - position_end = cfile.tellg(); - cfile.seekg(position_start); - // Record keyopt if (line.compare(0, 5, "RLBLO") == 0 || line.compare(0, 5, "rlblo") == 0) { ReadRLBLOCK(); - } else { - cfile.seekg(position_end); } } else if (first_char == 'N' || first_char == 'n') { + // store current position if we read in the node block + const int pos = cfile.tellg(); + std::getline(cfile, line); // test for NBLOCK if (debug) { std::cout << "Read N" << std::endl; } - // get line but do not advance - position_start = cfile.tellg(); - std::getline(cfile, line); - position_end = cfile.tellg(); - cfile.seekg(position_start); - // Record node block if (line.compare(0, 5, "NBLOC") == 0 || line.compare(0, 5, "nbloc") == 0) { - ReadNBlock(); - } else { - cfile.seekg(position_end); + ReadNBlock(pos); } + } else if (first_char == 'C' || first_char == 'c') { + std::getline(cfile, line); + if (debug) { std::cout << "Read C" << std::endl; } - // get line but do not advance - position_start = cfile.tellg(); - std::getline(cfile, line); - position_end = cfile.tellg(); - cfile.seekg(position_start); - // Record component block if (line.compare(0, 5, "CMBLO") == 0 || line.compare(0, 5, "cmblo") == 0) { + // std::cout << "Reading CMBLOCK" << std::endl; ReadCMBlock(); - } else { - cfile.seekg(position_end); } } else { if (debug) { @@ -1067,6 +1029,13 @@ class Archive { } } + // Read line and return the position prior to reading the line + int ReadLine() { + int pos = cfile.tellg(); + std::getline(cfile, line); + return pos; + } + // Convert ansys style connectivity to VTK connectivity // type_ref is a mapping between ansys element types and VTK element types nb::tuple ToVTK(NDArray type_ref) { @@ -1159,9 +1128,9 @@ NB_MODULE(_reader, m) { .def_ro("n_nodes", &Archive::n_nodes) .def_ro("nblock_start", &Archive::nblock_start) .def_ro("nblock_end", &Archive::nblock_end) - .def_ro("nblock_end", &Archive::nblock_end) .def("to_vtk", &Archive::ToVTK) .def("read", &Archive::Read) + .def("read_line", &Archive::ReadLine) .def("read_nblock", &Archive::ReadNBlock) .def("read_rlblock", &Archive::ReadRLBLOCK) .def("read_keyopt_line", &Archive::ReadKEYOPTLine) diff --git a/tests/test__reader.py b/tests/test__reader.py index 30934b3..e4924f3 100644 --- a/tests/test__reader.py +++ b/tests/test__reader.py @@ -139,6 +139,7 @@ def test_read_et(tmp_path: Path) -> None: fid.write(et_line) archive = _reader.Archive(filename) + archive.read_line() archive.read_et_line() assert np.array_equal([[4, 186]], archive.elem_type) @@ -149,6 +150,7 @@ def test_read_etblock(tmp_path: Path) -> None: fid.write(ETBLOCK_STR) archive = _reader.Archive(filename) + archive.read_line() archive.read_etblock() assert np.array_equal([[1, 181]], archive.elem_type) @@ -159,6 +161,7 @@ def test_read_eblock_not_solid(tmp_path: Path) -> None: fid.write(EBLOCK_STR_NOT_SOLID) archive = _reader.Archive(filename) + archive.read_line() archive.read_eblock() assert archive.n_elem == 0 @@ -169,6 +172,7 @@ def test_read_eblock(tmp_path: Path) -> None: fid.write(EBLOCK_STR) archive = _reader.Archive(filename) + archive.read_line() archive.read_eblock() assert archive.n_elem == 4 @@ -190,7 +194,9 @@ def test_read_keyopt(tmp_path: Path) -> None: fid.write(KEYOPT_STR) archive = _reader.Archive(filename) + archive.read_line() archive.read_keyopt_line() + archive.read_line() archive.read_keyopt_line() assert archive.keyopt[1][0] == [1, 0] assert archive.keyopt[1][1] == [2, 0] @@ -202,6 +208,7 @@ def test_read_rlblock(tmp_path: Path) -> None: fid.write(RLBLOCK_STR) archive = _reader.Archive(filename) + archive.read_line() archive.read_rlblock() assert archive.rnum == [2] # set number assert len(archive.rdat) == 1 @@ -216,10 +223,12 @@ def test_read_nblock(tmp_path: Path) -> None: fid.write(NBLOCK_STR) archive = _reader.Archive(filename, debug=True) - archive.read_nblock() + pos = archive.read_line() + archive.read_nblock(pos) archive.n_nodes == 9 assert np.array_equal(archive.nnum, NBLOCK_NODE_ID_ARR) assert np.allclose(archive.nodes, NBLOCK_POS_ARRAY) + assert archive.nblock_start == pos def test_read_nblock_incomplete(tmp_path: Path) -> None: @@ -228,7 +237,8 @@ def test_read_nblock_incomplete(tmp_path: Path) -> None: fid.write(NBLOCK_INCOMPLETE) archive = _reader.Archive(filename, debug=True) - archive.read_nblock() + pos = archive.read_line() + archive.read_nblock(pos) archive.n_nodes == 3 assert np.array_equal(archive.nnum, [1, 2, 3]) @@ -243,6 +253,7 @@ def test_read_cmblock_node(tmp_path: Path) -> None: fid.write(CMBLOCK_NODE_STR) archive = _reader.Archive(filename, debug=True) + archive.read_line() archive.read_cmblock() assert "INTERFACE" in archive.node_comps assert np.array_equal(archive.node_comps["INTERFACE"], NCOMP_INTERFACE) @@ -254,6 +265,7 @@ def test_read_cmblock_elem(tmp_path: Path) -> None: fid.write(CMBLOCK_ELEM_STR) archive = _reader.Archive(filename, debug=True) + archive.read_line() archive.read_cmblock() assert "ELMISC" in archive.elem_comps assert np.array_equal(archive.elem_comps["ELMISC"], ECOMP_ELMISC) @@ -263,8 +275,8 @@ def test_read_mesh200() -> None: filename = os.path.join(TESTFILES_PATH, "mesh200.cdb") archive = _reader.Archive(filename, debug=True) archive.read() - assert archive.n_elem == 1000 assert archive.n_nodes == 4961 + assert archive.n_elem == 1000 # spot check # 1290 0 0 4.0000000000000E-001 6.0000000000000E-001 From fb9cf68715cf1a2b9dfe3c3de01ef2bac38a4958 Mon Sep 17 00:00:00 2001 From: Alex Kaszynski Date: Sun, 21 Jul 2024 17:26:08 -0600 Subject: [PATCH 3/3] Implement memory mapping (#46) * use memory map * make cross platform * clean up compile time directives --- src/reader.cpp | 502 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 341 insertions(+), 161 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index d4de5de..938aad9 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -21,9 +20,15 @@ using namespace nb::literals; -#if defined(_WIN32) || defined(_WIN64) /* We are on Windows */ +#if defined(_WIN32) || defined(_WIN64) #define strtok_r strtok_s +#include +#else +#include +#include +#include +#include #endif // #define DEBUG @@ -166,19 +171,181 @@ static inline int ans_strtod(char *raw, int fltsz, double *arr) { return 0; } -int ReadEBlockCfile(std::ifstream &cfile, int *elem_off, int *elem, const int nelem) { - int i, j, n_node; +class MemoryMappedFile { + private: + size_t size; + char *start; +#ifdef _WIN32 + HANDLE fileHandle; + HANDLE mapHandle; +#else + int fd; +#endif - // set to start of the NBLOCK - char line[256]; // maximum of 19 values, at most 13 char per int is 228 - char *cursor = line; + public: + std::string line; + char *current; + + MemoryMappedFile(const char *filename) + : start(nullptr), current(nullptr), size(0) +#ifdef _WIN32 + , + fileHandle(INVALID_HANDLE_VALUE), mapHandle(nullptr) +#else + , + fd(-1) +#endif + { +#ifdef _WIN32 + fileHandle = CreateFile( + filename, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (fileHandle == INVALID_HANDLE_VALUE) { + throw std::runtime_error("Error opening file"); + } - if (!cfile.getline(line, sizeof(line))) { - return 0; + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(fileHandle, &fileSize)) { + CloseHandle(fileHandle); + throw std::runtime_error("Error getting file size"); + } + + size = static_cast(fileSize.QuadPart); + mapHandle = CreateFileMapping(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (mapHandle == nullptr) { + CloseHandle(fileHandle); + throw std::runtime_error("Error creating file mapping"); + } + + start = static_cast(MapViewOfFile(mapHandle, FILE_MAP_READ, 0, 0, size)); + if (start == nullptr) { + CloseHandle(mapHandle); + CloseHandle(fileHandle); + throw std::runtime_error("Error mapping file"); + } +#else + fd = open(filename, O_RDONLY); + if (fd == -1) { + throw std::runtime_error("Error opening file"); + } + + struct stat st; + if (fstat(fd, &st) == -1) { + close(fd); + throw std::runtime_error("Error getting file size"); + } + + size = st.st_size; + start = static_cast(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (start == MAP_FAILED) { + close(fd); + throw std::runtime_error("Error mapping file"); + } +#endif + current = start; + } + + ~MemoryMappedFile() { + close_file(); +#ifdef _WIN32 + if (fileHandle != INVALID_HANDLE_VALUE) { + CloseHandle(fileHandle); + } + if (mapHandle != nullptr) { + CloseHandle(mapHandle); + } +#else + if (fd != -1) { + close(fd); + } +#endif + } + + void close_file() { + if (start) { +#ifdef _WIN32 + UnmapViewOfFile(start); +#else + munmap(start, size); +#endif + start = nullptr; + current = nullptr; + } + } + + char &operator[](size_t index) { + // implement bounds checking? + // if (index >= size) { + // throw std::out_of_range("Index out of bounds"); + // } + return current[index]; + } + + void operator+=(size_t offset) { current += offset; } + + // Seek to the end of the line + void seek_eol() { + // check if at end of file + if (current >= start + size) { + // std::cout << "end" << std::endl; + return; + } + + while (current < start + size && *current != '\n') { + current++; + } + + if (current < start + size && *current == '\n') { + current++; + } + } + + bool eof() { return current >= start + size; } + + bool read_line() { + line.clear(); + if (current >= start + size) { + return false; + } + + char *line_start = current; + while (current < start + size && *current != '\n') { + line += *current++; + } + + if (current < start + size && *current == '\n') { + current++; + } + + return line_start != current; + } + + size_t current_line_length() const { + char *temp = current; + size_t length = 0; + while (temp < start + size && *temp != '\n') { + length++; + temp++; + } + return length; } - char *i_pos = strchr(line, 'i'); - char *close_paren_pos = strchr(line, ')'); + off_t tellg() const { return current - start; } +}; + +int ReadEBlockMemMap(MemoryMappedFile &memmap, int *elem_off, int *elem, const int nelem) { + int i, j, n_node; + + // set to start of the NBLOCK + // char line[256]; // maximum of 19 values, at most 13 char per int is 228 + + char *i_pos = strchr(memmap.current, 'i'); + char *close_paren_pos = strchr(memmap.current, ')'); if (i_pos == NULL || close_paren_pos == NULL || i_pos > close_paren_pos) { fprintf(stderr, "Invalid line format\n"); return 0; @@ -188,6 +355,7 @@ int ReadEBlockCfile(std::ifstream &cfile, int *elem_off, int *elem, const int ne sscanf(i_pos + 1, "%d", &isz); // Loop through elements + memmap.seek_eol(); int c = 0; for (i = 0; i < nelem; ++i) { // store start of each element @@ -195,56 +363,54 @@ int ReadEBlockCfile(std::ifstream &cfile, int *elem_off, int *elem, const int ne // Read the line and determine the number of values. MAPDL does not write all the // values on a single line - cfile.getline(line, sizeof(line)); - n_values = cfile.gcount() / isz; - - cursor = line; // is this necessary, why not just use line? + n_values = memmap.current_line_length() / isz; + // std::cout << n_values << std::endl; // It's possible that less nodes are written to the record than // indicated. In this case the line starts with a -1 // Check if at end of the block - if (checkneg(cursor, isz)) { - cursor += isz; + if (checkneg(memmap.current, isz)) { + memmap += isz; break; } // ANSYS archive format: // Field 1: material reference number - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 2: element type number - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 3: real constant reference number - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 4: section number - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 5: element coordinate system - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 6: Birth/death flag - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 7: Solid model reference - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 8: Coded shape key - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; // Field 9: Number of nodes, don't store this - n_node = fast_atoi(cursor, isz); - cursor += isz; + n_node = fast_atoi(memmap.current, isz); + memmap += isz; // /* // sanity check */ // /* if (n_node > 20){ */ @@ -254,11 +420,11 @@ int ReadEBlockCfile(std::ifstream &cfile, int *elem_off, int *elem, const int ne // /* } */ // Field 10: Not Used - cursor += isz; + memmap += isz; // Field 11: Element number - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; /* printf("reading element %d\n", elem[c - 1]); */ // Need an additional value for consistency with other formats @@ -267,19 +433,19 @@ int ReadEBlockCfile(std::ifstream &cfile, int *elem_off, int *elem, const int ne // Read the node indices in this line int n_read = n_values - 11; for (j = 0; j < n_read; j++) { - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; } // There's a second line if we've not read all the nodes + memmap.seek_eol(); if (n_read < n_node) { - cfile.getline(line, sizeof(line)); - cursor = line; - n_read = cfile.gcount() / isz; + n_read = memmap.current_line_length() / isz; for (j = 0; j < n_read; j++) { - elem[c++] = fast_atoi(cursor, isz); - cursor += isz; + elem[c++] = fast_atoi(memmap.current, isz); + memmap += isz; } + memmap.seek_eol(); } // Edge case where missing midside nodes are not written (because @@ -310,8 +476,8 @@ int write_array_ascii(const char *filename, const double *arr, const int nvalues return 0; } -int ReadNBlockCfile( - std::ifstream &cfile, +int ReadNBlockMemMap( + MemoryMappedFile &memmap, int *nnum, double *nodes, double *node_angles, @@ -320,38 +486,43 @@ int ReadNBlockCfile( const int f_size) { int i, j, i_val, eol; - char line[256]; - for (i = 0; i < nnodes; i++) { // Read a line from the file - cfile.getline(line, sizeof(line)); + int count = memmap.current_line_length(); // It's possible that less nodes are written to the record than // indicated. In this case the line starts with a -1 - if (line[0] == '-') { + if (memmap[0] == '-') { break; } - std::streamsize count = cfile.gcount(); - char *cursor = line; - - i_val = fast_atoi(cursor, d_size[0]); + i_val = fast_atoi(memmap.current, d_size[0]); #ifdef DEBUG - printf("%8d \n", i_val); + std::cout << "Node number " << i_val << std::endl; #endif nnum[i] = i_val; - cursor += d_size[0]; - cursor += d_size[1]; - cursor += d_size[2]; + memmap += d_size[0]; + memmap += d_size[1]; + memmap += d_size[2]; // read nodes int n_read = (count - d_size[0] * 3) / f_size; +#ifdef DEBUG + std::cout << "n_read: " << n_read << std::endl; +#endif int n_read_nodes = (n_read < 3) ? n_read : 3; for (j = 0; j < n_read_nodes; j++) { - ans_strtod(cursor, f_size, &nodes[3 * i + j]); - cursor += f_size; + ans_strtod(memmap.current, f_size, &nodes[3 * i + j]); +#ifdef DEBUG + std::cout << " " << nodes[3 * i + j] << " "; +#endif + memmap.current += f_size; } +#ifdef DEBUG + std::cout << std::endl; +#endif + // fill in unread nodes with zeros for (; j < 3; j++) { nodes[3 * i + j] = 0; @@ -359,24 +530,19 @@ int ReadNBlockCfile( // read in node angles if applicable for (; j < n_read; j++) { - eol = ans_strtod(cursor, f_size, &node_angles[3 * i + (j - 3)]); + eol = ans_strtod(memmap.current, f_size, &node_angles[3 * i + (j - 3)]); if (eol) break; - cursor += f_size; + memmap += f_size; } + + // seek to the end of the line + memmap.seek_eol(); } return i; } -int safe_int(const std::string &value) { - try { - return std::stoi(value); - } catch (...) { - return -1; - } -} - int getSizeOfEBLOCK(const std::string &line) { size_t lastComma = line.rfind(','); if (lastComma == std::string::npos) { @@ -490,9 +656,10 @@ NDArray InterpretComponent(const std::vector &component) { class Archive { private: - std::string line; + // std::string line; bool debug; std::string filename; + MemoryMappedFile memmap; public: bool read_parameters; @@ -500,7 +667,6 @@ class Archive { bool read_eblock; bool eblock_is_read = false; bool nblock_is_read = false; - std::ifstream cfile; std::vector> elem_type; std::unordered_map>> keyopt; @@ -532,16 +698,12 @@ class Archive { bool dbg = false, bool readEblock = true) : filename(fname), read_parameters(readParams), debug(dbg), read_eblock(readEblock), - cfile(fname) { + memmap(fname.c_str()) { // Likely bogus leak warnings. See: // https://nanobind.readthedocs.io/en/latest/faq.html#why-am-i-getting-errors-about-leaked-functions-and-types nb::set_leak_warnings(false); - if (!cfile.is_open()) { - throw std::runtime_error("No such file or directory: '" + filename + "'"); - } - if (read_parameters) { throw std::runtime_error("Read parameters has been deprecated"); } @@ -554,7 +716,8 @@ class Archive { std::cout << "reading ET" << std::endl; } - std::istringstream iss(line); + // Assumes that the memory map is already at the ET line + std::istringstream iss(memmap.line); std::string token; std::vector et_vals; @@ -582,7 +745,7 @@ class Archive { std::vector values; // Assumes current line is an ETBLOCK - std::istringstream iss(line); + std::istringstream iss(memmap.line); // skip first item (ETBLOCK) std::getline(iss, token, ','); @@ -598,19 +761,21 @@ class Archive { } // Skip format line (2i9,19a9) - std::getline(cfile, line); + memmap.seek_eol(); // Read each item int elem_id, elem_type_id; for (int i = 0; i < n_items; i++) { - std::getline(cfile, line); + // std::getline(cfile, line); + memmap.read_line(); iss.clear(); - iss.str(line); + iss.str(memmap.line); // We only care about the first two integers in the block, the rest are keyopts if (!(iss >> elem_id >> elem_type_id)) { // allow a soft return - std::cerr << "Failed to read ETBLOCK entry at line: " << line << std::endl; + std::cerr << "Failed to read ETBLOCK entry at line: " << memmap.line + << std::endl; return; } std::vector entry; @@ -628,24 +793,25 @@ class Archive { } // Assumes already start of EBLOCK - std::istringstream iss(line); + std::istringstream iss(memmap.line); // Only read in SOLID eblocks - std::transform(line.begin(), line.end(), line.begin(), [](unsigned char c) { - return std::tolower(c); - }); - if (line.find("solid") == std::string::npos) { + std::transform( + memmap.line.begin(), memmap.line.end(), memmap.line.begin(), [](unsigned char c) { + return std::tolower(c); + }); + if (memmap.line.find("solid") == std::string::npos) { return; } // Get size of EBLOCK from the last item in the line // Example: "EBLOCK,19,SOLID,,3588" - n_elem = getSizeOfEBLOCK(line); + n_elem = getSizeOfEBLOCK(memmap.line); // we have to allocate memory for the maximum size since we don't know that a priori int *elem_data = AllocateArray(n_elem * 30); elem_off_arr = MakeNDArray({n_elem + 1}); - int elem_sz = ReadEBlockCfile(cfile, elem_off_arr.data(), elem_data, n_elem); + int elem_sz = ReadEBlockMemMap(memmap, elem_off_arr.data(), elem_data, n_elem); // wrap the raw data but limit it to the size of the number of elements read // Note: This is faster than reallocating a new array but will use more memory @@ -663,7 +829,7 @@ class Archive { } // Assumes at KEYOPT line - std::istringstream iss(line); + std::istringstream iss(memmap.line); std::string token; // Skip the first token (KEYOPT) @@ -676,7 +842,8 @@ class Archive { etype = std::stoi(token); } catch (const std::invalid_argument &) { // soft error - std::cerr << "Invalid format in KEYOPT line for elem_num:" << line << std::endl; + std::cerr << "Invalid format in KEYOPT line for elem_num:" << memmap.line + << std::endl; return; } @@ -687,7 +854,7 @@ class Archive { keyopt_vals.push_back(std::stoi(token)); } catch (const std::invalid_argument &) { // soft error - std::cerr << "Invalid format in KEYOPT line:" << line << std::endl; + std::cerr << "Invalid format in KEYOPT line:" << memmap.line << std::endl; return; } } @@ -706,7 +873,7 @@ class Archive { std::vector set_dat; // Assumes line at RLBLOCK - std::istringstream iss(line); + std::istringstream iss(memmap.line); std::string token; // while (std::getline(iss, token, ',')) { // set_dat.push_back(std::stoi(token)); @@ -720,71 +887,74 @@ class Archive { std::getline(iss, token, ','); nset = std::stoi(token); } catch (...) { - std::cerr << "Error RLBLOCK line when reading nset:" << line << std::endl; + std::cerr << "Error RLBLOCK line when reading nset:" << memmap.line << std::endl; return; } try { std::getline(iss, token, ','); maxset = std::stoi(token); } catch (...) { - std::cerr << "Error RLBLOCK line when reading maxset:" << line << std::endl; + std::cerr << "Error RLBLOCK line when reading maxset:" << memmap.line + << std::endl; return; } try { std::getline(iss, token, ','); maxitems = std::stoi(token); } catch (...) { - std::cerr << "Error RLBLOCK line when reading maxitems:" << line << std::endl; + std::cerr << "Error RLBLOCK line when reading maxitems:" << memmap.line + << std::endl; return; } try { std::getline(iss, token, ','); nperline = std::stoi(token); } catch (...) { - std::cerr << "Error RLBLOCK line when reading nperline:" << line << std::endl; + std::cerr << "Error RLBLOCK line when reading nperline:" << memmap.line + << std::endl; return; } // Skip next two lines - std::getline(cfile, line); // (2i8,6g16.9) - std::getline(cfile, line); // (7g16.9) + memmap.seek_eol(); // (2i8,6g16.9) + memmap.seek_eol(); // (7g16.9) for (int i = 0; i < nset; i++) { // read real constant data in the form of: // 2 6 1.00000000 0.566900000E-07 0.00000000 0.000... - std::getline(cfile, line); + memmap.read_line(); std::vector rcon; // real constant number try { - rnum.push_back(std::stoi(line.substr(0, 8))); + rnum.push_back(std::stoi(memmap.line.substr(0, 8))); } catch (...) { - std::cerr << "Failed to read real constant ID when reading:" << line + std::cerr << "Failed to read real constant ID when reading:" << memmap.line << std::endl; return; } - int ncon = std::stoi(line.substr(8, 8)); + int ncon = std::stoi(memmap.line.substr(8, 8)); if (ncon > 6) { for (int j = 0; j < 6; j++) { - rcon.push_back(std::stod(line.substr(16 + 16 * j, 16))); + rcon.push_back(std::stod(memmap.line.substr(16 + 16 * j, 16))); ncon--; } - std::getline(cfile, line); + memmap.read_line(); while (ncon > 0) { for (int j = 0; j < 7 && ncon > 0; j++) { - rcon.push_back(std::stod(line.substr(16 * j, 16))); + rcon.push_back(std::stod(memmap.line.substr(16 * j, 16))); ncon--; } if (ncon > 0) { - std::getline(cfile, line); + memmap.read_line(); } } } else { for (int j = 0; j < ncon; j++) { - rcon.push_back(std::stod(line.substr(16 + 16 * j, 16))); + rcon.push_back(std::stod(memmap.line.substr(16 + 16 * j, 16))); } } @@ -802,14 +972,19 @@ class Archive { // Get size of NBLOCK // Assumes line is at NBLOCK - // std::cout << "line: " << line << std::endl; + // std::cout << "line: " << memmap.line << std::endl; try { // Number of nodes is last item in string - n_nodes = std::stoi(line.substr(line.rfind(',') + 1)); + n_nodes = std::stoi(memmap.line.substr(memmap.line.rfind(',') + 1)); } catch (...) { - std::cerr << "Failed to read number of nodes when reading:" << line << std::endl; + std::cerr << "Failed to read number of nodes when reading:" << memmap.line + << std::endl; return; } + if (debug) { + std::cout << "Reading " << n_nodes << " nodes" << std::endl; + } + int *nnum_data = AllocateArray(n_nodes); double *nodes_data = AllocateArray(n_nodes * 3); @@ -818,15 +993,13 @@ class Archive { double *node_angles_data = AllocateArray(n_nodes * 3, true); // Get format of nblock - std::getline(cfile, line); - NodeBlockFormat nfmt = GetNodeBlockFormat(line); - if (debug) { - std::cout << "Reading " << n_nodes << " nodes" << std::endl; - } + memmap.read_line(); + NodeBlockFormat nfmt = GetNodeBlockFormat(memmap.line); // Return actual number of nodes read and wrap the raw data - n_nodes = ReadNBlockCfile( - cfile, + // std::cout << memmap.tellg() << std::endl; + n_nodes = ReadNBlockMemMap( + memmap, nnum_data, nodes_data, node_angles_data, @@ -836,17 +1009,18 @@ class Archive { nnum_arr = WrapNDarray(nnum_data, {n_nodes}); nodes_arr = WrapNDarray(nodes_data, {n_nodes, 3}); node_angles_arr = WrapNDarray(node_angles_data, {n_nodes, 3}); + // std::cout << memmap.tellg() << std::endl; // Read final line, this is always "N,R5.3,LOC, -1," and store file // position. This is used for later access (or rewrite) of the node // block. - std::getline(cfile, line); - nblock_end = cfile.tellg(); + memmap.seek_eol(); + nblock_end = memmap.tellg(); // Must mark nblock is read since we can only read one nblock per archive file. nblock_is_read = true; if (debug) { - std::cout << "Last line is: " << line << std::endl; + // std::cout << "Last line is: " << memmap.line << std::endl; std::cout << "NBLOCK complete, read " << n_nodes << " nodes." << std::endl; } } @@ -858,7 +1032,7 @@ class Archive { } // Assumes line at CMBLOCK - std::istringstream iss(line); + std::istringstream iss(memmap.line); std::string token; std::vector split_line; @@ -867,7 +1041,7 @@ class Archive { } if (split_line.size() < 4) { - std::cerr << "Poor formatting of CMBLOCK: " << line << std::endl; + std::cerr << "Poor formatting of CMBLOCK: " << memmap.line << std::endl; return; } @@ -875,15 +1049,16 @@ class Archive { try { ncomp = std::stoi(split_line[3].substr(0, split_line[3].find('!')).c_str()); } catch (...) { - std::cerr << "Poor formatting of CMBLOCK: " << line << std::endl; + std::cerr << "Poor formatting of CMBLOCK: " << memmap.line << std::endl; return; } - std::getline(cfile, line); + memmap.read_line(); int isz; try { - isz = std::stoi( - line.substr(line.find('i') + 1, line.find(')') - line.find('i') - 1)); + isz = std::stoi(memmap.line.substr( + memmap.line.find('i') + 1, + memmap.line.find(')') - memmap.line.find('i') - 1)); } catch (...) { std::cerr << "Failed to read integer size in CMBLOCK" << std::endl; return; @@ -891,8 +1066,9 @@ class Archive { int nblock; try { - nblock = std::stoi( - line.substr(line.find('(') + 1, line.find('i') - line.find('(') - 1)); + nblock = std::stoi(memmap.line.substr( + memmap.line.find('(') + 1, + memmap.line.find('i') - memmap.line.find('(') - 1)); } catch (...) { std::cerr << "Failed to read number of integers per line in CMBLOCK" << std::endl; return; @@ -903,12 +1079,12 @@ class Archive { for (int i = 0; i < ncomp; ++i) { if (i % nblock == 0) { - std::getline(cfile, line); + memmap.read_line(); } try { - component[i] = std::stoi(line.substr(isz * (i % nblock), isz)); + component[i] = std::stoi(memmap.line.substr(isz * (i % nblock), isz)); } catch (...) { - std::cerr << "Failed to parse integer from CMBLOCK line: " << line + std::cerr << "Failed to parse integer from CMBLOCK line: " << memmap.line << std::endl; return; } @@ -939,18 +1115,20 @@ class Archive { // Parse based on the first character rather than reading the entire // line. It's faster and the parsing logic is always based on the first // character - first_char = cfile.peek(); -#ifdef DEBUG - std::cout << "Read character: " << static_cast(first_char) << std::endl; -#endif - if (cfile.eof()) { + + if (memmap.eof()) { #ifdef DEBUG std::cout << "Reached EOF" << std::endl; #endif break; + } - } else if (first_char == 'E' || first_char == 'e') { - std::getline(cfile, line); + first_char = memmap[0]; +#ifdef DEBUG + std::cout << "Read character: " << static_cast(first_char) << std::endl; +#endif + if (first_char == 'E' || first_char == 'e') { + memmap.read_line(); // E commands (ET or ETBLOCK) @@ -959,43 +1137,47 @@ class Archive { } // Record element type - if (line.compare(0, 3, "ET,") == 0 || line.compare(0, 3, "et,") == 0) { + if (memmap.line.compare(0, 3, "ET,") == 0 || + memmap.line.compare(0, 3, "et,") == 0) { ReadETLine(); } else if ( - line.compare(0, 6, "ETBLOC") == 0 || line.compare(0, 6, "etbloc") == 0) { + memmap.line.compare(0, 6, "ETBLOC") == 0 || + memmap.line.compare(0, 6, "etbloc") == 0) { ReadETBlock(); } else if ( - line.compare(0, 6, "EBLOCK") == 0 || - line.compare(0, 6, "eblock") == 0 && read_eblock) { + memmap.line.compare(0, 6, "EBLOCK") == 0 || + memmap.line.compare(0, 6, "eblock") == 0 && read_eblock) { ReadEBlock(); } } else if (first_char == 'K' || first_char == 'k') { - std::getline(cfile, line); + memmap.read_line(); if (debug) { std::cout << "Read K" << std::endl; } // Record keyopt - if (line.compare(0, 5, "KEYOP") == 0 || line.compare(0, 5, "keyop") == 0) { + if (memmap.line.compare(0, 5, "KEYOP") == 0 || + memmap.line.compare(0, 5, "keyop") == 0) { ReadKEYOPTLine(); } } else if (first_char == 'R' || first_char == 'r') { - std::getline(cfile, line); + memmap.read_line(); // test for RLBLOCK if (debug) { std::cout << "Read R" << std::endl; } // Record keyopt - if (line.compare(0, 5, "RLBLO") == 0 || line.compare(0, 5, "rlblo") == 0) { + if (memmap.line.compare(0, 5, "RLBLO") == 0 || + memmap.line.compare(0, 5, "rlblo") == 0) { ReadRLBLOCK(); } } else if (first_char == 'N' || first_char == 'n') { // store current position if we read in the node block - const int pos = cfile.tellg(); - std::getline(cfile, line); + const int pos = memmap.tellg(); + memmap.read_line(); // test for NBLOCK if (debug) { @@ -1003,19 +1185,21 @@ class Archive { } // Record node block - if (line.compare(0, 5, "NBLOC") == 0 || line.compare(0, 5, "nbloc") == 0) { + if (memmap.line.compare(0, 5, "NBLOC") == 0 || + memmap.line.compare(0, 5, "nbloc") == 0) { ReadNBlock(pos); } } else if (first_char == 'C' || first_char == 'c') { - std::getline(cfile, line); + memmap.read_line(); if (debug) { std::cout << "Read C" << std::endl; } // Record component block - if (line.compare(0, 5, "CMBLO") == 0 || line.compare(0, 5, "cmblo") == 0) { + if (memmap.line.compare(0, 5, "CMBLO") == 0 || + memmap.line.compare(0, 5, "cmblo") == 0) { // std::cout << "Reading CMBLOCK" << std::endl; ReadCMBlock(); } @@ -1024,17 +1208,13 @@ class Archive { std::cout << "No match, continuing..." << std::endl; } // Skip remainder of the line - cfile.ignore(std::numeric_limits::max(), '\n'); + memmap.seek_eol(); } } } // Read line and return the position prior to reading the line - int ReadLine() { - int pos = cfile.tellg(); - std::getline(cfile, line); - return pos; - } + int ReadLine() { return memmap.read_line(); } // Convert ansys style connectivity to VTK connectivity // type_ref is a mapping between ansys element types and VTK element types @@ -1060,7 +1240,7 @@ class Archive { return nb::make_tuple(offset_arr, celltypes_arr, cells_arr); } - ~Archive() { cfile.close(); } + ~Archive() { memmap.close_file(); } }; // Archive