Skip to content

from_ase stores ASE custom arrays as molecule-scope properties, causing schema mismatches for variable atom counts #42

Description

@Ramlaoui

Summary

atompack.from_ase currently appears to store ASE custom arrays as molecule-scope custom properties. This causes schema mismatches when writing multiple structures with different atom counts into one .atp file.

ASE arrays such as atoms.arrays["tags"] or atoms.arrays["fixed_mask"] have per-atom semantics: their first dimension is n_atoms. I would expect these to become AtomPack atom-scoped properties, or for from_ase to expose a way to preserve or specify that scope.

Instead, they are stored as molecule-scope properties with a fixed slot size. When the next molecule has a different atom count, writing fails.

Minimal Repro

from pathlib import Path
import tempfile

import atompack
import numpy as np
from ase import Atoms

path = Path(tempfile.mkdtemp()) / "variable_atom_array.atp"


def make(n):
    atoms = Atoms(
        numbers=[1] * n,
        positions=np.zeros((n, 3)),
        cell=np.eye(3),
        pbc=True,
    )
    atoms.arrays["fixed_mask"] = np.zeros(n, dtype=np.int64)
    return atompack.from_ase(atoms)


db = atompack.Database(str(path))
db.add_molecules([make(3), make(4)])
db.flush()

Actual Behavior

This fails with a schema mismatch similar to:

ValueError: Invalid data: Schema mismatch for section fixed_mask:
expected SchemaEntry { ..., per_atom: false, elem_bytes: 8, slot_bytes: 24 },
got SchemaEntry { ..., per_atom: false, elem_bytes: 8, slot_bytes: 32 }

The important bit is per_atom: false.

Expected Behavior

Since fixed_mask comes from atoms.arrays and has shape (n_atoms,), it should either:

  1. be stored as an atom-scoped property,
  2. provide a from_ase argument to declare custom array scope, or
  3. document clearly that from_ase(copy_arrays=True) stores all custom arrays as molecule-scope properties.

Workaround

This works:

from pathlib import Path
import tempfile

import atompack
import numpy as np
from ase import Atoms

path = Path(tempfile.mkdtemp()) / "variable_atom_array_workaround.atp"


def make(n):
    atoms = Atoms(
        numbers=[1] * n,
        positions=np.zeros((n, 3)),
        cell=np.eye(3),
        pbc=True,
    )

    mol = atompack.from_ase(
        atoms,
        copy_arrays=False,
        copy_info=False,
    )
    mol.set_property(
        "fixed_mask",
        np.zeros(n, dtype=np.int32),
        scope="atom",
    )
    return mol


db = atompack.Database(str(path))
db.add_molecules([make(3), make(4)])
db.flush()

Note: scope="atom" works, but the dtype must be one of the supported numeric dtypes. bool and int8 fail; int32, int64, float32, and float64 work.

Environment

Observed with atompack-db==0.4.0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions