11import os
22import json
3- from typing import Optional
3+ from typing import Optional , Union
44
55import numpy as np
66from scipy .spatial import cKDTree
1414DMN_FORMAT = "svv.domain/1.0"
1515
1616
17- def _ensure_ext (path : str ) -> str :
18- if not path .lower ().endswith (".dmn" ):
19- return path + ".dmn"
20- return path
17+ def ensure_dmn_path (path : Union [str , os .PathLike ]) -> str :
18+ """
19+ Normalize an output path for .dmn persistence.
20+
21+ - Accepts ``str`` or ``PathLike`` objects.
22+ - Ensures the returned path ends with a lowercase ``.dmn`` extension.
23+ - Collapses legacy ``.dmn.npz`` filenames (from NumPy ``savez`` behavior)
24+ to ``.dmn``.
25+ """
26+ path_str = os .fsdecode (os .fspath (path ))
27+ lower = path_str .lower ()
28+
29+ # Legacy: np.savez_compressed("name.dmn", ...) writes "name.dmn.npz"
30+ if lower .endswith (".npz" ) and lower [:- 4 ].endswith (".dmn" ):
31+ path_str = path_str [:- 4 ]
32+ lower = lower [:- 4 ]
33+
34+ if lower .endswith (".dmn" ):
35+ return path_str [:- 4 ] + ".dmn"
36+ return path_str + ".dmn"
37+
38+
39+ def _ensure_ext (path : Union [str , os .PathLike ]) -> str :
40+ return ensure_dmn_path (path )
2141
2242
2343def _compute_firsts_from_pts (PTS : np .ndarray ) -> np .ndarray :
@@ -39,8 +59,8 @@ def _compute_firsts_from_pts(PTS: np.ndarray) -> np.ndarray:
3959 return firsts
4060
4161
42- def write_dmn (domain , path : str , include_boundary : bool = False , include_mesh : bool = False ,
43- include_patch_normals : bool = True ) -> None :
62+ def write_dmn (domain , path : Union [ str , os . PathLike ] , include_boundary : bool = False , include_mesh : bool = False ,
63+ include_patch_normals : bool = True ) -> str :
4464 """
4565 Serialize a Domain instance into a .dmn file.
4666
@@ -188,9 +208,33 @@ def write_dmn(domain, path: str, include_boundary: bool = False, include_mesh: b
188208 # Use a file handle to avoid NumPy forcing a .npz extension
189209 with open (out_path , "wb" ) as fh :
190210 np .savez_compressed (fh , ** arrays )
211+ return out_path
212+
213+
214+ def resolve_dmn_read_path (path : Union [str , os .PathLike ]) -> str :
215+ """
216+ Resolve a path for reading a Domain from disk.
217+
218+ Accepts filenames both with and without the ``.dmn`` suffix. Also attempts
219+ to load legacy ``.dmn.npz`` files created by passing a ``.dmn`` filename
220+ directly to NumPy ``savez`` routines.
221+ """
222+ path_str = os .fsdecode (os .fspath (path ))
223+ if os .path .isfile (path_str ):
224+ return path_str
225+
226+ candidate = ensure_dmn_path (path_str )
227+ if os .path .isfile (candidate ):
228+ return candidate
229+
230+ legacy_candidate = candidate + ".npz"
231+ if os .path .isfile (legacy_candidate ):
232+ return legacy_candidate
233+
234+ return candidate
191235
192236
193- def read_dmn (path : str ):
237+ def read_dmn (path : Union [ str , os . PathLike ] ):
194238 """
195239 Deserialize a Domain from a .dmn file.
196240
@@ -213,7 +257,7 @@ def read_dmn(path: str):
213257 from svv .domain .domain import Domain
214258 from svv .domain .patch import Patch
215259
216- file_path = _ensure_ext (path )
260+ file_path = resolve_dmn_read_path (path )
217261 data = np .load (file_path , allow_pickle = False )
218262
219263 # Parse metadata
0 commit comments