Read every block of a PICGRID file as node-coordinate arrays.
For consumers that build cell geometry, so each axis needs at least two nodes.
124def read_picgrid_blocks(path: str, expected_nblk: int = None) -> list:
125 """!
126 @brief Read every block of a PICGRID file as node-coordinate arrays.
127 @details For consumers that build cell geometry, so each axis needs at least two nodes.
128 @param[in] path PICGRID path.
129 @param[in] expected_nblk Optional block count the caller requires.
130 @return List of `{"dims": (IM, JM, KM), "coords": array shaped (KM, JM, IM, 3)}`.
131 @throws ValueError on a missing or malformed file.
132 """
133 from periodic_spectral import require_numpy
134
135 numpy = require_numpy()
136 if not os.path.isfile(path):
137 raise ValueError(f"Grid file not found: {path}")
138 with open(path, "r", encoding="utf-8") as stream:
139 lines = content_lines(stream)
140 dims = read_picgrid_header(lines, path, expected_nblk)
141 blocks = []
142 for block_idx, (im, jm, km) in enumerate(dims):
143 if min(im, jm, km) < 2:
144 raise ValueError(f"PICGRID block {block_idx} dimensions {(im, jm, km)} are too small.")
145 coords = []
146 for lineno, line in lines:
147 coords.append(parse_coordinate_row(lineno, line, path))
148 if len(coords) == im * jm * km:
149 break
150 if len(coords) != im * jm * km:
151 raise ValueError(f"Grid file '{path}' missing coordinates for block {block_idx}.")
152 blocks.append({"dims": (im, jm, km),
153 "coords": numpy.asarray(coords, dtype=float).reshape((km, jm, im, 3))})
154 if next(lines, None) is not None:
155 raise ValueError(f"Grid file '{path}' has trailing coordinate rows.")
156 return blocks