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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions