@@ -615,16 +615,41 @@ class DumpReader(base.ReaderBase):
615615 **kwargs
616616 Other keyword arguments used in :class:`~MDAnalysis.coordinates.base.ReaderBase`
617617
618+ Additional keyword arguments
619+ ----------------------------
620+ timeunit : str, optional
621+ Native time unit of the dump file (default: ``"fs"`` for LAMMPS
622+ "real" units). Must be a valid MDAnalysis time unit.
623+ lengthunit : str, optional
624+ Native length unit of the dump file (default: ``"Angstrom"`` for
625+ LAMMPS "real" units). Must be a valid MDAnalysis length unit.
626+ energyunit : str, optional
627+ Native energy unit per mole of the dump file (default:
628+ ``"kcal/mol"`` for LAMMPS "real" units). Used together with
629+ `lengthunit` to derive the native force unit (energy/length).
630+
618631 Note
619632 ----
620- This reader assumes LAMMPS "real" units where time is in femtoseconds,
621- length is in Angstroms, velocities in Angstrom/femtosecond, and forces
622- in kcal/(mol*Angstrom). Forces are automatically converted to MDAnalysis
623- base units (kJ/(mol*Angstrom)) for consistency with other trajectory formats.
633+ By default, this reader assumes LAMMPS "real" units where time is in
634+ femtoseconds (``fs``), length is in Angstroms (``Angstrom``), velocities
635+ in ``Angstrom/fs``, and forces in ``kcal/(mol*Angstrom)``. You can
636+ override the native units with `timeunit`, `lengthunit`, and `energyunit`.
637+ The velocity unit is derived as ``lengthunit/timeunit``; the force unit
638+ is derived as ``energyunit/lengthunit`` (preserving ``/mol`` if present).
639+
640+ When ``convert_units=True`` (default), positions, velocities, and forces
641+ are converted from the native units to MDAnalysis base units, i.e.,
642+ positions to ``Angstrom``, time to ``ps`` (affecting velocity), and
643+ forces to ``kJ/(mol*Angstrom)``. This ensures consistency with other
644+ trajectory formats.
624645
625646 .. versionchanged:: 2.8.0
626647 Reading of arbitrary, additional columns is now supported.
627648 (Issue `#3504 <https://github.com/MDAnalysis/mdanalysis/issues/3504>`__)
649+ .. versionchanged:: 2.10.0
650+ Forces are converted to MDAnalysis base units by default and native
651+ units can be overridden via `timeunit`, `lengthunit`, and `energyunit`
652+ to accommodate different LAMMPS unit styles.
628653 .. versionchanged:: 2.4.0
629654 Now imports velocities and forces, translates the box to the origin,
630655 and optionally unwraps trajectories with image flags upon loading.
@@ -671,6 +696,7 @@ def __init__(
671696 additional_columns = None ,
672697 ** kwargs ,
673698 ):
699+ # Initialize first to set convert_units and ts kwargs
674700 super (DumpReader , self ).__init__ (filename , ** kwargs )
675701
676702 root , ext = os .path .splitext (self .filename )
@@ -685,6 +711,82 @@ def __init__(
685711 f"Please choose one of { option_string } "
686712 )
687713
714+ # Allow overriding native units per-instance to support LAMMPS unit styles
715+ # Defaults correspond to "real": time=fs, length=Angstrom, energy=kcal/mol
716+ timeunit = kwargs .pop ("timeunit" , None )
717+ lengthunit = kwargs .pop ("lengthunit" , None )
718+ energyunit = kwargs .pop ("energyunit" , None )
719+
720+ # Start from class defaults
721+ self .units = self .units .copy ()
722+
723+ # Helper to (re)compute velocity and force units from time/length/energy
724+ def _compute_units (_time , _length , _energy ):
725+ # velocity as length/time
726+ vel = None
727+ if _time is not None and _length is not None :
728+ vel = f"{ _length } /{ _time } "
729+
730+ # force as energy/length (add per-mol if provided in energy)
731+ # Examples:
732+ # - energy="kcal/mol", length="Angstrom" -> "kcal/(mol*Angstrom)"
733+ # - energy="eV", length="Angstrom" -> "eV/Angstrom"
734+ frc = None
735+ if _energy is not None and _length is not None :
736+ if "/mol" in _energy :
737+ base_energy = _energy .replace ("/mol" , "" )
738+ frc = f"{ base_energy } /(mol*{ _length } )"
739+ else :
740+ frc = f"{ _energy } /{ _length } "
741+ return vel , frc
742+
743+ # Apply overrides if provided
744+ if timeunit is not None :
745+ # Validate unit type if known
746+ try :
747+ if units .unit_types [timeunit ] != 'time' :
748+ raise TypeError (
749+ f"LAMMPS DumpReader: wrong unit { timeunit !r} for unit type 'time'"
750+ )
751+ except KeyError :
752+ raise ValueError (
753+ f"LAMMPS DumpReader: unknown time unit { timeunit !r} "
754+ ) from None
755+ self .units ['time' ] = timeunit
756+
757+ if lengthunit is not None :
758+ try :
759+ if units .unit_types [lengthunit ] != 'length' :
760+ raise TypeError (
761+ f"LAMMPS DumpReader: wrong unit { lengthunit !r} for unit type 'length'"
762+ )
763+ except KeyError :
764+ raise ValueError (
765+ f"LAMMPS DumpReader: unknown length unit { lengthunit !r} "
766+ ) from None
767+ self .units ['length' ] = lengthunit
768+
769+ # default energy for "real"
770+ default_energy = 'kcal/mol'
771+ if energyunit is None :
772+ energyunit = default_energy
773+ else :
774+ try :
775+ if units .unit_types [energyunit ] != 'energy' :
776+ raise TypeError (
777+ f"LAMMPS DumpReader: wrong unit { energyunit !r} for unit type 'energy'"
778+ )
779+ except KeyError :
780+ # Some compound forms like 'kcal/mol' may not be in unit_types; allow pass-through
781+ pass
782+
783+ # Derive velocity and force units based on final time/length/energy
784+ vunit , funit = _compute_units (self .units ['time' ], self .units ['length' ], energyunit )
785+ if vunit is not None :
786+ self .units ['velocity' ] = vunit
787+ if funit is not None :
788+ self .units ['force' ] = funit
789+
688790 self ._unwrap = unwrap_images
689791
690792 if (
0 commit comments