Skip to content

visualdynamics.io.adf

adf

I-DEAS Associated Data Files — .afu, .ati and .ash, read natively.

ADF is the binary sibling of the Universal File's dataset 58: the same function types, the same data-type codes, the same interleaved-uneven convention, in 512-byte blocks of little-endian float32. Shape files add a file-wide node table and per-mode records. The formats originate with SDRC I-DEAS and live on in Siemens NX. No public specification exists, so this reader was derived from paired sample files and from published documentation — never from anyone's source code.

Two facts a reader of the data should know, both established against those samples: ADF files store SI regardless of the writing session's unit system (round-tripped through all nine unit systems the format admits, with and without the G's flag), so imports arrive with units defined; and the container stores float32 only — data written as double precision was narrowed by the writer, and no reader can get it back.

The share with unv.py is deliberate: parsed function records become the same entry dicts UNV's dataset-58 parser produces and are grouped by _build_data_objects with SI factors, so an .afu and the equivalent .unv import through one code path and cannot disagree.

Functions:

Name Description
load

Read an ADF; functions and time histories group exactly like a

save

Write an ADF. Values go out in SI, which is what the format

Functions:

load

load(path: str | PathLike) -> Any

Read an ADF; functions and time histories group exactly like a universal file's dataset 58s, shapes become a ShapeSet.

Source code in src/visualdynamics/io/adf.py
def load(path: str | os.PathLike) -> Any:
    """Read an ADF; functions and time histories group exactly like a
    universal file's dataset 58s, shapes become a `ShapeSet`."""
    path = str(path)
    with open(path, 'rb') as f:
        raw = f.read()
    if raw[:4] != b'\xff\xff\xff\xff' or len(raw) % BLOCK:
        raise ValueError(f'{path} is not an Associated Data File')
    entries = _directory(raw)
    if not entries:
        raise ValueError(f'{path}: no records in the ADF directory')
    if any(e['record'] == NODE_TABLE for e in entries):
        return _load_shapes(raw, entries, path)
    return _load_functions(raw, entries, path)

save

save(obj, path, unit_system=None)

Write an ADF. Values go out in SI, which is what the format stores by definition — and in float32, which is all it has.

Source code in src/visualdynamics/io/adf.py
def save(obj, path, unit_system=None):
    """Write an ADF. Values go out in SI, which is what the format
    stores by definition — and in float32, which is all it has."""
    from ..core.shapes import ShapeSet

    path = str(path)
    extension = os.path.splitext(path)[1].lower()
    if isinstance(obj, ShapeSet):
        if extension != '.ash':
            raise ValueError('shapes write to .ash')
        return _save_shapes(obj, path)
    kind = FILE_KIND.get(extension)
    if kind is None:
        raise ValueError(f'{extension} is not an ADF extension')
    records = [_function_record(obj, k, k + 1)
               for k in range(obj.num_records)]
    _assemble(path, kind, records)