Skip to content

Handle numpy array for selective dynamics in Structure#4461

Merged
shyuep merged 14 commits intomaterialsproject:masterfrom
DanielYang59:fix-selective-dynamics-as-np-array
Jul 29, 2025
Merged

Handle numpy array for selective dynamics in Structure#4461
shyuep merged 14 commits intomaterialsproject:masterfrom
DanielYang59:fix-selective-dynamics-as-np-array

Conversation

@DanielYang59
Copy link
Contributor

@DanielYang59 DanielYang59 commented Jul 22, 2025

Summary

@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from 3e6f369 to 5943661 Compare July 22, 2025 15:34
dim = value.shape
if dim[1] != 3 or dim[0] != len(self.structure):
raise ValueError(f"{name} array must be same length as the structure.")
value = value.tolist()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this tolist is not necessary as value for both selective_dynamics and velocities are converted to numpy array if not via __setattr__?

if selective_dynamics is not None:
selective_dynamics = np.array(selective_dynamics)
if not selective_dynamics.all():
site_properties["selective_dynamics"] = selective_dynamics
if velocities:
velocities = np.array(velocities)
if velocities.any():
site_properties["velocities"] = velocities

@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from 8847f18 to 9065b33 Compare July 22, 2025 18:29
@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from 398c9ad to 8015819 Compare July 22, 2025 18:45
@DanielYang59 DanielYang59 marked this pull request as ready for review July 22, 2025 19:36
@DanielYang59 DanielYang59 marked this pull request as draft July 22, 2025 20:50
@shyuep
Copy link
Member

shyuep commented Jul 26, 2025

Looks good to me.

@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from 8015819 to 53de5c2 Compare July 26, 2025 08:38
@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from 53de5c2 to 3719026 Compare July 26, 2025 08:39

elif fmt == "json" or fnmatch(filename, "*.json*") or fnmatch(filename, "*.mson*"):
json_str = orjson.dumps(self.as_dict()).decode()
json_str = orjson.dumps(self.as_dict(), option=orjson.OPT_SERIALIZE_NUMPY).decode()
Copy link
Contributor Author

@DanielYang59 DanielYang59 Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For IMolecule, I'm a bit unsure if there's any case where a property need to be numpy array, but now that we allow arbitrary properties, I guess it's safer to turn on OPT_SERIALIZE_NUMPY by default:

def as_dict(self) -> dict:
"""JSON-serializable dict representation of Molecule."""
dct = {
"@module": type(self).__module__,
"@class": type(self).__name__,
"charge": self.charge,
"spin_multiplicity": self.spin_multiplicity,
"sites": [],
"properties": self.properties,
}

Also as far as I could tell there's no significant performance penalty (to turn it on when there's no numpy array) unlike the case for OPT_SORT_KEYS:

import timeit
import orjson
from pymatgen.core.structure import Structure

# Create a small structure (no NumPy arrays)
structure = Structure(
    lattice=[[10, 0, 0], [0, 10, 0], [0, 0, 10]],
    species=["H", "O"],
    coords=[[0, 0, 0], [0.5, 0.5, 0.5]],
    site_properties={"selective_dynamics": [[True, True, True], [False, False, False]]}
)

structure_dict = structure.as_dict()
N = 10_000

plain_time = timeit.timeit(
    stmt='orjson.dumps(structure_dict).decode()',
    globals={"orjson": orjson, "structure_dict": structure_dict},
    number=N
)

numpy_time = timeit.timeit(
    stmt='orjson.dumps(structure_dict, option=orjson.OPT_SERIALIZE_NUMPY).decode()',
    globals={"orjson": orjson, "structure_dict": structure_dict},
    number=N
)

print(f"orjson (plain):      {plain_time * 1000:.3f} ms for {N} runs")
print(f"orjson (+NUMPY):     {numpy_time * 1000:.3f} ms for {N} runs")
print(f"Overhead ratio:      {numpy_time / plain_time:.4f}x")

Gives:

orjson (plain):      8.585 ms for 10000 runs
orjson (+NUMPY):     8.808 ms for 10000 runs
Overhead ratio:      1.0260x

@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from 1de3fce to f66de36 Compare July 26, 2025 09:58
@DanielYang59 DanielYang59 marked this pull request as ready for review July 26, 2025 10:07
@DanielYang59 DanielYang59 force-pushed the fix-selective-dynamics-as-np-array branch from bec7265 to 384c2f2 Compare July 26, 2025 10:29
@shyuep shyuep merged commit 8ba4b6e into materialsproject:master Jul 29, 2025
44 checks passed
@DanielYang59 DanielYang59 deleted the fix-selective-dynamics-as-np-array branch July 29, 2025 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error: Type is not JSON serializable when Exporting POSCAR Structure to JSON

2 participants