Source code for upxo.interfaces.io.expop

"""
Multi-format export of NumPy arrays for UPXO (``ArrExp2d``).

``ArrExp2d.save(data, filename, ...)`` dispatches on file extension:

* ``.npy`` / ``.npz`` — NumPy (optional compression)
* ``.csv`` / ``.txt`` — text
* ``.xlsx`` — Excel via pandas
* ``.mat`` — SciPy (optional)
* ``.h5`` / ``.hdf5`` — h5py if installed
* ``.vtk`` / ``.vti`` — PyVista ImageData
* image formats — matplotlib visual or Pillow raw

Used by surrogate batch jobs and ad-hoc array I/O. Not a grain-structure
object serializer (use CTF/VTK GS methods and FE exporters for those).
"""

import numpy as np
import os

[docs] class ArrExp2d: """ Multi-format exporter for NumPy arrays (static helpers). ``ArrExp2d.save(data, filename, compression=False, **kwargs)`` chooses a handler from the file extension (``.npy``, ``.npz``, ``.csv``, ``.xlsx``, ``.mat``, ``.h5``/``.hdf5``, ``.vtk``/``.vti``, common image formats). Used by surrogate batch jobs and ad-hoc array I/O — not a full grain-structure serializer. Example ------- >>> from upxo.interfaces.io.expop import ArrExp2d as arex2 >>> arex2.save(label_field, 'slice_0001.npy') """
[docs] @staticmethod def save(data: np.ndarray, filename: str, compression=False, **kwargs): """ Main entry point. Detects extension and dispatches to the correct handler. """ _, ext = os.path.splitext(filename) ext = ext.lower() dispatch_map = { '.npy': ArrExp2d._save_npy, '.npz': ArrExp2d._save_npz, '.csv': ArrExp2d._save_csv, '.txt': ArrExp2d._save_csv, '.mat': ArrExp2d._save_mat, '.h5': ArrExp2d._save_hdf5, '.hdf5': ArrExp2d._save_hdf5, '.xlsx': ArrExp2d._save_excel, '.vtk': ArrExp2d._save_vtk, '.vti': ArrExp2d._save_vtk, '.png': ArrExp2d._save_image, '.jpg': ArrExp2d._save_image, '.jpeg': ArrExp2d._save_image, '.bmp': ArrExp2d._save_image, '.tiff': ArrExp2d._save_image, '.tif': ArrExp2d._save_image, } if ext not in dispatch_map: raise ValueError(f"Unsupported file format: {ext}") print(f"Exporting {data.shape} array to {filename}...") dispatch_map[ext](data, filename, compression=compression, **kwargs)
@staticmethod def _save_npy(data, filename, compression=False, **kwargs): if compression: np.savez_compressed(filename, data) else: np.save(filename, data) @staticmethod def _save_npz(data, filename, compression=False, **kwargs): if compression: np.savez_compressed(filename, data) else: np.savez(filename, data) @staticmethod def _save_npz_compressed_(data, filename, compression=False, **kwargs): np.savez_compressed(filename, data) @staticmethod def _save_csv(data, filename, compression=False, **kwargs): delimiter = kwargs.get('delimiter', ',') fmt = kwargs.get('fmt', '%.18e') np.savetxt(filename, data, delimiter=delimiter, fmt=fmt) @staticmethod def _save_mat(data, filename, compression=False, **kwargs): try: from scipy.io import savemat except ImportError: raise ImportError("Saving to .mat requires scipy. Run `pip install scipy`") var_name = kwargs.get('var_name', 'data') savemat(filename, {var_name: data}) @staticmethod def _save_hdf5(data, filename, compression=False, **kwargs): try: import h5py except ImportError: raise ImportError("Saving to HDF5 requires h5py. Run `pip install h5py`") dataset_name = kwargs.get('dataset_name', 'dataset_1') mode = kwargs.get('mode', 'w') with h5py.File(filename, mode) as f: f.create_dataset(dataset_name, data=data) @staticmethod def _save_excel(data, filename, **kwargs): try: import pandas as pd except ImportError: raise ImportError("Saving to Excel requires pandas. Run `pip install pandas`") df = pd.DataFrame(data) df.to_excel(filename, index=False, header=False) @staticmethod def _save_vtk(data, filename, **kwargs): try: import pyvista as pv except ImportError: raise ImportError("Saving to VTK requires pyvista. Run `pip install pyvista`") rows, cols = data.shape spacing = kwargs.get('spacing', (1, 1, 1)) origin = kwargs.get('origin', (0, 0, 0)) grid = pv.ImageData(dimensions=(rows, cols, 1), spacing=spacing, origin=origin) grid.point_data["values"] = data.flatten(order='F') grid.save(filename) @staticmethod def _save_image(data, filename, **kwargs): """ Saves as a common image format. kwargs: mode (str): 'visual' (default) or 'raw'. cmap (str): Colormap for visual mode (e.g., 'viridis', 'gray', 'inferno'). """ mode = kwargs.get('mode', 'visual') if mode == 'visual': # Option A: Presentations, quick checks, saving floats as images import matplotlib.pyplot as plt # origin='lower' puts (0,0) at bottom-left (scientific), # default is often top-left (image standard) origin = kwargs.get('origin', 'upper') plt.imsave(filename, data, cmap=kwargs.get('cmap', 'viridis'), origin=origin, vmin=kwargs.get('vmin', np.min(data)), vmax=kwargs.get('vmax', np.max(data)) ) elif mode == 'raw': # Option B: Raw Pixel Mapping (Uses PIL). Use for Masks, Computer Vision input, # Heightmaps try: from PIL import Image except ImportError: raise ImportError("Raw image export requires Pillow (pip install pillow).") # Warning: PIL expects uint8 (0-255) for standard formats # or uint16 (0-65535) for specific TIFF modes. if data.dtype != np.uint8 and not filename.endswith(('tif', 'tiff')): print("Warning: Converting non-uint8 data to image may result in clipping.") img_data = data.astype(np.uint8) else: img_data = data img = Image.fromarray(img_data) img.save(filename)