Repository: deepmodeling/dpdata (language: python)

Issue:
slow to import dpdata
I find that it would takes up to 30s to run just `import dpdata`  on the login node of HPC.  I did some investigation myself and find that it would load almost all the modules into memory when importing the `dpdata` module. 

Here is the relative code:
https://github.com/deepmodeling/dpdata/blob/master/dpdata/__init__.py#L21-L30

Is it possible to just remove the `__all__`  expression? I think it is for users who like to use `from dpdata import *`,  which is not a good practices IMO. 

Adding a function that could deal with the magnetic_force and spin when using the ABACUS's deltaspin module
## Adding a new function, two files were changed.

I add three parameters in the data when reading data from the OUT.ABACUS/running_scf.log.

When we use the deltaspin module during the scf calculation, the dpdata could grep the magnetic force and spin to the system and print the modified coords.npy and forces.npy.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Bug Fixes**
  - Corrected import statement for `py3Dmol` to ensure proper functionality.

- **Refactor**
  - Reorganized and reformatted import statements across multiple plugins for improved code readability.
  - Adjusted method signatures and added new functions to enhance the functionality of specific formats.

- **Chores**
  - Updated `.pre-commit-config.yaml` with new URLs and revisions for repositories.
  - Modified `.gitattributes` to enforce line endings for Python test files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Discussion / hints:
![image](https://github.com/deepmodeling/dpdata/assets/9496702/8e8729de-e2ea-4cc6-af82-1a298818438f)

<details>

```
import time: self [us] | cumulative | imported package
import time:       151 |        151 |   _io
import time:        51 |         51 |   marshal
import time:       231 |        231 |   posix
import time:       371 |        802 | _frozen_importlib_external
import time:        56 |         56 |   time
import time:       171 |        227 | zipimport
import time:        41 |         41 |     _codecs
import time:       328 |        369 |   codecs
import time:       249 |        249 |   encodings.aliases
import time:       360 |        977 | encodings
import time:       123 |        123 | encodings.utf_8
import time:       115 |        115 | _signal
import time:       145 |        145 | encodings.latin_1
import time:        28 |         28 |     _abc
import time:       150 |        177 |   abc
import time:       156 |        332 | io
import time:        93 |         93 |       _stat
import time:       166 |        258 |     stat
import time:       772 |        772 |     _collections_abc
import time:        94 |         94 |       genericpath
import time:       149 |        242 |     posixpath
import time:       362 |       1633 |   os
import time:       111 |        111 |   _sitebuiltins
import time:        49 |         49 |     _locale
import time:        89 |        137 |   _bootlocale
import time:       249 |        249 |         types
import time:       182 |        182 |         warnings
import time:       104 |        533 |       importlib
import time:       121 |        654 |     importlib.machinery
import time:       156 |        156 |               _heapq
import time:       161 |        316 |             heapq
import time:       634 |        634 |             itertools
import time:       127 |        127 |             keyword
import time:        62 |         62 |               _operator
import time:       268 |        330 |             operator
import time:       147 |        147 |             reprlib
import time:        61 |         61 |             _collections
import time:       887 |       2500 |           collections
import time:       187 |        187 |           collections.abc
import time:        47 |         47 |               _functools
import time:       460 |        507 |             functools
import time:       423 |        929 |           contextlib
import time:       729 |        729 |             enum
import time:        68 |         68 |               _sre
import time:       316 |        316 |                 sre_constants
import time:       312 |        627 |               sre_parse
import time:       292 |        987 |             sre_compile
import time:       146 |        146 |             copyreg
import time:       489 |       2349 |           re
import time:      1733 |       7696 |         typing
import time:       430 |       8126 |       importlib.abc
import time:       175 |       8300 |     importlib.util
import time:       146 |        146 |       fnmatch
import time:        56 |         56 |         nt
import time:        61 |         61 |         nt
import time:        45 |         45 |         nt
import time:        39 |         39 |         nt
import time:       191 |        391 |       ntpath
import time:        56 |         56 |       errno
import time:       100 |        100 |         urllib
import time:      1002 |       1101 |       urllib.parse
import time:       785 |       2478 |     pathlib
import time:       617 |      12048 |   __editable___deepmd_pytorch_0_1_3_dev32_g622a4f3_finder
import time:       152 |        152 |   __editable___deepmodeling_sphinx_0_1_4_dev1_gcfc0544_d20221105_finder
import time:       137 |        137 |   __editable___dpgen_0_11_2_dev40_g84f64dd_d20230618_finder
import time:       129 |        129 |   __editable___dpgen2_0_0_7_dev10_gb3b3bb1_finder
import time:       203 |        203 |   __editable___mddatasetbuilder_1_3_5_dev7_gd71b9da_d20221022_finder
import time:       129 |        129 |   __editable___qiuwenbot_0_1_dev42_gd6298d6_d20230703_finder
import time:       186 |        186 |   _distutils_hack
import time:       323 |        323 |   sitecustomize
import time:        90 |         90 |   usercustomize
import time:      3403 |      18674 | site
import time:       124 |        124 |   dpdata.lammps
import time:        73 |         73 |   dpdata.md
import time:        68 |         68 |   dpdata.vasp
import time:       334 |        334 |     glob
import time:       182 |        182 |         _weakrefset
import time:       347 |        529 |       weakref
import time:       100 |        100 |           org
import time:        11 |        110 |         org.python
import time:        12 |        121 |       org.python.core
import time:       152 |        801 |     copy
import time:       305 |        305 |       numpy._globals
import time:        95 |         95 |       numpy.__config__
import time:        66 |         66 |       numpy._distributor_init
import time:        96 |         96 |           __future__
import time:       146 |        146 |                   _json
import time:       292 |        437 |                 json.scanner
import time:       300 |        737 |               json.decoder
import time:       492 |        492 |               json.encoder
import time:       125 |       1352 |             json
import time:        63 |       1415 |           numpy._version
import time:        88 |       1597 |         numpy.version
import time:       264 |        264 |                 math
import time:       267 |        267 |                 _datetime
import time:      1099 |       1629 |               datetime
import time:     11504 |      13133 |             numpy.core._multiarray_umath
import time:       322 |        322 |                 numpy.compat._inspect
import time:       172 |        172 |                   pickle5
import time:       215 |        215 |                       _struct
import time:       137 |        351 |                     struct
import time:       265 |        265 |                     _compat_pickle
import time:       246 |        246 |                     _pickle
import time:       191 |        191 |                         org
import time:        75 |        265 |                       org.python
import time:        16 |        280 |                     org.python.core
import time:      1050 |       2191 |                   pickle
import time:       180 |       2541 |                 numpy.compat.py3k
import time:      2694 |       5556 |               numpy.compat
import time:        24 |       5580 |             numpy.compat._inspect
import time:       262 |      18973 |           numpy.core.overrides
import time:       508 |      19481 |         numpy.core.multiarray
import time:       210 |        210 |         numpy.core.umath
import time:       349 |        349 |           numbers
import time:       145 |        145 |           numpy.core._string_helpers
import time:       150 |        150 |             numpy.core._dtype
import time:       367 |        517 |           numpy.core._type_aliases
import time:       453 |       1462 |         numpy.core.numerictypes
import time:       382 |        382 |                 numpy.core._exceptions
import time:       230 |        611 |               numpy.core._methods
import time:       945 |       1556 |             numpy.core.fromnumeric
import time:       363 |       1918 |           numpy.core.shape_base
import time:       206 |        206 |           numpy.core._ufunc_config
import time:       589 |        589 |           numpy.core.arrayprint
import time:       136 |        136 |           numpy.core._asarray
import time:       984 |       3832 |         numpy.core.numeric
import time:      1043 |       1043 |         numpy.core.defchararray
import time:       323 |        323 |         numpy.core.records
import time:       175 |        175 |         numpy.core.memmap
import time:       220 |        220 |         numpy.core.function_base
import time:       150 |        150 |         numpy.core._machar
import time:       286 |        286 |         numpy.core.getlimits
import time:       867 |        867 |         numpy.core.einsumfunc
import time:       281 |        281 |           numpy.core._multiarray_tests
import time:      1536 |       1816 |         numpy.core._add_newdocs
import time:      2154 |       2154 |               signal
import time:       584 |        584 |               threading
import time:        57 |         57 |               pwd
import time:       182 |        182 |               grp
import time:       161 |        161 |               msvcrt
import time:       289 |        289 |               _posixsubprocess
import time:       301 |        301 |               select
import time:       705 |        705 |               selectors
import time:       572 |       5001 |             subprocess
import time:      2353 |       7354 |           platform
import time:       551 |       7905 |         numpy.core._add_newdocs_scalars
import time:       144 |        144 |         numpy.core._dtype_ctypes
import time:      1006 |       1006 |             _ast
import time:      1155 |       2161 |           ast
import time:       530 |        530 |             _ctypes
import time:       249 |        249 |             ctypes._endian
import time:       837 |       1615 |           ctypes
import time:       824 |       4599 |         numpy.core._internal
import time:       167 |        167 |         numpy._pytesttester
import time:       508 |      44777 |       numpy.core
import time:       356 |        356 |         numpy.lib.mixins
import time:       167 |        167 |             numpy.lib.ufunclike
import time:       387 |        553 |           numpy.lib.type_check
import time:       347 |        900 |         numpy.lib.scimath
import time:       220 |        220 |                     numpy.lib.stride_tricks
import time:       339 |        559 |                   numpy.lib.twodim_base
import time:       432 |        432 |                   numpy.linalg._umath_linalg
import time:       733 |       1723 |                 numpy.linalg.linalg
import time:       129 |       1851 |               numpy.linalg
import time:       290 |       2141 |             numpy.matrixlib.defmatrix
import time:       119 |       2259 |           numpy.matrixlib
import time:       289 |        289 |             numpy.lib.histograms
import time:      1116 |       1404 |           numpy.lib.function_base
import time:       424 |       4086 |         numpy.lib.index_tricks
import time:       476 |        476 |         numpy.lib.nanfunctions
import time:       523 |        523 |         numpy.lib.shape_base
import time:       708 |        708 |         numpy.lib.polynomial
import time:      2318 |       2318 |           textwrap
import time:       658 |       2975 |         numpy.lib.utils
import time:       298 |        298 |         numpy.lib.arraysetops
import time:      1018 |       1018 |           numpy.lib.format
import time:       250 |        250 |           numpy.lib._datasource
import time:       322 |        322 |           numpy.lib._iotools
import time:       655 |       2243 |         numpy.lib.npyio
import time:       167 |        167 |         numpy.lib.arrayterator
import time:       209 |        209 |         numpy.lib.arraypad
import time:       148 |        148 |         numpy.lib._version
import time:       400 |      13483 |       numpy.lib
import time:       168 |        168 |           numpy.fft._pocketfft_internal
import time:       547 |        714 |         numpy.fft._pocketfft
import time:       161 |        161 |         numpy.fft.helper
import time:       202 |       1076 |       numpy.fft
import time:       203 |        203 |           numpy.polynomial.polyutils
import time:       366 |        366 |           numpy.polynomial._polybase
import time:       350 |        918 |         numpy.polynomial.polynomial
import time:       318 |        318 |         numpy.polynomial.chebyshev
import time:       266 |        266 |         numpy.polynomial.legendre
import time:       278 |        278 |         numpy.polynomial.hermite
import time:       401 |        401 |         numpy.polynomial.hermite_e
import time:       288 |        288 |         numpy.polynomial.laguerre
import time:       185 |       2652 |       numpy.polynomial
import time:       194 |        194 |                 backports_abc
import time:       676 |        869 |               numpy.random._common
import time:       202 |        202 |                   binascii
import time:       312 |        514 |                 base64
import time:       611 |        611 |                   _hashlib
import time:       521 |        521 |                     _blake2
import time:       321 |        842 |                   hashlib
import time:       252 |       1703 |                 hmac
import time:       142 |        142 |                     _bisect
import time:       130 |        272 |                   bisect
import time:       137 |        137 |                   _random
import time:       129 |        129 |                   _sha512
import time:       399 |        935 |                 random
import time:       164 |       3314 |               secrets
import time:       607 |       4790 |             numpy.random.bit_generator
import time:       252 |        252 |             numpy.random._bounded_integers
import time:       200 |        200 |             numpy.random._mt19937
import time:       971 |       6211 |           numpy.random.mtrand
import time:       226 |        226 |           numpy.random._philox
import time:       290 |        290 |           numpy.random._pcg64
import time:       396 |        396 |           numpy.random._sfc64
import time:       565 |        565 |           numpy.random._generator
import time:       219 |       7903 |         numpy.random._pickle
import time:       213 |       8116 |       numpy.random
import time:       609 |        609 |       numpy.ctypeslib
import time:       156 |        156 |                 _opcode
import time:       296 |        452 |               opcode
import time:       478 |        929 |             dis
import time:       265 |        265 |                 token
import time:       960 |       1224 |               tokenize
import time:       156 |       1380 |             linecache
import time:      1683 |       3991 |           inspect
import time:      2972 |       6962 |         numpy.ma.core
import time:      1148 |       1148 |         numpy.ma.extras
import time:       250 |       8359 |       numpy.ma
import time:      2249 |      81783 |     numpy
import time:       179 |        179 |       monty
import time:       164 |        164 |         _uuid
import time:       410 |        574 |       uuid
import time:       265 |        265 |           pytz.exceptions
import time:       292 |        292 |           pytz.lazy
import time:       242 |        242 |           pytz.tzinfo
import time:       131 |        131 |           pytz.tzfile
import time:      1502 |       2429 |         pytz
import time:       137 |        137 |           dateutil._version
import time:       183 |        320 |         dateutil
import time:       194 |        194 |             mmap
import time:      2604 |       2797 |           pandas._typing
import time:       778 |        778 |                                 pandas._libs.tslibs.dtypes
import time:       147 |        147 |                                   pandas._libs.tslibs.base
import time:       167 |        167 |                                     pandas._libs.tslibs.np_datetime
import time:       389 |        555 |                                   pandas._libs.tslibs.nattype
import time:      1048 |       1048 |                                         six
import time:        71 |         71 |                                         six.moves
import time:       230 |        230 |                                         dateutil.tz._common
import time:       171 |        171 |                                         dateutil.tz._factories
import time:        41 |         41 |                                           six.moves.winreg
import time:       189 |        230 |                                         dateutil.tz.win
import time:       832 |       2580 |                                       dateutil.tz.tz
import time:       170 |       2749 |                                     dateutil.tz
import time:       404 |       3153 |                                   pandas._libs.tslibs.timezones
import time:       184 |        184 |                                     pandas._libs.tslibs.ccalendar
import time:       343 |        526 |                                   pandas._libs.tslibs.tzconversion
import time:       916 |        916 |                                           locale
import time:       576 |        576 |                                               pandas._config.config
import time:       275 |        275 |                                               pandas._config.dates
import time:       148 |        148 |                                               pandas._config.display
import time:       180 |       1177 |                                             pandas._config
import time:       147 |       1324 |                                           pandas._config.localization
import time:       177 |        177 |                                             backports_abc
import time:       541 |        541 |                                             calendar
import time:      1177 |       1894 |                                           pandas._libs.tslibs.strptime
import time:       399 |       4531 |                                         pandas._libs.tslibs.fields
import time:       778 |       5308 |                                       pandas._libs.tslibs.timedeltas
import time:       165 |        165 |                                         backports_abc
import time:       888 |       1053 |                                       pandas._libs.tslibs.timestamps
import time:       142 |        142 |                                       backports_abc
import time:       117 |        117 |                                       dateutil.easter
import time:       116 |        116 |                                         dateutil._common
import time:       208 |        323 |                                       dateutil.relativedelta
import time:       170 |        170 |                                       pandas._libs.properties
import time:      1562 |       8672 |                                     pandas._libs.tslibs.offsets
import time:       146 |        146 |                                     backports_abc
import time:        45 |         45 |                                           _string
import time:       761 |        805 |                                         string
import time:       740 |        740 |                                           _decimal
import time:       150 |        889 |                                         decimal
import time:      1179 |       2872 |                                       dateutil.parser._parser
import time:       272 |        272 |                                       dateutil.parser.isoparser
import time:       201 |       3344 |                                     dateutil.parser
import time:       549 |      12710 |                                   pandas._libs.tslibs.parsing
import time:       385 |      17473 |                                 pandas._libs.tslibs.conversion
import time:       373 |        373 |                                 pandas._libs.tslibs.period
import time:       242 |        242 |                                 pandas._libs.tslibs.vectorized
import time:       228 |      19092 |                               pandas._libs.tslibs
import time:        23 |      19114 |                             pandas._libs.tslibs.nattype
import time:       131 |        131 |                             pandas._libs.ops_dispatch
import time:       375 |      19619 |                           pandas._libs.missing
import time:       601 |      20220 |                         pandas._libs.hashtable
import time:       445 |        445 |                         pandas._libs.algos
import time:       839 |      21503 |                       pandas._libs.interval
import time:       112 |      21615 |                     pandas._libs
import time:        19 |      21633 |                   pandas._libs.properties
import time:       248 |      21881 |                 pandas.util._decorators
import time:        97 |         97 |                     pandas.core
import time:       131 |        228 |                   pandas.core.util
import time:       199 |        199 |                     pandas._libs.tslib
import time:       571 |        770 |                   pandas._libs.lib
import time:       174 |        174 |                   pandas._libs.hashing
import time:        91 |         91 |                     pandas.core.dtypes
import time:       260 |        260 |                       pandas.errors
import time:       287 |        287 |                       pandas.core.dtypes.generic
import time:       154 |        700 |                     pandas.core.dtypes.base
import time:       182 |        182 |                       pandas.core.dtypes.inference
import time:       766 |        948 |                     pandas.core.dtypes.dtypes
import time:       283 |       2021 |                   pandas.core.dtypes.common
import time:       180 |       3372 |                 pandas.core.util.hashing
import time:       131 |      25383 |               pandas.util
import time:      1894 |      27277 |             pandas.util.version
import time:       324 |      27601 |           pandas.compat.numpy
import time:       126 |        126 |             pyarrow
import time:       117 |        242 |           pandas.compat.pyarrow
import time:       218 |      30857 |         pandas.compat
import time:       566 |        566 |         pandas.core.config_init
import time:       150 |        150 |           pandas.core.dtypes.missing
import time:        92 |         92 |               pandas.util._exceptions
import time:       106 |        106 |               pandas.util._validators
import time:       328 |        525 |             pandas.core.dtypes.cast
import time:        82 |         82 |               pandas.core.array_algos
import time:       265 |        265 |                 pandas.core.common
import time:       151 |        415 |               pandas.core.construction
import time:       213 |        710 |             pandas.core.array_algos.take
import time:       121 |        121 |             pandas.core.indexers
import time:       540 |       1895 |           pandas.core.algorithms
import time:       184 |        184 |               pandas.compat.numpy.function
import time:        97 |         97 |                 pandas.compat._optional
import time:       266 |        362 |               pandas.core.missing
import time:        71 |         71 |                 pandas.core.roperator
import time:       215 |        215 |                   pandas._libs.ops
import time:        83 |         83 |                     pandas.core.computation
import time:       149 |        149 |                       numexpr.__config__
import time:      1648 |       1648 |                       numexpr.interpreter
import time:        70 |         70 |                               distutils.debug
import time:       221 |        221 |                               distutils.errors
import time:       249 |        249 |                                   traceback
import time:        38 |         38 |                                   atexit
import time:      1412 |       1699 |                                 logging
import time:       137 |        137 |                                 email
import time:      1167 |       1167 |                                     gettext
import time:       121 |       1288 |                                   getopt
import time:       359 |       1646 |                                 distutils.fancy_getopt
import time:       247 |        247 |                                   sysconfig
import time:        86 |         86 |                                   distutils.dep_util
import time:        61 |         61 |                                     distutils._log
import time:        82 |        143 |                                   distutils.spawn
import time:       179 |        653 |                                 distutils.util
import time:       494 |       4627 |                               distutils.dist
import time:       119 |        119 |                                 distutils.dir_util
import time:       108 |        108 |                                 distutils.file_util
import time:       199 |        199 |                                       zlib
import time:       213 |        213 |                                         _compression
import time:       155 |        155 |                                         _bz2
import time:       190 |        556 |                                       bz2
import time:       272 |        272 |                                         _lzma
import time:       179 |        451 |                                       lzma
import time:       433 |       1638 |                                     shutil
import time:       595 |       2233 |                                   zipfile
import time:       127 |       2360 |                                 distutils.archive_util
import time:       167 |       2752 |                               distutils.cmd
import time:      1394 |       1394 |                                 configparser
import time:       129 |       1523 |                               distutils.config
import time:       121 |        121 |                               distutils.extension
import time:       599 |       9910 |                             _distutils_hack.override
import time:       132 |        132 |                             setuptools._deprecation_warning
import time:       115 |        115 |                                 setuptools.extern
import time:        68 |         68 |                                     setuptools._vendor
import time:       182 |        182 |                                       _csv
import time:       241 |        422 |                                     csv
import time:       176 |        176 |                                     setuptools._vendor.zipp
import time:       118 |        118 |                                         uu
import time:       128 |        128 |                                         quopri
import time:       296 |        296 |                                             _socket
import time:       167 |        167 |                                             array
import time:      1323 |       1786 |                                           socket
import time:       200 |        200 |                                           email._parseaddr
import time:        82 |         82 |                                             email.base64mime
import time:       155 |        155 |                                             email.quoprimime
import time:       305 |        305 |                                             email.errors
import time:        77 |         77 |                                             email.encoders
import time:       155 |        772 |                                           email.charset
import time:       363 |       3119 |                                         email.utils
import time:       546 |        546 |                                           email.header
import time:       200 |        746 |                                         email._policybase
import time:       199 |        199 |                                         email._encoded_words
import time:        87 |         87 |                                         email.iterators
import time:       432 |       4827 |                                       email.message
import time:        69 |         69 |                                         setuptools._vendor.importlib_metadata._functools
import time:       105 |        174 |                                       setuptools._vendor.importlib_metadata._text
import time:       108 |        108 |                                       setuptools._vendor.importlib_metadata._compat
import time:       200 |       5308 |                                     setuptools._vendor.importlib_metadata._adapters
import time:       280 |        280 |                                     setuptools._vendor.importlib_metadata._meta
import time:       162 |        162 |                                     setuptools._vendor.importlib_metadata._py39compat
import time:       311 |        311 |                                     setuptools._vendor.importlib_metadata._collections
import time:        95 |         95 |                                     setuptools._vendor.importlib_metadata._itertools
import time:      1126 |       7943 |                                   setuptools._vendor.importlib_metadata
import time:       102 |       8045 |                                 setuptools.extern.importlib_metadata
import time:       300 |        300 |                                   zipp
import time:        72 |         72 |                                       importlib_metadata._functools
import time:       116 |        188 |                                     importlib_metadata._text
import time:       206 |        393 |                                   importlib_metadata._adapters
import time:        82 |         82 |                                     importlib_metadata._compat
import time:       162 |        244 |                                   importlib_metadata._meta
import time:       165 |        165 |                                   importlib_metadata._collections
import time:        72 |         72 |                                   importlib_metadata._itertools
import time:       966 |       2138 |                                 importlib_metadata
import time:       238 |        238 |                                     tempfile
import time:       110 |        348 |                                   importlib._common
import time:       201 |        548 |                                 importlib.resources
import time:       111 |      10954 |                               setuptools._importlib
import time:       452 |        452 |                                 email.feedparser
import time:       148 |        599 |                               email.parser
import time:      4982 |      16535 |                             setuptools.version
import time:       137 |        137 |                                 distutils.filelist
import time:       102 |        238 |                               setuptools.monkey
import time:       135 |        372 |                             setuptools.extension
import time:       107 |        107 |                               distutils.log
import time:       138 |        138 |                               distutils.command
import time:        74 |         74 |                                 setuptools._vendor.packaging
import time:        63 |        136 |                               setuptools.extern.packaging
import time:       142 |        142 |                                 setuptools._vendor.ordered_set
import time:        46 |        187 |                               setuptools.extern.ordered_set
import time:       165 |        165 |                                       _queue
import time:       257 |        421 |                                     queue
import time:       152 |        152 |                                     setuptools._vendor.more_itertools.recipes
import time:       605 |       1177 |                                   setuptools._vendor.more_itertools.more
import time:        97 |       1273 |                                 setuptools._vendor.more_itertools
import time:        49 |       1322 |                               setuptools.extern.more_itertools
import time:       747 |        747 |                               setuptools._normalization
import time:       184 |        184 |                                 distutils.command.bdist
import time:       149 |        332 |                               setuptools.command
import time:        80 |         80 |                               setuptools.windows_support
import time:       434 |        434 |                                         dataclasses
import time:       353 |        353 |                                                 setuptools._vendor.packaging._elffile
import time:       319 |        671 |                                               setuptools._vendor.packaging._manylinux
import time:       194 |        194 |                                               setuptools._vendor.packaging._musllinux
import time:       390 |       1254 |                                             setuptools.extern.packaging.tags
import time:       104 |        104 |                                               setuptools.extern.packaging._structures
import time:       520 |        624 |                                             setuptools.extern.packaging.version
import time:       277 |       2154 |                                           setuptools.extern.packaging.utils
import time:      3526 |       5680 |                                         setuptools.extern.packaging.specifiers
import time:      3718 |       9831 |                                       setuptools.extern.packaging._tokenizer
import time:       369 |      10200 |                                     setuptools.extern.packaging._parser
import time:       290 |        290 |                                     setuptools.extern.packaging.markers
import time:       158 |      10647 |                                   setuptools.extern.packaging.requirements
import time:        87 |         87 |                                     setuptools._path
import time:       525 |        611 |                                   setuptools.config.expand
import time:       669 |      11926 |                                 setuptools.config.setupcfg
import time:       115 |      12041 |                               setuptools.config
import time:       133 |        133 |                                 setuptools.errors
import time:      1533 |       1533 |                                     email._header_value_parser
import time:       403 |       1936 |                                   email.headerregistry
import time:       393 |       2329 |                                 setuptools.config._apply_pyprojecttoml
import time:       304 |       2764 |                               setuptools.config.pyprojecttoml
import time:       307 |        307 |                               setuptools.discovery
import time:        81 |         81 |                                     setuptools._vendor.jaraco
import time:        68 |        148 |                                   setuptools.extern.jaraco
import time:       182 |        182 |                                   setuptools.extern.jaraco.functools
import time:       683 |        683 |                                   setuptools.extern.jaraco.context
import time:       429 |       1440 |                                 setuptools.extern.jaraco.text
import time:       155 |       1594 |                               setuptools._reqs
import time:        74 |         74 |                                 setuptools._itertools
import time:       142 |        215 |                               setuptools._entry_points
import time:       472 |      20436 |                             setuptools.dist
import time:        67 |         67 |                                 setuptools.py34compat
import time:        87 |        154 |                               setuptools._imp
import time:       132 |        285 |                             setuptools.depends
import time:        82 |         82 |                             setuptools.logging
import time:       784 |      48533 |                           distutils
import time:       431 |      48964 |                         distutils.version
import time:       571 |      49534 |                       numexpr.expressions
import time:       189 |        189 |                         numexpr.utils
import time:      1020 |       1209 |                       numexpr.necompiler
import time:       105 |        105 |                       numexpr.version
import time:       707 |      53349 |                     pandas.core.computation.check
import time:       178 |      53609 |                   pandas.core.computation.expressions
import time:        99 |         99 |                   pandas.core.ops.missing
import time:        72 |         72 |                   pandas.core.ops.dispatch
import time:        68 |         68 |                   pandas.core.ops.invalid
import time:       199 |      54260 |                 pandas.core.ops.array_ops
import time:       110 |        110 |                 pandas.core.ops.common
import time:       122 |        122 |                 pandas.core.ops.docstrings
import time:        92 |         92 |                 pandas.core.ops.mask_ops
import time:       149 |        149 |                 pandas.core.ops.methods
import time:       213 |      55013 |               pandas.core.ops
import time:       190 |        190 |               pandas.core.sorting
import time:       437 |      56183 |             pandas.core.arrays.base
import time:       187 |        187 |                   bottleneck.reduce
import time:        95 |         95 |                   bottleneck.nonreduce
import time:        86 |         86 |                   bottleneck.nonreduce_axis
import time:        93 |         93 |                   bottleneck.move
import time:       112 |        112 |                     bottleneck.slow.reduce
import time:        65 |         65 |                     bottleneck.slow.nonreduce
import time:        82 |         82 |                     bottleneck.slow.nonreduce_axis
import time:       151 |        151 |                     bottleneck.slow.move
import time:       105 |        514 |                   bottleneck.slow
import time:        69 |         69 |                     bottleneck.benchmark
import time:        56 |         56 |                         gc
import time:       140 |        196 |                       timeit
import time:        67 |        262 |                     bottleneck.benchmark.autotimeit
import time:       128 |        458 |                   bottleneck.benchmark.bench
import time:        97 |         97 |                   bottleneck.benchmark.bench_detailed
import time:        66 |         66 |                     bottleneck.tests
import time:       129 |        195 |                   bottleneck.tests.util
import time:        93 |         93 |                   bottleneck._pytesttester
import time:        63 |         63 |                   bottleneck._version
import time:       719 |       2594 |                 pandas.core.nanops
import time:       117 |        117 |                 pandas.core.array_algos.masked_reductions
import time:       320 |        320 |                 pandas.core.arraylike
import time:       427 |       3456 |               pandas.core.arrays.masked
import time:       237 |       3693 |             pandas.core.arrays.boolean
import time:       256 |        256 |               pandas._libs.arrays
import time:       253 |        253 |               pandas.core.accessor
import time:        75 |         75 |                 pandas.core.array_algos.transforms
import time:       343 |        418 |               pandas.core.arrays._mixins
import time:       577 |        577 |               pandas.core.base
import time:      1596 |       1596 |                   pandas.core.strings.accessor
import time:       174 |        174 |                   pandas.core.strings.base
import time:       108 |       1877 |                 pandas.core.strings
import time:       160 |        160 |                 unicodedata
import time:       289 |       2325 |               pandas.core.strings.object_array
import time:       107 |        107 |                 pandas.io
import time:       120 |        227 |               pandas.io.formats
import time:       101 |        101 |               pandas.io.formats.console
import time:       671 |       4825 |             pandas.core.arrays.categorical
import time:        80 |         80 |                 pandas.tseries
import time:       268 |        268 |                 pandas.tseries.frequencies
import time:       788 |       1136 |               pandas.core.arrays.datetimelike
import time:       109 |        109 |               pandas.core.arrays._ranges
import time:       226 |        226 |                 pandas.core.arrays.numeric
import time:        77 |         77 |                   pandas.core.tools
import time:       127 |        203 |                 pandas.core.tools.numeric
import time:       289 |        717 |               pandas.core.arrays.integer
import time:        88 |         88 |               pandas.tseries.offsets
import time:       458 |       2506 |             pandas.core.arrays.datetimes
import time:       196 |        196 |             pandas.core.arrays.floating
import time:        74 |         74 |                 pandas.core.indexes
import time:       370 |        370 |                 pandas._libs.index
import time:       335 |        335 |                 pandas._libs.join
import time:       314 |        314 |                         pandas._libs.sparse
import time:       152 |        152 |                         pandas.core.arrays.sparse.dtype
import time:       227 |        227 |                         pandas.io.formats.printing
import time:       320 |       1012 |                       pandas.core.arrays.sparse.array
import time:       192 |       1203 |                     pandas.core.arrays.sparse.accessor
import time:        87 |       1290 |                   pandas.core.arrays.sparse
import time:       150 |       1440 |                 pandas.core.dtypes.concat
import time:       106 |        106 |                 pandas.core.array_algos.putmask
import time:       105 |        105 |                 pandas.core.indexes.frozen
import time:      1726 |       4153 |               pandas.core.indexes.base
import time:       793 |       4946 |             pandas.core.arrays.interval
import time:       412 |        412 |             pandas.core.arrays.numpy_
import time:       754 |        754 |             pandas.core.arrays.period
import time:       382 |        382 |             pandas.core.arrays.string_
import time:       321 |        321 |             pandas.core.arrays.string_arrow
import time:       350 |        350 |             pandas.core.arrays.timedeltas
import time:       183 |      74744 |           pandas.core.arrays
import time:       126 |        126 |           pandas.core.flags
import time:       316 |        316 |               pandas._libs.reduction
import time:       317 |        317 |                     pandas.core.indexes.extension
import time:       339 |        656 |                   pandas.core.indexes.category
import time:       338 |        338 |                       pandas.core.indexes.numeric
import time:       114 |        114 |                       pandas.core.tools.timedeltas
import time:       478 |        928 |                     pandas.core.indexes.datetimelike
import time:        98 |         98 |                     pandas.core.tools.times
import time:       695 |       1719 |                   pandas.core.indexes.datetimes
import time:      1061 |       1061 |                     pandas.core.indexes.multi
import time:       176 |        176 |                     pandas.core.indexes.timedeltas
import time:       588 |       1824 |                   pandas.core.indexes.interval
import time:       324 |        324 |                   pandas.core.indexes.period
import time:       857 |        857 |                   pandas.core.indexes.range
import time:       152 |       5531 |                 pandas.core.indexes.api
import time:       143 |       5673 |               pandas.core.aggregation
import time:       405 |        405 |               pandas.core.apply
import time:       230 |        230 |                     pandas._libs.indexing
import time:      1093 |       1322 |                   pandas.core.indexing
import time:        82 |         82 |                       pandas.core.reshape
import time:       184 |        184 |                             backports_abc
import time:       487 |        670 |                           pandas._libs.internals
import time:       241 |        241 |                             pandas._libs.writers
import time:       144 |        144 |                             pandas.core.array_algos.quantile
import time:       158 |        158 |                             pandas.core.array_algos.replace
import time:       652 |       1193 |                           pandas.core.internals.blocks
import time:       123 |       1986 |                         pandas.core.internals.api
import time:       173 |        173 |                           pandas.core.internals.base
import time:       442 |        615 |                         pandas.core.internals.array_manager
import time:       252 |        252 |                             pandas.core.internals.ops
import time:       417 |        669 |                           pandas.core.internals.managers
import time:       203 |        871 |                         pandas.core.internals.concat
import time:       100 |       3570 |                       pandas.core.internals
import time:       225 |       3876 |                     pandas.core.reshape.concat
import time:       238 |        238 |                         gzip
import time:       860 |       1097 |                       pandas.io.common
import time:       670 |       1767 |                     pandas.io.formats.format
import time:       196 |       5838 |                   pandas.core.describe
import time:       228 |        228 |                   pandas.core.internals.construction
import time:        90 |         90 |                   pandas.core.shared_docs
import time:        73 |         73 |                         pandas._libs.window
import time:       330 |        403 |                       pandas._libs.window.aggregations
import time:       140 |        140 |                       pandas.core.util.numba_
import time:        91 |         91 |                       pandas.core.window.common
import time:       186 |        186 |                       pandas.core.window.doc
import time:       282 |        282 |                         pandas._libs.window.indexers
import time:       301 |        582 |                       pandas.core.window.indexers
import time:       130 |        130 |                       pandas.core.window.numba_
import time:       161 |        161 |                       pandas.core.window.online
import time:      1133 |       1133 |                       pandas.core.window.rolling
import time:       427 |       3249 |                     pandas.core.window.ewm
import time:       558 |        558 |                     pandas.core.window.expanding
import time:        94 |       3901 |                   pandas.core.window
import time:      4291 |      15669 |                 pandas.core.generic
import time:        88 |         88 |                   pandas.core.reshape.util
import time:       318 |        406 |                 pandas.core.reshape.melt
import time:       253 |        253 |                   pandas._libs.reshape
import time:       438 |        438 |                   pandas.core.indexes.accessors
import time:        98 |         98 |                     pandas.arrays
import time:       310 |        408 |                   pandas.core.tools.datetimes
import time:       864 |        864 |                     pandas.plotting._core
import time:       138 |        138 |                     pandas.plotting._misc
import time:       104 |       1105 |                   pandas.plotting
import time:      2323 |       4526 |                 pandas.core.series
import time:       359 |        359 |                 pandas.io.formats.info
import time:     14559 |      35516 |               pandas.core.frame
import time:       211 |        211 |               pandas.core.groupby.base
import time:       386 |        386 |                 pandas._libs.groupby
import time:       136 |        136 |                 pandas.core.groupby.numba_
import time:        82 |         82 |                     pandas.core.groupby.categorical
import time:       235 |        316 |                   pandas.core.groupby.grouper
import time:       449 |        765 |                 pandas.core.groupby.ops
import time:      1632 |       2918 |               pandas.core.groupby.groupby
import time:      1375 |      46412 |             pandas.core.groupby.generic
import time:        95 |      46506 |           pandas.core.groupby
import time:       158 |     123577 |         pandas.core.api
import time:       130 |        130 |         pandas.tseries.api
import time:        89 |         89 |                 pandas.core.computation.common
import time:       154 |        243 |               pandas.core.computation.align
import time:       272 |        272 |                     pprint
import time:       193 |        193 |                     pandas.compat.chainmap
import time:       270 |        734 |                   pandas.core.computation.scope
import time:       366 |       1100 |                 pandas.core.computation.ops
import time:       139 |        139 |                 pandas.core.computation.parsing
import time:      1032 |       2269 |               pandas.core.computation.expr
import time:       243 |       2754 |             pandas.core.computation.engines
import time:       150 |       2903 |           pandas.core.computation.eval
import time:        93 |       2995 |         pandas.core.computation.api
import time:       794 |        794 |           pandas.core.reshape.merge
import time:       725 |        725 |           pandas.core.reshape.pivot
import time:       305 |        305 |           pandas.core.reshape.reshape
import time:       193 |        193 |           pandas.core.reshape.tile
import time:       169 |       2184 |         pandas.core.reshape.api
import time:       149 |        149 |           pandas.api.extensions
import time:        92 |         92 |           pandas.api.indexers
import time:       100 |        100 |             pandas.core.dtypes.api
import time:       100 |        200 |           pandas.api.types
import time:       148 |        588 |         pandas.api
import time:       133 |        133 |         pandas.util._print_versions
import time:       105 |        105 |           pandas.io.clipboards
import time:       170 |        170 |                 backports_abc
import time:       564 |        733 |               pandas._libs.parsers
import time:       152 |        152 |               pandas.io.excel._util
import time:       145 |        145 |                     pandas.io.date_converters
import time:      1257 |       1401 |                   pandas.io.parsers.base_parser
import time:       185 |        185 |                   pandas.io.parsers.c_parser_wrapper
import time:       314 |        314 |                   pandas.io.parsers.python_parser
import time:      1118 |       3017 |                 pandas.io.parsers.readers
import time:       108 |       3125 |               pandas.io.parsers
import time:       189 |        189 |               pandas.io.excel._odfreader
import time:       308 |        308 |               pandas.io.excel._openpyxl
import time:       130 |        130 |               pandas.io.excel._pyxlsb
import time:       121 |        121 |               pandas.io.excel._xlrd
import time:       833 |       5587 |             pandas.io.excel._base
import time:       187 |        187 |               pandas._libs.json
import time:       131 |        131 |                 pandas.io.formats._color_data
import time:       173 |        173 |                 pandas.io.formats.css
import time:       430 |        734 |               pandas.io.formats.excel
import time:       185 |       1105 |             pandas.io.excel._odswriter
import time:       183 |        183 |             pandas.io.excel._xlsxwriter
import time:       135 |        135 |             pandas.io.excel._xlwt
import time:       118 |       7126 |           pandas.io.excel
import time:       242 |        242 |           pandas.io.feather_format
import time:       109 |        109 |           pandas.io.gbq
import time:       516 |        516 |           pandas.io.html
import time:       495 |        495 |               pandas.io.json._normalize
import time:       121 |        121 |               pandas.io.json._table_schema
import time:       675 |       1290 |             pandas.io.json._json
import time:       112 |       1402 |           pandas.io.json
import time:        84 |         84 |           pandas.io.orc
import time:       293 |        293 |           pandas.io.parquet
import time:       170 |        170 |             pandas.compat.pickle_compat
import time:       234 |        404 |           pandas.io.pickle
import time:       370 |        370 |             pandas.core.computation.pytables
import time:      1355 |       1724 |           pandas.io.pytables
import time:       145 |        145 |             pandas.io.sas.sasreader
import time:       104 |        248 |           pandas.io.sas
import time:        84 |         84 |           pandas.io.spss
import time:       541 |        541 |           pandas.io.sql
import time:      1035 |       1035 |           pandas.io.stata
import time:       524 |        524 |           pandas.io.xml
import time:       194 |      14623 |         pandas.io.api
import time:       120 |        120 |         pandas.util._tester
import time:       201 |        201 |               pandas._testing._random
import time:       148 |        148 |               pandas._testing.contexts
import time:       190 |        538 |             pandas._testing._io
import time:        98 |         98 |             pandas._testing._warnings
import time:       145 |        145 |                 cmath
import time:       179 |        323 |               pandas._libs.testing
import time:       204 |        527 |             pandas._testing.asserters
import time:        81 |         81 |             pandas._testing.compat
import time:       472 |       1714 |           pandas._testing
import time:        78 |       1791 |         pandas.testing
import time:        76 |         76 |         pandas._version
import time:       326 |     180708 |       pandas
import time:       126 |        126 |           backports_abc
import time:       823 |        823 |           typing_extensions
import time:       117 |        117 |             backports_abc
import time:       111 |        111 |               backports_abc
import time:       109 |        109 |                 backports_abc
import time:       565 |        673 |               pydantic.typing
import time:      1573 |       2356 |             pydantic.errors
import time:       107 |        107 |               backports_abc
import time:       101 |        101 |                 backports_abc
import time:       209 |        310 |               pydantic.version
import time:       915 |       1331 |             pydantic.utils
import time:       529 |       4332 |           pydantic.class_validators
import time:       410 |        410 |           pydantic.config
import time:       100 |        100 |             backports_abc
import time:       853 |        853 |               ipaddress
import time:       106 |        106 |                 backports_abc
import time:       106 |        106 |                 colorsys
import time:       507 |        719 |               pydantic.color
import time:       106 |        106 |                 backports_abc
import time:        99 |         99 |                   backports_abc
import time:      1450 |       1450 |                   pydantic.datetime_parse
import time:       517 |       2065 |                 pydantic.validators
import time:       952 |       3123 |               pydantic.networks
import time:       105 |        105 |                 backports_abc
import time:      1460 |       1564 |               pydantic.types
import time:       241 |       6498 |             pydantic.json
import time:       419 |       7015 |           pydantic.error_wrappers
import time:       106 |        106 |             backports_abc
import time:       620 |        725 |           pydantic.fields
import time:       105 |        105 |             backports_abc
import time:       262 |        262 |             pydantic.parse
import time:       313 |        313 |               backports_abc
import time:       610 |        922 |             pydantic.schema
import time:      1503 |       2790 |           pydantic.main
import time:       644 |      16860 |         pydantic.dataclasses
import time:       105 |        105 |           backports_abc
import time:       265 |        370 |         pydantic.annotated_types
import time:       268 |        268 |         pydantic.decorator
import time:       102 |        102 |           backports_abc
import time:       647 |        748 |         pydantic.env_settings
import time:       238 |        238 |         pydantic.tools
import time:       301 |      18783 |       pydantic
import time:       100 |        100 |       bson
import time:       196 |        196 |               ruamel.yaml.compat
import time:       348 |        544 |             ruamel.yaml.error
import time:       157 |        157 |               ruamel.yaml.util
import time:       175 |        331 |             ruamel.yaml.reader
import time:       360 |        360 |               ruamel.yaml.tokens
import time:       427 |        786 |             ruamel.yaml.scanner
import time:       222 |        222 |               ruamel.yaml.events
import time:        83 |         83 |                   ruamel.yaml.anchor
import time:       164 |        247 |                 ruamel.yaml.scalarstring
import time:       591 |        838 |               ruamel.yaml.comments
import time:       285 |       1343 |             ruamel.yaml.parser
import time:       130 |        130 |               ruamel.yaml.nodes
import time:       135 |        265 |             ruamel.yaml.composer
import time:       155 |        155 |               ruamel.yaml.scalarint
import time:       115 |        115 |               ruamel.yaml.scalarfloat
import time:        94 |         94 |               ruamel.yaml.scalarbool
import time:        90 |         90 |               ruamel.yaml.timestamp
import time:       436 |        888 |             ruamel.yaml.constructor
import time:       898 |        898 |             ruamel.yaml.emitter
import time:       158 |        158 |             ruamel.yaml.serializer
import time:       303 |        303 |             ruamel.yaml.representer
import time:       291 |       5803 |           _ruamel_yaml
import time:       276 |        276 |           ruamel.yaml.resolver
import time:       233 |       6311 |         ruamel.yaml.cyaml
import time:       179 |        179 |           ruamel.yaml.loader
import time:       151 |        151 |           ruamel.yaml.dumper
import time:       389 |        718 |         ruamel.yaml.main
import time:       253 |       7281 |       ruamel.yaml
import time:       389 |     208011 |     monty.json
import time:       190 |        190 |       monty.io
import time:        80 |         80 |       monty.msgpack
import time:        96 |         96 |         msgpack._version
import time:       147 |        147 |         msgpack.exceptions
import time:       204 |        204 |         msgpack.ext
import time:       187 |        187 |         msgpack._cmsgpack
import time:       146 |        777 |       msgpack
import time:       116 |       1162 |     monty.serialization
import time:       123 |        123 |     dpdata.md.pbc
import time:       462 |        462 |       importlib.metadata
import time:        75 |         75 |         dpdata.siesta
import time:       120 |        195 |       dpdata.siesta.aiMD_output
import time:        80 |         80 |       dpdata.siesta.output
import time:        82 |         82 |         dpdata.plugin
import time:       134 |        216 |       dpdata.format
import time:        68 |         68 |         dpdata.xyz
import time:       141 |        208 |       dpdata.xyz.quip_gap_xyz
import time:        98 |         98 |       dpdata.xyz.xyz
import time:        68 |         68 |         dpdata.pwmat
import time:       938 |        938 |         dpdata.periodic_table
import time:       130 |       1135 |       dpdata.pwmat.atomconfig
import time:       131 |        131 |       dpdata.pwmat.movement
import time:        86 |         86 |         dpdata.pymatgen
import time:       146 |        146 |           pymatgen
import time:        97 |         97 |             monty.fractions
import time:       256 |        256 |                         scipy._lib._testutils
import time:       214 |        470 |                       scipy._lib
import time:       186 |        656 |                     scipy._lib.deprecation
import time:       155 |        155 |                     scipy.__config__
import time:        88 |         88 |                     scipy.version
import time:        76 |         76 |                     scipy._distributor_init
import time:       370 |        370 |                     scipy._lib._pep440
import time:       375 |        375 |                       scipy._lib._ccallback_c
import time:       199 |        573 |                     scipy._lib._ccallback
import time:      2843 |       4757 |                   scipy
import time:      3630 |       3630 |                   scipy.constants.codata
import time:       200 |        200 |                   scipy.constants.constants
import time:       723 |       9309 |                 scipy.constants
import time:       595 |       9903 |               pymatgen.core.units
import time:       106 |        106 |                 pymatgen.util
import time:       632 |        632 |                 fractions
import time:       203 |        940 |               pymatgen.util.string
import time:     12333 |      23176 |             pymatgen.core.periodic_table
import time:       526 |      23798 |           pymatgen.core.composition
import time:       243 |        243 |                   multiprocessing.process
import time:       208 |        208 |                   multiprocessing.reduction
import time:       429 |        879 |                 multiprocessing.context
import time:       159 |       1037 |               multiprocessing
import time:       171 |       1208 |             monty.dev
import time:        69 |         69 |                 pymatgen.util.numpy
import time:       388 |        456 |               pymatgen.util.coord_cython
import time:       208 |        664 |             pymatgen.util.coord
import time:       110 |        110 |             pymatgen.util.num
import time:       189 |        189 |                 numpy.typing._nested_sequence
import time:        74 |         74 |                 numpy.typing._nbit
import time:       687 |        687 |                 numpy.typing._char_codes
import time:       217 |        217 |                 numpy.typing._scalars
import time:       100 |        100 |                 numpy.typing._shape
import time:       189 |        189 |                   numpy.typing._generic_alias
import time:      1111 |       1299 |                 numpy.typing._dtype_like
import time:      1218 |       1218 |                 numpy.typing._array_like
import time:       390 |        390 |                 numpy.typing._add_docstring
import time:       319 |       4489 |               numpy.typing
import time:       246 |       4734 |             pymatgen.util.typing
import time:       460 |       7175 |           pymatgen.core.lattice
import time:        84 |         84 |               pymatgen.electronic_structure
import time:       463 |        546 |             pymatgen.electronic_structure.core
import time:       308 |        854 |           pymatgen.core.operations
import time:       248 |        248 |           pymatgen.core.sites
import time:       167 |        167 |                   wcwidth.table_wide
import time:       157 |        157 |                   wcwidth.table_zero
import time:        70 |         70 |                   wcwidth.unicode_versions
import time:       165 |        557 |                 wcwidth.wcwidth
import time:       119 |        675 |               wcwidth
import time:       623 |        623 |                 html.entities
import time:       315 |        937 |               html
import time:      1428 |       3039 |             tabulate
import time:       300 |        300 |             pymatgen.core.bonds
import time:        97 |         97 |               pymatgen.symmetry
import time:       446 |        446 |                   _sqlite3
import time:       244 |        690 |                 sqlite3.dbapi2
import time:       102 |        791 |               sqlite3
import time:       130 |        130 |               monty.design_patterns
import time:     12118 |      12118 |               pymatgen.symmetry.groups
import time:       175 |        175 |               pymatgen.symmetry.settings
import time:       315 |      13622 |             pymatgen.symmetry.maggroups
import time:      3270 |      20231 |           pymatgen.core.structure
import time:       483 |      52932 |         pymatgen.core
import time:       149 |      53166 |       dpdata.pymatgen.molecule
import time:       285 |        285 |         lmp
import time:       295 |        579 |       dpdata.lammps.dump
import time:       113 |        113 |       dpdata.lammps.lmp
import time:       873 |        873 |       dpdata.driver
import time:       146 |        146 |               ase.data.vdw
import time:       285 |        430 |             ase.data
import time:       224 |        654 |           ase.atom
import time:       176 |        176 |             ase.units
import time:       497 |        497 |                   ase.formula
import time:       373 |        870 |                 ase.utils
import time:       179 |       1048 |               ase.utils.arraywrapper
import time:       738 |       1786 |             ase.cell
import time:       121 |        121 |             ase.stress
import time:       125 |        125 |               ase.geometry.cell
import time:       126 |        126 |                 ase.geometry.minkowski_reduction
import time:       133 |        259 |               ase.geometry.geometry
import time:        74 |         74 |               ase.geometry.distance
import time:       102 |        558 |             ase.geometry
import time:       199 |        199 |             ase.symbols
import time:       597 |       3436 |           ase.atoms
import time:       176 |        176 |           ase.parallel
import time:       159 |       4424 |         ase
import time:        71 |         71 |             ase.calculators
import time:       306 |        306 |               ase.outputs
import time:       151 |        151 |               ase.calculators.abc
import time:       496 |        952 |             ase.calculators.calculator
import time:       204 |       1226 |           ase.calculators.singlepoint
import time:       379 |        379 |                   scipy.linalg._fblas
import time:        72 |         72 |                   scipy.linalg._cblas
import time:        30 |         30 |                   scipy.linalg._fblas_64
import time:       208 |        689 |                 scipy.linalg.blas
import time:       608 |        608 |                   scipy.linalg._flapack
import time:        52 |         52 |                   scipy.linalg._clapack
import time:        30 |         30 |                   scipy.linalg._flapack_64
import time:       347 |        347 |                   scipy._lib._util
import time:       973 |       2007 |                 scipy.linalg.lapack
import time:       166 |       2860 |               scipy.linalg.misc
import time:       152 |        152 |                   scipy.linalg._flinalg
import time:       137 |        289 |                 scipy.linalg.flinalg
import time:       217 |        217 |                 scipy.linalg.decomp
import time:       107 |        107 |                 scipy.linalg.decomp_svd
import time:       197 |        197 |                 scipy.linalg._solve_toeplitz
import time:       285 |       1093 |               scipy.linalg.basic
import time:       100 |        100 |               scipy.linalg.decomp_lu
import time:        95 |         95 |               scipy.linalg._decomp_ldl
import time:        87 |         87 |               scipy.linalg.decomp_cholesky
import time:        98 |         98 |               scipy.linalg.decomp_qr
import time:        98 |         98 |               scipy.linalg._decomp_qz
import time:       198 |        198 |               scipy.linalg.decomp_schur
import time:        85 |         85 |               scipy.linalg._decomp_polar
import time:       149 |        149 |                 scipy.linalg.special_matrices
import time:        99 |         99 |                 scipy.linalg._expm_frechet
import time:       194 |        194 |                   scipy.linalg._matfuncs_sqrtm_triu
import time:       110 |        304 |                 scipy.linalg._matfuncs_sqrtm
import time:       223 |        773 |               scipy.linalg.matfuncs
import time:       145 |        145 |               scipy.linalg._solvers
import time:        69 |         69 |               scipy.linalg._procrustes
import time:       281 |        281 |                 scipy.linalg.cython_blas
import time:       906 |        906 |                 scipy.linalg.cython_lapack
import time:       219 |       1405 |               scipy.linalg._decomp_update
import time:       133 |        133 |                     scipy.sparse.sputils
import time:       378 |        510 |                   scipy.sparse.base
import time:       853 |        853 |                     scipy.sparse._sparsetools
import time:       198 |        198 |                       scipy.sparse.data
import time:       148 |        148 |                       scipy.sparse.dia
import time:       119 |        119 |                       scipy.sparse._index
import time:       454 |        917 |                     scipy.sparse.compressed
import time:       151 |       1920 |                   scipy.sparse.csr
import time:       118 |        118 |                   scipy.sparse.csc
import time:       477 |        477 |                     scipy.sparse._csparsetools
import time:       652 |       1129 |                   scipy.sparse.lil
import time:       212 |        212 |                   scipy.sparse.dok
import time:       164 |        164 |                   scipy.sparse.coo
import time:       245 |        245 |                   scipy.sparse.bsr
import time:       156 |        156 |                   scipy.sparse.construct
import time:        78 |         78 |                   scipy.sparse.extract
import time:        78 |         78 |                   scipy.sparse._matrix_io
import time:       117 |        117 |                     scipy.sparse.csgraph._laplacian
import time:       157 |        157 |                         scipy.sparse.csgraph._tools
import time:       108 |        265 |                       scipy.sparse.csgraph._validation
import time:       242 |        507 |                     scipy.sparse.csgraph._shortest_path
import time:       150 |        150 |                     scipy.sparse.csgraph._traversal
import time:       189 |        189 |                     scipy.sparse.csgraph._min_spanning_tree
import time:       261 |        261 |                     scipy.sparse.csgraph._flow
import time:       197 |        197 |                     scipy.sparse.csgraph._matching
import time:       188 |        188 |                     scipy.sparse.csgraph._reordering
import time:       143 |       1749 |                   scipy.sparse.csgraph
import time:       293 |       6645 |                 scipy.sparse
import time:       102 |       6746 |               scipy.linalg._sketches
import time:       126 |        126 |               scipy.linalg._decomp_cossin
import time:       246 |      14218 |             scipy.linalg
import time:       121 |        121 |             ase.utils.parsemath
import time:       818 |      15155 |           ase.constraints
import time:       208 |        208 |           ase.io.jsonio
import time:       186 |        186 |           ase.io.pickletrajectory
import time:       281 |      17055 |         ase.io.trajectory
import time:       275 |        275 |           ase.io.ulm
import time:       250 |        525 |         ase.io.bundletrajectory
import time:       246 |        246 |         ase.io.netcdftrajectory
import time:       673 |        673 |         ase.io.formats
import time:       132 |      23052 |       ase.io
import time:        79 |         79 |         dpdata.cp2k
import time:       197 |        197 |         dpdata.unit
import time:        83 |         83 |         dpdata.cp2k.cell
import time:       401 |        759 |       dpdata.cp2k.output
import time:        72 |         72 |         dpdata.gaussian
import time:       176 |        176 |             swig_runtime_data4
import time:      8634 |       8810 |           openbabel.openbabel
import time:       196 |       9006 |         openbabel
import time:       233 |       9309 |       dpdata.gaussian.gjf
import time:       192 |        192 |       dpdata.gaussian.log
import time:      1198 |       1198 |         h5py._errors
import time:       569 |        569 |             h5py.defs
import time:       209 |        209 |             h5py._objects
import time:       116 |        116 |             h5py.h5py_warnings
import time:       225 |       1118 |           h5py.h5
import time:       263 |       1381 |         h5py.version
import time:       167 |        167 |           h5py.h5r
import time:       126 |        126 |                 h5py.utils
import time:       265 |        391 |               h5py.h5s
import time:       121 |        121 |               h5py.h5ac
import time:       623 |       1134 |             h5py.h5p
import time:       899 |       2033 |           h5py.h5t
import time:       275 |       2474 |         h5py._conv
import time:       186 |        186 |         h5py.h5z
import time:       132 |        132 |           h5py._proxy
import time:       259 |        391 |         h5py.h5a
import time:       370 |        370 |         h5py.h5d
import time:       190 |        190 |         h5py.h5ds
import time:       312 |        312 |           h5py.h5g
import time:       163 |        163 |           h5py.h5i
import time:       298 |        772 |         h5py.h5f
import time:       218 |        218 |         h5py.h5fd
import time:       131 |        131 |         h5py.h5pl
import time:        98 |         98 |         h5py._hl
import time:        82 |         82 |           h5py._hl.compat
import time:       284 |        366 |         h5py._hl.filters
import time:       475 |        475 |         h5py._hl.base
import time:       252 |        252 |             h5py.h5o
import time:       219 |        219 |             h5py.h5l
import time:       160 |        160 |                 backports_abc
import time:       388 |        547 |               h5py._selector
import time:       201 |        201 |               h5py._hl.selections
import time:        99 |         99 |               h5py._hl.selections2
import time:       100 |        100 |               h5py._hl.datatype
import time:       235 |        235 |               h5py._hl.vds
import time:       430 |       1610 |             h5py._hl.dataset
import time:       410 |       2489 |           h5py._hl.group
import time:       278 |       2767 |         h5py._hl.files
import time:       176 |        176 |         h5py._hl.attrs
import time:       429 |      11614 |       h5py
import time:       101 |        101 |         dpdata.deepmd
import time:        84 |         84 |         dpdata.deepmd.raw
import time:       175 |        359 |       dpdata.deepmd.comp
import time:       553 |        553 |             wcmatch.__meta__
import time:       113 |        666 |           wcmatch
import time:       380 |        380 |             bracex.__meta__
import time:       700 |       1079 |           bracex
import time:       791 |        791 |             wcmatch.util
import time:       109 |        109 |             wcmatch.posix
import time:       938 |        938 |             wcmatch._wcmatch
import time:      5316 |       7153 |           wcmatch._wcparse
import time:      1273 |      10169 |         wcmatch.glob
import time:       151 |      10320 |       dpdata.deepmd.hdf5
import time:       242 |        242 |       dpdata.deepmd.mixed
import time:       102 |        102 |         dpdata.fhi_aims
import time:       183 |        284 |       dpdata.fhi_aims.output
import time:      2559 |       2559 |           rdkit.rdBase
import time:       228 |       2787 |         rdkit
import time:        98 |         98 |           rdkit.RDPaths
import time:       160 |        160 |           pyPgSQL
import time:       194 |        451 |         rdkit.RDConfig
import time:      4763 |       4763 |           rdkit.DataStructs.cDataStructs
import time:       160 |       4923 |         rdkit.DataStructs
import time:      1427 |       1427 |           rdkit.Geometry.rdGeometry
import time:       159 |       1585 |         rdkit.Geometry
import time:      8377 |       8377 |         rdkit.Chem.rdchem
import time:      6005 |       6005 |         rdkit.Chem.rdmolfiles
import time:      6171 |       6171 |         rdkit.Chem.rdmolops
import time:       573 |        573 |         rdkit.Chem.rdCIPLabeler
import time:      1347 |       1347 |           rdkit.Chem.rdinchi
import time:       193 |        193 |           rdkit.RDLogger
import time:       277 |       1815 |         rdkit.Chem.inchi
import time:       764 |        764 |         rdkit.Chem.rdMolInterchange
import time:       703 |        703 |         rdkit.Chem.rdCoordGen
import time:       396 |      34545 |       rdkit.Chem
import time:       122 |        122 |         dpdata.rdkit
import time:       187 |        309 |       dpdata.rdkit.utils
import time:        79 |         79 |         dpdata.amber
import time:       106 |        106 |                 scipy._lib.doccer
import time:        82 |         82 |                 scipy.io.matlab.byteordercodes
import time:       268 |        455 |               scipy.io.matlab.miobase
import time:       164 |        164 |                 scipy.io.matlab.mio_utils
import time:       303 |        467 |               scipy.io.matlab.mio4
import time:       193 |        193 |                   scipy.io.matlab.streams
import time:       306 |        306 |                   scipy.io.matlab.mio5_params
import time:       314 |        811 |                 scipy.io.matlab.mio5_utils
import time:       510 |       1321 |               scipy.io.matlab.mio5
import time:       324 |       2565 |             scipy.io.matlab.mio
import time:       129 |       2694 |           scipy.io.matlab
import time:       287 |        287 |           scipy.io.netcdf
import time:       159 |        159 |           scipy.io._fortran
import time:       283 |        283 |           scipy.io.mmio
import time:       234 |        234 |           scipy.io.idl
import time:       163 |        163 |               scipy.io.harwell_boeing._fortran_format_parser
import time:       229 |        392 |             scipy.io.harwell_boeing.hb
import time:        96 |        488 |           scipy.io.harwell_boeing
import time:       146 |       4288 |         scipy.io
import time:       107 |        107 |             parmed._version
import time:       403 |        403 |             parmed.exceptions
import time:       172 |        172 |             parmed.periodic_table
import time:       263 |        263 |             parmed.residue
import time:       130 |        130 |                 openmm
import time:        13 |        143 |               openmm.unit
import time:       149 |        149 |                 parmed.unit.mymatrix
import time:       147 |        147 |                 parmed.unit.baseunit
import time:        85 |         85 |                   parmed.unit.basedimension
import time:        86 |        171 |                 parmed.unit.standard_dimensions
import time:       226 |        690 |               parmed.unit.unit
import time:       171 |        171 |               parmed.unit.quantity
import time:        81 |         81 |                   parmed.unit.unit_operators
import time:       119 |        119 |                   parmed.unit.prefix
import time:     29379 |      29578 |                 parmed.unit.unit_definitions
import time:       297 |      29874 |               parmed.unit.unit_math
import time:       647 |        647 |               parmed.unit.constants
import time:      1005 |      32528 |             parmed.unit
import time:       139 |        139 |               parmed.utils.pairlist
import time:       449 |        449 |                 parmed.constants
import time:       135 |        135 |                       openmm
import time:        72 |        207 |                     openmm.vec3
import time:       208 |        414 |                   parmed.vec3
import time:       158 |        572 |                 parmed.geometry
import time:      1831 |       2850 |               parmed.topologyobjects
import time:       235 |       3223 |             parmed.utils
import time:       127 |        127 |                 openmm
import time:       193 |        319 |               parmed.utils.decorators
import time:       163 |        163 |                 parmed.rdkit.rdkit
import time:        95 |        258 |               parmed.rdkit
import time:       104 |        104 |               openmm
import time:       871 |       1550 |             parmed.structure
import time:       144 |        144 |                           urllib.response
import time:       205 |        348 |                         urllib.error
import time:       720 |        720 |                             http
import time:      1098 |       1098 |                               _ssl
import time:      3540 |       4637 |                             ssl
import time:       853 |       6209 |                           http.client
import time:      1170 |       7379 |                         urllib.request
import time:       161 |       7887 |                       parmed.utils.io
import time:       142 |       8029 |                     parmed.formats.registry
import time:       357 |        357 |                           parmed.modeller.residue
import time:      2703 |       2703 |                           parmed.amber.offlib
import time:     14092 |      14092 |                           parmed.modeller.standardtemplates
import time:       171 |      17321 |                         parmed.modeller
import time:        25 |      17346 |                       parmed.modeller.residue
import time:       366 |      17711 |                     parmed.formats.mol2
import time:       427 |        427 |                       ftplib
import time:       320 |        320 |                           parmed.formats.pdbx.PdbxContainers
import time:       315 |        635 |                         parmed.formats.pdbx.PdbxReader
import time:       144 |        144 |                         parmed.formats.pdbx.PdbxWriter
import time:       155 |        933 |                       parmed.formats.pdbx
import time:       102 |        102 |                       parmed.symmetry
import time:      1005 |       2465 |                     parmed.formats.pdb
import time:       220 |        220 |                     parmed.formats.pqr
import time:       586 |        586 |                           parmed.charmm.psf
import time:       175 |        175 |                             parmed.parameters
import time:       141 |        141 |                             parmed.charmm._charmmfile
import time:       496 |        812 |                           parmed.charmm.parameters
import time:       211 |        211 |                           parmed.charmm.charmmcrds
import time:       132 |       1740 |                         parmed.charmm
import time:        19 |       1759 |                       parmed.charmm.psf
import time:       190 |       1948 |                     parmed.formats.psf
import time:       123 |        123 |                     parmed.formats.sdf
import time:       140 |      30632 |                   parmed.formats
import time:        17 |      30648 |                 parmed.formats.registry
import time:       195 |        195 |                         cmd
import time:       285 |        285 |                         bdb
import time:       149 |        149 |                           codeop
import time:       174 |        322 |                         code
import time:       613 |       1413 |                       pdb
import time:       105 |        105 |                         parmed.utils.fortranformat._exceptions
import time:       457 |        561 |                       parmed.utils.fortranformat._edit_descriptors
import time:       122 |        122 |                       parmed.utils.fortranformat._misc
import time:       131 |        131 |                       parmed.utils.fortranformat.config
import time:       191 |       2416 |                     parmed.utils.fortranformat._input
import time:       153 |        153 |                       parmed.utils.fortranformat._lexer
import time:       171 |        324 |                     parmed.utils.fortranformat._parser
import time:       169 |       2908 |                   parmed.utils.fortranformat.FortranRecordReader
import time:       177 |        177 |                     parmed.utils.fortranformat._output
import time:       128 |        305 |                   parmed.utils.fortranformat.FortranRecordWriter
import time:       114 |       3326 |                 parmed.utils.fortranformat
import time:       792 |      34765 |               parmed.amber.amberformat
import time:       434 |        434 |               parmed.amber.asciicrd
import time:       277 |        277 |               parmed.amber.mask
import time:       254 |        254 |                     backports_abc
import time:       852 |        852 |                     netCDF4.utils
import time:       109 |        109 |                     netCDF4.sys
import time:        42 |         42 |                     netCDF4.posixpath
import time:        38 |         38 |                     netCDF4.cftime
import time:      2545 |       2545 |                       cftime._cftime
import time:       351 |       2895 |                     cftime
import time:        91 |         91 |                     netCDF4.numpy
import time:        50 |         50 |                     netCDF4.weakref
import time:        48 |         48 |                     netCDF4.warnings
import time:        47 |         47 |                     netCDF4.subprocess
import time:        45 |         45 |                     netCDF4.pathlib
import time:        44 |         44 |                     netCDF4.glob
import time:        44 |         44 |                     netCDF4.numpy
import time:      5043 |       9596 |                   netCDF4._netCDF4
import time:       266 |       9861 |                 netCDF4
import time:       501 |        501 |                 parmed.utils.netcdf
import time:       338 |      10700 |               parmed.amber.netcdffiles
import time:      3139 |       3139 |               parmed.amber.parameters
import time:       255 |        255 |                   openmm
import time:       631 |        885 |                 parmed.amber._amberparm
import time:       246 |        246 |                 parmed.amber._chamberparm
import time:       371 |        371 |                 parmed.amber._tinkerparm
import time:       206 |       1708 |               parmed.amber.readparm
import time:       743 |        743 |               parmed.amber.titratable_residues
import time:       224 |      51986 |             parmed.amber
import time:       754 |        754 |                   parmed.gromacs._cpp
import time:       187 |        941 |                 parmed.gromacs._gromacsfile
import time:       959 |       1900 |               parmed.gromacs.gromacstop
import time:       242 |        242 |               parmed.gromacs.gromacsgro
import time:     78553 |      80694 |             parmed.gromacs
import time:       212 |        212 |               parmed.dlpoly.dlpolyfield
import time:       130 |        130 |               parmed.dlpoly.dlpolyconfig
import time:       112 |        452 |             parmed.dlpoly
import time:       180 |        180 |               parmed.namd.namdbinfiles
import time:        95 |        274 |             parmed.namd
import time:       579 |        579 |               parmed.openmm.reporters
import time:       124 |        124 |                 lxml
import time:       169 |        169 |                   backports_abc
import time:       131 |        131 |                     backports_abc
import time:       638 |        769 |                   lxml._elementpath
import time:       117 |        117 |                   rnc2rng
import time:      3598 |       4651 |                 lxml.etree
import time:       409 |       5184 |               parmed.openmm.parameters
import time:       203 |        203 |               parmed.openmm.topsystem
import time:       111 |        111 |               parmed.openmm.utils
import time:       192 |        192 |               parmed.openmm.xmlfile
import time:       120 |       6386 |             parmed.openmm
import time:       105 |        105 |                 pyrosetta
import time:       135 |        239 |               parmed.rosetta.pose
import time:        86 |        325 |             parmed.rosetta
import time:       180 |        180 |               parmed.tinker.tinkerfiles
import time:        80 |        259 |             parmed.tinker
import time:       195 |        195 |                   sierra
import time:        70 |        264 |                 parmed.entos.imports
import time:       213 |        477 |               parmed.entos.converters
import time:        84 |        560 |             parmed.entos
import time:       451 |        451 |                   parmed.tools.exceptions
import time:       240 |        691 |                 parmed.tools.argumentlist
import time:       105 |        105 |                 parmed.tools.parmlist
import time:      1289 |       2084 |               parmed.tools.actions
import time:       120 |       2204 |             parmed.tools
import time:       771 |     182151 |           parmed
import time:        99 |     182249 |         dpdata.amber.mask
import time:       172 |     186787 |       dpdata.amber.md
import time:       160 |        160 |       dpdata.amber.sqm
import time:        95 |         95 |         dpdata.psi4
import time:       186 |        281 |       dpdata.psi4.output
import time:        92 |         92 |         dpdata.qe
import time:       167 |        258 |       dpdata.qe.scf
import time:       173 |        173 |       dpdata.qe.traj
import time:        84 |         84 |         dpdata.gromacs
import time:       159 |        243 |       dpdata.gromacs.gro
import time:       140 |        140 |       dpdata.vasp.outcar
import time:       101 |        101 |       dpdata.vasp.poscar
import time:       112 |        112 |             xml
import time:       756 |        867 |           xml.etree
import time:       434 |        434 |           xml.etree.ElementPath
import time:       343 |        343 |             pyexpat
import time:       499 |        842 |           _elementtree
import time:       586 |       2728 |         xml.etree.ElementTree
import time:       111 |       2838 |       dpdata.vasp.xml
import time:       131 |        131 |       dpdata.utils
import time:        81 |         81 |         dpdata.abacus
import time:       150 |        150 |         dpdata.abacus.scf
import time:       164 |        394 |       dpdata.abacus.md
import time:       102 |        102 |       dpdata.abacus.relax
import time:       275 |        275 |       encodings.cp437
import time:       314 |        314 |       dpdata.data_type
import time:     18046 |     358465 |     dpdata.plugins
import time:      1260 |     651936 |   dpdata.system
import time:       113 |        113 |   dpdata._version
import time:       346 |        346 |     dpdata.rdkit.sanitize
import time:       200 |        546 |   dpdata.bond_order_system
import time:       264 |     653120 | dpdata
```

</details>
@link89 it will be helpful if you show the output of `python -X importtime -c 'import dpdata'` to let us know which module takes the longest time.
[log.txt](https://github.com/deepmodeling/dpdata/files/12472277/log.txt)
Here is the log.
![image](https://github.com/deepmodeling/dpdata/assets/9496702/1816132b-ebc6-4333-a710-3f300bd9cd65)



Fix patch:
diff --git a/.git_archival.txt b/.git_archival.txt
new file mode 100644
index 000000000..8fb235d70
--- /dev/null
+++ b/.git_archival.txt
@@ -0,0 +1,4 @@
+node: $Format:%H$
+node-date: $Format:%cI$
+describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$
+ref-names: $Format:%D$
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..00a7b00c9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,1 @@
+.git_archival.txt  export-subst
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 000000000..650ff59ef
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,23 @@
+name: Python package
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - name: Set up Python
+      uses: actions/setup-python@v5
+      with:
+        python-version: 3.12
+    - run: curl -LsSf https://astral.sh/uv/install.sh | sh
+    - name: Install dependencies
+      run: uv pip install --system .[amber,ase,pymatgen,benchmark] rdkit openbabel-wheel
+    - name: Run benchmarks
+      uses: CodSpeedHQ/action@v2
+      with:
+        token: ${{ secrets.CODSPEED_TOKEN }}
+        run: pytest benchmark/ --codspeed
diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml
new file mode 100644
index 000000000..73dc81f15
--- /dev/null
+++ b/.github/workflows/pyright.yml
@@ -0,0 +1,19 @@
+on:
+  - push
+  - pull_request
+
+name: Type checker
+jobs:
+  pyright:
+    name: pyright
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@master
+    - uses: actions/setup-python@v5
+      with:
+        python-version: '3.12'
+    - run: pip install uv
+    - run: uv pip install --system -e .[amber,ase,pymatgen] rdkit openbabel-wheel
+    - uses: jakebailey/pyright-action@v2
+      with:
+        version: 1.1.363
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a7a2afa8d..23bea3ebd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,7 +2,7 @@
 # See https://pre-commit.com/hooks.html for more hooks
 repos:
 -   repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.5.0
+    rev: v4.6.0
     hooks:
     # there are many log files in tests
     # TODO: seperate py files and log files
@@ -16,10 +16,12 @@ repos:
     -   id: check-merge-conflict
     -   id: check-symlinks
     -   id: check-toml
+    -   id: mixed-line-ending
+        args: ["--fix=lf"]
 # Python
 -   repo: https://github.com/astral-sh/ruff-pre-commit
     # Ruff version.
-    rev: v0.3.5
+    rev: v0.4.7
     hooks:
     - id: ruff
       args: ["--fix"]
diff --git a/benchmark/__init__.py b/benchmark/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/conf.py b/docs/conf.py
index 0f9ff0974..e3c0b3d4c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -11,6 +11,8 @@
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 #
+from __future__ import annotations
+
 import os
 import subprocess as sp
 import sys
@@ -207,3 +209,4 @@ def setup(app):
 # jupyterlite
 jupyterlite_contents = "./nb"
 jupyterlite_bind_ipynb_suffix = False
+jupyterlite_silence = False
diff --git a/docs/credits.rst b/docs/credits.rst
index 54fd98842..f8cfc8439 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -1,4 +1,4 @@
-Authors
-=======
-
+Authors
+=======
+
 .. git-shortlog-authors::
diff --git a/docs/environment.yml b/docs/environment.yml
index b0a80ffac..89d2e5cad 100644
--- a/docs/environment.yml
+++ b/docs/environment.yml
@@ -1,8 +1,9 @@
 name: dpdata
 channels:
   - https://repo.mamba.pm/emscripten-forge
-  - https://repo.mamba.pm/conda-forge
+  - conda-forge
 dependencies:
+  - xeus-python
   - numpy
   - scipy
   - monty
diff --git a/docs/make_format.py b/docs/make_format.py
index 4e78ce53a..2b3c03c67 100644
--- a/docs/make_format.py
+++ b/docs/make_format.py
@@ -1,8 +1,15 @@
+from __future__ import annotations
+
 import csv
 import os
+import sys
 from collections import defaultdict
 from inspect import Parameter, Signature, cleandoc, signature
-from typing import Literal
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+else:
+    from typing_extensions import Literal
 
 from numpydoc.docscrape import Parameter as numpydoc_Parameter
 from numpydoc.docscrape_sphinx import SphinxDocString
@@ -134,13 +141,13 @@ def generate_sub_format_pages(formats: dict):
     for format, alias in formats.items():
         # format: Format, alias: list[str]
         buff = []
-        buff.append(".. _%s:" % format.__name__)
+        buff.append(f".. _{format.__name__}:")
         buff.append("")
         for aa in alias:
-            buff.append("%s format" % aa)
+            buff.append(f"{aa} format")
             buff.append("=" * len(buff[-1]))
             buff.append("")
-        buff.append("Class: %s" % get_cls_link(format))
+        buff.append(f"Class: {get_cls_link(format)}")
         buff.append("")
 
         docstring = format.__doc__
@@ -158,10 +165,10 @@ def generate_sub_format_pages(formats: dict):
             buff.append(f".. _{format.__name__}_{method}:")
             buff.append("")
             if method.startswith("from_"):
-                buff.append("Convert from this format to %s" % method_classes[method])
+                buff.append(f"Convert from this format to {method_classes[method]}")
                 buff.append("`" * len(buff[-1]))
             elif method.startswith("to_"):
-                buff.append("Convert from %s to this format" % method_classes[method])
+                buff.append(f"Convert from {method_classes[method]} to this format")
                 buff.append("`" * len(buff[-1]))
             buff.append("")
             method_obj = getattr(format, method)
@@ -224,10 +231,7 @@ def generate_sub_format_pages(formats: dict):
                     buff.append("""   :noindex:""")
                 buff.append("")
                 if docstring is None or method not in format.__dict__:
-                    docstring = (
-                        """   Convert this format to :class:`%s`."""
-                        % (method_classes[method])
-                    )
+                    docstring = f"""   Convert this format to :class:`{method_classes[method]}`."""
                 doc_obj = SphinxDocString(docstring)
                 if len(doc_obj["Parameters"]) > 0:
                     doc_obj["Parameters"] = [
@@ -295,7 +299,7 @@ def generate_sub_format_pages(formats: dict):
                     )
                 ):
                     docstring = (
-                        "Convert :class:`%s` to this format." % (method_classes[method])
+                        f"Convert :class:`{method_classes[method]}` to this format."
                     )
                 doc_obj = SphinxDocString(docstring)
                 if len(doc_obj["Parameters"]) > 0:
@@ -324,7 +328,7 @@ def generate_sub_format_pages(formats: dict):
             buff.append("")
             buff.append("")
 
-        with open("formats/%s.rst" % format.__name__, "w") as rstfile:
+        with open(f"formats/{format.__name__}.rst", "w") as rstfile:
             rstfile.write("\n".join(buff))
 
 
@@ -342,8 +346,8 @@ def generate_sub_format_pages(formats: dict):
         for kk, vv in formats.items():
             writer.writerow(
                 {
-                    "Format": ":ref:`%s`" % kk.__name__,
-                    "Alias": "\n".join("``%s``" % vvv for vvv in vv),
+                    "Format": f":ref:`{kk.__name__}`",
+                    "Alias": "\n".join(f"``{vvv}``" for vvv in vv),
                     "Supported Conversions": "\n".join(
                         method_links[mtd].format(kk.__name__, mtd)
                         for mtd in check_supported(kk)
@@ -364,7 +368,7 @@ def generate_sub_format_pages(formats: dict):
             writer.writerow(
                 {
                     "Class": get_cls_link(kk),
-                    "Alias": "\n".join("``%s``" % vvv for vvv in vv),
+                    "Alias": "\n".join(f"``{vvv}``" for vvv in vv),
                 }
             )
 
@@ -381,7 +385,7 @@ def generate_sub_format_pages(formats: dict):
             writer.writerow(
                 {
                     "Class": get_cls_link(kk),
-                    "Alias": "\n".join("``%s``" % vvv for vvv in vv),
+                    "Alias": "\n".join(f"``{vvv}``" for vvv in vv),
                 }
             )
     generate_sub_format_pages(formats)
diff --git a/docs/nb/try_dpdata.ipynb b/docs/nb/try_dpdata.ipynb
index 7dc225b4b..1a0a73280 100644
--- a/docs/nb/try_dpdata.ipynb
+++ b/docs/nb/try_dpdata.ipynb
@@ -13,6 +13,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "from __future__ import annotations\n",
+    "\n",
     "import dpdata"
    ]
   },
diff --git a/docs/rtd_environment.yml b/docs/rtd_environment.yml
index 2752fc1cc..7eae4aad6 100644
--- a/docs/rtd_environment.yml
+++ b/docs/rtd_environment.yml
@@ -1,6 +1,7 @@
 name: dpdata
 channels:
-  - https://repo.mamba.pm/conda-forge
+  - conda-forge
 dependencies:
+  - mamba
   - pip:
     - ..[docs]
diff --git a/dpdata/__about__.py b/dpdata/__about__.py
index d5cfca647..3ee47d3c2 100644
--- a/dpdata/__about__.py
+++ b/dpdata/__about__.py
@@ -1,1 +1,3 @@
+from __future__ import annotations
+
 __version__ = "unknown"
diff --git a/dpdata/__init__.py b/dpdata/__init__.py
index dd853697e..f2cd233ff 100644
--- a/dpdata/__init__.py
+++ b/dpdata/__init__.py
@@ -1,17 +1,7 @@
-# monty needs lzma
-# See https://github.com/pandas-dev/pandas/pull/27882
-try:
-    import lzma  # noqa: F401
-except ImportError:
-
-    class fakemodule:
-        pass
-
-    import sys
-
-    sys.modules["lzma"] = fakemodule
+from __future__ import annotations
 
 from . import lammps, md, vasp
+from .bond_order_system import BondOrderSystem
 from .system import LabeledSystem, MultiSystems, System
 
 try:
@@ -19,18 +9,6 @@ class fakemodule:
 except ImportError:
     from .__about__ import __version__
 
-# BondOrder System has dependency on rdkit
-try:
-    # prevent conflict with dpdata.rdkit
-    import rdkit as _  # noqa: F401
-
-    USE_RDKIT = True
-except ModuleNotFoundError:
-    USE_RDKIT = False
-
-if USE_RDKIT:
-    from .bond_order_system import BondOrderSystem
-
 __all__ = [
     "__version__",
     "lammps",
diff --git a/dpdata/__main__.py b/dpdata/__main__.py
new file mode 100644
index 000000000..4c60f3f26
--- /dev/null
+++ b/dpdata/__main__.py
@@ -0,0 +1,6 @@
+from __future__ import annotations
+
+from dpdata.cli import dpdata_cli
+
+if __name__ == "__main__":
+    dpdata_cli()
diff --git a/dpdata/abacus/md.py b/dpdata/abacus/md.py
index 4b8ec0387..fa1841777 100644
--- a/dpdata/abacus/md.py
+++ b/dpdata/abacus/md.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import warnings
 
@@ -28,7 +30,7 @@ def get_path_out(fname, inlines):
     for line in inlines:
         if len(line) > 0 and "suffix" in line and "suffix" == line.split()[0]:
             suffix = line.split()[1]
-            path_out = os.path.join(fname, "OUT.%s/" % suffix)
+            path_out = os.path.join(fname, f"OUT.{suffix}/")
             break
     return path_out
 
@@ -189,7 +191,7 @@ def get_frame(fname):
             unconv_stru += "%d " % i
     ndump = len(energy)
     if unconv_stru != "":
-        warnings.warn("Structure %s are unconverged and not collected!" % unconv_stru)
+        warnings.warn(f"Structure {unconv_stru} are unconverged and not collected!")
 
     for iframe in range(ndump):
         stress[iframe] *= np.linalg.det(cells[iframe, :, :].reshape([3, 3]))
diff --git a/dpdata/abacus/relax.py b/dpdata/abacus/relax.py
index 346120c4a..976243b82 100644
--- a/dpdata/abacus/relax.py
+++ b/dpdata/abacus/relax.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 
 import numpy as np
@@ -183,7 +185,7 @@ def get_frame(fname):
     )
 
     logf = get_log_file(fname, inlines)
-    assert os.path.isfile(logf), "Error: can not find %s" % logf
+    assert os.path.isfile(logf), f"Error: can not find {logf}"
     with open(logf) as f1:
         lines = f1.readlines()
 
diff --git a/dpdata/abacus/scf.py b/dpdata/abacus/scf.py
index 975ef5397..193e4d4b5 100644
--- a/dpdata/abacus/scf.py
+++ b/dpdata/abacus/scf.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import re
 import warnings
@@ -23,7 +25,7 @@
 
 def CheckFile(ifile):
     if not os.path.isfile(ifile):
-        print("Can not find file %s" % ifile)
+        print(f"Can not find file {ifile}")
         return False
     return True
 
@@ -38,10 +40,10 @@ def get_block(lines, keyword, skip=0, nlines=None):
             found = True
             blk_idx = idx + 1 + skip
             line_idx = 0
-            while len(re.split("\s+", lines[blk_idx])) == 0:
+            while len(re.split(r"\s+", lines[blk_idx])) == 0:
                 blk_idx += 1
             while line_idx < nlines and blk_idx != len(lines):
-                if len(re.split("\s+", lines[blk_idx])) == 0 or lines[blk_idx] == "":
+                if len(re.split(r"\s+", lines[blk_idx])) == 0 or lines[blk_idx] == "":
                     blk_idx += 1
                     continue
                 ret.append(lines[blk_idx])
@@ -91,7 +93,7 @@ def get_path_out(fname, inlines):
     for line in inlines:
         if "suffix" in line and "suffix" == line.split()[0]:
             suffix = line.split()[1]
-            path_out = os.path.join(fname, "OUT.%s/running_scf.log" % suffix)
+            path_out = os.path.join(fname, f"OUT.{suffix}/running_scf.log")
             break
     return path_out
 
@@ -131,7 +133,7 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None):
                 tmp = np.matmul(xyz, cell)
                 xyz = tmp
             else:
-                print("coord_type = %s" % coord_type)
+                print(f"coord_type = {coord_type}")
                 raise RuntimeError(
                     "Input coordination type is invalid.\n Only direct and cartesian are accepted."
                 )
@@ -329,7 +331,7 @@ def get_nele_from_stru(geometry_inlines):
             for iline in range(
                 keyword_line_index[idx] + 1, keyword_line_index[idx + 1]
             ):
-                if len(re.split("\s+", geometry_inlines[iline])) >= 3:
+                if len(re.split(r"\s+", geometry_inlines[iline])) >= 3:
                     nele += 1
     return nele
 
@@ -367,11 +369,11 @@ def make_unlabeled_stru(
     for iele in range(len(data["atom_names"])):
         out += data["atom_names"][iele] + " "
         if mass is not None:
-            out += "%.3f " % mass[iele]
+            out += f"{mass[iele]:.3f} "
         else:
             out += "1 "
         if pp_file is not None:
-            out += "%s\n" % pp_file[iele]
+            out += f"{pp_file[iele]}\n"
         else:
             out += "\n"
     out += "\n"
@@ -380,12 +382,12 @@ def make_unlabeled_stru(
         assert len(numerical_orbital) == len(data["atom_names"])
         out += "NUMERICAL_ORBITAL\n"
         for iele in range(len(numerical_orbital)):
-            out += "%s\n" % numerical_orbital[iele]
+            out += f"{numerical_orbital[iele]}\n"
         out += "\n"
 
     if numerical_descriptor is not None:
         assert isinstance(numerical_descriptor, str)
-        out += "NUMERICAL_DESCRIPTOR\n%s\n" % numerical_descriptor
+        out += f"NUMERICAL_DESCRIPTOR\n{numerical_descriptor}\n"
         out += "\n"
 
     out += "LATTICE_CONSTANT\n"
diff --git a/dpdata/amber/mask.py b/dpdata/amber/mask.py
index e3ae1e8da..155e2a7be 100644
--- a/dpdata/amber/mask.py
+++ b/dpdata/amber/mask.py
@@ -1,5 +1,7 @@
 """Amber mask."""
 
+from __future__ import annotations
+
 try:
     import parmed
 except ImportError:
diff --git a/dpdata/amber/md.py b/dpdata/amber/md.py
index 279ce55ef..f3217fbd9 100644
--- a/dpdata/amber/md.py
+++ b/dpdata/amber/md.py
@@ -1,8 +1,9 @@
+from __future__ import annotations
+
 import os
 import re
 
 import numpy as np
-from scipy.io import netcdf_file
 
 from dpdata.amber.mask import pick_by_amber_mask
 from dpdata.unit import EnergyConversion
@@ -44,6 +45,8 @@ def read_amber_traj(
     labeled : bool
         Whether to return labeled data
     """
+    from scipy.io import netcdf_file
+
     flag_atom_type = False
     flag_atom_numb = False
     amber_types = []
diff --git a/dpdata/amber/sqm.py b/dpdata/amber/sqm.py
index 5dcbf9955..1be3802a2 100644
--- a/dpdata/amber/sqm.py
+++ b/dpdata/amber/sqm.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from dpdata.periodic_table import ELEMENTS
diff --git a/dpdata/ase_calculator.py b/dpdata/ase_calculator.py
index 65a462a52..1de760a5a 100644
--- a/dpdata/ase_calculator.py
+++ b/dpdata/ase_calculator.py
@@ -1,6 +1,8 @@
-from typing import TYPE_CHECKING, List, Optional
+from __future__ import annotations
 
-from ase.calculators.calculator import (
+from typing import TYPE_CHECKING
+
+from ase.calculators.calculator import (  # noqa: TID253
     Calculator,
     PropertyNotImplementedError,
     all_changes,
@@ -23,7 +25,10 @@ class DPDataCalculator(Calculator):
         dpdata driver
     """
 
-    name = "dpdata"
+    @property
+    def name(self) -> str:
+        return "dpdata"
+
     implemented_properties = ["energy", "free_energy", "forces", "virial", "stress"]
 
     def __init__(self, driver: Driver, **kwargs) -> None:
@@ -32,9 +37,9 @@ def __init__(self, driver: Driver, **kwargs) -> None:
 
     def calculate(
         self,
-        atoms: Optional["Atoms"] = None,
-        properties: List[str] = ["energy", "forces"],
-        system_changes: List[str] = all_changes,
+        atoms: Atoms | None = None,
+        properties: list[str] = ["energy", "forces"],
+        system_changes: list[str] = all_changes,
     ):
         """Run calculation with a driver.
 
@@ -48,10 +53,10 @@ def calculate(
         system_changes : List[str], optional
             unused, only for function signature compatibility, by default all_changes
         """
-        if atoms is not None:
-            self.atoms = atoms.copy()
+        assert atoms is not None
+        atoms = atoms.copy()
 
-        system = dpdata.System(self.atoms, fmt="ase/structure")
+        system = dpdata.System(atoms, fmt="ase/structure")
         data = system.predict(driver=self.driver).data
 
         self.results["energy"] = data["energies"][0]
diff --git a/dpdata/bond_order_system.py b/dpdata/bond_order_system.py
index e2449ee66..7a23acca5 100644
--- a/dpdata/bond_order_system.py
+++ b/dpdata/bond_order_system.py
@@ -1,9 +1,10 @@
 # %%
 # Bond Order System
+from __future__ import annotations
+
 from copy import deepcopy
 
 import numpy as np
-from rdkit.Chem import Conformer
 
 import dpdata.rdkit.utils
 from dpdata.rdkit.sanitize import Sanitizer
@@ -97,11 +98,14 @@ def from_fmt_obj(self, fmtobj, file_name, **kwargs):
         mol = fmtobj.from_bond_order_system(file_name, **kwargs)
         self.from_rdkit_mol(mol)
         if hasattr(fmtobj.from_bond_order_system, "post_func"):
-            for post_f in fmtobj.from_bond_order_system.post_func:
+            for post_f in fmtobj.from_bond_order_system.post_func:  # type: ignore
                 self.post_funcs.get_plugin(post_f)(self)
         return self
 
     def to_fmt_obj(self, fmtobj, *args, **kwargs):
+        from rdkit.Chem import Conformer
+
+        assert self.rdkit_mol is not None
         self.rdkit_mol.RemoveAllConformers()
         for ii in range(self.get_nframes()):
             conf = Conformer()
@@ -144,9 +148,9 @@ def get_formal_charges(self):
         """Return the formal charges on each atom."""
         return self.data["formal_charges"]
 
-    def copy(self):
+    def copy(self):  # type: ignore
         new_mol = deepcopy(self.rdkit_mol)
-        self.__class__(data=deepcopy(self.data), rdkit_mol=new_mol)
+        return self.__class__(data=deepcopy(self.data), rdkit_mol=new_mol)
 
     def __add__(self, other):
         raise NotImplementedError(
diff --git a/dpdata/cli.py b/dpdata/cli.py
index 07e7b4b5d..aadff1a8d 100644
--- a/dpdata/cli.py
+++ b/dpdata/cli.py
@@ -1,7 +1,8 @@
 """Command line interface for dpdata."""
 
+from __future__ import annotations
+
 import argparse
-from typing import Optional
 
 from . import __version__
 from .system import LabeledSystem, MultiSystems, System
@@ -37,9 +38,7 @@ def dpdata_parser() -> argparse.ArgumentParser:
     )
     parser.add_argument("--type-map", "-t", type=str, nargs="+", help="type map")
 
-    parser.add_argument(
-        "--version", action="version", version="dpdata v%s" % __version__
-    )
+    parser.add_argument("--version", action="version", version=f"dpdata v{__version__}")
     return parser
 
 
@@ -61,11 +60,11 @@ def convert(
     *,
     from_file: str,
     from_format: str = "auto",
-    to_file: Optional[str] = None,
-    to_format: Optional[str] = None,
+    to_file: str | None = None,
+    to_format: str | None = None,
     no_labeled: bool = False,
     multi: bool = False,
-    type_map: Optional[list] = None,
+    type_map: list | None = None,
     **kwargs,
 ):
     """Convert files from one format to another one.
diff --git a/dpdata/cp2k/cell.py b/dpdata/cp2k/cell.py
index 7af73353e..a3021b815 100644
--- a/dpdata/cp2k/cell.py
+++ b/dpdata/cp2k/cell.py
@@ -1,4 +1,5 @@
 # %%
+from __future__ import annotations
 
 import numpy as np
 
diff --git a/dpdata/cp2k/output.py b/dpdata/cp2k/output.py
index c84355c46..bd827595e 100644
--- a/dpdata/cp2k/output.py
+++ b/dpdata/cp2k/output.py
@@ -1,4 +1,6 @@
 # %%
+from __future__ import annotations
+
 import math
 import re
 from collections import OrderedDict
diff --git a/dpdata/data_type.py b/dpdata/data_type.py
index b5141a4e0..bbc7401d6 100644
--- a/dpdata/data_type.py
+++ b/dpdata/data_type.py
@@ -1,5 +1,7 @@
+from __future__ import annotations
+
 from enum import Enum, unique
-from typing import TYPE_CHECKING, Tuple
+from typing import TYPE_CHECKING
 
 import numpy as np
 
@@ -50,7 +52,7 @@ def __init__(
         self,
         name: str,
         dtype: type,
-        shape: Tuple[int, Axis] = None,
+        shape: tuple[int | Axis, ...] | None = None,
         required: bool = True,
     ) -> None:
         self.name = name
@@ -58,8 +60,9 @@ def __init__(
         self.shape = shape
         self.required = required
 
-    def real_shape(self, system: "System") -> Tuple[int]:
+    def real_shape(self, system: System) -> tuple[int]:
         """Returns expected real shape of a system."""
+        assert self.shape is not None
         shape = []
         for ii in self.shape:
             if ii is Axis.NFRAMES:
@@ -70,7 +73,7 @@ def real_shape(self, system: "System") -> Tuple[int]:
                 shape.append(system.get_natoms())
             elif ii is Axis.NBONDS:
                 # BondOrderSystem
-                shape.append(system.get_nbonds())
+                shape.append(system.get_nbonds())  # type: ignore
             elif ii == -1:
                 shape.append(AnyInt(-1))
             elif isinstance(ii, int):
@@ -79,7 +82,7 @@ def real_shape(self, system: "System") -> Tuple[int]:
                 raise RuntimeError("Shape is not an int!")
         return tuple(shape)
 
-    def check(self, system: "System"):
+    def check(self, system: System):
         """Check if a system has correct data of this type.
 
         Parameters
@@ -121,7 +124,7 @@ def check(self, system: "System"):
                 else:
                     raise RuntimeError("Unsupported type to check shape")
         elif self.required:
-            raise DataError("%s not found in data" % self.name)
+            raise DataError(f"{self.name} not found in data")
 
 
 __system_data_type_plugin = Plugin()
diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py
index 7b909b162..ab0044477 100644
--- a/dpdata/deepmd/comp.py
+++ b/dpdata/deepmd/comp.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import glob
 import os
 import shutil
diff --git a/dpdata/deepmd/hdf5.py b/dpdata/deepmd/hdf5.py
index ef54d862a..34ae9dbef 100644
--- a/dpdata/deepmd/hdf5.py
+++ b/dpdata/deepmd/hdf5.py
@@ -3,16 +3,15 @@
 from __future__ import annotations
 
 import warnings
+from typing import TYPE_CHECKING
 
-try:
-    import h5py
-except ImportError:
-    pass
 import numpy as np
-from wcmatch.glob import globfilter
 
 import dpdata
 
+if TYPE_CHECKING:
+    import h5py
+
 __all__ = ["to_system_data", "dump"]
 
 
@@ -35,6 +34,8 @@ def to_system_data(
     labels : bool
         labels
     """
+    from wcmatch.glob import globfilter
+
     g = f[folder] if folder else f
 
     data = {}
@@ -139,7 +140,7 @@ def to_system_data(
 
         for ii in sets:
             set = g[ii]
-            fn = "%s.npy" % prop["fn"]
+            fn = "{}.npy".format(prop["fn"])
             if fn in set.keys():
                 dd = set[fn][:]
                 nframes = dd.shape[0]
@@ -257,7 +258,7 @@ def dump(
         for dt, prop in data_types.items():
             if dt in reshaped_data:
                 set_folder.create_dataset(
-                    "%s.npy" % prop["fn"], data=reshaped_data[dt][set_stt:set_end]
+                    "{}.npy".format(prop["fn"]), data=reshaped_data[dt][set_stt:set_end]
                 )
 
     if nopbc:
diff --git a/dpdata/deepmd/mixed.py b/dpdata/deepmd/mixed.py
index 0d0ad89d9..b25107dbc 100644
--- a/dpdata/deepmd/mixed.py
+++ b/dpdata/deepmd/mixed.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import glob
 import os
 import shutil
diff --git a/dpdata/deepmd/raw.py b/dpdata/deepmd/raw.py
index c7a64ec47..e772714a1 100644
--- a/dpdata/deepmd/raw.py
+++ b/dpdata/deepmd/raw.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import warnings
 
diff --git a/dpdata/dftbplus/output.py b/dpdata/dftbplus/output.py
index ba8f6c840..0f10c3ac9 100644
--- a/dpdata/dftbplus/output.py
+++ b/dpdata/dftbplus/output.py
@@ -1,9 +1,9 @@
-from typing import Tuple
+from __future__ import annotations
 
 import numpy as np
 
 
-def read_dftb_plus(fn_1: str, fn_2: str) -> Tuple[str, np.ndarray, float, np.ndarray]:
+def read_dftb_plus(fn_1: str, fn_2: str) -> tuple[str, np.ndarray, float, np.ndarray]:
     """Read from DFTB+ input and output.
 
     Parameters
diff --git a/dpdata/driver.py b/dpdata/driver.py
index 81d9a9ede..b5ff53403 100644
--- a/dpdata/driver.py
+++ b/dpdata/driver.py
@@ -1,12 +1,14 @@
 """Driver plugin system."""
 
+from __future__ import annotations
+
 from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Callable, List, Union
+from typing import TYPE_CHECKING, Callable
 
 from .plugin import Plugin
 
 if TYPE_CHECKING:
-    import ase
+    import ase.calculators.calculator
 
 
 class Driver(ABC):
@@ -43,7 +45,7 @@ def register(key: str) -> Callable:
         return Driver.__DriverPlugin.register(key)
 
     @staticmethod
-    def get_driver(key: str) -> "Driver":
+    def get_driver(key: str) -> type[Driver]:
         """Get a driver plugin.
 
         Parameters
@@ -97,7 +99,7 @@ def label(self, data: dict) -> dict:
         return NotImplemented
 
     @property
-    def ase_calculator(self) -> "ase.calculators.calculator.Calculator":
+    def ase_calculator(self) -> ase.calculators.calculator.Calculator:
         """Returns an ase calculator based on this driver."""
         from .ase_calculator import DPDataCalculator
 
@@ -130,7 +132,7 @@ class HybridDriver(Driver):
     This driver is the hybrid of SQM and DP.
     """
 
-    def __init__(self, drivers: List[Union[dict, Driver]]) -> None:
+    def __init__(self, drivers: list[dict | Driver]) -> None:
         self.drivers = []
         for driver in drivers:
             if isinstance(driver, Driver):
@@ -157,6 +159,7 @@ def label(self, data: dict) -> dict:
         dict
             labeled data with energies and forces
         """
+        labeled_data = {}
         for ii, driver in enumerate(self.drivers):
             lb_data = driver.label(data.copy())
             if ii == 0:
@@ -199,7 +202,7 @@ def register(key: str) -> Callable:
         return Minimizer.__MinimizerPlugin.register(key)
 
     @staticmethod
-    def get_minimizer(key: str) -> "Minimizer":
+    def get_minimizer(key: str) -> type[Minimizer]:
         """Get a minimizer plugin.
 
         Parameters
diff --git a/dpdata/fhi_aims/output.py b/dpdata/fhi_aims/output.py
index c4cd92573..762e8bf4d 100755
--- a/dpdata/fhi_aims/output.py
+++ b/dpdata/fhi_aims/output.py
@@ -1,14 +1,16 @@
+from __future__ import annotations
+
 import re
 import warnings
 
 import numpy as np
 
-latt_patt = "\|\s+([0-9]{1,}[.][0-9]*)\s+([0-9]{1,}[.][0-9]*)\s+([0-9]{1,}[.][0-9]*)"
-pos_patt_first = "\|\s+[0-9]{1,}[:]\s\w+\s(\w+)(\s.*[-]?[0-9]{1,}[.][0-9]*)(\s+[-]?[0-9]{1,}[.][0-9]*)(\s+[-]?[0-9]{1,}[.][0-9]*)"
-pos_patt_other = "\s+[a][t][o][m]\s+([-]?[0-9]{1,}[.][0-9]*)\s+([-]?[0-9]{1,}[.][0-9]*)\s+([-]?[0-9]{1,}[.][0-9]*)\s+(\w{1,2})"
-force_patt = "\|\s+[0-9]{1,}\s+([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})\s+([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})\s+([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})"
-eng_patt = "Total energy uncorrected.*([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})\s+eV"
-# atom_numb_patt="Number of atoms.*([0-9]{1,})"
+latt_patt = r"\|\s+([0-9]{1,}[.][0-9]*)\s+([0-9]{1,}[.][0-9]*)\s+([0-9]{1,}[.][0-9]*)"
+pos_patt_first = r"\|\s+[0-9]{1,}[:]\s\w+\s(\w+)(\s.*[-]?[0-9]{1,}[.][0-9]*)(\s+[-]?[0-9]{1,}[.][0-9]*)(\s+[-]?[0-9]{1,}[.][0-9]*)"
+pos_patt_other = r"\s+[a][t][o][m]\s+([-]?[0-9]{1,}[.][0-9]*)\s+([-]?[0-9]{1,}[.][0-9]*)\s+([-]?[0-9]{1,}[.][0-9]*)\s+(\w{1,2})"
+force_patt = r"\|\s+[0-9]{1,}\s+([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})\s+([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})\s+([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})"
+eng_patt = r"Total energy uncorrected.*([-]?[0-9]{1,}[.][0-9]*[E][+-][0-9]{1,})\s+eV"
+# atom_numb_patt=r"Number of atoms.*([0-9]{1,})"
 
 debug = False
 
diff --git a/dpdata/format.py b/dpdata/format.py
index c231ef729..ade83c21c 100644
--- a/dpdata/format.py
+++ b/dpdata/format.py
@@ -1,5 +1,7 @@
 """Implement the format plugin system."""
 
+from __future__ import annotations
+
 import os
 from abc import ABC
 
@@ -163,7 +165,7 @@ def decorator(object):
             if not isinstance(func_name, (list, tuple, set)):
                 object.post_func = (func_name,)
             else:
-                object.post_func = func_name
+                object.post_func = tuple(func_name)
             return object
 
         return decorator
@@ -184,7 +186,7 @@ def from_system(self, file_name, **kwargs):
             system data, whose keys are defined in System.DTYPES
         """
         raise NotImplementedError(
-            "%s doesn't support System.from" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support System.from"
         )
 
     def to_system(self, data, *args, **kwargs):
@@ -200,7 +202,7 @@ def to_system(self, data, *args, **kwargs):
             keyword arguments that will be passed from the method
         """
         raise NotImplementedError(
-            "%s doesn't support System.to" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support System.to"
         )
 
     def from_labeled_system(self, file_name, **kwargs):
@@ -219,7 +221,7 @@ def from_labeled_system(self, file_name, **kwargs):
             system data, whose keys are defined in LabeledSystem.DTYPES
         """
         raise NotImplementedError(
-            "%s doesn't support LabeledSystem.from" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support LabeledSystem.from"
         )
 
     def to_labeled_system(self, data, *args, **kwargs):
@@ -254,7 +256,7 @@ def from_bond_order_system(self, file_name, **kwargs):
             system data
         """
         raise NotImplementedError(
-            "%s doesn't support BondOrderSystem.from" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support BondOrderSystem.from"
         )
 
     def to_bond_order_system(self, data, rdkit_mol, *args, **kwargs):
@@ -313,7 +315,7 @@ def from_multi_systems(self, directory, **kwargs):
                 if os.path.isdir(os.path.join(directory, name))
             ]
         raise NotImplementedError(
-            "%s doesn't support MultiSystems.from" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support MultiSystems.from"
         )
 
     def to_multi_systems(self, formulas, directory, **kwargs):
@@ -333,7 +335,7 @@ def to_multi_systems(self, formulas, directory, **kwargs):
         if self.MultiMode == self.MultiModes.Directory:
             return [os.path.join(directory, ff) for ff in formulas]
         raise NotImplementedError(
-            "%s doesn't support MultiSystems.to" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support MultiSystems.to"
         )
 
     def mix_system(self, *system, type_map, **kwargs):
@@ -354,5 +356,5 @@ def mix_system(self, *system, type_map, **kwargs):
             dict of mixed system with key 'atom_numbs'
         """
         raise NotImplementedError(
-            "%s doesn't support System.from" % (self.__class__.__name__)
+            f"{self.__class__.__name__} doesn't support System.from"
         )
diff --git a/dpdata/gaussian/gjf.py b/dpdata/gaussian/gjf.py
index 21300a60b..b83dad1c2 100644
--- a/dpdata/gaussian/gjf.py
+++ b/dpdata/gaussian/gjf.py
@@ -3,27 +3,19 @@
 # under LGPL 3.0 license
 """Generate Gaussian input file."""
 
+from __future__ import annotations
+
 import itertools
 import re
 import uuid
 import warnings
-from typing import List, Optional, Tuple, Union
 
 import numpy as np
-from scipy.sparse import csr_matrix
-from scipy.sparse.csgraph import connected_components
 
-try:
-    from openbabel import openbabel
-except ImportError:
-    try:
-        import openbabel
-    except ImportError:
-        openbabel = None
 from dpdata.periodic_table import Element
 
 
-def _crd2frag(symbols: List[str], crds: np.ndarray) -> Tuple[int, List[int]]:
+def _crd2frag(symbols: list[str], crds: np.ndarray) -> tuple[int, list[int]]:
     """Detect fragments from coordinates.
 
     Parameters
@@ -53,10 +45,13 @@ def _crd2frag(symbols: List[str], crds: np.ndarray) -> Tuple[int, List[int]]:
     ImportError
         if Open Babel is not installed
     """
-    if openbabel is None:
-        raise ImportError(
-            "Open Babel (Python interface) should be installed to detect fragmentation!"
-        )
+    from scipy.sparse import csr_matrix
+    from scipy.sparse.csgraph import connected_components
+
+    try:
+        from openbabel import openbabel
+    except ImportError:
+        import openbabel
     atomnumber = len(symbols)
     # Use openbabel to connect atoms
     mol = openbabel.OBMol()
@@ -108,12 +103,12 @@ def detect_multiplicity(symbols: np.ndarray) -> int:
 
 def make_gaussian_input(
     sys_data: dict,
-    keywords: Union[str, List[str]],
-    multiplicity: Union[str, int] = "auto",
+    keywords: str | list[str],
+    multiplicity: str | int = "auto",
     charge: int = 0,
     fragment_guesses: bool = False,
-    basis_set: Optional[str] = None,
-    keywords_high_multiplicity: Optional[str] = None,
+    basis_set: str | None = None,
+    keywords_high_multiplicity: str | None = None,
     nproc: int = 1,
 ) -> str:
     """Make gaussian input file.
diff --git a/dpdata/gaussian/log.py b/dpdata/gaussian/log.py
index 66881dc1a..204cf464c 100644
--- a/dpdata/gaussian/log.py
+++ b/dpdata/gaussian/log.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from ..periodic_table import ELEMENTS
diff --git a/dpdata/gromacs/gro.py b/dpdata/gromacs/gro.py
index b643eea86..aca2443b8 100644
--- a/dpdata/gromacs/gro.py
+++ b/dpdata/gromacs/gro.py
@@ -1,4 +1,6 @@
 #!/usr/bin/env python3
+from __future__ import annotations
+
 import re
 
 import numpy as np
diff --git a/dpdata/lammps/dump.py b/dpdata/lammps/dump.py
index 906fed9ee..f0ade2b03 100644
--- a/dpdata/lammps/dump.py
+++ b/dpdata/lammps/dump.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python3
+from __future__ import annotations
 
 import os
 import sys
diff --git a/dpdata/lammps/lmp.py b/dpdata/lammps/lmp.py
index 317b30ed4..604b18d12 100644
--- a/dpdata/lammps/lmp.py
+++ b/dpdata/lammps/lmp.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python3
+from __future__ import annotations
 
 import numpy as np
 
diff --git a/dpdata/md/msd.py b/dpdata/md/msd.py
index cfb446dde..dfad95507 100644
--- a/dpdata/md/msd.py
+++ b/dpdata/md/msd.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from .pbc import system_pbc_shift
diff --git a/dpdata/md/pbc.py b/dpdata/md/pbc.py
index 4eee7c654..e57576615 100644
--- a/dpdata/md/pbc.py
+++ b/dpdata/md/pbc.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 
diff --git a/dpdata/md/rdf.py b/dpdata/md/rdf.py
index de8f1c746..b41be525b 100644
--- a/dpdata/md/rdf.py
+++ b/dpdata/md/rdf.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 
diff --git a/dpdata/md/water.py b/dpdata/md/water.py
index 0cb82cc99..cda4ad48b 100644
--- a/dpdata/md/water.py
+++ b/dpdata/md/water.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from .pbc import posi_diff, posi_shift
diff --git a/dpdata/openmx/omx.py b/dpdata/openmx/omx.py
index bd4b7031e..d3afff00f 100644
--- a/dpdata/openmx/omx.py
+++ b/dpdata/openmx/omx.py
@@ -1,4 +1,6 @@
 #!/usr/bin/python3
+from __future__ import annotations
+
 import numpy as np
 
 from ..unit import (
diff --git a/dpdata/orca/output.py b/dpdata/orca/output.py
index 13f072f30..a23013fda 100644
--- a/dpdata/orca/output.py
+++ b/dpdata/orca/output.py
@@ -1,64 +1,64 @@
-from typing import Tuple
-
-import numpy as np
-
-
-def read_orca_sp_output(fn: str) -> Tuple[np.ndarray, np.ndarray, float, np.ndarray]:
-    """Read from ORCA output.
-
-    Note that both the energy and the gradient should be printed.
-
-    Parameters
-    ----------
-    fn : str
-        file name
-
-    Returns
-    -------
-    np.ndarray
-        atomic symbols
-    np.ndarray
-        atomic coordinates
-    float
-        total potential energy
-    np.ndarray
-        atomic forces
-    """
-    coord = None
-    symbols = None
-    forces = None
-    energy = None
-    with open(fn) as f:
-        flag = 0
-        for line in f:
-            if flag in (1, 3, 4):
-                flag += 1
-            elif flag == 2:
-                s = line.split()
-                if not len(s):
-                    flag = 0
-                else:
-                    symbols.append(s[0].capitalize())
-                    coord.append([float(s[1]), float(s[2]), float(s[3])])
-            elif flag == 5:
-                s = line.split()
-                if not len(s):
-                    flag = 0
-                else:
-                    forces.append([float(s[3]), float(s[4]), float(s[5])])
-            elif line.startswith("CARTESIAN COORDINATES (ANGSTROEM)"):
-                # coord
-                flag = 1
-                coord = []
-                symbols = []
-            elif line.startswith("CARTESIAN GRADIENT"):
-                flag = 3
-                forces = []
-            elif line.startswith("FINAL SINGLE POINT ENERGY"):
-                energy = float(line.split()[-1])
-    symbols = np.array(symbols)
-    forces = -np.array(forces)
-    coord = np.array(coord)
-    assert coord.shape == forces.shape
-
-    return symbols, coord, energy, forces
+from __future__ import annotations
+
+import numpy as np
+
+
+def read_orca_sp_output(fn: str) -> tuple[np.ndarray, np.ndarray, float, np.ndarray]:
+    """Read from ORCA output.
+
+    Note that both the energy and the gradient should be printed.
+
+    Parameters
+    ----------
+    fn : str
+        file name
+
+    Returns
+    -------
+    np.ndarray
+        atomic symbols
+    np.ndarray
+        atomic coordinates
+    float
+        total potential energy
+    np.ndarray
+        atomic forces
+    """
+    coord = None
+    symbols = None
+    forces = None
+    energy = None
+    with open(fn) as f:
+        flag = 0
+        for line in f:
+            if flag in (1, 3, 4):
+                flag += 1
+            elif flag == 2:
+                s = line.split()
+                if not len(s):
+                    flag = 0
+                else:
+                    symbols.append(s[0].capitalize())
+                    coord.append([float(s[1]), float(s[2]), float(s[3])])
+            elif flag == 5:
+                s = line.split()
+                if not len(s):
+                    flag = 0
+                else:
+                    forces.append([float(s[3]), float(s[4]), float(s[5])])
+            elif line.startswith("CARTESIAN COORDINATES (ANGSTROEM)"):
+                # coord
+                flag = 1
+                coord = []
+                symbols = []
+            elif line.startswith("CARTESIAN GRADIENT"):
+                flag = 3
+                forces = []
+            elif line.startswith("FINAL SINGLE POINT ENERGY"):
+                energy = float(line.split()[-1])
+    symbols = np.array(symbols)
+    forces = -np.array(forces)
+    coord = np.array(coord)
+    assert coord.shape == forces.shape
+
+    return symbols, coord, energy, forces
diff --git a/dpdata/periodic_table.py b/dpdata/periodic_table.py
index e6f16bc97..e6b56cb0b 100644
--- a/dpdata/periodic_table.py
+++ b/dpdata/periodic_table.py
@@ -1,9 +1,11 @@
-from pathlib import Path
+from __future__ import annotations
 
-from monty.serialization import loadfn
+import json
+from pathlib import Path
 
-fpdt = str(Path(__file__).absolute().parent / "periodic_table.json")
-_pdt = loadfn(fpdt)
+fpdt = Path(__file__).absolute().parent / "periodic_table.json"
+with fpdt.open("r") as fpdt:
+    _pdt = json.load(fpdt)
 ELEMENTS = [
     "H",
     "He",
@@ -108,13 +110,28 @@
     "Md",
     "No",
     "Lr",
+    "Rf",
+    "Db",
+    "Sg",
+    "Bh",
+    "Hs",
+    "Mt",
+    "Ds",
+    "Rg",
+    "Cn",
+    "Nh",
+    "Fl",
+    "Mc",
+    "Lv",
+    "Ts",
+    "Og",
 ]
 
 
 class Element:
     def __init__(self, symbol: str):
         assert symbol in ELEMENTS
-        self.symbol = "%s" % symbol
+        self.symbol = f"{symbol}"
         d = _pdt[symbol]
         self._Z = d["atomic_no"]
         self._name = d["name"]
@@ -127,7 +144,7 @@ def __str__(self):
         return self.symbol
 
     def __repr__(self):
-        return "Element : %s" % self.symbol
+        return f"Element : {self.symbol}"
 
     @classmethod
     def from_Z(cls, Z):
diff --git a/dpdata/plugin.py b/dpdata/plugin.py
index 20e51eb2d..9e18e2122 100644
--- a/dpdata/plugin.py
+++ b/dpdata/plugin.py
@@ -1,5 +1,7 @@
 """Base of plugin systems."""
 
+from __future__ import annotations
+
 
 class Plugin:
     """A class to register plugins.
diff --git a/dpdata/plugins/3dmol.py b/dpdata/plugins/3dmol.py
index ec994dd9b..56ec25161 100644
--- a/dpdata/plugins/3dmol.py
+++ b/dpdata/plugins/3dmol.py
@@ -1,4 +1,4 @@
-from typing import Tuple
+from __future__ import annotations
 
 import numpy as np
 
@@ -17,7 +17,7 @@ def to_system(
         self,
         data: dict,
         f_idx: int = 0,
-        size: Tuple[int] = (300, 300),
+        size: tuple[int] = (300, 300),
         style: dict = {"stick": {}, "sphere": {"radius": 0.4}},
         **kwargs,
     ):
diff --git a/dpdata/plugins/__init__.py b/dpdata/plugins/__init__.py
index 66364aa25..15634bc0a 100644
--- a/dpdata/plugins/__init__.py
+++ b/dpdata/plugins/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import importlib
 from pathlib import Path
 
diff --git a/dpdata/plugins/abacus.py b/dpdata/plugins/abacus.py
index 754221be0..eb2d7786f 100644
--- a/dpdata/plugins/abacus.py
+++ b/dpdata/plugins/abacus.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.abacus.md
 import dpdata.abacus.relax
 import dpdata.abacus.scf
diff --git a/dpdata/plugins/amber.py b/dpdata/plugins/amber.py
index cdc92a30b..42fce5528 100644
--- a/dpdata/plugins/amber.py
+++ b/dpdata/plugins/amber.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import subprocess as sp
 import tempfile
@@ -124,7 +126,7 @@ class SQMDriver(Driver):
     -15.41111246
     """
 
-    def __init__(self, sqm_exec: str = "sqm", **kwargs: dict) -> None:
+    def __init__(self, sqm_exec: str = "sqm", **kwargs) -> None:
         self.sqm_exec = sqm_exec
         self.kwargs = kwargs
 
diff --git a/dpdata/plugins/ase.py b/dpdata/plugins/ase.py
index 127611f65..2ca229268 100644
--- a/dpdata/plugins/ase.py
+++ b/dpdata/plugins/ase.py
@@ -1,4 +1,7 @@
-from typing import TYPE_CHECKING, Optional, Type
+from __future__ import annotations
+
+import os
+from typing import TYPE_CHECKING, Generator
 
 import numpy as np
 
@@ -6,15 +9,9 @@
 from dpdata.driver import Driver, Minimizer
 from dpdata.format import Format
 
-try:
-    import ase.io
-    from ase.calculators.calculator import PropertyNotImplementedError
-    from ase.io import Trajectory
-
-    if TYPE_CHECKING:
-        from ase.optimize.optimize import Optimizer
-except ImportError:
-    pass
+if TYPE_CHECKING:
+    import ase
+    from ase.optimize.optimize import Optimizer
 
 
 @Format.register("ase/structure")
@@ -28,7 +25,7 @@ class ASEStructureFormat(Format):
     automatic detection fails.
     """
 
-    def from_system(self, atoms: "ase.Atoms", **kwargs) -> dict:
+    def from_system(self, atoms: ase.Atoms, **kwargs) -> dict:
         """Convert ase.Atoms to a System.
 
         Parameters
@@ -62,7 +59,7 @@ def from_system(self, atoms: "ase.Atoms", **kwargs) -> dict:
         }
         return info_dict
 
-    def from_labeled_system(self, atoms: "ase.Atoms", **kwargs) -> dict:
+    def from_labeled_system(self, atoms: ase.Atoms, **kwargs) -> dict:
         """Convert ase.Atoms to a LabeledSystem. Energies and forces
         are calculated by the calculator.
 
@@ -84,6 +81,8 @@ def from_labeled_system(self, atoms: "ase.Atoms", **kwargs) -> dict:
             ASE will raise RuntimeError if the atoms does not
             have a calculator
         """
+        from ase.calculators.calculator import PropertyNotImplementedError
+
         info_dict = self.from_system(atoms)
         try:
             energies = atoms.get_potential_energy(force_consistent=True)
@@ -96,7 +95,7 @@ def from_labeled_system(self, atoms: "ase.Atoms", **kwargs) -> dict:
             "forces": np.array([forces]),
         }
         try:
-            stress = atoms.get_stress(False)
+            stress = atoms.get_stress(voigt=False)
         except PropertyNotImplementedError:
             pass
         else:
@@ -107,12 +106,12 @@ def from_labeled_system(self, atoms: "ase.Atoms", **kwargs) -> dict:
     def from_multi_systems(
         self,
         file_name: str,
-        begin: Optional[int] = None,
-        end: Optional[int] = None,
-        step: Optional[int] = None,
-        ase_fmt: Optional[str] = None,
+        begin: int | None = None,
+        end: int | None = None,
+        step: int | None = None,
+        ase_fmt: str | None = None,
         **kwargs,
-    ) -> "ase.Atoms":
+    ) -> Generator[ase.Atoms, None, None]:
         """Convert a ASE supported file to ASE Atoms.
 
         It will finally be converted to MultiSystems.
@@ -137,10 +136,12 @@ def from_multi_systems(
         ase.Atoms
             ASE atoms in the file
         """
+        import ase.io
+
         frames = ase.io.read(file_name, format=ase_fmt, index=slice(begin, end, step))
         yield from frames
 
-    def to_system(self, data, **kwargs):
+    def to_system(self, data, **kwargs) -> list[ase.Atoms]:
         """Convert System to ASE Atom obj."""
         from ase import Atoms
 
@@ -158,7 +159,7 @@ def to_system(self, data, **kwargs):
 
         return structures
 
-    def to_labeled_system(self, data, *args, **kwargs):
+    def to_labeled_system(self, data, *args, **kwargs) -> list[ase.Atoms]:
         """Convert System to ASE Atoms object."""
         from ase import Atoms
         from ase.calculators.singlepoint import SinglePointCalculator
@@ -197,9 +198,9 @@ class ASETrajFormat(Format):
     def from_system(
         self,
         file_name: str,
-        begin: Optional[int] = 0,
-        end: Optional[int] = None,
-        step: Optional[int] = 1,
+        begin: int | None = 0,
+        end: int | None = None,
+        step: int | None = 1,
         **kwargs,
     ) -> dict:
         """Read ASE's trajectory file to `System` of multiple frames.
@@ -222,6 +223,8 @@ def from_system(
         dict_frames: dict
             a dictionary containing data of multiple frames
         """
+        from ase.io import Trajectory
+
         traj = Trajectory(file_name)
         sub_traj = traj[begin:end:step]
         dict_frames = ASEStructureFormat().from_system(sub_traj[0])
@@ -239,9 +242,9 @@ def from_system(
     def from_labeled_system(
         self,
         file_name: str,
-        begin: Optional[int] = 0,
-        end: Optional[int] = None,
-        step: Optional[int] = 1,
+        begin: int | None = 0,
+        end: int | None = None,
+        step: int | None = 1,
         **kwargs,
     ) -> dict:
         """Read ASE's trajectory file to `System` of multiple frames.
@@ -264,6 +267,8 @@ def from_labeled_system(
         dict_frames: dict
             a dictionary containing data of multiple frames
         """
+        from ase.io import Trajectory
+
         traj = Trajectory(file_name)
         sub_traj = traj[begin:end:step]
 
@@ -296,6 +301,46 @@ def from_labeled_system(
 
         return dict_frames
 
+    def to_system(self, data, file_name: str = "confs.traj", **kwargs) -> None:
+        """Convert System to ASE Atoms object.
+
+        Parameters
+        ----------
+        file_name : str
+            path to file
+        """
+        from ase.io import Trajectory
+
+        if os.path.isfile(file_name):
+            os.remove(file_name)
+
+        list_atoms = ASEStructureFormat().to_system(data, **kwargs)
+        traj = Trajectory(file_name, "a")
+        _ = [traj.write(atom) for atom in list_atoms]
+        traj.close()
+        return
+
+    def to_labeled_system(
+        self, data, file_name: str = "labeled_confs.traj", *args, **kwargs
+    ) -> None:
+        """Convert System to ASE Atoms object.
+
+        Parameters
+        ----------
+        file_name : str
+            path to file
+        """
+        from ase.io import Trajectory
+
+        if os.path.isfile(file_name):
+            os.remove(file_name)
+
+        list_atoms = ASEStructureFormat().to_labeled_system(data, *args, **kwargs)
+        traj = Trajectory(file_name, "a")
+        _ = [traj.write(atom) for atom in list_atoms]
+        traj.close()
+        return
+
 
 @Driver.register("ase")
 class ASEDriver(Driver):
@@ -307,7 +352,7 @@ class ASEDriver(Driver):
         ASE calculator
     """
 
-    def __init__(self, calculator: "ase.calculators.calculator.Calculator") -> None:
+    def __init__(self, calculator: ase.calculators.calculator.Calculator) -> None:
         """Setup the driver."""
         self.calculator = calculator
 
@@ -359,9 +404,9 @@ class ASEMinimizer(Minimizer):
     def __init__(
         self,
         driver: Driver,
-        optimizer: Optional[Type["Optimizer"]] = None,
+        optimizer: type[Optimizer] | None = None,
         fmax: float = 5e-3,
-        max_steps: Optional[int] = None,
+        max_steps: int | None = None,
         optimizer_kwargs: dict = {},
     ) -> None:
         self.calculator = driver.ase_calculator
diff --git a/dpdata/plugins/cp2k.py b/dpdata/plugins/cp2k.py
index 162098f70..f5c1b5394 100644
--- a/dpdata/plugins/cp2k.py
+++ b/dpdata/plugins/cp2k.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import glob
 
 import dpdata.cp2k.output
diff --git a/dpdata/plugins/deepmd.py b/dpdata/plugins/deepmd.py
index 9daadcf38..1ed79f72e 100644
--- a/dpdata/plugins/deepmd.py
+++ b/dpdata/plugins/deepmd.py
@@ -1,11 +1,8 @@
 from __future__ import annotations
 
 import os
+from typing import TYPE_CHECKING
 
-try:
-    import h5py
-except ImportError:
-    pass
 import numpy as np
 
 import dpdata
@@ -16,6 +13,9 @@
 from dpdata.driver import Driver
 from dpdata.format import Format
 
+if TYPE_CHECKING:
+    import h5py
+
 
 @Format.register("deepmd")
 @Format.register("deepmd/raw")
@@ -202,6 +202,8 @@ def _from_system(
         TypeError
             file_name is not str or h5py.Group or h5py.File
         """
+        import h5py
+
         if isinstance(file_name, (h5py.Group, h5py.File)):
             return dpdata.deepmd.hdf5.to_system_data(
                 file_name, "", type_map=type_map, labels=labels
@@ -300,6 +302,8 @@ def to_system(
         **kwargs : dict
             other parameters
         """
+        import h5py
+
         if isinstance(file_name, (h5py.Group, h5py.File)):
             dpdata.deepmd.hdf5.dump(
                 file_name, "", data, set_size=set_size, comp_prec=comp_prec
@@ -330,6 +334,8 @@ def from_multi_systems(self, directory: str, **kwargs) -> h5py.Group:
         h5py.Group
             a HDF5 group in the HDF5 file
         """
+        import h5py
+
         with h5py.File(directory, "r") as f:
             for ff in f.keys():
                 yield f[ff]
@@ -353,6 +359,8 @@ def to_multi_systems(
         h5py.Group
             a HDF5 group with the name of formula
         """
+        import h5py
+
         with h5py.File(directory, "w") as f:
             for ff in formulas:
                 yield f.create_group(ff)
diff --git a/dpdata/plugins/dftbplus.py b/dpdata/plugins/dftbplus.py
index 5c8b46828..247fedc9e 100644
--- a/dpdata/plugins/dftbplus.py
+++ b/dpdata/plugins/dftbplus.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from dpdata.dftbplus.output import read_dftb_plus
diff --git a/dpdata/plugins/fhi_aims.py b/dpdata/plugins/fhi_aims.py
index 45b181fc0..3c198aff6 100644
--- a/dpdata/plugins/fhi_aims.py
+++ b/dpdata/plugins/fhi_aims.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.fhi_aims.output
 from dpdata.format import Format
 
diff --git a/dpdata/plugins/gaussian.py b/dpdata/plugins/gaussian.py
index a22ce8630..b55447b91 100644
--- a/dpdata/plugins/gaussian.py
+++ b/dpdata/plugins/gaussian.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import subprocess as sp
 import tempfile
@@ -81,7 +83,7 @@ class GaussianDriver(Driver):
     -1102.714590995794
     """
 
-    def __init__(self, gaussian_exec: str = "g16", **kwargs: dict) -> None:
+    def __init__(self, gaussian_exec: str = "g16", **kwargs) -> None:
         self.gaussian_exec = gaussian_exec
         self.kwargs = kwargs
 
diff --git a/dpdata/plugins/gromacs.py b/dpdata/plugins/gromacs.py
index 20e508355..12dece718 100644
--- a/dpdata/plugins/gromacs.py
+++ b/dpdata/plugins/gromacs.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.gromacs.gro
 from dpdata.format import Format
 
diff --git a/dpdata/plugins/lammps.py b/dpdata/plugins/lammps.py
index be89be9d0..65e7f5701 100644
--- a/dpdata/plugins/lammps.py
+++ b/dpdata/plugins/lammps.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.lammps.dump
 import dpdata.lammps.lmp
 from dpdata.format import Format
diff --git a/dpdata/plugins/list.py b/dpdata/plugins/list.py
index 68a140748..f70368836 100644
--- a/dpdata/plugins/list.py
+++ b/dpdata/plugins/list.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from dpdata.format import Format
 
 
diff --git a/dpdata/plugins/n2p2.py b/dpdata/plugins/n2p2.py
index 7162f09fa..b70d6e6fb 100644
--- a/dpdata/plugins/n2p2.py
+++ b/dpdata/plugins/n2p2.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from dpdata.format import Format
diff --git a/dpdata/plugins/openmx.py b/dpdata/plugins/openmx.py
index 675d1d2c1..4e16566dc 100644
--- a/dpdata/plugins/openmx.py
+++ b/dpdata/plugins/openmx.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.md.pbc
 import dpdata.openmx.omx
 from dpdata.format import Format
diff --git a/dpdata/plugins/orca.py b/dpdata/plugins/orca.py
index 2585743e1..9dc32c32c 100644
--- a/dpdata/plugins/orca.py
+++ b/dpdata/plugins/orca.py
@@ -1,51 +1,53 @@
-import numpy as np
-
-from dpdata.format import Format
-from dpdata.orca.output import read_orca_sp_output
-from dpdata.unit import EnergyConversion, ForceConversion
-
-energy_convert = EnergyConversion("hartree", "eV").value()
-force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value()
-
-
-@Format.register("orca/spout")
-class ORCASPOutFormat(Format):
-    """ORCA single point energy output.
-
-    Note that both the energy and the gradient should be
-    printed into the output file.
-    """
-
-    def from_labeled_system(self, file_name: str, **kwargs) -> dict:
-        """Read from ORCA single point energy output.
-
-        Parameters
-        ----------
-        file_name : str
-            file name
-        **kwargs
-            keyword arguments
-
-        Returns
-        -------
-        dict
-            system data
-        """
-        symbols, coord, energy, forces = read_orca_sp_output(file_name)
-
-        atom_names, atom_types, atom_numbs = np.unique(
-            symbols, return_inverse=True, return_counts=True
-        )
-        natoms = coord.shape[0]
-
-        return {
-            "atom_types": atom_types,
-            "atom_names": list(atom_names),
-            "atom_numbs": list(atom_numbs),
-            "coords": coord.reshape((1, natoms, 3)),
-            "energies": np.array([energy * energy_convert]),
-            "forces": (forces * force_convert).reshape((1, natoms, 3)),
-            "cells": np.zeros((1, 3, 3)),
-            "orig": np.zeros(3),
-            "nopbc": True,
-        }
+from __future__ import annotations
+
+import numpy as np
+
+from dpdata.format import Format
+from dpdata.orca.output import read_orca_sp_output
+from dpdata.unit import EnergyConversion, ForceConversion
+
+energy_convert = EnergyConversion("hartree", "eV").value()
+force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value()
+
+
+@Format.register("orca/spout")
+class ORCASPOutFormat(Format):
+    """ORCA single point energy output.
+
+    Note that both the energy and the gradient should be
+    printed into the output file.
+    """
+
+    def from_labeled_system(self, file_name: str, **kwargs) -> dict:
+        """Read from ORCA single point energy output.
+
+        Parameters
+        ----------
+        file_name : str
+            file name
+        **kwargs
+            keyword arguments
+
+        Returns
+        -------
+        dict
+            system data
+        """
+        symbols, coord, energy, forces = read_orca_sp_output(file_name)
+
+        atom_names, atom_types, atom_numbs = np.unique(
+            symbols, return_inverse=True, return_counts=True
+        )
+        natoms = coord.shape[0]
+
+        return {
+            "atom_types": atom_types,
+            "atom_names": list(atom_names),
+            "atom_numbs": list(atom_numbs),
+            "coords": coord.reshape((1, natoms, 3)),
+            "energies": np.array([energy * energy_convert]),
+            "forces": (forces * force_convert).reshape((1, natoms, 3)),
+            "cells": np.zeros((1, 3, 3)),
+            "orig": np.zeros(3),
+            "nopbc": True,
+        }
diff --git a/dpdata/plugins/psi4.py b/dpdata/plugins/psi4.py
index ec7d9df1b..a0cf00e46 100644
--- a/dpdata/plugins/psi4.py
+++ b/dpdata/plugins/psi4.py
@@ -1,102 +1,104 @@
-import numpy as np
-
-from dpdata.format import Format
-from dpdata.psi4.input import write_psi4_input
-from dpdata.psi4.output import read_psi4_output
-from dpdata.unit import EnergyConversion, ForceConversion
-
-energy_convert = EnergyConversion("hartree", "eV").value()
-force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value()
-
-
-@Format.register("psi4/out")
-class PSI4OutFormat(Format):
-    """Psi4 output.
-
-    Note that both the energy and the gradient should be
-    printed into the output file.
-    """
-
-    def from_labeled_system(self, file_name: str, **kwargs) -> dict:
-        """Read from Psi4 output.
-
-        Parameters
-        ----------
-        file_name : str
-            file name
-        **kwargs
-            keyword arguments
-
-        Returns
-        -------
-        dict
-            system data
-        """
-        symbols, coord, energy, forces = read_psi4_output(file_name)
-
-        atom_names, atom_types, atom_numbs = np.unique(
-            symbols, return_inverse=True, return_counts=True
-        )
-        natoms = coord.shape[0]
-
-        return {
-            "atom_types": atom_types,
-            "atom_names": list(atom_names),
-            "atom_numbs": list(atom_numbs),
-            "coords": (coord).reshape((1, natoms, 3)),
-            "energies": np.array([energy * energy_convert]),
-            "forces": (forces * force_convert).reshape((1, natoms, 3)),
-            "cells": np.zeros((1, 3, 3)),
-            "orig": np.zeros(3),
-            "nopbc": True,
-        }
-
-
-@Format.register("psi4/inp")
-class PSI4InputFormat(Format):
-    """Psi4 input file."""
-
-    def to_system(
-        self,
-        data: dict,
-        file_name: str,
-        method: str,
-        basis: str,
-        charge: int = 0,
-        multiplicity: int = 1,
-        frame_idx=0,
-        **kwargs,
-    ):
-        """Write PSI4 input.
-
-        Parameters
-        ----------
-        data : dict
-            system data
-        file_name : str
-            file name
-        method : str
-            computational method
-        basis : str
-            basis set; see https://psicode.org/psi4manual/master/basissets_tables.html
-        charge : int, default=0
-            charge of system
-        multiplicity : int, default=1
-            multiplicity of system
-        frame_idx : int, default=0
-            The index of the frame to dump
-        **kwargs
-            keyword arguments
-        """
-        types = np.array(data["atom_names"])[data["atom_types"]]
-        with open(file_name, "w") as fout:
-            fout.write(
-                write_psi4_input(
-                    types=types,
-                    coords=data["coords"][frame_idx],
-                    method=method,
-                    basis=basis,
-                    charge=charge,
-                    multiplicity=multiplicity,
-                )
-            )
+from __future__ import annotations
+
+import numpy as np
+
+from dpdata.format import Format
+from dpdata.psi4.input import write_psi4_input
+from dpdata.psi4.output import read_psi4_output
+from dpdata.unit import EnergyConversion, ForceConversion
+
+energy_convert = EnergyConversion("hartree", "eV").value()
+force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value()
+
+
+@Format.register("psi4/out")
+class PSI4OutFormat(Format):
+    """Psi4 output.
+
+    Note that both the energy and the gradient should be
+    printed into the output file.
+    """
+
+    def from_labeled_system(self, file_name: str, **kwargs) -> dict:
+        """Read from Psi4 output.
+
+        Parameters
+        ----------
+        file_name : str
+            file name
+        **kwargs
+            keyword arguments
+
+        Returns
+        -------
+        dict
+            system data
+        """
+        symbols, coord, energy, forces = read_psi4_output(file_name)
+
+        atom_names, atom_types, atom_numbs = np.unique(
+            symbols, return_inverse=True, return_counts=True
+        )
+        natoms = coord.shape[0]
+
+        return {
+            "atom_types": atom_types,
+            "atom_names": list(atom_names),
+            "atom_numbs": list(atom_numbs),
+            "coords": (coord).reshape((1, natoms, 3)),
+            "energies": np.array([energy * energy_convert]),
+            "forces": (forces * force_convert).reshape((1, natoms, 3)),
+            "cells": np.zeros((1, 3, 3)),
+            "orig": np.zeros(3),
+            "nopbc": True,
+        }
+
+
+@Format.register("psi4/inp")
+class PSI4InputFormat(Format):
+    """Psi4 input file."""
+
+    def to_system(
+        self,
+        data: dict,
+        file_name: str,
+        method: str,
+        basis: str,
+        charge: int = 0,
+        multiplicity: int = 1,
+        frame_idx=0,
+        **kwargs,
+    ):
+        """Write PSI4 input.
+
+        Parameters
+        ----------
+        data : dict
+            system data
+        file_name : str
+            file name
+        method : str
+            computational method
+        basis : str
+            basis set; see https://psicode.org/psi4manual/master/basissets_tables.html
+        charge : int, default=0
+            charge of system
+        multiplicity : int, default=1
+            multiplicity of system
+        frame_idx : int, default=0
+            The index of the frame to dump
+        **kwargs
+            keyword arguments
+        """
+        types = np.array(data["atom_names"])[data["atom_types"]]
+        with open(file_name, "w") as fout:
+            fout.write(
+                write_psi4_input(
+                    types=types,
+                    coords=data["coords"][frame_idx],
+                    method=method,
+                    basis=basis,
+                    charge=charge,
+                    multiplicity=multiplicity,
+                )
+            )
diff --git a/dpdata/plugins/pwmat.py b/dpdata/plugins/pwmat.py
index 11257c4d0..80f219b6c 100644
--- a/dpdata/plugins/pwmat.py
+++ b/dpdata/plugins/pwmat.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 import dpdata.pwmat.atomconfig
diff --git a/dpdata/plugins/pymatgen.py b/dpdata/plugins/pymatgen.py
index 82b64e715..322298c3c 100644
--- a/dpdata/plugins/pymatgen.py
+++ b/dpdata/plugins/pymatgen.py
@@ -1,11 +1,31 @@
+from __future__ import annotations
+
 import numpy as np
 
 import dpdata.pymatgen.molecule
+import dpdata.pymatgen.structure
 from dpdata.format import Format
 
 
 @Format.register("pymatgen/structure")
 class PyMatgenStructureFormat(Format):
+    def from_system(self, structure, **kwargs) -> dict:
+        """Convert pymatgen.core.Structure to System.
+
+        Parameters
+        ----------
+        structure : pymatgen.core.Structure
+            a Pymatgen Structure, containing a structure
+        **kwargs : dict
+            other parameters
+
+        Returns
+        -------
+        dict
+            data dict
+        """
+        return dpdata.pymatgen.structure.from_system_data(structure)
+
     def to_system(self, data, **kwargs):
         """Convert System to Pymatgen Structure obj."""
         structures = []
diff --git a/dpdata/plugins/qe.py b/dpdata/plugins/qe.py
index 6a98eedd8..682bb202e 100644
--- a/dpdata/plugins/qe.py
+++ b/dpdata/plugins/qe.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.md.pbc
 import dpdata.qe.scf
 import dpdata.qe.traj
diff --git a/dpdata/plugins/rdkit.py b/dpdata/plugins/rdkit.py
index ff7638cb6..f01b277d6 100644
--- a/dpdata/plugins/rdkit.py
+++ b/dpdata/plugins/rdkit.py
@@ -1,20 +1,20 @@
-from dpdata.format import Format
-
-try:
-    import rdkit.Chem
+from __future__ import annotations
 
-    import dpdata.rdkit.utils
-except ModuleNotFoundError:
-    pass
+import dpdata.rdkit.utils
+from dpdata.format import Format
 
 
 @Format.register("mol")
 @Format.register("mol_file")
 class MolFormat(Format):
     def from_bond_order_system(self, file_name, **kwargs):
+        import rdkit.Chem
+
         return rdkit.Chem.MolFromMolFile(file_name, sanitize=False, removeHs=False)
 
     def to_bond_order_system(self, data, mol, file_name, frame_idx=0, **kwargs):
+        import rdkit.Chem
+
         assert frame_idx < mol.GetNumConformers()
         rdkit.Chem.MolToMolFile(mol, file_name, confId=frame_idx)
 
@@ -24,6 +24,8 @@ def to_bond_order_system(self, data, mol, file_name, frame_idx=0, **kwargs):
 class SdfFormat(Format):
     def from_bond_order_system(self, file_name, **kwargs):
         """Note that it requires all molecules in .sdf file must be of the same topology."""
+        import rdkit.Chem
+
         mols = [
             m
             for m in rdkit.Chem.SDMolSupplier(file_name, sanitize=False, removeHs=False)
@@ -35,6 +37,8 @@ def from_bond_order_system(self, file_name, **kwargs):
         return mol
 
     def to_bond_order_system(self, data, mol, file_name, frame_idx=-1, **kwargs):
+        import rdkit.Chem
+
         sdf_writer = rdkit.Chem.SDWriter(file_name)
         if frame_idx == -1:
             for ii in range(mol.GetNumConformers()):
diff --git a/dpdata/plugins/siesta.py b/dpdata/plugins/siesta.py
index 662b5c0e0..906eeb51f 100644
--- a/dpdata/plugins/siesta.py
+++ b/dpdata/plugins/siesta.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import dpdata.siesta.aiMD_output
 import dpdata.siesta.output
 from dpdata.format import Format
diff --git a/dpdata/plugins/vasp.py b/dpdata/plugins/vasp.py
index c182bb956..d0681cebf 100644
--- a/dpdata/plugins/vasp.py
+++ b/dpdata/plugins/vasp.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 import dpdata.vasp.outcar
diff --git a/dpdata/plugins/xyz.py b/dpdata/plugins/xyz.py
index fdb5bf3b1..322bf77cb 100644
--- a/dpdata/plugins/xyz.py
+++ b/dpdata/plugins/xyz.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from dpdata.format import Format
diff --git a/dpdata/psi4/input.py b/dpdata/psi4/input.py
index ad0532817..3959cb753 100644
--- a/dpdata/psi4/input.py
+++ b/dpdata/psi4/input.py
@@ -1,4 +1,9 @@
-import numpy as np
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    import numpy as np
 
 # Angston is used in Psi4 by default
 template = """molecule {{
diff --git a/dpdata/psi4/output.py b/dpdata/psi4/output.py
index e93858de8..c06eb1829 100644
--- a/dpdata/psi4/output.py
+++ b/dpdata/psi4/output.py
@@ -1,74 +1,74 @@
-from typing import Tuple
-
-import numpy as np
-
-from dpdata.unit import LengthConversion
-
-
-def read_psi4_output(fn: str) -> Tuple[str, np.ndarray, float, np.ndarray]:
-    """Read from Psi4 output.
-
-    Note that both the energy and the gradient should be printed.
-
-    Parameters
-    ----------
-    fn : str
-        file name
-
-    Returns
-    -------
-    str
-        atomic symbols
-    np.ndarray
-        atomic coordinates
-    float
-        total potential energy
-    np.ndarray
-        atomic forces
-    """
-    coord = None
-    symbols = None
-    forces = None
-    energy = None
-    length_unit = None
-    with open(fn) as f:
-        flag = 0
-        for line in f:
-            if flag in (1, 3, 4, 5, 6):
-                flag += 1
-            elif flag == 2:
-                s = line.split()
-                if not len(s):
-                    flag = 0
-                else:
-                    symbols.append(s[0].capitalize())
-                    coord.append([float(s[1]), float(s[2]), float(s[3])])
-            elif flag == 7:
-                s = line.split()
-                if not len(s):
-                    flag = 0
-                else:
-                    forces.append([float(s[1]), float(s[2]), float(s[3])])
-            elif line.startswith(
-                "       Center              X                  Y                   Z               Mass"
-            ):
-                # coord
-                flag = 1
-                coord = []
-                symbols = []
-            elif line.startswith("    Geometry (in "):
-                # remove ),
-                length_unit = line.split()[2][:-2].lower()
-            elif line.startswith("  ## Total Gradient"):
-                flag = 3
-                forces = []
-            elif line.startswith("    Total Energy ="):
-                energy = float(line.split()[-1])
-    assert length_unit is not None
-    length_convert = LengthConversion(length_unit, "angstrom").value()
-    symbols = np.array(symbols)
-    forces = -np.array(forces)
-    coord = np.array(coord) * length_convert
-    assert coord.shape == forces.shape
-
-    return symbols, coord, energy, forces
+from __future__ import annotations
+
+import numpy as np
+
+from dpdata.unit import LengthConversion
+
+
+def read_psi4_output(fn: str) -> tuple[str, np.ndarray, float, np.ndarray]:
+    """Read from Psi4 output.
+
+    Note that both the energy and the gradient should be printed.
+
+    Parameters
+    ----------
+    fn : str
+        file name
+
+    Returns
+    -------
+    str
+        atomic symbols
+    np.ndarray
+        atomic coordinates
+    float
+        total potential energy
+    np.ndarray
+        atomic forces
+    """
+    coord = None
+    symbols = None
+    forces = None
+    energy = None
+    length_unit = None
+    with open(fn) as f:
+        flag = 0
+        for line in f:
+            if flag in (1, 3, 4, 5, 6):
+                flag += 1
+            elif flag == 2:
+                s = line.split()
+                if not len(s):
+                    flag = 0
+                else:
+                    symbols.append(s[0].capitalize())
+                    coord.append([float(s[1]), float(s[2]), float(s[3])])
+            elif flag == 7:
+                s = line.split()
+                if not len(s):
+                    flag = 0
+                else:
+                    forces.append([float(s[1]), float(s[2]), float(s[3])])
+            elif line.startswith(
+                "       Center              X                  Y                   Z               Mass"
+            ):
+                # coord
+                flag = 1
+                coord = []
+                symbols = []
+            elif line.startswith("    Geometry (in "):
+                # remove ),
+                length_unit = line.split()[2][:-2].lower()
+            elif line.startswith("  ## Total Gradient"):
+                flag = 3
+                forces = []
+            elif line.startswith("    Total Energy ="):
+                energy = float(line.split()[-1])
+    assert length_unit is not None
+    length_convert = LengthConversion(length_unit, "angstrom").value()
+    symbols = np.array(symbols)
+    forces = -np.array(forces)
+    coord = np.array(coord) * length_convert
+    assert coord.shape == forces.shape
+
+    return symbols, coord, energy, forces
diff --git a/dpdata/pwmat/atomconfig.py b/dpdata/pwmat/atomconfig.py
index 28cfaebcf..62eff77ca 100644
--- a/dpdata/pwmat/atomconfig.py
+++ b/dpdata/pwmat/atomconfig.py
@@ -1,4 +1,6 @@
 #!/usr/bin/python3
+from __future__ import annotations
+
 import numpy as np
 
 from ..periodic_table import ELEMENTS
@@ -58,7 +60,7 @@ def from_system_data(system, f_idx=0, skip_zeros=True):
     ret += "\n"
     for ii in system["cells"][f_idx]:
         for jj in ii:
-            ret += "%.16e " % jj
+            ret += f"{jj:.16e} "
         ret += "\n"
     ret += "POSITION"
     ret += "\n"
diff --git a/dpdata/pwmat/movement.py b/dpdata/pwmat/movement.py
index 748744d6d..ccfd819db 100644
--- a/dpdata/pwmat/movement.py
+++ b/dpdata/pwmat/movement.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import warnings
 
 import numpy as np
diff --git a/dpdata/py.typed b/dpdata/py.typed
new file mode 100644
index 000000000..e69de29bb
diff --git a/dpdata/pymatgen/molecule.py b/dpdata/pymatgen/molecule.py
index 13d4046c1..8d397984a 100644
--- a/dpdata/pymatgen/molecule.py
+++ b/dpdata/pymatgen/molecule.py
@@ -1,13 +1,13 @@
-import numpy as np
+from __future__ import annotations
 
-try:
-    from pymatgen.core import Molecule
-except ImportError:
-    pass
 from collections import Counter
 
+import numpy as np
+
 
 def to_system_data(file_name, protect_layer=9):
+    from pymatgen.core import Molecule
+
     mol = Molecule.from_file(file_name)
     elem_mol = list(str(site.species.elements[0]) for site in mol.sites)
     elem_counter = Counter(elem_mol)
diff --git a/dpdata/pymatgen/structure.py b/dpdata/pymatgen/structure.py
new file mode 100644
index 000000000..36e411c02
--- /dev/null
+++ b/dpdata/pymatgen/structure.py
@@ -0,0 +1,21 @@
+from __future__ import annotations
+
+import numpy as np
+
+
+def from_system_data(structure) -> dict:
+    symbols = [site.species_string for site in structure]
+    atom_names = list(structure.symbol_set)
+    atom_numbs = [symbols.count(symbol) for symbol in atom_names]
+    atom_types = np.array([atom_names.index(symbol) for symbol in symbols]).astype(int)
+    coords = structure.cart_coords
+    cells = structure.lattice.matrix
+
+    info_dict = {
+        "atom_names": atom_names,
+        "atom_numbs": atom_numbs,
+        "atom_types": atom_types,
+        "coords": np.array([coords]),
+        "cells": np.array([cells]),
+    }
+    return info_dict
diff --git a/dpdata/qe/scf.py b/dpdata/qe/scf.py
index cd9c6f283..37e5fbab6 100755
--- a/dpdata/qe/scf.py
+++ b/dpdata/qe/scf.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python3
+from __future__ import annotations
 
 import os
 
diff --git a/dpdata/qe/traj.py b/dpdata/qe/traj.py
index e27990cbe..1fbf0f71c 100644
--- a/dpdata/qe/traj.py
+++ b/dpdata/qe/traj.py
@@ -1,4 +1,6 @@
 #!/usr/bin/python3
+from __future__ import annotations
+
 import warnings
 
 import numpy as np
diff --git a/dpdata/rdkit/sanitize.py b/dpdata/rdkit/sanitize.py
index 061de3d9d..2b0d76634 100644
--- a/dpdata/rdkit/sanitize.py
+++ b/dpdata/rdkit/sanitize.py
@@ -1,18 +1,9 @@
+from __future__ import annotations
+
 import os
 import time
 from copy import deepcopy
 
-from rdkit import Chem
-from rdkit.Chem.rdchem import BondType
-
-# openbabel
-try:
-    from openbabel import openbabel
-
-    USE_OBABEL = True
-except ModuleNotFoundError as e:
-    USE_OBABEL = False
-
 
 def get_explicit_valence(atom, verbose=False):
     exp_val_calculated_from_bonds = int(
@@ -32,6 +23,8 @@ def get_explicit_valence(atom, verbose=False):
 
 def regularize_formal_charges(mol, sanitize=True, verbose=False):
     """Regularize formal charges of atoms."""
+    from rdkit import Chem
+
     assert isinstance(mol, Chem.rdchem.Mol)
     for atom in mol.GetAtoms():
         assign_formal_charge_for_atom(atom, verbose)
@@ -47,6 +40,8 @@ def regularize_formal_charges(mol, sanitize=True, verbose=False):
 
 def assign_formal_charge_for_atom(atom, verbose=False):
     """Assigen formal charge according to 8-electron rule for element B,C,N,O,S,P,As."""
+    from rdkit import Chem
+
     assert isinstance(atom, Chem.rdchem.Atom)
     valence = get_explicit_valence(atom, verbose)
     if atom.GetSymbol() == "B":
@@ -135,6 +130,8 @@ def get_terminal_NR2s(atom):
 
 
 def sanitize_phosphate_Patom(P_atom, verbose=True):
+    from rdkit import Chem
+
     if P_atom.GetSymbol() == "P":
         terminal_oxygens = get_terminal_oxygens(P_atom)
         mol = P_atom.GetOwningMol()
@@ -161,6 +158,8 @@ def sanitize_phosphate(mol):
 
 
 def sanitize_sulfate_Satom(S_atom, verbose=True):
+    from rdkit import Chem
+
     if S_atom.GetSymbol() == "S":
         terminal_oxygens = get_terminal_oxygens(S_atom)
         mol = S_atom.GetOwningMol()
@@ -187,6 +186,8 @@ def sanitize_sulfate(mol):
 
 
 def sanitize_carboxyl_Catom(C_atom, verbose=True):
+    from rdkit import Chem
+
     if C_atom.GetSymbol() == "C":
         terminal_oxygens = get_terminal_oxygens(C_atom)
         mol = C_atom.GetOwningMol()
@@ -214,6 +215,8 @@ def sanitize_carboxyl(mol):
 
 
 def sanitize_guanidine_Catom(C_atom, verbose=True):
+    from rdkit import Chem
+
     if C_atom.GetSymbol() == "C":
         terminal_NR2s = get_terminal_NR2s(C_atom)
         mol = C_atom.GetOwningMol()
@@ -241,6 +244,8 @@ def sanitize_guanidine(mol):
 
 
 def sanitize_nitro_Natom(N_atom, verbose=True):
+    from rdkit import Chem
+
     if N_atom.GetSymbol() == "N":
         terminal_oxygens = get_terminal_oxygens(N_atom)
         mol = N_atom.GetOwningMol()
@@ -275,6 +280,8 @@ def is_terminal_nitrogen(N_atom):
 
 
 def sanitize_nitrine_Natom(atom, verbose=True):
+    from rdkit import Chem
+
     if atom.GetSymbol() == "N" and len(atom.GetNeighbors()) == 2:
         mol = atom.GetOwningMol()
         nei1, nei2 = atom.GetNeighbors()[0], atom.GetNeighbors()[1]
@@ -312,6 +319,8 @@ def contain_hetero_aromatic(mol):
 
 # for carbon with explicit valence > 4
 def regularize_carbon_bond_order(atom, verbose=True):
+    from rdkit import Chem
+
     if atom.GetSymbol() == "C" and get_explicit_valence(atom) > 4:
         if verbose:
             print("Detecting carbon with explicit valence > 4, fixing it...")
@@ -330,6 +339,8 @@ def regularize_carbon_bond_order(atom, verbose=True):
 
 # for nitrogen with explicit valence > 4
 def regularize_nitrogen_bond_order(atom, verbose=True):
+    from rdkit import Chem
+
     mol = atom.GetOwningMol()
     if atom.GetSymbol() == "N" and get_explicit_valence(atom) > 4:
         O_atoms = get_terminal_oxygens(atom)
@@ -363,6 +374,9 @@ def mol_edit_log(mol, i, j):
 
 
 def kekulize_aromatic_heterocycles(mol_in, assign_formal_charge=True, sanitize=True):
+    from rdkit import Chem
+    from rdkit.Chem.rdchem import BondType
+
     mol = Chem.RWMol(mol_in)
     rings = Chem.rdmolops.GetSymmSSSR(mol)
     rings = [list(i) for i in list(rings)]
@@ -566,6 +580,9 @@ def hetero_priority(idx, mol):
 def convert_by_obabel(
     mol, cache_dir=os.path.join(os.getcwd(), ".cache"), obabel_path="obabel"
 ):
+    from openbabel import openbabel
+    from rdkit import Chem
+
     if not os.path.exists(cache_dir):
         os.mkdir(cache_dir)
     if mol.HasProp("_Name"):
@@ -585,6 +602,8 @@ def convert_by_obabel(
 
 
 def super_sanitize_mol(mol, name=None, verbose=True):
+    from rdkit import Chem
+
     if name is None:
         if mol.HasProp("_Name"):
             name = mol.GetProp("_Name")
@@ -655,11 +674,6 @@ def _check_level(self, level):
             raise ValueError(
                 f"Invalid level '{level}', please set to 'low', 'medium' or 'high'"
             )
-        else:
-            if level == "high" and not USE_OBABEL:
-                raise ModuleNotFoundError(
-                    "obabel not installed, high level sanitizer cannot work"
-                )
 
     def _handle_exception(self, error_info):
         if self.raise_errors:
@@ -669,6 +683,8 @@ def _handle_exception(self, error_info):
 
     def sanitize(self, mol):
         """Sanitize mol according to `self.level`. If failed, return None."""
+        from rdkit import Chem
+
         if self.level == "low":
             try:
                 Chem.SanitizeMol(mol)
diff --git a/dpdata/rdkit/utils.py b/dpdata/rdkit/utils.py
index 25cf97cd1..efeef6070 100644
--- a/dpdata/rdkit/utils.py
+++ b/dpdata/rdkit/utils.py
@@ -1,11 +1,11 @@
-try:
-    from rdkit import Chem
-except ModuleNotFoundError:
-    pass
+from __future__ import annotations
+
 import numpy as np
 
 
 def mol_to_system_data(mol):
+    from rdkit import Chem
+
     if not isinstance(mol, Chem.rdchem.Mol):
         raise TypeError(f"rdkit.Chem.Mol required, not {type(mol)}")
 
@@ -52,6 +52,8 @@ def mol_to_system_data(mol):
 
 
 def system_data_to_mol(data):
+    from rdkit import Chem
+
     mol_ed = Chem.RWMol()
     atom_symbols = [data["atom_names"][i] for i in data["atom_types"]]
     # add atoms
diff --git a/dpdata/siesta/aiMD_output.py b/dpdata/siesta/aiMD_output.py
index 4e1890ecc..daa4f6a25 100644
--- a/dpdata/siesta/aiMD_output.py
+++ b/dpdata/siesta/aiMD_output.py
@@ -1,4 +1,5 @@
 # !/usr/bin/python3
+from __future__ import annotations
 
 import numpy as np
 
diff --git a/dpdata/siesta/output.py b/dpdata/siesta/output.py
index 7418d5433..0c944d5b5 100644
--- a/dpdata/siesta/output.py
+++ b/dpdata/siesta/output.py
@@ -1,4 +1,5 @@
 #!/usr/bin/python3
+from __future__ import annotations
 
 import numpy as np
 
diff --git a/dpdata/stat.py b/dpdata/stat.py
index 1f6193af3..5ec395708 100644
--- a/dpdata/stat.py
+++ b/dpdata/stat.py
@@ -1,4 +1,6 @@
-from abc import ABCMeta, abstractproperty
+from __future__ import annotations
+
+from abc import ABCMeta, abstractmethod
 from functools import lru_cache
 
 import numpy as np
@@ -52,20 +54,22 @@ class ErrorsBase(metaclass=ABCMeta):
     SYSTEM_TYPE = object
 
     def __init__(self, system_1: SYSTEM_TYPE, system_2: SYSTEM_TYPE) -> None:
-        assert isinstance(system_1, self.SYSTEM_TYPE), (
-            "system_1 should be %s" % self.SYSTEM_TYPE.__name__
-        )
-        assert isinstance(system_2, self.SYSTEM_TYPE), (
-            "system_2 should be %s" % self.SYSTEM_TYPE.__name__
-        )
+        assert isinstance(
+            system_1, self.SYSTEM_TYPE
+        ), f"system_1 should be {self.SYSTEM_TYPE.__name__}"
+        assert isinstance(
+            system_2, self.SYSTEM_TYPE
+        ), f"system_2 should be {self.SYSTEM_TYPE.__name__}"
         self.system_1 = system_1
         self.system_2 = system_2
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def e_errors(self) -> np.ndarray:
         """Energy errors."""
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def f_errors(self) -> np.ndarray:
         """Force errors."""
 
@@ -114,12 +118,16 @@ class Errors(ErrorsBase):
     @lru_cache()
     def e_errors(self) -> np.ndarray:
         """Energy errors."""
+        assert isinstance(self.system_1, self.SYSTEM_TYPE)
+        assert isinstance(self.system_2, self.SYSTEM_TYPE)
         return self.system_1["energies"] - self.system_2["energies"]
 
     @property
     @lru_cache()
     def f_errors(self) -> np.ndarray:
         """Force errors."""
+        assert isinstance(self.system_1, self.SYSTEM_TYPE)
+        assert isinstance(self.system_2, self.SYSTEM_TYPE)
         return (self.system_1["forces"] - self.system_2["forces"]).ravel()
 
 
@@ -147,6 +155,8 @@ class MultiErrors(ErrorsBase):
     @lru_cache()
     def e_errors(self) -> np.ndarray:
         """Energy errors."""
+        assert isinstance(self.system_1, self.SYSTEM_TYPE)
+        assert isinstance(self.system_2, self.SYSTEM_TYPE)
         errors = []
         for nn in self.system_1.systems.keys():
             ss1 = self.system_1[nn]
@@ -158,6 +168,8 @@ def e_errors(self) -> np.ndarray:
     @lru_cache()
     def f_errors(self) -> np.ndarray:
         """Force errors."""
+        assert isinstance(self.system_1, self.SYSTEM_TYPE)
+        assert isinstance(self.system_2, self.SYSTEM_TYPE)
         errors = []
         for nn in self.system_1.systems.keys():
             ss1 = self.system_1[nn]
diff --git a/dpdata/system.py b/dpdata/system.py
index 3be86ef48..2613166a7 100644
--- a/dpdata/system.py
+++ b/dpdata/system.py
@@ -1,20 +1,33 @@
 # %%
+from __future__ import annotations
+
 import glob
 import hashlib
+import numbers
 import os
+import sys
 import warnings
 from copy import deepcopy
-from typing import Any, Dict, Optional, Tuple, Union
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Iterable,
+    overload,
+)
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+else:
+    from typing_extensions import Literal
 
 import numpy as np
-from monty.json import MSONable
-from monty.serialization import dumpfn, loadfn
 
 import dpdata
 import dpdata.md.pbc
 
 # ensure all plugins are loaded!
 import dpdata.plugins
+import dpdata.plugins.deepmd
 from dpdata.amber.mask import load_param_file, pick_by_amber_mask
 from dpdata.data_type import Axis, DataError, DataType, get_data_types
 from dpdata.driver import Driver, Minimizer
@@ -28,6 +41,9 @@
     utf8len,
 )
 
+if TYPE_CHECKING:
+    import parmed
+
 
 def load_format(fmt):
     fmt = fmt.lower()
@@ -41,7 +57,7 @@ def load_format(fmt):
     )
 
 
-class System(MSONable):
+class System:
     """The data System.
 
     A data System (a concept used by `deepmd-kit <https://github.com/deepmodeling/deepmd-kit>`_)
@@ -66,11 +82,11 @@ class System(MSONable):
 
     Attributes
     ----------
-    DTYPES : tuple[DataType]
+    DTYPES : tuple[DataType, ...]
         data types of this class
     """
 
-    DTYPES = (
+    DTYPES: tuple[DataType, ...] = (
         DataType("atom_numbs", list, (Axis.NTYPES,)),
         DataType("atom_names", list, (Axis.NTYPES,)),
         DataType("atom_types", np.ndarray, (Axis.NATOMS,)),
@@ -86,13 +102,14 @@ class System(MSONable):
 
     def __init__(
         self,
-        file_name=None,
-        fmt="auto",
-        type_map=None,
-        begin=0,
-        step=1,
-        data=None,
-        convergence_check=True,
+        # some formats do not use string as input
+        file_name: Any = None,
+        fmt: str = "auto",
+        type_map: list[str] | None = None,
+        begin: int = 0,
+        step: int = 1,
+        data: dict[str, Any] | None = None,
+        convergence_check: bool = True,
         **kwargs,
     ):
         """Constructor.
@@ -213,13 +230,13 @@ def check_data(self):
 
     post_funcs = Plugin()
 
-    def from_fmt(self, file_name, fmt="auto", **kwargs):
+    def from_fmt(self, file_name: Any, fmt: str = "auto", **kwargs: Any):
         fmt = fmt.lower()
         if fmt == "auto":
             fmt = os.path.basename(file_name).split(".")[-1].lower()
         return self.from_fmt_obj(load_format(fmt), file_name, **kwargs)
 
-    def from_fmt_obj(self, fmtobj, file_name, **kwargs):
+    def from_fmt_obj(self, fmtobj: Format, file_name: Any, **kwargs: Any):
         data = fmtobj.from_system(file_name, **kwargs)
         if data:
             if isinstance(data, (list, tuple)):
@@ -229,11 +246,11 @@ def from_fmt_obj(self, fmtobj, file_name, **kwargs):
                 self.data = {**self.data, **data}
                 self.check_data()
             if hasattr(fmtobj.from_system, "post_func"):
-                for post_f in fmtobj.from_system.post_func:
+                for post_f in fmtobj.from_system.post_func:  # type: ignore
                     self.post_funcs.get_plugin(post_f)(self)
         return self
 
-    def to(self, fmt: str, *args, **kwargs) -> "System":
+    def to(self, fmt: str, *args: Any, **kwargs: Any) -> System:
         """Dump systems to the specific format.
 
         Parameters
@@ -252,7 +269,7 @@ def to(self, fmt: str, *args, **kwargs) -> "System":
         """
         return self.to_fmt_obj(load_format(fmt), *args, **kwargs)
 
-    def to_fmt_obj(self, fmtobj, *args, **kwargs):
+    def to_fmt_obj(self, fmtobj: Format, *args: Any, **kwargs: Any):
         return fmtobj.to_system(self.data, *args, **kwargs)
 
     def __repr__(self):
@@ -270,13 +287,32 @@ def __str__(self):
         ret += "\n" + "  ".join(map(str, self.get_atom_numbs()))
         return ret
 
+    @overload
+    def __getitem__(self, key: int | slice | list | np.ndarray) -> System: ...
+    @overload
+    def __getitem__(
+        self, key: Literal["atom_names", "real_atom_names"]
+    ) -> list[str]: ...
+    @overload
+    def __getitem__(self, key: Literal["atom_numbs"]) -> list[int]: ...
+    @overload
+    def __getitem__(self, key: Literal["nopbc"]) -> bool: ...
+    @overload
+    def __getitem__(
+        self, key: Literal["orig", "coords", "energies", "forces", "virials"]
+    ) -> np.ndarray: ...
+    @overload
+    def __getitem__(self, key: str) -> Any:
+        # other cases, for example customized data
+        ...
+
     def __getitem__(self, key):
         """Returns proerty stored in System by key or by idx."""
         if isinstance(key, (int, slice, list, np.ndarray)):
             return self.sub_system(key)
         return self.data[key]
 
-    def __len__(self):
+    def __len__(self) -> int:
         """Returns number of frames in the system."""
         return self.get_nframes()
 
@@ -295,11 +331,15 @@ def __add__(self, others):
             raise RuntimeError("Unspported data structure")
         return self.__class__.from_dict({"data": self_copy.data})
 
-    def dump(self, filename, indent=4):
+    def dump(self, filename: str, indent: int = 4):
         """Dump .json or .yaml file."""
+        from monty.serialization import dumpfn
+
         dumpfn(self.as_dict(), filename, indent=indent)
 
-    def map_atom_types(self, type_map=None) -> np.ndarray:
+    def map_atom_types(
+        self, type_map: dict[str, int] | list[str] | None = None
+    ) -> np.ndarray:
         """Map the atom types of the system.
 
         Parameters
@@ -338,11 +378,25 @@ def map_atom_types(self, type_map=None) -> np.ndarray:
         return new_atom_types
 
     @staticmethod
-    def load(filename):
+    def load(filename: str):
         """Rebuild System obj. from .json or .yaml file."""
+        from monty.serialization import loadfn
+
         return loadfn(filename)
 
-    def as_dict(self):
+    @classmethod
+    def from_dict(cls, data: dict):
+        """Construct a System instance from a data dict."""
+        from monty.serialization import MontyDecoder  # type: ignore
+
+        decoded = {
+            k: MontyDecoder().process_decoded(v)
+            for k, v in data.items()
+            if not k.startswith("@")
+        }
+        return cls(**decoded)
+
+    def as_dict(self) -> dict:
         """Returns data dict of System instance."""
         d = {
             "@module": self.__class__.__module__,
@@ -351,23 +405,23 @@ def as_dict(self):
         }
         return d
 
-    def get_atom_names(self):
+    def get_atom_names(self) -> list[str]:
         """Returns name of atoms."""
         return self.data["atom_names"]
 
-    def get_atom_types(self):
+    def get_atom_types(self) -> np.ndarray:
         """Returns type of atoms."""
         return self.data["atom_types"]
 
-    def get_atom_numbs(self):
+    def get_atom_numbs(self) -> list[int]:
         """Returns number of atoms."""
         return self.data["atom_numbs"]
 
-    def get_nframes(self):
+    def get_nframes(self) -> int:
         """Returns number of frames in the system."""
         return len(self.data["cells"])
 
-    def get_natoms(self):
+    def get_natoms(self) -> int:
         """Returns total number of atoms in the system."""
         return len(self.data["atom_types"])
 
@@ -379,7 +433,7 @@ def copy(self):
         """Returns a copy of the system."""
         return self.__class__.from_dict({"data": deepcopy(self.data)})
 
-    def sub_system(self, f_idx):
+    def sub_system(self, f_idx: int | slice | list | np.ndarray):
         """Construct a subsystem from the system.
 
         Parameters
@@ -394,15 +448,18 @@ def sub_system(self, f_idx):
         """
         tmp = self.__class__()
         # convert int to array_like
-        if isinstance(f_idx, (int, np.int64)):
+        if isinstance(f_idx, numbers.Integral):
             f_idx = np.array([f_idx])
+        assert not isinstance(f_idx, int)
         for tt in self.DTYPES:
             if tt.name not in self.data:
                 # skip optional data
                 continue
             if tt.shape is not None and Axis.NFRAMES in tt.shape:
                 axis_nframes = tt.shape.index(Axis.NFRAMES)
-                new_shape = [slice(None) for _ in self.data[tt.name].shape]
+                new_shape: list[slice | np.ndarray | list] = [
+                    slice(None) for _ in self.data[tt.name].shape
+                ]
                 new_shape[axis_nframes] = f_idx
                 tmp.data[tt.name] = self.data[tt.name][tuple(new_shape)]
             else:
@@ -410,7 +467,7 @@ def sub_system(self, f_idx):
                 tmp.data[tt.name] = self.data[tt.name]
         return tmp
 
-    def append(self, system):
+    def append(self, system: System) -> bool:
         """Append a system to this system.
 
         Parameters
@@ -450,9 +507,9 @@ def append(self, system):
             # check if the first shape is nframes
             if tt.shape is not None and Axis.NFRAMES in tt.shape:
                 if tt.name not in self.data and tt.name in system.data:
-                    raise RuntimeError("system has %s, but this does not" % tt.name)
+                    raise RuntimeError(f"system has {tt.name}, but this does not")
                 elif tt.name in self.data and tt.name not in system.data:
-                    raise RuntimeError("this has %s, but system does not" % tt.name)
+                    raise RuntimeError(f"this has {tt.name}, but system does not")
                 elif tt.name not in self.data and tt.name not in system.data:
                     # skip if both not exist
                     continue
@@ -466,7 +523,7 @@ def append(self, system):
             self.data["nopbc"] = False
         return True
 
-    def convert_to_mixed_type(self, type_map=None):
+    def convert_to_mixed_type(self, type_map: list[str] | None = None):
         """Convert the data dict to mixed type format structure, in order to append systems
         with different formula but the same number of atoms. Change the 'atom_names' to
         one placeholder type 'MIXED_TOKEN' and add 'real_atom_types' to store the real type
@@ -492,7 +549,7 @@ def convert_to_mixed_type(self, type_map=None):
         self.data["atom_numbs"] = [natoms]
         self.data["atom_names"] = ["MIXED_TOKEN"]
 
-    def sort_atom_names(self, type_map=None):
+    def sort_atom_names(self, type_map: list[str] | None = None):
         """Sort atom_names of the system and reorder atom_numbs and atom_types accoarding
         to atom_names. If type_map is not given, atom_names will be sorted by
         alphabetical order. If type_map is given, atom_names will be type_map.
@@ -504,7 +561,7 @@ def sort_atom_names(self, type_map=None):
         """
         self.data = sort_atom_names(self.data, type_map=type_map)
 
-    def check_type_map(self, type_map):
+    def check_type_map(self, type_map: list[str] | None):
         """Assign atom_names to type_map if type_map is given and different from
         atom_names.
 
@@ -516,7 +573,16 @@ def check_type_map(self, type_map):
         if type_map is not None and type_map != self.data["atom_names"]:
             self.sort_atom_names(type_map=type_map)
 
-    def apply_type_map(self, type_map):
+    def apply_type_map(self, type_map: list[str]):
+        """Customize the element symbol order and  it should maintain order
+        consistency in dpgen or deepmd-kit. It is especially recommended
+        for multiple complexsystems with multiple elements.
+
+        Parameters
+        ----------
+        type_map : list
+            type_map
+        """
         if type_map is not None and isinstance(type_map, list):
             self.check_type_map(type_map)
         else:
@@ -537,13 +603,15 @@ def sort_atom_types(self) -> np.ndarray:
                 continue
             if tt.shape is not None and Axis.NATOMS in tt.shape:
                 axis_natoms = tt.shape.index(Axis.NATOMS)
-                new_shape = [slice(None) for _ in self.data[tt.name].shape]
+                new_shape: list[slice | np.ndarray] = [
+                    slice(None) for _ in self.data[tt.name].shape
+                ]
                 new_shape[axis_natoms] = idx
                 self.data[tt.name] = self.data[tt.name][tuple(new_shape)]
         return idx
 
     @property
-    def formula(self):
+    def formula(self) -> str:
         """Return the formula of this system, like C3H5O2."""
         return "".join(
             [
@@ -555,7 +623,7 @@ def formula(self):
         )
 
     @property
-    def uniq_formula(self):
+    def uniq_formula(self) -> str:
         """Return the uniq_formula of this system.
         The uniq_formula sort the elements in formula by names.
         Systems with the same uniq_formula can be append together.
@@ -605,7 +673,7 @@ def short_name(self) -> str:
             return short_formula
         return self.formula_hash
 
-    def extend(self, systems):
+    def extend(self, systems: Iterable[System]):
         """Extend a system list to this system.
 
         Parameters
@@ -623,7 +691,7 @@ def apply_pbc(self):
         self.data["coords"] = np.matmul(ncoord, self.data["cells"])
 
     @post_funcs.register("remove_pbc")
-    def remove_pbc(self, protect_layer=9):
+    def remove_pbc(self, protect_layer: int = 9):
         """This method does NOT delete the definition of the cells, it
         (1) revises the cell to a cubic cell and ensures that the cell
         boundary to any atom in the system is no less than `protect_layer`
@@ -638,7 +706,7 @@ def remove_pbc(self, protect_layer=9):
         assert protect_layer >= 0, "the protect_layer should be no less than 0"
         remove_pbc(self.data, protect_layer)
 
-    def affine_map(self, trans, f_idx=0):
+    def affine_map(self, trans, f_idx: int | numbers.Integral = 0):
         assert np.linalg.det(trans) != 0
         self.data["cells"][f_idx] = np.matmul(self.data["cells"][f_idx], trans)
         self.data["coords"][f_idx] = np.matmul(self.data["coords"][f_idx], trans)
@@ -656,7 +724,7 @@ def rot_lower_triangular(self):
         for ii in range(self.get_nframes()):
             self.rot_frame_lower_triangular(ii)
 
-    def rot_frame_lower_triangular(self, f_idx=0):
+    def rot_frame_lower_triangular(self, f_idx: int | numbers.Integral = 0):
         qq, rr = np.linalg.qr(self.data["cells"][f_idx].T)
         if np.linalg.det(qq) < 0:
             qq = -qq
@@ -673,11 +741,11 @@ def rot_frame_lower_triangular(self, f_idx=0):
         self.affine_map(rot, f_idx=f_idx)
         return np.matmul(qq, rot)
 
-    def add_atom_names(self, atom_names):
+    def add_atom_names(self, atom_names: list[str]):
         """Add atom_names that do not exist."""
         self.data = add_atom_names(self.data, atom_names)
 
-    def replicate(self, ncopy):
+    def replicate(self, ncopy: list[int] | tuple[int, int, int]):
         """Replicate the each frame  in the system in 3 dimensions.
         Each frame in the system will become a supercell.
 
@@ -708,9 +776,11 @@ def replicate(self, ncopy):
         tmp.data["atom_numbs"] = list(
             np.array(np.copy(data["atom_numbs"])) * np.prod(ncopy)
         )
-        tmp.data["atom_types"] = np.sort(
-            np.tile(np.copy(data["atom_types"]), np.prod(ncopy)), kind="stable"
+        tmp.data["atom_types"] = np.tile(
+            np.copy(data["atom_types"]), (int(np.prod(ncopy)),) + (1,)
         )
+        tmp.data["atom_types"] = np.transpose(tmp.data["atom_types"]).reshape([-1])
+
         tmp.data["cells"] = np.copy(data["cells"])
         for ii in range(3):
             tmp.data["cells"][:, ii, :] *= ncopy[ii]
@@ -729,7 +799,7 @@ def replicate(self, ncopy):
         )
         return tmp
 
-    def replace(self, initial_atom_type, end_atom_type, replace_num):
+    def replace(self, initial_atom_type: str, end_atom_type: str, replace_num: int):
         if type(self) is not dpdata.System:
             raise RuntimeError(
                 "Must use method replace() of the instance of class dpdata.System"
@@ -774,7 +844,11 @@ def replace(self, initial_atom_type, end_atom_type, replace_num):
         self.sort_atom_types()
 
     def perturb(
-        self, pert_num, cell_pert_fraction, atom_pert_distance, atom_pert_style="normal"
+        self,
+        pert_num: int,
+        cell_pert_fraction: float,
+        atom_pert_distance: float,
+        atom_pert_style: str = "normal",
     ):
         """Perturb each frame in the system randomly.
         The cell will be deformed randomly, and atoms will be displaced by a random distance in random direction.
@@ -842,7 +916,7 @@ def nopbc(self):
         return False
 
     @nopbc.setter
-    def nopbc(self, value):
+    def nopbc(self, value: bool):
         self.data["nopbc"] = value
 
     def shuffle(self):
@@ -851,7 +925,9 @@ def shuffle(self):
         self.data = self.sub_system(idx).data
         return idx
 
-    def predict(self, *args: Any, driver: str = "dp", **kwargs: Any) -> "LabeledSystem":
+    def predict(
+        self, *args: Any, driver: str | Driver = "dp", **kwargs: Any
+    ) -> LabeledSystem:
         """Predict energies and forces by a driver.
 
         Parameters
@@ -880,8 +956,8 @@ def predict(self, *args: Any, driver: str = "dp", **kwargs: Any) -> "LabeledSyst
         return LabeledSystem(data=data)
 
     def minimize(
-        self, *args: Any, minimizer: Union[str, Minimizer], **kwargs: Any
-    ) -> "LabeledSystem":
+        self, *args: Any, minimizer: str | Minimizer, **kwargs: Any
+    ) -> LabeledSystem:
         """Minimize the geometry.
 
         Parameters
@@ -903,7 +979,11 @@ def minimize(
         data = minimizer.minimize(self.data.copy())
         return LabeledSystem(data=data)
 
-    def pick_atom_idx(self, idx, nopbc=None):
+    def pick_atom_idx(
+        self,
+        idx: int | numbers.Integral | list[int] | slice | np.ndarray,
+        nopbc: bool | None = None,
+    ):
         """Pick atom index.
 
         Parameters
@@ -919,15 +999,18 @@ def pick_atom_idx(self, idx, nopbc=None):
             new system
         """
         new_sys = self.copy()
-        if isinstance(idx, (int, np.int64)):
+        if isinstance(idx, numbers.Integral):
             idx = np.array([idx])
+        assert not isinstance(idx, int)
         for tt in self.DTYPES:
             if tt.name not in self.data:
                 # skip optional data
                 continue
             if tt.shape is not None and Axis.NATOMS in tt.shape:
                 axis_natoms = tt.shape.index(Axis.NATOMS)
-                new_shape = [slice(None) for _ in self.data[tt.name].shape]
+                new_shape: list[slice | np.ndarray | list[int]] = [
+                    slice(None) for _ in self.data[tt.name].shape
+                ]
                 new_shape[axis_natoms] = idx
                 new_sys.data[tt.name] = self.data[tt.name][tuple(new_shape)]
         # recalculate atom_numbs according to atom_types
@@ -939,7 +1022,7 @@ def pick_atom_idx(self, idx, nopbc=None):
             new_sys.nopbc = nopbc
         return new_sys
 
-    def remove_atom_names(self, atom_names):
+    def remove_atom_names(self, atom_names: str | list[str]):
         """Remove atom names and all such atoms.
         For example, you may not remove EP atoms in TIP4P/Ew water, which
         is not a real atom.
@@ -965,7 +1048,13 @@ def remove_atom_names(self, atom_names):
         new_sys.data["atom_numbs"] = new_sys.data["atom_numbs"][: len(new_atom_names)]
         return new_sys
 
-    def pick_by_amber_mask(self, param, maskstr, pass_coords=False, nopbc=None):
+    def pick_by_amber_mask(
+        self,
+        param: str | parmed.Structure,
+        maskstr: str,
+        pass_coords: bool = False,
+        nopbc: bool | None = None,
+    ):
         """Pick atoms by amber mask.
 
         Parameters
@@ -995,7 +1084,7 @@ def pick_by_amber_mask(self, param, maskstr, pass_coords=False, nopbc=None):
             return self.pick_atom_idx(idx, nopbc=nopbc)
 
     @classmethod
-    def register_data_type(cls, *data_type: Tuple[DataType]):
+    def register_data_type(cls, *data_type: DataType):
         """Register data type.
 
         Parameters
@@ -1015,7 +1104,7 @@ def register_data_type(cls, *data_type: Tuple[DataType]):
         cls.DTYPES = tuple(dtypes_dict.values())
 
 
-def get_cell_perturb_matrix(cell_pert_fraction):
+def get_cell_perturb_matrix(cell_pert_fraction: float):
     if cell_pert_fraction < 0:
         raise RuntimeError("cell_pert_fraction can not be negative")
     e0 = np.random.rand(6)
@@ -1030,7 +1119,10 @@ def get_cell_perturb_matrix(cell_pert_fraction):
     return cell_pert_matrix
 
 
-def get_atom_perturb_vector(atom_pert_distance, atom_pert_style="normal"):
+def get_atom_perturb_vector(
+    atom_pert_distance: float,
+    atom_pert_style: str = "normal",
+):
     random_vector = None
     if atom_pert_distance < 0:
         raise RuntimeError("atom_pert_distance can not be negative")
@@ -1100,7 +1192,7 @@ class LabeledSystem(System):
             The number of skipped frames when loading MD trajectory.
     """
 
-    DTYPES = System.DTYPES + (
+    DTYPES: tuple[DataType, ...] = System.DTYPES + (
         DataType("energies", np.ndarray, (Axis.NFRAMES,)),
         DataType("forces", np.ndarray, (Axis.NFRAMES, Axis.NATOMS, 3)),
         DataType("virials", np.ndarray, (Axis.NFRAMES, 3, 3), required=False),
@@ -1119,7 +1211,7 @@ def from_fmt_obj(self, fmtobj, file_name, **kwargs):
                 self.data = {**self.data, **data}
                 self.check_data()
             if hasattr(fmtobj.from_labeled_system, "post_func"):
-                for post_f in fmtobj.from_labeled_system.post_func:
+                for post_f in fmtobj.from_labeled_system.post_func:  # type: ignore
                     self.post_funcs.get_plugin(post_f)(self)
         return self
 
@@ -1133,7 +1225,7 @@ def __str__(self):
         ret += "\nFrame Numbers      : %d" % self.get_nframes()
         ret += "\nAtom Numbers       : %d" % self.get_natoms()
         status = "Yes" if self.has_virial() else "No"
-        ret += "\nIncluding Virials  : %s" % status
+        ret += f"\nIncluding Virials  : {status}"
         ret += "\nElement List       :"
         ret += "\n-------------------"
         ret += "\n" + "  ".join(map(str, self.get_atom_names()))
@@ -1155,11 +1247,11 @@ def __add__(self, others):
             raise RuntimeError("Unspported data structure")
         return self.__class__.from_dict({"data": self_copy.data})
 
-    def has_virial(self):
+    def has_virial(self) -> bool:
         # return ('virials' in self.data) and (len(self.data['virials']) > 0)
         return "virials" in self.data
 
-    def affine_map_fv(self, trans, f_idx):
+    def affine_map_fv(self, trans, f_idx: int | numbers.Integral):
         assert np.linalg.det(trans) != 0
         self.data["forces"][f_idx] = np.matmul(self.data["forces"][f_idx], trans)
         if self.has_virial():
@@ -1167,12 +1259,12 @@ def affine_map_fv(self, trans, f_idx):
                 trans.T, np.matmul(self.data["virials"][f_idx], trans)
             )
 
-    def rot_frame_lower_triangular(self, f_idx=0):
+    def rot_frame_lower_triangular(self, f_idx: int | numbers.Integral = 0):
         trans = System.rot_frame_lower_triangular(self, f_idx=f_idx)
         self.affine_map_fv(trans, f_idx=f_idx)
         return trans
 
-    def correction(self, hl_sys):
+    def correction(self, hl_sys: LabeledSystem) -> LabeledSystem:
         """Get energy and force correction between self and a high-level LabeledSystem.
         The self's coordinates will be kept, but energy and forces will be replaced by
         the correction between these two systems.
@@ -1201,7 +1293,7 @@ def correction(self, hl_sys):
             )
         return corrected_sys
 
-    def remove_outlier(self, threshold: float = 8.0) -> "LabeledSystem":
+    def remove_outlier(self, threshold: float = 8.0) -> LabeledSystem:
         r"""Remove outlier frames from the system.
 
         Remove the frames whose energies satisfy the condition
@@ -1252,14 +1344,16 @@ def __init__(self, *systems, type_map=None):
         type_map : list of str
             Maps atom type to name
         """
-        self.systems = {}
+        self.systems: dict[str, System] = {}
         if type_map is not None:
-            self.atom_names = type_map
+            self.atom_names: list[str] = type_map
         else:
-            self.atom_names = []
+            self.atom_names: list[str] = []
         self.append(*systems)
 
-    def from_fmt_obj(self, fmtobj, directory, labeled=True, **kwargs):
+    def from_fmt_obj(
+        self, fmtobj: Format, directory, labeled: bool = True, **kwargs: Any
+    ):
         if not isinstance(fmtobj, dpdata.plugins.deepmd.DeePMDMixedFormat):
             for dd in fmtobj.from_multi_systems(directory, **kwargs):
                 if labeled:
@@ -1283,7 +1377,7 @@ def from_fmt_obj(self, fmtobj, directory, labeled=True, **kwargs):
             self.append(*system_list)
             return self
 
-    def to_fmt_obj(self, fmtobj, directory, *args, **kwargs):
+    def to_fmt_obj(self, fmtobj: Format, directory, *args: Any, **kwargs: Any):
         if not isinstance(fmtobj, dpdata.plugins.deepmd.DeePMDMixedFormat):
             for fn, ss in zip(
                 fmtobj.to_multi_systems(
@@ -1302,7 +1396,7 @@ def to_fmt_obj(self, fmtobj, directory, *args, **kwargs):
                 )
         return self
 
-    def to(self, fmt: str, *args, **kwargs) -> "MultiSystems":
+    def to(self, fmt: str, *args: Any, **kwargs: Any) -> MultiSystems:
         """Dump systems to the specific format.
 
         Parameters
@@ -1346,13 +1440,19 @@ def __add__(self, others):
         raise RuntimeError("Unspported data structure")
 
     @classmethod
-    def from_file(cls, file_name, fmt, **kwargs):
+    def from_file(cls, file_name, fmt: str, **kwargs: Any):
         multi_systems = cls()
         multi_systems.load_systems_from_file(file_name=file_name, fmt=fmt, **kwargs)
         return multi_systems
 
     @classmethod
-    def from_dir(cls, dir_name, file_name, fmt="auto", type_map=None):
+    def from_dir(
+        cls,
+        dir_name: str,
+        file_name: str,
+        fmt: str = "auto",
+        type_map: list[str] | None = None,
+    ):
         multi_systems = cls()
         target_file_list = sorted(
             glob.glob(f"./{dir_name}/**/{file_name}", recursive=True)
@@ -1363,15 +1463,16 @@ def from_dir(cls, dir_name, file_name, fmt="auto", type_map=None):
             )
         return multi_systems
 
-    def load_systems_from_file(self, file_name=None, fmt=None, **kwargs):
+    def load_systems_from_file(self, file_name=None, fmt: str | None = None, **kwargs):
+        assert fmt is not None
         fmt = fmt.lower()
         return self.from_fmt_obj(load_format(fmt), file_name, **kwargs)
 
-    def get_nframes(self):
+    def get_nframes(self) -> int:
         """Returns number of frames in all systems."""
         return sum(len(system) for system in self.systems.values())
 
-    def append(self, *systems):
+    def append(self, *systems: System | MultiSystems):
         """Append systems or MultiSystems to systems.
 
         Parameters
@@ -1388,7 +1489,7 @@ def append(self, *systems):
             else:
                 raise RuntimeError("Object must be System or MultiSystems!")
 
-    def __append(self, system):
+    def __append(self, system: System):
         if not system.formula:
             return
         # prevent changing the original system
@@ -1400,7 +1501,7 @@ def __append(self, system):
         else:
             self.systems[formula] = system.copy()
 
-    def check_atom_names(self, system):
+    def check_atom_names(self, system: System):
         """Make atom_names in all systems equal, prevent inconsistent atom_types."""
         # new_in_system = set(system["atom_names"]) - set(self.atom_names)
         # new_in_self = set(self.atom_names) - set(system["atom_names"])
@@ -1421,7 +1522,9 @@ def check_atom_names(self, system):
             system.add_atom_names(new_in_self)
         system.sort_atom_names(type_map=self.atom_names)
 
-    def predict(self, *args: Any, driver="dp", **kwargs: Any) -> "MultiSystems":
+    def predict(
+        self, *args: Any, driver: str | Driver = "dp", **kwargs: Any
+    ) -> MultiSystems:
         """Predict energies and forces by a driver.
 
         Parameters
@@ -1446,8 +1549,8 @@ def predict(self, *args: Any, driver="dp", **kwargs: Any) -> "MultiSystems":
         return new_multisystems
 
     def minimize(
-        self, *args: Any, minimizer: Union[str, Minimizer], **kwargs: Any
-    ) -> "MultiSystems":
+        self, *args: Any, minimizer: str | Minimizer, **kwargs: Any
+    ) -> MultiSystems:
         """Minimize geometry by a minimizer.
 
         Parameters
@@ -1480,7 +1583,11 @@ def minimize(
             new_multisystems.append(ss.minimize(*args, minimizer=minimizer, **kwargs))
         return new_multisystems
 
-    def pick_atom_idx(self, idx, nopbc=None):
+    def pick_atom_idx(
+        self,
+        idx: int | numbers.Integral | list[int] | slice | np.ndarray,
+        nopbc: bool | None = None,
+    ):
         """Pick atom index.
 
         Parameters
@@ -1500,7 +1607,7 @@ def pick_atom_idx(self, idx, nopbc=None):
             new_sys.append(ss.pick_atom_idx(idx, nopbc=nopbc))
         return new_sys
 
-    def correction(self, hl_sys: "MultiSystems"):
+    def correction(self, hl_sys: MultiSystems) -> MultiSystems:
         """Get energy and force correction between self (assumed low-level) and a high-level MultiSystems.
         The self's coordinates will be kept, but energy and forces will be replaced by
         the correction between these two systems.
@@ -1535,12 +1642,14 @@ def correction(self, hl_sys: "MultiSystems"):
         for nn in self.systems.keys():
             ll_ss = self[nn]
             hl_ss = hl_sys[nn]
+            assert isinstance(ll_ss, LabeledSystem)
+            assert isinstance(hl_ss, LabeledSystem)
             corrected_sys.append(ll_ss.correction(hl_ss))
         return corrected_sys
 
     def train_test_split(
-        self, test_size: Union[float, int], seed: Optional[int] = None
-    ) -> Tuple["MultiSystems", "MultiSystems", Dict[str, np.ndarray]]:
+        self, test_size: float | int, seed: int | None = None
+    ) -> tuple[MultiSystems, MultiSystems, dict[str, np.ndarray]]:
         """Split systems into random train and test subsets.
 
         Parameters
@@ -1596,7 +1705,7 @@ def train_test_split(
         return train_systems, test_systems, test_system_idx
 
 
-def get_cls_name(cls: object) -> str:
+def get_cls_name(cls: type[Any]) -> str:
     """Returns the fully qualified name of a class, such as `np.ndarray`.
 
     Parameters
@@ -1631,32 +1740,30 @@ def add_format_methods():
 
     for method, formatcls in Format.get_from_methods().items():
 
-        def get_func(ff):
+        def get_func_from(ff):
             # ff is not initized when defining from_format so cannot be polluted
             def from_format(self, file_name, **kwargs):
                 return self.from_fmt_obj(ff(), file_name, **kwargs)
 
-            from_format.__doc__ = "Read data from :class:`%s` format." % (
-                get_cls_name(ff)
-            )
+            from_format.__doc__ = f"Read data from :class:`{get_cls_name(ff)}` format."
             return from_format
 
-        setattr(System, method, get_func(formatcls))
-        setattr(LabeledSystem, method, get_func(formatcls))
-        setattr(MultiSystems, method, get_func(formatcls))
+        setattr(System, method, get_func_from(formatcls))
+        setattr(LabeledSystem, method, get_func_from(formatcls))
+        setattr(MultiSystems, method, get_func_from(formatcls))
 
     for method, formatcls in Format.get_to_methods().items():
 
-        def get_func(ff):
+        def get_func_to(ff):
             def to_format(self, *args, **kwargs):
                 return self.to_fmt_obj(ff(), *args, **kwargs)
 
-            to_format.__doc__ = "Dump data to :class:`%s` format." % (get_cls_name(ff))
+            to_format.__doc__ = f"Dump data to :class:`{get_cls_name(ff)}` format."
             return to_format
 
-        setattr(System, method, get_func(formatcls))
-        setattr(LabeledSystem, method, get_func(formatcls))
-        setattr(MultiSystems, method, get_func(formatcls))
+        setattr(System, method, get_func_to(formatcls))
+        setattr(LabeledSystem, method, get_func_to(formatcls))
+        setattr(MultiSystems, method, get_func_to(formatcls))
 
     # at this point, System.DTYPES and LabeledSystem.DTYPES has been initialized
     System.register_data_type(*get_data_types(labeled=False))
diff --git a/dpdata/unit.py b/dpdata/unit.py
index eba07b412..09981b969 100644
--- a/dpdata/unit.py
+++ b/dpdata/unit.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
 from abc import ABC
 
-from scipy import constants
+from scipy import constants  # noqa: TID253
 
 AVOGADRO = constants.Avogadro  # Avagadro constant
 ELE_CHG = constants.elementary_charge  # Elementary Charge, in C
diff --git a/dpdata/utils.py b/dpdata/utils.py
index cf4a109ee..e008120ea 100644
--- a/dpdata/utils.py
+++ b/dpdata/utils.py
@@ -1,9 +1,34 @@
+from __future__ import annotations
+
+import sys
+from typing import overload
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+else:
+    from typing_extensions import Literal
 import numpy as np
 
 from dpdata.periodic_table import Element
 
 
-def elements_index_map(elements, standard=False, inverse=False):
+@overload
+def elements_index_map(
+    elements: list[str], standard: bool, inverse: Literal[True]
+) -> dict[int, str]: ...
+@overload
+def elements_index_map(
+    elements: list[str], standard: bool, inverse: Literal[False] = ...
+) -> dict[str, int]: ...
+@overload
+def elements_index_map(
+    elements: list[str], standard: bool, inverse: bool = False
+) -> dict[str, int] | dict[int, str]: ...
+
+
+def elements_index_map(
+    elements: list[str], standard: bool = False, inverse: bool = False
+) -> dict:
     if standard:
         elements.sort(key=lambda x: Element(x).Z)
     if inverse:
diff --git a/dpdata/vasp/outcar.py b/dpdata/vasp/outcar.py
index 0eddac91a..0fa4cb68e 100644
--- a/dpdata/vasp/outcar.py
+++ b/dpdata/vasp/outcar.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import re
 import warnings
 
diff --git a/dpdata/vasp/poscar.py b/dpdata/vasp/poscar.py
index 95f9c15ed..102e79041 100644
--- a/dpdata/vasp/poscar.py
+++ b/dpdata/vasp/poscar.py
@@ -1,4 +1,5 @@
 #!/usr/bin/python3
+from __future__ import annotations
 
 import numpy as np
 
@@ -59,12 +60,12 @@ def from_system_data(system, f_idx=0, skip_zeros=True):
     ret += "1.0\n"
     for ii in system["cells"][f_idx]:
         for jj in ii:
-            ret += "%.16e " % jj
+            ret += f"{jj:.16e} "
         ret += "\n"
     for idx, ii in enumerate(system["atom_names"]):
         if system["atom_numbs"][idx] == 0:
             continue
-        ret += "%s " % ii
+        ret += f"{ii} "
     ret += "\n"
     for ii in system["atom_numbs"]:
         if ii == 0:
diff --git a/dpdata/vasp/xml.py b/dpdata/vasp/xml.py
index a534fd0cf..352b107ed 100755
--- a/dpdata/vasp/xml.py
+++ b/dpdata/vasp/xml.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python3
+from __future__ import annotations
 
 import xml.etree.ElementTree as ET
 
diff --git a/dpdata/xyz/quip_gap_xyz.py b/dpdata/xyz/quip_gap_xyz.py
index 068bec1fb..b23b27e07 100644
--- a/dpdata/xyz/quip_gap_xyz.py
+++ b/dpdata/xyz/quip_gap_xyz.py
@@ -1,5 +1,7 @@
 #!/usr/bin/env python3
 # %%
+from __future__ import annotations
+
 import re
 from collections import OrderedDict
 
diff --git a/dpdata/xyz/xyz.py b/dpdata/xyz/xyz.py
index 745a97b1b..0c36ac32b 100644
--- a/dpdata/xyz/xyz.py
+++ b/dpdata/xyz/xyz.py
@@ -1,4 +1,4 @@
-from typing import Tuple
+from __future__ import annotations
 
 import numpy as np
 
@@ -31,7 +31,7 @@ def coord_to_xyz(coord: np.ndarray, types: list) -> str:
     return "\n".join(buff)
 
 
-def xyz_to_coord(xyz: str) -> Tuple[np.ndarray, list]:
+def xyz_to_coord(xyz: str) -> tuple[np.ndarray, list]:
     """Convert xyz format to coordinates and types.
 
     Parameters
diff --git a/plugin_example/dpdata_random/__init__.py b/plugin_example/dpdata_random/__init__.py
index 22820e0fa..cc14faca3 100644
--- a/plugin_example/dpdata_random/__init__.py
+++ b/plugin_example/dpdata_random/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from dpdata.format import Format
diff --git a/pyproject.toml b/pyproject.toml
index 8fe408eb4..6efe08188 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,5 @@
 [build-system]
-requires = ["setuptools>=61", "setuptools_scm[toml]>=6.2"]
+requires = ["setuptools>=61", "setuptools_scm[toml]>=7"]
 build-backend = "setuptools.build_meta"
 
 [project]
@@ -26,6 +26,7 @@ dependencies = [
     'h5py',
     'wcmatch',
     'importlib_metadata>=1.4; python_version < "3.8"',
+    'typing_extensions; python_version < "3.8"',
 ]
 requires-python = ">=3.7"
 readme = "README.md"
@@ -56,7 +57,11 @@ docs = [
     'sphinx-argparse',
     'rdkit',
     'jupyterlite-sphinx',
-    'jupyterlite-xeus-python',
+    'jupyterlite-xeus',
+]
+benchmark = [
+    'pytest',
+    'pytest-codspeed',
 ]
 
 [tool.setuptools.packages.find]
@@ -78,6 +83,8 @@ select = [
     "D", # pydocstyle
     "UP", # pyupgrade
     "I", # isort
+    "TID253", # banned-module-level-imports
+    "TCH", # flake8-type-checking
 ]
 ignore = [
     "E501", # line too long
@@ -103,3 +110,25 @@ ignore-init-module-imports = true
 
 [tool.ruff.lint.pydocstyle]
 convention = "numpy"
+
+[tool.ruff.lint.flake8-tidy-imports]
+banned-module-level-imports = [
+    "pymatgen",
+    "ase",
+    "openbabel",
+    "rdkit",
+    "parmed",
+    "deepmd",
+    "h5py",
+    "wcmatch",
+    "monty",
+    "scipy",
+]
+
+[tool.ruff.lint.isort]
+required-imports = ["from __future__ import annotations"]
+
+[tool.pyright]
+include = [
+    "dpdata/*.py",
+]


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3e88b72b7..4a17fcde1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,17 +12,15 @@ jobs:
         python-version: ["3.7", "3.8", "3.12"]
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     # set up conda
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: ${{ matrix.python-version }}
-    # install rdkit and openbabel
-    - name: Install rdkit
-      run: python -m pip install rdkit openbabel-wheel
+    - run: curl -LsSf https://astral.sh/uv/install.sh | sh
     - name: Install dependencies
-      run: python -m pip install .[amber,ase,pymatgen] coverage ./tests/plugin
+      run: uv pip install --system .[amber,ase,pymatgen] coverage ./tests/plugin rdkit openbabel-wheel
     - name: Test
       run: cd tests && coverage run --source=../dpdata -m unittest && cd .. && coverage combine tests/.coverage && coverage report
     - name: Run codecov
diff --git a/.github/workflows/test_import.yml b/.github/workflows/test_import.yml
index b04d05cde..de2e53f51 100644
--- a/.github/workflows/test_import.yml
+++ b/.github/workflows/test_import.yml
@@ -1,17 +1,18 @@
-name: test Python import
-
-on:
-  - push
-  - pull_request
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v2
-    - uses: actions/setup-python@v2
-      with:
-        python-version: '3.9'
-        architecture: 'x64'
-    - run: python -m pip install .
-    - run: python -c 'import dpdata'
+name: test Python import
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - uses: actions/setup-python@v5
+      with:
+        python-version: '3.9'
+        architecture: 'x64'
+    - run: python -m pip install uv
+    - run: python -m uv pip install --system .
+    - run: python -c 'import dpdata'
diff --git a/benchmark/test_import.py b/benchmark/test_import.py
new file mode 100644
index 000000000..846d72b25
--- /dev/null
+++ b/benchmark/test_import.py
@@ -0,0 +1,20 @@
+from __future__ import annotations
+
+import subprocess
+import sys
+
+import pytest
+
+
+@pytest.mark.benchmark
+def test_import():
+    """Test import dpdata."""
+    subprocess.check_output(
+        [sys.executable, "-c", "'from dpdata import LabeledSystem'"]
+    ).decode("ascii")
+
+
+@pytest.mark.benchmark
+def test_cli():
+    """Test dpdata command."""
+    subprocess.check_output([sys.executable, "-m", "dpdata", "-h"]).decode("ascii")
diff --git a/tests/comp_sys.py b/tests/comp_sys.py
index f4663780b..99879af61 100644
--- a/tests/comp_sys.py
+++ b/tests/comp_sys.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 
diff --git a/tests/context.py b/tests/context.py
index 77a7557d3..3214e28ea 100644
--- a/tests/context.py
+++ b/tests/context.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import sys
 
diff --git a/tests/plugin/dpdata_plugin_test/__init__.py b/tests/plugin/dpdata_plugin_test/__init__.py
index b3821cb34..ef26e7c1d 100644
--- a/tests/plugin/dpdata_plugin_test/__init__.py
+++ b/tests/plugin/dpdata_plugin_test/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 from dpdata.data_type import Axis, DataType, register_data_type
diff --git a/tests/poscars/poscar_ref_oh.py b/tests/poscars/poscar_ref_oh.py
index f120183ed..2d29aeeb6 100644
--- a/tests/poscars/poscar_ref_oh.py
+++ b/tests/poscars/poscar_ref_oh.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 
diff --git a/tests/poscars/test_lammps_dump_s_su.py b/tests/poscars/test_lammps_dump_s_su.py
index 28673dfc7..967c767aa 100644
--- a/tests/poscars/test_lammps_dump_s_su.py
+++ b/tests/poscars/test_lammps_dump_s_su.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/psi4/psi4.out b/tests/psi4/psi4.out
index d0d9f8bb8..bafdcd4a7 100644
--- a/tests/psi4/psi4.out
+++ b/tests/psi4/psi4.out
@@ -1,549 +1,549 @@
-
-    -----------------------------------------------------------------------
-          Psi4: An Open-Source Ab Initio Electronic Structure Package
-                               Psi4 1.6.1 release
-
-                         Git: Rev {HEAD} 5b9f6e3 
-
-
-    D. G. A. Smith, L. A. Burns, A. C. Simmonett, R. M. Parrish,
-    M. C. Schieber, R. Galvelis, P. Kraus, H. Kruse, R. Di Remigio,
-    A. Alenaizan, A. M. James, S. Lehtola, J. P. Misiewicz, M. Scheurer,
-    R. A. Shaw, J. B. Schriber, Y. Xie, Z. L. Glick, D. A. Sirianni,
-    J. S. O'Brien, J. M. Waldrop, A. Kumar, E. G. Hohenstein,
-    B. P. Pritchard, B. R. Brooks, H. F. Schaefer III, A. Yu. Sokolov,
-    K. Patkowski, A. E. DePrince III, U. Bozkaya, R. A. King,
-    F. A. Evangelista, J. M. Turney, T. D. Crawford, C. D. Sherrill,
-    J. Chem. Phys. 152(18) 184108 (2020). https://doi.org/10.1063/5.0006002
-
-                            Additional Code Authors
-    E. T. Seidl, C. L. Janssen, E. F. Valeev, M. L. Leininger,
-    J. F. Gonthier, R. M. Richard, H. R. McAlexander, M. Saitow, X. Wang,
-    P. Verma, M. H. Lechner, and A. Jiang
-
-             Previous Authors, Complete List of Code Contributors,
-                       and Citations for Specific Modules
-    https://github.com/psi4/psi4/blob/master/codemeta.json
-    https://github.com/psi4/psi4/graphs/contributors
-    http://psicode.org/psi4manual/master/introduction.html#citing-psifour
-
-    -----------------------------------------------------------------------
-
-
-    Psi4 started on: Saturday, 10 December 2022 09:13AM
-
-    Process ID: 4075549
-    Host:       exp-2-41
-    PSIDATADIR: /home/njzjz/anaconda3/envs/p4env/share/psi4
-    Memory:     500.0 MiB
-    Threads:    1
-    
-  ==> Input File <==
-
---------------------------------------------------------------------------
-molecule {
-C -2.048123 8.737055 18.514156
-C -4.662446 9.798136 17.933256
-H -1.010928 7.930652 16.895966
-H -2.425558 7.214861 19.887138
-H -0.843649 10.172359 19.427254
-H -6.115021 8.325339 17.677330
-H -4.535513 10.918460 16.180138
-H -5.257615 11.056429 19.484720
-0 1
-unit bohr
-}
-set basis def2-TZVPPD
-set gradient_write on
-G, wfn = gradient("WB97M-D3BJ", return_wfn=True)
-wfn.energy()
-wfn.gradient().print_out()--------------------------------------------------------------------------
-
-Scratch directory: /scratch/njzjz/job_17958150/
-gradient() will perform analytic gradient computation.
-
-*** tstart() called on exp-2-41
-*** at Sat Dec 10 09:13:42 2022
-
-   => Loading Basis Set <=
-
-    Name: DEF2-TZVPPD
-    Role: ORBITAL
-    Keyword: BASIS
-    atoms 1-2 entry C          line   144 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-tzvppd.gbs 
-    atoms 3-8 entry H          line    14 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-tzvppd.gbs 
-
-   => WB97M-D3BJ: Empirical Dispersion <=
-
-    Grimme's -D3 (BJ-damping) Dispersion Correction
-    Grimme S.; Ehrlich S.; Goerigk L. (2011), J. Comput. Chem., 32: 1456
-    Parametrisation from:    A. Najib, L. Goerigk, J. Comput. Theory Chem.,14, 5725, 2018
-
-        s6 =       1.000000
-        s8 =       0.390800
-        a1 =       0.566000
-        a2 =       3.128000
-
-
-         ---------------------------------------------------------
-                                   SCF
-               by Justin Turney, Rob Parrish, Andy Simmonett
-                          and Daniel G. A. Smith
-                              RKS Reference
-                        1 Threads,    500 MiB Core
-         ---------------------------------------------------------
-
-  ==> Geometry <==
-
-    Molecular point group: c1
-    Full point group: C1
-
-    Geometry (in Bohr), charge = 0, multiplicity = 1:
-
-       Center              X                  Y                   Z               Mass       
-    ------------   -----------------  -----------------  -----------------  -----------------
-         C            1.309059187335    -0.530960676560     0.283395850372    12.000000000000
-         C           -1.305263812665     0.530120323440    -0.297504149628    12.000000000000
-         H            2.346254187335    -1.337363676560    -1.334794149628     1.007825032230
-         H            0.931624187335    -2.053154676560     1.656377850372     1.007825032230
-         H            2.513533187335     0.904343323440     1.196493850372     1.007825032230
-         H           -2.757838812665    -0.942676676560    -0.553430149628     1.007825032230
-         H           -1.178330812665     1.650444323440    -2.050622149628     1.007825032230
-         H           -1.900432812665     1.788413323440     1.253959850372     1.007825032230
-
-  Running in c1 symmetry.
-
-  Rotational constants: A =      2.61492  B =      0.67176  C =      0.67128 [cm^-1]
-  Rotational constants: A =  78393.23592  B =  20138.72637  C =  20124.39598 [MHz]
-  Nuclear repulsion =   42.114432251814023
-
-  Charge       = 0
-  Multiplicity = 1
-  Electrons    = 18
-  Nalpha       = 9
-  Nbeta        = 9
-
-  ==> Algorithm <==
-
-  SCF Algorithm Type is DF.
-  DIIS enabled.
-  MOM disabled.
-  Fractional occupation disabled.
-  Guess Type is SAD.
-  Energy threshold   = 1.00e-08
-  Density threshold  = 1.00e-08
-  Integral threshold = 1.00e-12
-
-  ==> Primary Basis <==
-
-  Basis Set: DEF2-TZVPPD
-    Blend: DEF2-TZVPPD
-    Number of shells: 68
-    Number of basis functions: 176
-    Number of Cartesian functions: 194
-    Spherical Harmonics?: true
-    Max angular momentum: 3
-
-  ==> DFT Potential <==
-
-   => LibXC <=
-
-    Version 5.1.5
-    S. Lehtola, C. Steigemann, M. J. Oliveira, and M. A. Marques, SoftwareX 7, 1 (2018) (10.1016/j.softx.2017.11.002)
-
-   => Composite Functional: WB97M-D3BJ <= 
-
-    wB97M-V with D3(BJ) instead of VV10 dispersion 
-
-    A. Najib, L. Goerigk, J. Comput. Theory Chem.,14, 5725, 2018
-    N. Mardirossian, M. Head-Gordon, J. Chem. Phys. 144, 214110, 2016
-
-
-    Deriv               =              1
-    GGA                 =           TRUE
-    Meta                =           TRUE
-
-    Exchange Hybrid     =           TRUE
-    MP2 Hybrid          =          FALSE
-
-   => Exchange-Correlation Functionals <=
-
-    1.0000   wB97M-V exchange-correlation functional
-
-   => Exact (HF) Exchange <=
-
-    0.8500            HF,LR [omega = 0.3000]
-    0.1500               HF 
-
-   => LibXC Density Thresholds  <==
-
-    XC_HYB_MGGA_XC_WB97M_V:  1.00E-13 
-
-   => Molecular Quadrature <=
-
-    Radial Scheme          =       TREUTLER
-    Pruning Scheme         =           NONE
-    Nuclear Scheme         =       TREUTLER
-
-    Blocking Scheme        =         OCTREE
-    BS radius alpha        =              1
-    Pruning alpha          =              1
-    Radial Points          =             75
-    Spherical Points       =            302
-    Total Points           =         174231
-    Total Blocks           =           1336
-    Max Points             =            256
-    Max Functions          =            176
-    Weights Tolerance      =       1.00E-15
-
-   => Loading Basis Set <=
-
-    Name: (DEF2-TZVPPD AUX)
-    Role: JKFIT
-    Keyword: DF_BASIS_SCF
-    atoms 1-2 entry C          line   198 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-universal-jkfit.gbs 
-    atoms 3-8 entry H          line    18 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-universal-jkfit.gbs 
-
-  ==> Integral Setup <==
-
-  DFHelper Memory: AOs need 0.186 GiB; user supplied 0.186 GiB. Using in-core AOs.
-
-  ==> MemDFJK: Density-Fitted J/K Matrices <==
-
-    J tasked:                   Yes
-    K tasked:                   Yes
-    wK tasked:                  Yes
-    Omega:                3.000E-01
-    OpenMP threads:               1
-    Memory [MiB]:               190
-    Algorithm:                 Core
-    Schwarz Cutoff:           1E-12
-    Mask sparsity (%):       0.0065
-    Fitting Condition:        1E-10
-
-   => Auxiliary Basis Set <=
-
-  Basis Set: (DEF2-TZVPPD AUX)
-    Blend: DEF2-UNIVERSAL-JKFIT
-    Number of shells: 86
-    Number of basis functions: 258
-    Number of Cartesian functions: 298
-    Spherical Harmonics?: true
-    Max angular momentum: 4
-
-  Cached 8.4% of DFT collocation blocks in 0.165 [GiB].
-
-  Minimum eigenvalue in the overlap matrix is 1.6743889872E-05.
-  Reciprocal condition number of the overlap matrix is 1.3935089665E-06.
-    Using symmetric orthogonalization.
-
-  ==> Pre-Iterations <==
-
-  SCF Guess: Superposition of Atomic Densities via on-the-fly atomic UHF (no occupation information).
-
-   -------------------------
-    Irrep   Nso     Nmo    
-   -------------------------
-     A        176     176 
-   -------------------------
-    Total     176     176
-   -------------------------
-
-  ==> Iterations <==
-
-                           Total Energy        Delta E     RMS |[F,P]|
-
-   @DF-RKS iter SAD:   -79.28992761806539   -7.92899e+01   0.00000e+00 
-   @DF-RKS iter   1:   -79.67766568691725   -3.87738e-01   3.47782e-03 DIIS/ADIIS
-   @DF-RKS iter   2:   -79.75816627738935   -8.05006e-02   2.66336e-03 DIIS/ADIIS
-   @DF-RKS iter   3:   -79.86799006059530   -1.09824e-01   1.31950e-04 DIIS/ADIIS
-   @DF-RKS iter   4:   -79.86851631941872   -5.26259e-04   3.15890e-05 DIIS
-   @DF-RKS iter   5:   -79.86854815436648   -3.18349e-05   5.34471e-06 DIIS
-   @DF-RKS iter   6:   -79.86854928519799   -1.13083e-06   7.04641e-07 DIIS
-   @DF-RKS iter   7:   -79.86854931592435   -3.07264e-08   1.22071e-07 DIIS
-   @DF-RKS iter   8:   -79.86854931651604   -5.91683e-10   4.97847e-08 DIIS
-   @DF-RKS iter   9:   -79.86854931656264   -4.65974e-11   1.14257e-08 DIIS
-   @DF-RKS iter  10:   -79.86854931656606   -3.42482e-12   1.80398e-09 DIIS
-  Energy and wave function converged.
-
-
-  ==> Post-Iterations <==
-
-   Electrons on quadrature grid:
-      Ntotal   =   18.0000127756 ; deviation = 1.278e-05 
-
-    Orbital Energies [Eh]
-    ---------------------
-
-    Doubly Occupied:                                                      
-
-       1A    -10.334126     2A    -10.333729     3A     -0.875736  
-       4A     -0.726393     5A     -0.543563     6A     -0.542017  
-       7A     -0.469628     8A     -0.441635     9A     -0.432885  
-
-    Virtual:                                                              
-
-      10A      0.045316    11A      0.065512    12A      0.109548  
-      13A      0.110094    14A      0.133878    15A      0.135749  
-      16A      0.153144    17A      0.168647    18A      0.170387  
-      19A      0.200953    20A      0.215127    21A      0.217926  
-      22A      0.244649    23A      0.250721    24A      0.263767  
-      25A      0.282711    26A      0.286959    27A      0.305733  
-      28A      0.317522    29A      0.324458    30A      0.335664  
-      31A      0.352086    32A      0.354271    33A      0.370013  
-      34A      0.372250    35A      0.374795    36A      0.385099  
-      37A      0.410674    38A      0.413507    39A      0.415273  
-      40A      0.440023    41A      0.444402    42A      0.462684  
-      43A      0.466197    44A      0.474520    45A      0.495234  
-      46A      0.496664    47A      0.513014    48A      0.619717  
-      49A      0.627507    50A      0.655424    51A      0.689718  
-      52A      0.706969    53A      0.721094    54A      0.742198  
-      55A      0.748018    56A      0.749627    57A      0.752230  
-      58A      0.786391    59A      0.789003    60A      0.821353  
-      61A      0.918116    62A      0.925919    63A      0.967642  
-      64A      0.991853    65A      1.087734    66A      1.114006  
-      67A      1.167096    68A      1.170033    69A      1.183356  
-      70A      1.195247    71A      1.214751    72A      1.237566  
-      73A      1.302182    74A      1.333099    75A      1.363925  
-      76A      1.393038    77A      1.406261    78A      1.436580  
-      79A      1.556664    80A      1.558475    81A      1.583158  
-      82A      1.619032    83A      1.627605    84A      1.634514  
-      85A      1.663730    86A      1.679034    87A      1.693637  
-      88A      1.704871    89A      1.885641    90A      1.917077  
-      91A      2.023709    92A      2.053746    93A      2.136997  
-      94A      2.301575    95A      2.355002    96A      2.613456  
-      97A      2.677037    98A      2.694888    99A      2.755999  
-     100A      2.773010   101A      2.788802   102A      2.836255  
-     103A      2.840682   104A      2.916091   105A      2.963365  
-     106A      2.979641   107A      2.991705   108A      3.039915  
-     109A      3.052744   110A      3.129687   111A      3.137876  
-     112A      3.147850   113A      3.155208   114A      3.244080  
-     115A      3.259555   116A      3.294333   117A      3.314367  
-     118A      3.342167   119A      3.422823   120A      3.431515  
-     121A      3.533123   122A      3.564563   123A      3.588110  
-     124A      3.627788   125A      3.640406   126A      3.679402  
-     127A      3.713545   128A      3.739019   129A      3.864460  
-     130A      3.875511   131A      3.937208   132A      3.974559  
-     133A      3.998605   134A      4.017810   135A      4.093466  
-     136A      4.111754   137A      4.123870   138A      4.160962  
-     139A      4.181011   140A      4.207929   141A      4.216245  
-     142A      4.244307   143A      4.248379   144A      4.336607  
-     145A      4.362675   146A      4.386331   147A      4.416730  
-     148A      4.535234   149A      4.558945   150A      4.609936  
-     151A      4.655039   152A      4.693997   153A      4.717652  
-     154A      4.892855   155A      4.913920   156A      4.939741  
-     157A      4.952953   158A      5.012049   159A      5.070396  
-     160A      5.246373   161A      5.293864   162A      5.347947  
-     163A      5.357896   164A      5.364785   165A      5.373530  
-     166A      5.390169   167A      5.418753   168A      5.480380  
-     169A      5.558268   170A      5.620145   171A      5.692490  
-     172A      5.711134   173A      5.756651   174A      5.791996  
-     175A     23.799935   176A     23.927837  
-
-    Final Occupation by Irrep:
-              A 
-    DOCC [     9 ]
-
-  @DF-RKS Final Energy:   -79.86854931656606
-
-   => Energetics <=
-
-    Nuclear Repulsion Energy =             42.1144322518140228
-    One-Electron Energy =                -189.0217292834274190
-    Two-Electron Energy =                  75.8975285315186738
-    DFT Exchange-Correlation Energy =      -8.8527898464713530
-    Empirical Dispersion Energy =          -0.0059909700000000
-    VV10 Nonlocal Energy =                  0.0000000000000000
-    Total Energy =                        -79.8685493165660603
-
-Computation Completed
-
-
-Properties will be evaluated at   0.000000,   0.000000,   0.000000 [a0]
-
-Properties computed using the SCF density matrix
-
-
- Multipole Moments:
-
- ------------------------------------------------------------------------------------
-     Multipole            Electronic (a.u.)      Nuclear  (a.u.)        Total (a.u.)
- ------------------------------------------------------------------------------------
-
- L = 1.  Multiply by 2.5417464519 to convert [e a0] to [Debye]
- Dipole X            :          0.0200295           -0.0224186           -0.0023891
- Dipole Y            :         -0.0036566            0.0049638            0.0013072
- Dipole Z            :         -0.0746146            0.0833353            0.0087207
- Magnitude           :                                                    0.0091361
-
- ------------------------------------------------------------------------------------
-
-*** tstop() called on exp-2-41 at Sat Dec 10 09:14:39 2022
-Module time:
-	user time   =      55.28 seconds =       0.92 minutes
-	system time =       0.33 seconds =       0.01 minutes
-	total time  =         57 seconds =       0.95 minutes
-Total time:
-	user time   =      55.28 seconds =       0.92 minutes
-	system time =       0.33 seconds =       0.01 minutes
-	total time  =         57 seconds =       0.95 minutes
-
-*** tstart() called on exp-2-41
-*** at Sat Dec 10 09:14:39 2022
-
-
-         ------------------------------------------------------------
-                                   SCF GRAD                          
-                          Rob Parrish, Justin Turney,                
-                       Andy Simmonett, and Alex Sokolov              
-         ------------------------------------------------------------
-
-  ==> Geometry <==
-
-    Molecular point group: c1
-    Full point group: C1
-
-    Geometry (in Bohr), charge = 0, multiplicity = 1:
-
-       Center              X                  Y                   Z               Mass       
-    ------------   -----------------  -----------------  -----------------  -----------------
-         C            1.309059187335    -0.530960676560     0.283395850372    12.000000000000
-         C           -1.305263812665     0.530120323440    -0.297504149628    12.000000000000
-         H            2.346254187335    -1.337363676560    -1.334794149628     1.007825032230
-         H            0.931624187335    -2.053154676560     1.656377850372     1.007825032230
-         H            2.513533187335     0.904343323440     1.196493850372     1.007825032230
-         H           -2.757838812665    -0.942676676560    -0.553430149628     1.007825032230
-         H           -1.178330812665     1.650444323440    -2.050622149628     1.007825032230
-         H           -1.900432812665     1.788413323440     1.253959850372     1.007825032230
-
-  Nuclear repulsion =   42.114432251814023
-
-  ==> Basis Set <==
-
-  Basis Set: DEF2-TZVPPD
-    Blend: DEF2-TZVPPD
-    Number of shells: 68
-    Number of basis functions: 176
-    Number of Cartesian functions: 194
-    Spherical Harmonics?: true
-    Max angular momentum: 3
-
-  ==> DFJKGrad: Density-Fitted SCF Gradients <==
-
-    Gradient:                    1
-    J tasked:                  Yes
-    K tasked:                  Yes
-    wK tasked:                 Yes
-    Omega:               3.000E-01
-    OpenMP threads:              1
-    Integrals threads:           1
-    Memory [MiB]:              375
-    Schwarz Cutoff:          1E-12
-    Fitting Condition:       1E-10
-
-   => Auxiliary Basis Set <=
-
-  Basis Set: (DEF2-TZVPPD AUX)
-    Blend: DEF2-UNIVERSAL-JKFIT
-    Number of shells: 86
-    Number of basis functions: 258
-    Number of Cartesian functions: 298
-    Spherical Harmonics?: true
-    Max angular momentum: 4
-
-  ==> DFT Potential <==
-
-   => LibXC <=
-
-    Version 5.1.5
-    S. Lehtola, C. Steigemann, M. J. Oliveira, and M. A. Marques, SoftwareX 7, 1 (2018) (10.1016/j.softx.2017.11.002)
-
-   => Composite Functional: WB97M-D3BJ <= 
-
-    wB97M-V with D3(BJ) instead of VV10 dispersion 
-
-    A. Najib, L. Goerigk, J. Comput. Theory Chem.,14, 5725, 2018
-    N. Mardirossian, M. Head-Gordon, J. Chem. Phys. 144, 214110, 2016
-
-
-    Deriv               =              1
-    GGA                 =           TRUE
-    Meta                =           TRUE
-
-    Exchange Hybrid     =           TRUE
-    MP2 Hybrid          =          FALSE
-
-   => Exchange-Correlation Functionals <=
-
-    1.0000   wB97M-V exchange-correlation functional
-
-   => Exact (HF) Exchange <=
-
-    0.8500            HF,LR [omega = 0.3000]
-    0.1500               HF 
-
-   => LibXC Density Thresholds  <==
-
-    XC_HYB_MGGA_XC_WB97M_V:  1.00E-13 
-
-   => Molecular Quadrature <=
-
-    Radial Scheme          =       TREUTLER
-    Pruning Scheme         =           NONE
-    Nuclear Scheme         =       TREUTLER
-
-    Blocking Scheme        =         OCTREE
-    BS radius alpha        =              1
-    Pruning alpha          =              1
-    Radial Points          =             75
-    Spherical Points       =            302
-    Total Points           =         174231
-    Total Blocks           =           1336
-    Max Points             =            256
-    Max Functions          =            176
-    Weights Tolerance      =       1.00E-15
-
-
-  -Total Gradient:
-     Atom            X                  Y                   Z
-    ------   -----------------  -----------------  -----------------
-       1        0.001895773784     0.011781866898    -0.015170522698
-       2       -0.000546756434    -0.012395177679     0.012855204444
-       3        0.008623824979    -0.004386034056    -0.005762912894
-       4       -0.013730630020    -0.003687033163     0.003133079806
-       5        0.004399576588     0.007252138017     0.005168262011
-       6       -0.008316925113    -0.006142832108    -0.000486968302
-       7        0.007554932585     0.001672379716    -0.007775590500
-       8        0.000118796203     0.005904507716     0.008042064203
-
-
-*** tstop() called on exp-2-41 at Sat Dec 10 09:14:47 2022
-Module time:
-	user time   =       8.05 seconds =       0.13 minutes
-	system time =       0.04 seconds =       0.00 minutes
-	total time  =          8 seconds =       0.13 minutes
-Total time:
-	user time   =      63.34 seconds =       1.06 minutes
-	system time =       0.37 seconds =       0.01 minutes
-	total time  =         65 seconds =       1.08 minutes
-  ## Total Gradient (Symmetry 0) ##
-  Irrep: 1 Size: 8 x 3
-
-                 1                   2                   3
-
-    1     0.00189577378438     0.01178186689846    -0.01517052269765
-    2    -0.00054675643432    -0.01239517767892     0.01285520444405
-    3     0.00862382497882    -0.00438603405641    -0.00576291289370
-    4    -0.01373063001962    -0.00368703316336     0.00313307980576
-    5     0.00439957658795     0.00725213801722     0.00516826201141
-    6    -0.00831692511314    -0.00614283210761    -0.00048696830158
-    7     0.00755493258543     0.00167237971637    -0.00777559049988
-    8     0.00011879620295     0.00590450771644     0.00804206420271
-
-
-
-
-    Psi4 stopped on: Saturday, 10 December 2022 09:14AM
-    Psi4 wall time for execution: 0:01:04.42
-
-*** Psi4 exiting successfully. Buy a developer a beer!
\ No newline at end of file
+
+    -----------------------------------------------------------------------
+          Psi4: An Open-Source Ab Initio Electronic Structure Package
+                               Psi4 1.6.1 release
+
+                         Git: Rev {HEAD} 5b9f6e3 
+
+
+    D. G. A. Smith, L. A. Burns, A. C. Simmonett, R. M. Parrish,
+    M. C. Schieber, R. Galvelis, P. Kraus, H. Kruse, R. Di Remigio,
+    A. Alenaizan, A. M. James, S. Lehtola, J. P. Misiewicz, M. Scheurer,
+    R. A. Shaw, J. B. Schriber, Y. Xie, Z. L. Glick, D. A. Sirianni,
+    J. S. O'Brien, J. M. Waldrop, A. Kumar, E. G. Hohenstein,
+    B. P. Pritchard, B. R. Brooks, H. F. Schaefer III, A. Yu. Sokolov,
+    K. Patkowski, A. E. DePrince III, U. Bozkaya, R. A. King,
+    F. A. Evangelista, J. M. Turney, T. D. Crawford, C. D. Sherrill,
+    J. Chem. Phys. 152(18) 184108 (2020). https://doi.org/10.1063/5.0006002
+
+                            Additional Code Authors
+    E. T. Seidl, C. L. Janssen, E. F. Valeev, M. L. Leininger,
+    J. F. Gonthier, R. M. Richard, H. R. McAlexander, M. Saitow, X. Wang,
+    P. Verma, M. H. Lechner, and A. Jiang
+
+             Previous Authors, Complete List of Code Contributors,
+                       and Citations for Specific Modules
+    https://github.com/psi4/psi4/blob/master/codemeta.json
+    https://github.com/psi4/psi4/graphs/contributors
+    http://psicode.org/psi4manual/master/introduction.html#citing-psifour
+
+    -----------------------------------------------------------------------
+
+
+    Psi4 started on: Saturday, 10 December 2022 09:13AM
+
+    Process ID: 4075549
+    Host:       exp-2-41
+    PSIDATADIR: /home/njzjz/anaconda3/envs/p4env/share/psi4
+    Memory:     500.0 MiB
+    Threads:    1
+    
+  ==> Input File <==
+
+--------------------------------------------------------------------------
+molecule {
+C -2.048123 8.737055 18.514156
+C -4.662446 9.798136 17.933256
+H -1.010928 7.930652 16.895966
+H -2.425558 7.214861 19.887138
+H -0.843649 10.172359 19.427254
+H -6.115021 8.325339 17.677330
+H -4.535513 10.918460 16.180138
+H -5.257615 11.056429 19.484720
+0 1
+unit bohr
+}
+set basis def2-TZVPPD
+set gradient_write on
+G, wfn = gradient("WB97M-D3BJ", return_wfn=True)
+wfn.energy()
+wfn.gradient().print_out()--------------------------------------------------------------------------
+
+Scratch directory: /scratch/njzjz/job_17958150/
+gradient() will perform analytic gradient computation.
+
+*** tstart() called on exp-2-41
+*** at Sat Dec 10 09:13:42 2022
+
+   => Loading Basis Set <=
+
+    Name: DEF2-TZVPPD
+    Role: ORBITAL
+    Keyword: BASIS
+    atoms 1-2 entry C          line   144 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-tzvppd.gbs 
+    atoms 3-8 entry H          line    14 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-tzvppd.gbs 
+
+   => WB97M-D3BJ: Empirical Dispersion <=
+
+    Grimme's -D3 (BJ-damping) Dispersion Correction
+    Grimme S.; Ehrlich S.; Goerigk L. (2011), J. Comput. Chem., 32: 1456
+    Parametrisation from:    A. Najib, L. Goerigk, J. Comput. Theory Chem.,14, 5725, 2018
+
+        s6 =       1.000000
+        s8 =       0.390800
+        a1 =       0.566000
+        a2 =       3.128000
+
+
+         ---------------------------------------------------------
+                                   SCF
+               by Justin Turney, Rob Parrish, Andy Simmonett
+                          and Daniel G. A. Smith
+                              RKS Reference
+                        1 Threads,    500 MiB Core
+         ---------------------------------------------------------
+
+  ==> Geometry <==
+
+    Molecular point group: c1
+    Full point group: C1
+
+    Geometry (in Bohr), charge = 0, multiplicity = 1:
+
+       Center              X                  Y                   Z               Mass       
+    ------------   -----------------  -----------------  -----------------  -----------------
+         C            1.309059187335    -0.530960676560     0.283395850372    12.000000000000
+         C           -1.305263812665     0.530120323440    -0.297504149628    12.000000000000
+         H            2.346254187335    -1.337363676560    -1.334794149628     1.007825032230
+         H            0.931624187335    -2.053154676560     1.656377850372     1.007825032230
+         H            2.513533187335     0.904343323440     1.196493850372     1.007825032230
+         H           -2.757838812665    -0.942676676560    -0.553430149628     1.007825032230
+         H           -1.178330812665     1.650444323440    -2.050622149628     1.007825032230
+         H           -1.900432812665     1.788413323440     1.253959850372     1.007825032230
+
+  Running in c1 symmetry.
+
+  Rotational constants: A =      2.61492  B =      0.67176  C =      0.67128 [cm^-1]
+  Rotational constants: A =  78393.23592  B =  20138.72637  C =  20124.39598 [MHz]
+  Nuclear repulsion =   42.114432251814023
+
+  Charge       = 0
+  Multiplicity = 1
+  Electrons    = 18
+  Nalpha       = 9
+  Nbeta        = 9
+
+  ==> Algorithm <==
+
+  SCF Algorithm Type is DF.
+  DIIS enabled.
+  MOM disabled.
+  Fractional occupation disabled.
+  Guess Type is SAD.
+  Energy threshold   = 1.00e-08
+  Density threshold  = 1.00e-08
+  Integral threshold = 1.00e-12
+
+  ==> Primary Basis <==
+
+  Basis Set: DEF2-TZVPPD
+    Blend: DEF2-TZVPPD
+    Number of shells: 68
+    Number of basis functions: 176
+    Number of Cartesian functions: 194
+    Spherical Harmonics?: true
+    Max angular momentum: 3
+
+  ==> DFT Potential <==
+
+   => LibXC <=
+
+    Version 5.1.5
+    S. Lehtola, C. Steigemann, M. J. Oliveira, and M. A. Marques, SoftwareX 7, 1 (2018) (10.1016/j.softx.2017.11.002)
+
+   => Composite Functional: WB97M-D3BJ <= 
+
+    wB97M-V with D3(BJ) instead of VV10 dispersion 
+
+    A. Najib, L. Goerigk, J. Comput. Theory Chem.,14, 5725, 2018
+    N. Mardirossian, M. Head-Gordon, J. Chem. Phys. 144, 214110, 2016
+
+
+    Deriv               =              1
+    GGA                 =           TRUE
+    Meta                =           TRUE
+
+    Exchange Hybrid     =           TRUE
+    MP2 Hybrid          =          FALSE
+
+   => Exchange-Correlation Functionals <=
+
+    1.0000   wB97M-V exchange-correlation functional
+
+   => Exact (HF) Exchange <=
+
+    0.8500            HF,LR [omega = 0.3000]
+    0.1500               HF 
+
+   => LibXC Density Thresholds  <==
+
+    XC_HYB_MGGA_XC_WB97M_V:  1.00E-13 
+
+   => Molecular Quadrature <=
+
+    Radial Scheme          =       TREUTLER
+    Pruning Scheme         =           NONE
+    Nuclear Scheme         =       TREUTLER
+
+    Blocking Scheme        =         OCTREE
+    BS radius alpha        =              1
+    Pruning alpha          =              1
+    Radial Points          =             75
+    Spherical Points       =            302
+    Total Points           =         174231
+    Total Blocks           =           1336
+    Max Points             =            256
+    Max Functions          =            176
+    Weights Tolerance      =       1.00E-15
+
+   => Loading Basis Set <=
+
+    Name: (DEF2-TZVPPD AUX)
+    Role: JKFIT
+    Keyword: DF_BASIS_SCF
+    atoms 1-2 entry C          line   198 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-universal-jkfit.gbs 
+    atoms 3-8 entry H          line    18 file /home/njzjz/anaconda3/envs/p4env/share/psi4/basis/def2-universal-jkfit.gbs 
+
+  ==> Integral Setup <==
+
+  DFHelper Memory: AOs need 0.186 GiB; user supplied 0.186 GiB. Using in-core AOs.
+
+  ==> MemDFJK: Density-Fitted J/K Matrices <==
+
+    J tasked:                   Yes
+    K tasked:                   Yes
+    wK tasked:                  Yes
+    Omega:                3.000E-01
+    OpenMP threads:               1
+    Memory [MiB]:               190
+    Algorithm:                 Core
+    Schwarz Cutoff:           1E-12
+    Mask sparsity (%):       0.0065
+    Fitting Condition:        1E-10
+
+   => Auxiliary Basis Set <=
+
+  Basis Set: (DEF2-TZVPPD AUX)
+    Blend: DEF2-UNIVERSAL-JKFIT
+    Number of shells: 86
+    Number of basis functions: 258
+    Number of Cartesian functions: 298
+    Spherical Harmonics?: true
+    Max angular momentum: 4
+
+  Cached 8.4% of DFT collocation blocks in 0.165 [GiB].
+
+  Minimum eigenvalue in the overlap matrix is 1.6743889872E-05.
+  Reciprocal condition number of the overlap matrix is 1.3935089665E-06.
+    Using symmetric orthogonalization.
+
+  ==> Pre-Iterations <==
+
+  SCF Guess: Superposition of Atomic Densities via on-the-fly atomic UHF (no occupation information).
+
+   -------------------------
+    Irrep   Nso     Nmo    
+   -------------------------
+     A        176     176 
+   -------------------------
+    Total     176     176
+   -------------------------
+
+  ==> Iterations <==
+
+                           Total Energy        Delta E     RMS |[F,P]|
+
+   @DF-RKS iter SAD:   -79.28992761806539   -7.92899e+01   0.00000e+00 
+   @DF-RKS iter   1:   -79.67766568691725   -3.87738e-01   3.47782e-03 DIIS/ADIIS
+   @DF-RKS iter   2:   -79.75816627738935   -8.05006e-02   2.66336e-03 DIIS/ADIIS
+   @DF-RKS iter   3:   -79.86799006059530   -1.09824e-01   1.31950e-04 DIIS/ADIIS
+   @DF-RKS iter   4:   -79.86851631941872   -5.26259e-04   3.15890e-05 DIIS
+   @DF-RKS iter   5:   -79.86854815436648   -3.18349e-05   5.34471e-06 DIIS
+   @DF-RKS iter   6:   -79.86854928519799   -1.13083e-06   7.04641e-07 DIIS
+   @DF-RKS iter   7:   -79.86854931592435   -3.07264e-08   1.22071e-07 DIIS
+   @DF-RKS iter   8:   -79.86854931651604   -5.91683e-10   4.97847e-08 DIIS
+   @DF-RKS iter   9:   -79.86854931656264   -4.65974e-11   1.14257e-08 DIIS
+   @DF-RKS iter  10:   -79.86854931656606   -3.42482e-12   1.80398e-09 DIIS
+  Energy and wave function converged.
+
+
+  ==> Post-Iterations <==
+
+   Electrons on quadrature grid:
+      Ntotal   =   18.0000127756 ; deviation = 1.278e-05 
+
+    Orbital Energies [Eh]
+    ---------------------
+
+    Doubly Occupied:                                                      
+
+       1A    -10.334126     2A    -10.333729     3A     -0.875736  
+       4A     -0.726393     5A     -0.543563     6A     -0.542017  
+       7A     -0.469628     8A     -0.441635     9A     -0.432885  
+
+    Virtual:                                                              
+
+      10A      0.045316    11A      0.065512    12A      0.109548  
+      13A      0.110094    14A      0.133878    15A      0.135749  
+      16A      0.153144    17A      0.168647    18A      0.170387  
+      19A      0.200953    20A      0.215127    21A      0.217926  
+      22A      0.244649    23A      0.250721    24A      0.263767  
+      25A      0.282711    26A      0.286959    27A      0.305733  
+      28A      0.317522    29A      0.324458    30A      0.335664  
+      31A      0.352086    32A      0.354271    33A      0.370013  
+      34A      0.372250    35A      0.374795    36A      0.385099  
+      37A      0.410674    38A      0.413507    39A      0.415273  
+      40A      0.440023    41A      0.444402    42A      0.462684  
+      43A      0.466197    44A      0.474520    45A      0.495234  
+      46A      0.496664    47A      0.513014    48A      0.619717  
+      49A      0.627507    50A      0.655424    51A      0.689718  
+      52A      0.706969    53A      0.721094    54A      0.742198  
+      55A      0.748018    56A      0.749627    57A      0.752230  
+      58A      0.786391    59A      0.789003    60A      0.821353  
+      61A      0.918116    62A      0.925919    63A      0.967642  
+      64A      0.991853    65A      1.087734    66A      1.114006  
+      67A      1.167096    68A      1.170033    69A      1.183356  
+      70A      1.195247    71A      1.214751    72A      1.237566  
+      73A      1.302182    74A      1.333099    75A      1.363925  
+      76A      1.393038    77A      1.406261    78A      1.436580  
+      79A      1.556664    80A      1.558475    81A      1.583158  
+      82A      1.619032    83A      1.627605    84A      1.634514  
+      85A      1.663730    86A      1.679034    87A      1.693637  
+      88A      1.704871    89A      1.885641    90A      1.917077  
+      91A      2.023709    92A      2.053746    93A      2.136997  
+      94A      2.301575    95A      2.355002    96A      2.613456  
+      97A      2.677037    98A      2.694888    99A      2.755999  
+     100A      2.773010   101A      2.788802   102A      2.836255  
+     103A      2.840682   104A      2.916091   105A      2.963365  
+     106A      2.979641   107A      2.991705   108A      3.039915  
+     109A      3.052744   110A      3.129687   111A      3.137876  
+     112A      3.147850   113A      3.155208   114A      3.244080  
+     115A      3.259555   116A      3.294333   117A      3.314367  
+     118A      3.342167   119A      3.422823   120A      3.431515  
+     121A      3.533123   122A      3.564563   123A      3.588110  
+     124A      3.627788   125A      3.640406   126A      3.679402  
+     127A      3.713545   128A      3.739019   129A      3.864460  
+     130A      3.875511   131A      3.937208   132A      3.974559  
+     133A      3.998605   134A      4.017810   135A      4.093466  
+     136A      4.111754   137A      4.123870   138A      4.160962  
+     139A      4.181011   140A      4.207929   141A      4.216245  
+     142A      4.244307   143A      4.248379   144A      4.336607  
+     145A      4.362675   146A      4.386331   147A      4.416730  
+     148A      4.535234   149A      4.558945   150A      4.609936  
+     151A      4.655039   152A      4.693997   153A      4.717652  
+     154A      4.892855   155A      4.913920   156A      4.939741  
+     157A      4.952953   158A      5.012049   159A      5.070396  
+     160A      5.246373   161A      5.293864   162A      5.347947  
+     163A      5.357896   164A      5.364785   165A      5.373530  
+     166A      5.390169   167A      5.418753   168A      5.480380  
+     169A      5.558268   170A      5.620145   171A      5.692490  
+     172A      5.711134   173A      5.756651   174A      5.791996  
+     175A     23.799935   176A     23.927837  
+
+    Final Occupation by Irrep:
+              A 
+    DOCC [     9 ]
+
+  @DF-RKS Final Energy:   -79.86854931656606
+
+   => Energetics <=
+
+    Nuclear Repulsion Energy =             42.1144322518140228
+    One-Electron Energy =                -189.0217292834274190
+    Two-Electron Energy =                  75.8975285315186738
+    DFT Exchange-Correlation Energy =      -8.8527898464713530
+    Empirical Dispersion Energy =          -0.0059909700000000
+    VV10 Nonlocal Energy =                  0.0000000000000000
+    Total Energy =                        -79.8685493165660603
+
+Computation Completed
+
+
+Properties will be evaluated at   0.000000,   0.000000,   0.000000 [a0]
+
+Properties computed using the SCF density matrix
+
+
+ Multipole Moments:
+
+ ------------------------------------------------------------------------------------
+     Multipole            Electronic (a.u.)      Nuclear  (a.u.)        Total (a.u.)
+ ------------------------------------------------------------------------------------
+
+ L = 1.  Multiply by 2.5417464519 to convert [e a0] to [Debye]
+ Dipole X            :          0.0200295           -0.0224186           -0.0023891
+ Dipole Y            :         -0.0036566            0.0049638            0.0013072
+ Dipole Z            :         -0.0746146            0.0833353            0.0087207
+ Magnitude           :                                                    0.0091361
+
+ ------------------------------------------------------------------------------------
+
+*** tstop() called on exp-2-41 at Sat Dec 10 09:14:39 2022
+Module time:
+	user time   =      55.28 seconds =       0.92 minutes
+	system time =       0.33 seconds =       0.01 minutes
+	total time  =         57 seconds =       0.95 minutes
+Total time:
+	user time   =      55.28 seconds =       0.92 minutes
+	system time =       0.33 seconds =       0.01 minutes
+	total time  =         57 seconds =       0.95 minutes
+
+*** tstart() called on exp-2-41
+*** at Sat Dec 10 09:14:39 2022
+
+
+         ------------------------------------------------------------
+                                   SCF GRAD                          
+                          Rob Parrish, Justin Turney,                
+                       Andy Simmonett, and Alex Sokolov              
+         ------------------------------------------------------------
+
+  ==> Geometry <==
+
+    Molecular point group: c1
+    Full point group: C1
+
+    Geometry (in Bohr), charge = 0, multiplicity = 1:
+
+       Center              X                  Y                   Z               Mass       
+    ------------   -----------------  -----------------  -----------------  -----------------
+         C            1.309059187335    -0.530960676560     0.283395850372    12.000000000000
+         C           -1.305263812665     0.530120323440    -0.297504149628    12.000000000000
+         H            2.346254187335    -1.337363676560    -1.334794149628     1.007825032230
+         H            0.931624187335    -2.053154676560     1.656377850372     1.007825032230
+         H            2.513533187335     0.904343323440     1.196493850372     1.007825032230
+         H           -2.757838812665    -0.942676676560    -0.553430149628     1.007825032230
+         H           -1.178330812665     1.650444323440    -2.050622149628     1.007825032230
+         H           -1.900432812665     1.788413323440     1.253959850372     1.007825032230
+
+  Nuclear repulsion =   42.114432251814023
+
+  ==> Basis Set <==
+
+  Basis Set: DEF2-TZVPPD
+    Blend: DEF2-TZVPPD
+    Number of shells: 68
+    Number of basis functions: 176
+    Number of Cartesian functions: 194
+    Spherical Harmonics?: true
+    Max angular momentum: 3
+
+  ==> DFJKGrad: Density-Fitted SCF Gradients <==
+
+    Gradient:                    1
+    J tasked:                  Yes
+    K tasked:                  Yes
+    wK tasked:                 Yes
+    Omega:               3.000E-01
+    OpenMP threads:              1
+    Integrals threads:           1
+    Memory [MiB]:              375
+    Schwarz Cutoff:          1E-12
+    Fitting Condition:       1E-10
+
+   => Auxiliary Basis Set <=
+
+  Basis Set: (DEF2-TZVPPD AUX)
+    Blend: DEF2-UNIVERSAL-JKFIT
+    Number of shells: 86
+    Number of basis functions: 258
+    Number of Cartesian functions: 298
+    Spherical Harmonics?: true
+    Max angular momentum: 4
+
+  ==> DFT Potential <==
+
+   => LibXC <=
+
+    Version 5.1.5
+    S. Lehtola, C. Steigemann, M. J. Oliveira, and M. A. Marques, SoftwareX 7, 1 (2018) (10.1016/j.softx.2017.11.002)
+
+   => Composite Functional: WB97M-D3BJ <= 
+
+    wB97M-V with D3(BJ) instead of VV10 dispersion 
+
+    A. Najib, L. Goerigk, J. Comput. Theory Chem.,14, 5725, 2018
+    N. Mardirossian, M. Head-Gordon, J. Chem. Phys. 144, 214110, 2016
+
+
+    Deriv               =              1
+    GGA                 =           TRUE
+    Meta                =           TRUE
+
+    Exchange Hybrid     =           TRUE
+    MP2 Hybrid          =          FALSE
+
+   => Exchange-Correlation Functionals <=
+
+    1.0000   wB97M-V exchange-correlation functional
+
+   => Exact (HF) Exchange <=
+
+    0.8500            HF,LR [omega = 0.3000]
+    0.1500               HF 
+
+   => LibXC Density Thresholds  <==
+
+    XC_HYB_MGGA_XC_WB97M_V:  1.00E-13 
+
+   => Molecular Quadrature <=
+
+    Radial Scheme          =       TREUTLER
+    Pruning Scheme         =           NONE
+    Nuclear Scheme         =       TREUTLER
+
+    Blocking Scheme        =         OCTREE
+    BS radius alpha        =              1
+    Pruning alpha          =              1
+    Radial Points          =             75
+    Spherical Points       =            302
+    Total Points           =         174231
+    Total Blocks           =           1336
+    Max Points             =            256
+    Max Functions          =            176
+    Weights Tolerance      =       1.00E-15
+
+
+  -Total Gradient:
+     Atom            X                  Y                   Z
+    ------   -----------------  -----------------  -----------------
+       1        0.001895773784     0.011781866898    -0.015170522698
+       2       -0.000546756434    -0.012395177679     0.012855204444
+       3        0.008623824979    -0.004386034056    -0.005762912894
+       4       -0.013730630020    -0.003687033163     0.003133079806
+       5        0.004399576588     0.007252138017     0.005168262011
+       6       -0.008316925113    -0.006142832108    -0.000486968302
+       7        0.007554932585     0.001672379716    -0.007775590500
+       8        0.000118796203     0.005904507716     0.008042064203
+
+
+*** tstop() called on exp-2-41 at Sat Dec 10 09:14:47 2022
+Module time:
+	user time   =       8.05 seconds =       0.13 minutes
+	system time =       0.04 seconds =       0.00 minutes
+	total time  =          8 seconds =       0.13 minutes
+Total time:
+	user time   =      63.34 seconds =       1.06 minutes
+	system time =       0.37 seconds =       0.01 minutes
+	total time  =         65 seconds =       1.08 minutes
+  ## Total Gradient (Symmetry 0) ##
+  Irrep: 1 Size: 8 x 3
+
+                 1                   2                   3
+
+    1     0.00189577378438     0.01178186689846    -0.01517052269765
+    2    -0.00054675643432    -0.01239517767892     0.01285520444405
+    3     0.00862382497882    -0.00438603405641    -0.00576291289370
+    4    -0.01373063001962    -0.00368703316336     0.00313307980576
+    5     0.00439957658795     0.00725213801722     0.00516826201141
+    6    -0.00831692511314    -0.00614283210761    -0.00048696830158
+    7     0.00755493258543     0.00167237971637    -0.00777559049988
+    8     0.00011879620295     0.00590450771644     0.00804206420271
+
+
+
+
+    Psi4 stopped on: Saturday, 10 December 2022 09:14AM
+    Psi4 wall time for execution: 0:01:04.42
+
+*** Psi4 exiting successfully. Buy a developer a beer!
diff --git a/tests/pwmat/config_ref_ch4.py b/tests/pwmat/config_ref_ch4.py
index 71aef7fe1..72499398e 100644
--- a/tests/pwmat/config_ref_ch4.py
+++ b/tests/pwmat/config_ref_ch4.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 
diff --git a/tests/pwmat/config_ref_oh.py b/tests/pwmat/config_ref_oh.py
index 6f3e05619..ad546019a 100644
--- a/tests/pwmat/config_ref_oh.py
+++ b/tests/pwmat/config_ref_oh.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import numpy as np
 
 
diff --git a/tests/test_abacus_md.py b/tests/test_abacus_md.py
index 782ed5214..ddcb7734a 100644
--- a/tests/test_abacus_md.py
+++ b/tests/test_abacus_md.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_abacus_pw_scf.py b/tests/test_abacus_pw_scf.py
index eb712fbeb..8d13dddcf 100644
--- a/tests/test_abacus_pw_scf.py
+++ b/tests/test_abacus_pw_scf.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_abacus_relax.py b/tests/test_abacus_relax.py
index 65d73e53f..b752a4262 100644
--- a/tests/test_abacus_relax.py
+++ b/tests/test_abacus_relax.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_abacus_stru_dump.py b/tests/test_abacus_stru_dump.py
index 46cb5de6a..356aa57f4 100644
--- a/tests/test_abacus_stru_dump.py
+++ b/tests/test_abacus_stru_dump.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_amber_md.py b/tests/test_amber_md.py
index 3995371ee..b0a060585 100644
--- a/tests/test_amber_md.py
+++ b/tests/test_amber_md.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_amber_sqm.py b/tests/test_amber_sqm.py
index 7f14ff84c..b7f091100 100644
--- a/tests/test_amber_sqm.py
+++ b/tests/test_amber_sqm.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_ase_traj.py b/tests/test_ase_traj.py
index b6eab27e1..98bc8bc04 100644
--- a/tests/test_ase_traj.py
+++ b/tests/test_ase_traj.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, CompSys, IsPBC
@@ -65,5 +67,29 @@ def setUp(self):
         self.v_places = 4
 
 
+@unittest.skipIf(skip_ase, "skip ase related test. install ase to fix")
+class TestASEtraj4(unittest.TestCase, CompSys, IsPBC):
+    def setUp(self):
+        self.system_1 = dpdata.System("ase_traj/MoS2", fmt="deepmd")
+        self.system_1.to(file_name="ase_traj/tmp.traj", fmt="ase/traj")
+        self.system_2 = dpdata.System("ase_traj/tmp.traj", fmt="ase/traj")
+        self.places = 6
+        self.e_places = 6
+        self.f_places = 6
+        self.v_places = 4
+
+
+@unittest.skipIf(skip_ase, "skip ase related test. install ase to fix")
+class TestASEtraj4Labeled(unittest.TestCase, CompLabeledSys, IsPBC):
+    def setUp(self):
+        self.system_1 = dpdata.LabeledSystem("ase_traj/MoS2", fmt="deepmd")
+        self.system_1.to(file_name="ase_traj/tmp1.traj", fmt="ase/traj")
+        self.system_2 = dpdata.LabeledSystem("ase_traj/tmp1.traj", fmt="ase/traj")
+        self.places = 6
+        self.e_places = 6
+        self.f_places = 6
+        self.v_places = 4
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_bond_order_system.py b/tests/test_bond_order_system.py
index 41a167fbc..104e18f1f 100644
--- a/tests/test_bond_order_system.py
+++ b/tests/test_bond_order_system.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import glob
 import os
 import unittest
diff --git a/tests/test_cell_to_low_triangle.py b/tests/test_cell_to_low_triangle.py
index c080c8e5f..34d0a90ae 100644
--- a/tests/test_cell_to_low_triangle.py
+++ b/tests/test_cell_to_low_triangle.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 423b8896a..9d70db5ff 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,4 +1,7 @@
+from __future__ import annotations
+
 import subprocess as sp
+import sys
 import unittest
 
 from context import dpdata
@@ -26,3 +29,12 @@ def setUpClass(cls) -> None:
     @classmethod
     def tearDownClass(cls) -> None:
         cls.system = None
+
+
+class TestClassScript(unittest.TestCase):
+    def test_class_script(self):
+        expected_version = dpdata.__version__
+        output = sp.check_output([sys.executable, "-m", "dpdata", "--version"]).decode(
+            "ascii"
+        )
+        assert output.splitlines()[0] == f"dpdata v{expected_version}"
diff --git a/tests/test_corr.py b/tests/test_corr.py
index 463c99af9..a7c6f7c4a 100644
--- a/tests/test_corr.py
+++ b/tests/test_corr.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, IsPBC
diff --git a/tests/test_cp2k_aimd_output.py b/tests/test_cp2k_aimd_output.py
index bce242500..46f292b11 100644
--- a/tests/test_cp2k_aimd_output.py
+++ b/tests/test_cp2k_aimd_output.py
@@ -1,4 +1,6 @@
 # %%
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys
diff --git a/tests/test_cp2k_output.py b/tests/test_cp2k_output.py
index 0e4b153dc..da58e87ce 100644
--- a/tests/test_cp2k_output.py
+++ b/tests/test_cp2k_output.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys
diff --git a/tests/test_custom_data_type.py b/tests/test_custom_data_type.py
index 006d6b01e..e94ba5e0f 100644
--- a/tests/test_custom_data_type.py
+++ b/tests/test_custom_data_type.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
 import unittest
 
-import h5py
+import h5py  # noqa: TID253
 import numpy as np
 
 import dpdata
diff --git a/tests/test_deepmd_comp.py b/tests/test_deepmd_comp.py
index 46f8e7414..284287866 100644
--- a/tests/test_deepmd_comp.py
+++ b/tests/test_deepmd_comp.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_deepmd_hdf5.py b/tests/test_deepmd_hdf5.py
index 20d16c370..b4a22f3c1 100644
--- a/tests/test_deepmd_hdf5.py
+++ b/tests/test_deepmd_hdf5.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_deepmd_mixed.py b/tests/test_deepmd_mixed.py
index 7e522e065..02044932e 100644
--- a/tests/test_deepmd_mixed.py
+++ b/tests/test_deepmd_mixed.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_deepmd_raw.py b/tests/test_deepmd_raw.py
index 1b0567260..af875fdea 100644
--- a/tests/test_deepmd_raw.py
+++ b/tests/test_deepmd_raw.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import shutil
 import unittest
diff --git a/tests/test_dftbplus.py b/tests/test_dftbplus.py
index 2a2913a52..29cdaa92e 100644
--- a/tests/test_dftbplus.py
+++ b/tests/test_dftbplus.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_elements_index.py b/tests/test_elements_index.py
index 45408b4d8..186d7b806 100644
--- a/tests/test_elements_index.py
+++ b/tests/test_elements_index.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from dpdata.system import elements_index_map
diff --git a/tests/test_empty.py b/tests/test_empty.py
index 8787f9543..12913bab9 100644
--- a/tests/test_empty.py
+++ b/tests/test_empty.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_fhi_md_multi_elem_output.py b/tests/test_fhi_md_multi_elem_output.py
index a20c45bdd..b11a52f54 100644
--- a/tests/test_fhi_md_multi_elem_output.py
+++ b/tests/test_fhi_md_multi_elem_output.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_fhi_md_output.py b/tests/test_fhi_md_output.py
index d205e3911..391cc319a 100644
--- a/tests/test_fhi_md_output.py
+++ b/tests/test_fhi_md_output.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_fhi_output.py b/tests/test_fhi_output.py
index 067e5f699..bd3582f31 100644
--- a/tests/test_fhi_output.py
+++ b/tests/test_fhi_output.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_from_pymatgen.py b/tests/test_from_pymatgen.py
new file mode 100644
index 000000000..7689a9d5e
--- /dev/null
+++ b/tests/test_from_pymatgen.py
@@ -0,0 +1,32 @@
+from __future__ import annotations
+
+import os
+import unittest
+
+from comp_sys import CompSys
+from context import dpdata
+
+try:
+    from pymatgen.core import Structure  # noqa: F401
+
+    exist_module = True
+except Exception:
+    exist_module = False
+
+
+@unittest.skipIf(not exist_module, "skip pymatgen")
+class TestFormPytmatgen(unittest.TestCase, CompSys):
+    def setUp(self):
+        structure = Structure.from_file(os.path.join("poscars", "POSCAR.P42nmc"))
+        self.system_1 = dpdata.System(structure, fmt="pymatgen/structure")
+        self.system_2 = dpdata.System(
+            os.path.join("poscars", "POSCAR.P42nmc"), fmt="poscar"
+        )
+        self.places = 6
+        self.e_places = 6
+        self.f_places = 6
+        self.v_places = 6
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/test_gaussian_driver.py b/tests/test_gaussian_driver.py
index 07150bc7b..ff1638488 100644
--- a/tests/test_gaussian_driver.py
+++ b/tests/test_gaussian_driver.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import importlib
 import os
 import shutil
diff --git a/tests/test_gaussian_gjf.py b/tests/test_gaussian_gjf.py
index 2e5f4ea8f..6bed23f61 100644
--- a/tests/test_gaussian_gjf.py
+++ b/tests/test_gaussian_gjf.py
@@ -1,27 +1,29 @@
-import os
-import unittest
-
-from comp_sys import CompSys
-from context import dpdata
-
-
-class TestGaussianGJF(unittest.TestCase):
-    def setUp(self):
-        self.system = dpdata.LabeledSystem("poscars/OUTCAR.h2o.md", fmt="vasp/outcar")
-
-    def test_dump_gaussian_gjf(self):
-        self.system.to_gaussian_gjf("tmp.gjf", keywords="force b3lyp/6-31g*")
-        os.remove("tmp.gjf")
-
-
-class TestGaussianGJFComp(unittest.TestCase, CompSys):
-    def setUp(self):
-        self.system_1 = dpdata.LabeledSystem(
-            "poscars/OUTCAR.h2o.md", fmt="vasp/outcar"
-        )[0]
-        self.system_1.to_gaussian_gjf("tmp.gjf", keywords="force b3lyp/6-31g*")
-        self.system_2 = dpdata.System(
-            "tmp.gjf", fmt="gaussian/gjf", type_map=self.system_1.get_atom_names()
-        )
-        os.remove("tmp.gjf")
-        self.places = 6
+from __future__ import annotations
+
+import os
+import unittest
+
+from comp_sys import CompSys
+from context import dpdata
+
+
+class TestGaussianGJF(unittest.TestCase):
+    def setUp(self):
+        self.system = dpdata.LabeledSystem("poscars/OUTCAR.h2o.md", fmt="vasp/outcar")
+
+    def test_dump_gaussian_gjf(self):
+        self.system.to_gaussian_gjf("tmp.gjf", keywords="force b3lyp/6-31g*")
+        os.remove("tmp.gjf")
+
+
+class TestGaussianGJFComp(unittest.TestCase, CompSys):
+    def setUp(self):
+        self.system_1 = dpdata.LabeledSystem(
+            "poscars/OUTCAR.h2o.md", fmt="vasp/outcar"
+        )[0]
+        self.system_1.to_gaussian_gjf("tmp.gjf", keywords="force b3lyp/6-31g*")
+        self.system_2 = dpdata.System(
+            "tmp.gjf", fmt="gaussian/gjf", type_map=self.system_1.get_atom_names()
+        )
+        os.remove("tmp.gjf")
+        self.places = 6
diff --git a/tests/test_gaussian_log.py b/tests/test_gaussian_log.py
index 6622e6841..784fd5945 100644
--- a/tests/test_gaussian_log.py
+++ b/tests/test_gaussian_log.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_gromacs_gro.py b/tests/test_gromacs_gro.py
index 2971755f1..674c65100 100644
--- a/tests/test_gromacs_gro.py
+++ b/tests/test_gromacs_gro.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_json.py b/tests/test_json.py
index 545e5db8c..0b6f1b9dd 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, IsPBC
diff --git a/tests/test_lammps_dump_idx.py b/tests/test_lammps_dump_idx.py
index 272cc222e..39379158b 100644
--- a/tests/test_lammps_dump_idx.py
+++ b/tests/test_lammps_dump_idx.py
@@ -1,4 +1,5 @@
 # The index should map to that in the dump file
+from __future__ import annotations
 
 import os
 import unittest
diff --git a/tests/test_lammps_dump_shift_origin.py b/tests/test_lammps_dump_shift_origin.py
index 4ecd6f873..a74442347 100644
--- a/tests/test_lammps_dump_shift_origin.py
+++ b/tests/test_lammps_dump_shift_origin.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompSys, IsPBC
diff --git a/tests/test_lammps_dump_skipload.py b/tests/test_lammps_dump_skipload.py
index 224ec6d1f..299e1db48 100644
--- a/tests/test_lammps_dump_skipload.py
+++ b/tests/test_lammps_dump_skipload.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_lammps_dump_to_system.py b/tests/test_lammps_dump_to_system.py
index af9748a51..4d634037c 100644
--- a/tests/test_lammps_dump_to_system.py
+++ b/tests/test_lammps_dump_to_system.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_lammps_dump_unfold.py b/tests/test_lammps_dump_unfold.py
index 1e78d9756..587602c8a 100644
--- a/tests/test_lammps_dump_unfold.py
+++ b/tests/test_lammps_dump_unfold.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_lammps_lmp_dump.py b/tests/test_lammps_lmp_dump.py
index 8e9cfb328..25525f76b 100644
--- a/tests/test_lammps_lmp_dump.py
+++ b/tests/test_lammps_lmp_dump.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_lammps_lmp_to_system.py b/tests/test_lammps_lmp_to_system.py
index 19e133121..444b1dd43 100644
--- a/tests/test_lammps_lmp_to_system.py
+++ b/tests/test_lammps_lmp_to_system.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_lammps_read_from_trajs.py b/tests/test_lammps_read_from_trajs.py
index f1e5afdd1..578ae471e 100644
--- a/tests/test_lammps_read_from_trajs.py
+++ b/tests/test_lammps_read_from_trajs.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_msd.py b/tests/test_msd.py
index 52b1ce935..7148b0b5a 100644
--- a/tests/test_msd.py
+++ b/tests/test_msd.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_multisystems.py b/tests/test_multisystems.py
index 2bda13a9b..88d4593a1 100644
--- a/tests/test_multisystems.py
+++ b/tests/test_multisystems.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import tempfile
 import unittest
diff --git a/tests/test_n2p2.py b/tests/test_n2p2.py
index 855a27524..32ac64473 100644
--- a/tests/test_n2p2.py
+++ b/tests/test_n2p2.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_openmx.py b/tests/test_openmx.py
index 0705ed0a6..2122e8f47 100644
--- a/tests/test_openmx.py
+++ b/tests/test_openmx.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_openmx_check_convergence.py b/tests/test_openmx_check_convergence.py
index 362c89c58..b19ad6e8d 100644
--- a/tests/test_openmx_check_convergence.py
+++ b/tests/test_openmx_check_convergence.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_orca_spout.py b/tests/test_orca_spout.py
index ecb1a5ca8..7e8cb7179 100644
--- a/tests/test_orca_spout.py
+++ b/tests/test_orca_spout.py
@@ -1,90 +1,92 @@
-import unittest
-
-import numpy as np
-from comp_sys import CompLabeledSys, IsNoPBC
-from context import dpdata
-
-
-class TestOrcaSP(unittest.TestCase, CompLabeledSys, IsNoPBC):
-    def setUp(self):
-        energy_convert = dpdata.unit.EnergyConversion("hartree", "eV").value()
-        force_convert = dpdata.unit.ForceConversion(
-            "hartree/bohr", "eV/angstrom"
-        ).value()
-
-        self.system_1 = dpdata.LabeledSystem("orca/orca.spout", fmt="orca/spout")
-
-        self.system_2 = dpdata.LabeledSystem(
-            data={
-                "atom_types": np.array(
-                    [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 2, 1, 1, 1, 1, 3, 1, 3, 1]
-                ),
-                "atom_names": ["C", "H", "N", "O"],
-                "atom_numbs": [8, 11, 1, 2],
-                "coords": np.array(
-                    [
-                        [
-                            [-1.74744e00, 2.17247e00, 3.84400e-02],
-                            [-3.05879e00, 1.67227e00, 3.01100e-02],
-                            [-3.27420e00, 2.83660e-01, -7.86000e-03],
-                            [-2.17997e00, -5.85250e-01, -3.67900e-02],
-                            [-6.42570e-01, 1.30482e00, 1.03900e-02],
-                            [-8.75230e-01, -8.54100e-02, -2.75900e-02],
-                            [-1.58753e00, 3.24402e00, 6.79500e-02],
-                            [-6.23100e-02, -7.96870e-01, -5.06600e-02],
-                            [-2.34094e00, -1.65578e00, -6.62400e-02],
-                            [7.43270e-01, 1.92237e00, 2.35500e-02],
-                            [1.91059e00, 9.25720e-01, 4.06000e-03],
-                            [8.36210e-01, 2.54508e00, 9.40240e-01],
-                            [8.33280e-01, 2.58745e00, -8.63170e-01],
-                            [3.18351e00, 1.63882e00, 3.70200e-02],
-                            [1.86317e00, 2.99040e-01, -9.14520e-01],
-                            [1.84793e00, 2.69100e-01, 8.99400e-01],
-                            [3.28398e00, 2.20442e00, -8.37230e-01],
-                            [3.95753e00, 9.35910e-01, 5.24200e-02],
-                            [-4.10991e00, 2.51820e00, 5.88000e-02],
-                            [-3.99914e00, 3.47933e00, 8.64100e-02],
-                            [-4.52084e00, -2.33960e-01, -1.68400e-02],
-                            [-5.31660e00, 3.15930e-01, 2.60000e-03],
-                        ]
-                    ]
-                ),
-                "energies": np.array([-513.113388868587]) * energy_convert,
-                "forces": np.array(
-                    [
-                        [
-                            [-5.7786180e-03, -2.4701072e-02, -6.2814900e-04],
-                            [3.2387534e-02, 2.0888587e-02, 4.9131600e-04],
-                            [-1.3022767e-02, 1.3820567e-02, 4.5543300e-04],
-                            [-4.4279100e-04, 1.4037682e-02, 4.7424800e-04],
-                            [-6.3908530e-03, -8.4241470e-03, -1.2989690e-03],
-                            [-8.9298450e-03, 1.6404774e-02, 5.9260500e-04],
-                            [-1.0737810e-03, -1.5698400e-04, 4.1875000e-05],
-                            [4.6807600e-04, 2.2149790e-03, -4.0880000e-06],
-                            [-3.9808540e-03, 2.3696040e-03, 1.0808900e-04],
-                            [5.0405390e-03, 1.0389430e-03, 2.3300390e-03],
-                            [3.1264600e-03, 2.7682000e-04, -3.8873520e-03],
-                            [1.8491730e-03, -4.7411320e-03, -1.3538358e-02],
-                            [3.7527600e-04, -4.2966570e-03, 1.1656289e-02],
-                            [2.0499798e-02, -7.3734830e-03, -2.0336553e-02],
-                            [-6.7151250e-03, 1.3060670e-03, 1.2263601e-02],
-                            [1.6883400e-03, 6.5851660e-03, -1.2895903e-02],
-                            [5.3261000e-04, -2.0068781e-02, 2.8391439e-02],
-                            [-2.2997109e-02, 2.6866202e-02, -3.1128700e-03],
-                            [-2.7960063e-02, 6.0005960e-03, 2.3320500e-04],
-                            [1.5089874e-02, -3.2118582e-02, -9.5217200e-04],
-                            [-1.9753810e-02, -1.1993684e-02, -2.8813700e-04],
-                            [3.5987936e-02, 2.0645360e-03, -9.5588000e-05],
-                        ]
-                    ]
-                )
-                * force_convert,
-                "cells": np.zeros((1, 3, 3)),
-                "orig": np.zeros(3),
-                "nopbc": True,
-            }
-        )
-        self.places = 6
-        self.e_places = 9
-        self.f_places = 9
-        self.v_places = 6
+from __future__ import annotations
+
+import unittest
+
+import numpy as np
+from comp_sys import CompLabeledSys, IsNoPBC
+from context import dpdata
+
+
+class TestOrcaSP(unittest.TestCase, CompLabeledSys, IsNoPBC):
+    def setUp(self):
+        energy_convert = dpdata.unit.EnergyConversion("hartree", "eV").value()
+        force_convert = dpdata.unit.ForceConversion(
+            "hartree/bohr", "eV/angstrom"
+        ).value()
+
+        self.system_1 = dpdata.LabeledSystem("orca/orca.spout", fmt="orca/spout")
+
+        self.system_2 = dpdata.LabeledSystem(
+            data={
+                "atom_types": np.array(
+                    [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 2, 1, 1, 1, 1, 3, 1, 3, 1]
+                ),
+                "atom_names": ["C", "H", "N", "O"],
+                "atom_numbs": [8, 11, 1, 2],
+                "coords": np.array(
+                    [
+                        [
+                            [-1.74744e00, 2.17247e00, 3.84400e-02],
+                            [-3.05879e00, 1.67227e00, 3.01100e-02],
+                            [-3.27420e00, 2.83660e-01, -7.86000e-03],
+                            [-2.17997e00, -5.85250e-01, -3.67900e-02],
+                            [-6.42570e-01, 1.30482e00, 1.03900e-02],
+                            [-8.75230e-01, -8.54100e-02, -2.75900e-02],
+                            [-1.58753e00, 3.24402e00, 6.79500e-02],
+                            [-6.23100e-02, -7.96870e-01, -5.06600e-02],
+                            [-2.34094e00, -1.65578e00, -6.62400e-02],
+                            [7.43270e-01, 1.92237e00, 2.35500e-02],
+                            [1.91059e00, 9.25720e-01, 4.06000e-03],
+                            [8.36210e-01, 2.54508e00, 9.40240e-01],
+                            [8.33280e-01, 2.58745e00, -8.63170e-01],
+                            [3.18351e00, 1.63882e00, 3.70200e-02],
+                            [1.86317e00, 2.99040e-01, -9.14520e-01],
+                            [1.84793e00, 2.69100e-01, 8.99400e-01],
+                            [3.28398e00, 2.20442e00, -8.37230e-01],
+                            [3.95753e00, 9.35910e-01, 5.24200e-02],
+                            [-4.10991e00, 2.51820e00, 5.88000e-02],
+                            [-3.99914e00, 3.47933e00, 8.64100e-02],
+                            [-4.52084e00, -2.33960e-01, -1.68400e-02],
+                            [-5.31660e00, 3.15930e-01, 2.60000e-03],
+                        ]
+                    ]
+                ),
+                "energies": np.array([-513.113388868587]) * energy_convert,
+                "forces": np.array(
+                    [
+                        [
+                            [-5.7786180e-03, -2.4701072e-02, -6.2814900e-04],
+                            [3.2387534e-02, 2.0888587e-02, 4.9131600e-04],
+                            [-1.3022767e-02, 1.3820567e-02, 4.5543300e-04],
+                            [-4.4279100e-04, 1.4037682e-02, 4.7424800e-04],
+                            [-6.3908530e-03, -8.4241470e-03, -1.2989690e-03],
+                            [-8.9298450e-03, 1.6404774e-02, 5.9260500e-04],
+                            [-1.0737810e-03, -1.5698400e-04, 4.1875000e-05],
+                            [4.6807600e-04, 2.2149790e-03, -4.0880000e-06],
+                            [-3.9808540e-03, 2.3696040e-03, 1.0808900e-04],
+                            [5.0405390e-03, 1.0389430e-03, 2.3300390e-03],
+                            [3.1264600e-03, 2.7682000e-04, -3.8873520e-03],
+                            [1.8491730e-03, -4.7411320e-03, -1.3538358e-02],
+                            [3.7527600e-04, -4.2966570e-03, 1.1656289e-02],
+                            [2.0499798e-02, -7.3734830e-03, -2.0336553e-02],
+                            [-6.7151250e-03, 1.3060670e-03, 1.2263601e-02],
+                            [1.6883400e-03, 6.5851660e-03, -1.2895903e-02],
+                            [5.3261000e-04, -2.0068781e-02, 2.8391439e-02],
+                            [-2.2997109e-02, 2.6866202e-02, -3.1128700e-03],
+                            [-2.7960063e-02, 6.0005960e-03, 2.3320500e-04],
+                            [1.5089874e-02, -3.2118582e-02, -9.5217200e-04],
+                            [-1.9753810e-02, -1.1993684e-02, -2.8813700e-04],
+                            [3.5987936e-02, 2.0645360e-03, -9.5588000e-05],
+                        ]
+                    ]
+                )
+                * force_convert,
+                "cells": np.zeros((1, 3, 3)),
+                "orig": np.zeros(3),
+                "nopbc": True,
+            }
+        )
+        self.places = 6
+        self.e_places = 9
+        self.f_places = 9
+        self.v_places = 6
diff --git a/tests/test_periodic_table.py b/tests/test_periodic_table.py
index 6b856e913..3cf36b99b 100644
--- a/tests/test_periodic_table.py
+++ b/tests/test_periodic_table.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from context import dpdata
diff --git a/tests/test_perturb.py b/tests/test_perturb.py
index b89a8c7f2..eea711167 100644
--- a/tests/test_perturb.py
+++ b/tests/test_perturb.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 from unittest.mock import patch
 
diff --git a/tests/test_pick_atom_idx.py b/tests/test_pick_atom_idx.py
index 0dc069911..ef3368f35 100644
--- a/tests/test_pick_atom_idx.py
+++ b/tests/test_pick_atom_idx.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompSys, IsNoPBC
diff --git a/tests/test_predict.py b/tests/test_predict.py
index f08125ab2..9721323ec 100644
--- a/tests/test_predict.py
+++ b/tests/test_predict.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
@@ -104,7 +106,9 @@ def setUp(self):
         )
         zero_driver = ZeroDriver()
         self.system_1 = ori_sys.predict(driver=zero_driver)
-        self.system_2 = ori_sys.minimize(driver=zero_driver, minimizer="ase")
+        self.system_2 = ori_sys.minimize(
+            driver=zero_driver, minimizer="ase", max_steps=100
+        )
         self.places = 6
         self.e_places = 6
         self.f_places = 6
@@ -121,7 +125,9 @@ def setUp(self):
         zero_driver = ZeroDriver()
         self.system_1 = list(multi_sys.predict(driver=zero_driver).systems.values())[0]
         self.system_2 = list(
-            multi_sys.minimize(driver=zero_driver, minimizer="ase").systems.values()
+            multi_sys.minimize(
+                driver=zero_driver, minimizer="ase", max_steps=100
+            ).systems.values()
         )[0]
         self.places = 6
         self.e_places = 6
diff --git a/tests/test_psi4.py b/tests/test_psi4.py
index b9c2124e4..5454fb5aa 100644
--- a/tests/test_psi4.py
+++ b/tests/test_psi4.py
@@ -1,95 +1,97 @@
-import tempfile
-import textwrap
-import unittest
-
-import numpy as np
-from comp_sys import CompLabeledSys, IsNoPBC
-from context import dpdata
-
-
-class TestPsi4Output(unittest.TestCase, CompLabeledSys, IsNoPBC):
-    def setUp(self):
-        length_convert = dpdata.unit.LengthConversion("bohr", "angstrom").value()
-        energy_convert = dpdata.unit.EnergyConversion("hartree", "eV").value()
-        force_convert = dpdata.unit.ForceConversion(
-            "hartree/bohr", "eV/angstrom"
-        ).value()
-
-        self.system_1 = dpdata.LabeledSystem("psi4/psi4.out", fmt="psi4/out")
-
-        self.system_2 = dpdata.LabeledSystem(
-            data={
-                "atom_types": np.array([0, 0, 1, 1, 1, 1, 1, 1]),
-                "atom_names": ["C", "H"],
-                "atom_numbs": [2, 6],
-                "coords": np.array(
-                    [
-                        [
-                            [1.309059187335, -0.530960676560, 0.283395850372],
-                            [-1.305263812665, 0.530120323440, -0.297504149628],
-                            [2.346254187335, -1.337363676560, -1.334794149628],
-                            [0.931624187335, -2.053154676560, 1.656377850372],
-                            [2.513533187335, 0.904343323440, 1.196493850372],
-                            [-2.757838812665, -0.942676676560, -0.553430149628],
-                            [-1.178330812665, 1.650444323440, -2.050622149628],
-                            [-1.900432812665, 1.788413323440, 1.253959850372],
-                        ]
-                    ]
-                )
-                * length_convert,
-                "energies": np.array([-79.8685493165660603]) * energy_convert,
-                "forces": -np.array(
-                    [
-                        [
-                            [0.00189577378438, 0.01178186689846, -0.01517052269765],
-                            [-0.00054675643432, -0.01239517767892, 0.01285520444405],
-                            [0.00862382497882, -0.00438603405641, -0.00576291289370],
-                            [-0.01373063001962, -0.00368703316336, 0.00313307980576],
-                            [0.00439957658795, 0.00725213801722, 0.00516826201141],
-                            [-0.00831692511314, -0.00614283210761, -0.00048696830158],
-                            [0.00755493258543, 0.00167237971637, -0.00777559049988],
-                            [0.00011879620295, 0.00590450771644, 0.00804206420271],
-                        ]
-                    ]
-                )
-                * force_convert,
-                "cells": np.zeros((1, 3, 3)),
-                "orig": np.zeros(3),
-                "nopbc": True,
-            }
-        )
-        self.places = 6
-        self.e_places = 6
-        self.f_places = 6
-        self.v_places = 6
-
-
-class TestPsi4Input(unittest.TestCase):
-    def test_psi4_input(self):
-        system = dpdata.LabeledSystem("psi4/psi4.out", fmt="psi4/out")
-        with tempfile.NamedTemporaryFile("r") as f:
-            system.to_psi4_inp(f.name, method="WB97M-D3BJ", basis="def2-TZVPPD")
-            content = f.read()
-        self.assertEqual(
-            content,
-            textwrap.dedent(
-                """\
-                molecule {
-                C      0.692724290     -0.280972290      0.149966626
-                C     -0.690715864      0.280527594     -0.157432416
-                H      1.241584247     -0.707702380     -0.706342645
-                H      0.492994289     -1.086482665      0.876517411
-                H      1.330104482      0.478557878      0.633157279
-                H     -1.459385451     -0.498843014     -0.292862623
-                H     -0.623545813      0.873377524     -1.085142510
-                H     -1.005665735      0.946387574      0.663566976
-                0 1
-                }
-                set basis def2-TZVPPD
-                set gradient_write on
-                G, wfn = gradient("WB97M-D3BJ", return_wfn=True)
-                wfn.energy()
-                wfn.gradient().print_out()
-            """
-            ),
-        )
+from __future__ import annotations
+
+import tempfile
+import textwrap
+import unittest
+
+import numpy as np
+from comp_sys import CompLabeledSys, IsNoPBC
+from context import dpdata
+
+
+class TestPsi4Output(unittest.TestCase, CompLabeledSys, IsNoPBC):
+    def setUp(self):
+        length_convert = dpdata.unit.LengthConversion("bohr", "angstrom").value()
+        energy_convert = dpdata.unit.EnergyConversion("hartree", "eV").value()
+        force_convert = dpdata.unit.ForceConversion(
+            "hartree/bohr", "eV/angstrom"
+        ).value()
+
+        self.system_1 = dpdata.LabeledSystem("psi4/psi4.out", fmt="psi4/out")
+
+        self.system_2 = dpdata.LabeledSystem(
+            data={
+                "atom_types": np.array([0, 0, 1, 1, 1, 1, 1, 1]),
+                "atom_names": ["C", "H"],
+                "atom_numbs": [2, 6],
+                "coords": np.array(
+                    [
+                        [
+                            [1.309059187335, -0.530960676560, 0.283395850372],
+                            [-1.305263812665, 0.530120323440, -0.297504149628],
+                            [2.346254187335, -1.337363676560, -1.334794149628],
+                            [0.931624187335, -2.053154676560, 1.656377850372],
+                            [2.513533187335, 0.904343323440, 1.196493850372],
+                            [-2.757838812665, -0.942676676560, -0.553430149628],
+                            [-1.178330812665, 1.650444323440, -2.050622149628],
+                            [-1.900432812665, 1.788413323440, 1.253959850372],
+                        ]
+                    ]
+                )
+                * length_convert,
+                "energies": np.array([-79.8685493165660603]) * energy_convert,
+                "forces": -np.array(
+                    [
+                        [
+                            [0.00189577378438, 0.01178186689846, -0.01517052269765],
+                            [-0.00054675643432, -0.01239517767892, 0.01285520444405],
+                            [0.00862382497882, -0.00438603405641, -0.00576291289370],
+                            [-0.01373063001962, -0.00368703316336, 0.00313307980576],
+                            [0.00439957658795, 0.00725213801722, 0.00516826201141],
+                            [-0.00831692511314, -0.00614283210761, -0.00048696830158],
+                            [0.00755493258543, 0.00167237971637, -0.00777559049988],
+                            [0.00011879620295, 0.00590450771644, 0.00804206420271],
+                        ]
+                    ]
+                )
+                * force_convert,
+                "cells": np.zeros((1, 3, 3)),
+                "orig": np.zeros(3),
+                "nopbc": True,
+            }
+        )
+        self.places = 6
+        self.e_places = 6
+        self.f_places = 6
+        self.v_places = 6
+
+
+class TestPsi4Input(unittest.TestCase):
+    def test_psi4_input(self):
+        system = dpdata.LabeledSystem("psi4/psi4.out", fmt="psi4/out")
+        with tempfile.NamedTemporaryFile("r") as f:
+            system.to_psi4_inp(f.name, method="WB97M-D3BJ", basis="def2-TZVPPD")
+            content = f.read()
+        self.assertEqual(
+            content,
+            textwrap.dedent(
+                """\
+                molecule {
+                C      0.692724290     -0.280972290      0.149966626
+                C     -0.690715864      0.280527594     -0.157432416
+                H      1.241584247     -0.707702380     -0.706342645
+                H      0.492994289     -1.086482665      0.876517411
+                H      1.330104482      0.478557878      0.633157279
+                H     -1.459385451     -0.498843014     -0.292862623
+                H     -0.623545813      0.873377524     -1.085142510
+                H     -1.005665735      0.946387574      0.663566976
+                0 1
+                }
+                set basis def2-TZVPPD
+                set gradient_write on
+                G, wfn = gradient("WB97M-D3BJ", return_wfn=True)
+                wfn.energy()
+                wfn.gradient().print_out()
+            """
+            ),
+        )
diff --git a/tests/test_pwmat_config_dump.py b/tests/test_pwmat_config_dump.py
index 9389c7a97..e4d5a5a8e 100644
--- a/tests/test_pwmat_config_dump.py
+++ b/tests/test_pwmat_config_dump.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_pwmat_config_to_system.py b/tests/test_pwmat_config_to_system.py
index 0956f9569..59fd73399 100644
--- a/tests/test_pwmat_config_to_system.py
+++ b/tests/test_pwmat_config_to_system.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_pwmat_mlmd.py b/tests/test_pwmat_mlmd.py
index 4a920c150..8dcdb1efc 100644
--- a/tests/test_pwmat_mlmd.py
+++ b/tests/test_pwmat_mlmd.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_pwmat_movement.py b/tests/test_pwmat_movement.py
index 68a9e681a..14e976b24 100644
--- a/tests/test_pwmat_movement.py
+++ b/tests/test_pwmat_movement.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_pymatgen_molecule.py b/tests/test_pymatgen_molecule.py
index 231bd97ff..e6a1b5ee5 100644
--- a/tests/test_pymatgen_molecule.py
+++ b/tests/test_pymatgen_molecule.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_qe_cp_traj.py b/tests/test_qe_cp_traj.py
index 6a9631064..9e0629867 100644
--- a/tests/test_qe_cp_traj.py
+++ b/tests/test_qe_cp_traj.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_qe_cp_traj_skipload.py b/tests/test_qe_cp_traj_skipload.py
index 2964e716b..43cbe88d9 100644
--- a/tests/test_qe_cp_traj_skipload.py
+++ b/tests/test_qe_cp_traj_skipload.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_qe_pw_scf.py b/tests/test_qe_pw_scf.py
index 57a739fb3..8703e7c24 100644
--- a/tests/test_qe_pw_scf.py
+++ b/tests/test_qe_pw_scf.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_qe_pw_scf_crystal_atomic_positions.py b/tests/test_qe_pw_scf_crystal_atomic_positions.py
index 01c4df21f..383ea6cd7 100644
--- a/tests/test_qe_pw_scf_crystal_atomic_positions.py
+++ b/tests/test_qe_pw_scf_crystal_atomic_positions.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_qe_pw_scf_energy_bug.py b/tests/test_qe_pw_scf_energy_bug.py
index 8360a7a92..b66ce924b 100644
--- a/tests/test_qe_pw_scf_energy_bug.py
+++ b/tests/test_qe_pw_scf_energy_bug.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from context import dpdata
diff --git a/tests/test_quip_gap_xyz.py b/tests/test_quip_gap_xyz.py
index b383bd2f4..a265544ce 100644
--- a/tests/test_quip_gap_xyz.py
+++ b/tests/test_quip_gap_xyz.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, IsPBC
diff --git a/tests/test_remove_atom_names.py b/tests/test_remove_atom_names.py
index d2d4abc7e..9fbd8faf0 100644
--- a/tests/test_remove_atom_names.py
+++ b/tests/test_remove_atom_names.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, IsNoPBC
diff --git a/tests/test_remove_outlier.py b/tests/test_remove_outlier.py
index b2cb52fcf..c08de0bf4 100644
--- a/tests/test_remove_outlier.py
+++ b/tests/test_remove_outlier.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_remove_pbc.py b/tests/test_remove_pbc.py
index d5befd771..d70a2f028 100644
--- a/tests/test_remove_pbc.py
+++ b/tests/test_remove_pbc.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_replace.py b/tests/test_replace.py
index b16c388b5..b91941374 100644
--- a/tests/test_replace.py
+++ b/tests/test_replace.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 from unittest.mock import patch
 
diff --git a/tests/test_replicate.py b/tests/test_replicate.py
index 99104c3ca..ded164c17 100644
--- a/tests/test_replicate.py
+++ b/tests/test_replicate.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
 import unittest
 
+import numpy as np
 from comp_sys import CompSys, IsPBC
 from context import dpdata
 
@@ -34,5 +37,32 @@ def setUp(self):
         self.places = 6
 
 
+class TestReplicateTriclinicBox(unittest.TestCase, CompSys, IsPBC):
+    def setUp(self):
+        self.system_1 = dpdata.System()
+        self.system_1.data["atom_names"] = ["foo", "bar"]
+        self.system_1.data["atom_types"] = np.array([1, 0], dtype=int)
+        self.system_1.data["atom_numbs"] = [1, 1]
+        self.system_1.data["cells"] = np.array(
+            [10, 0, 0, 0, 10, 0, 0, 0, 10], dtype=float
+        ).reshape(1, 3, 3)
+        self.system_1.data["coords"] = np.array(
+            [0, 0, 0, 0, 0, 1], dtype=float
+        ).reshape(1, 2, 3)
+        self.system_1 = self.system_1.replicate([2, 1, 1])
+
+        self.system_2 = dpdata.System()
+        self.system_2.data["atom_names"] = ["foo", "bar"]
+        self.system_2.data["atom_types"] = np.array([1, 1, 0, 0], dtype=int)
+        self.system_2.data["atom_numbs"] = [2, 2]
+        self.system_2.data["cells"] = np.array(
+            [20, 0, 0, 0, 10, 0, 0, 0, 10], dtype=float
+        ).reshape(1, 3, 3)
+        self.system_2.data["coords"] = np.array(
+            [0, 0, 0, 10, 0, 0, 0, 0, 1, 10, 0, 1], dtype=float
+        ).reshape(1, 4, 3)
+        self.places = 6
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_shuffle.py b/tests/test_shuffle.py
index 9c4622143..3ac33c2f5 100644
--- a/tests/test_shuffle.py
+++ b/tests/test_shuffle.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, IsPBC
diff --git a/tests/test_siesta_aiMD_output.py b/tests/test_siesta_aiMD_output.py
index a1ba31b6d..4dcb04453 100644
--- a/tests/test_siesta_aiMD_output.py
+++ b/tests/test_siesta_aiMD_output.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_siesta_output.py b/tests/test_siesta_output.py
index 9ff0167a0..c649f7d0e 100644
--- a/tests/test_siesta_output.py
+++ b/tests/test_siesta_output.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_split_dataset.py b/tests/test_split_dataset.py
index a5419b7b1..ac0960cfe 100644
--- a/tests/test_split_dataset.py
+++ b/tests/test_split_dataset.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_sqm_driver.py b/tests/test_sqm_driver.py
index 3dbc6df4a..d7c0da73b 100644
--- a/tests/test_sqm_driver.py
+++ b/tests/test_sqm_driver.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import shutil
 import unittest
 
diff --git a/tests/test_stat.py b/tests/test_stat.py
index 9ae8a175b..863cea6c6 100644
--- a/tests/test_stat.py
+++ b/tests/test_stat.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from context import dpdata
diff --git a/tests/test_system_append.py b/tests/test_system_append.py
index a2c30b238..7c325113f 100644
--- a/tests/test_system_append.py
+++ b/tests/test_system_append.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_system_apply_pbc.py b/tests/test_system_apply_pbc.py
index 9cf44ae08..2114cf6a8 100644
--- a/tests/test_system_apply_pbc.py
+++ b/tests/test_system_apply_pbc.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_system_set_type.py b/tests/test_system_set_type.py
index 4bb14b621..d8362ec7b 100644
--- a/tests/test_system_set_type.py
+++ b/tests/test_system_set_type.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_to_ase.py b/tests/test_to_ase.py
index 60dc931d9..09b830baa 100644
--- a/tests/test_to_ase.py
+++ b/tests/test_to_ase.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_to_list.py b/tests/test_to_list.py
index d559ffce2..998f12650 100644
--- a/tests/test_to_list.py
+++ b/tests/test_to_list.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 from comp_sys import CompLabeledSys, IsPBC
diff --git a/tests/test_to_pymatgen.py b/tests/test_to_pymatgen.py
index d58153962..72d1b27ad 100644
--- a/tests/test_to_pymatgen.py
+++ b/tests/test_to_pymatgen.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
@@ -5,7 +7,7 @@
 from context import dpdata
 
 try:
-    from pymatgen import Structure  # noqa: F401
+    from pymatgen.core import Structure  # noqa: F401
 
     exist_module = True
 except Exception:
@@ -19,7 +21,7 @@ def setUp(self):
         system_1.from_lammps_lmp(
             os.path.join("poscars", "conf.lmp"), type_map=["O", "H"]
         )
-        system_1.to_pymatgen_structure()[0].to("poscar", "tmp.POSCAR")
+        system_1.to_pymatgen_structure()[0].to(filename="tmp.POSCAR", fmt="poscar")
         self.system_1 = system_1
         self.system_2 = dpdata.System("tmp.POSCAR")
         self.places = 6
diff --git a/tests/test_to_pymatgen_entry.py b/tests/test_to_pymatgen_entry.py
index fd8f40fcf..dfdeb4680 100644
--- a/tests/test_to_pymatgen_entry.py
+++ b/tests/test_to_pymatgen_entry.py
@@ -1,8 +1,10 @@
+from __future__ import annotations
+
 import os
 import unittest
 
 from context import dpdata
-from monty.serialization import loadfn
+from monty.serialization import loadfn  # noqa: TID253
 
 try:
     from pymatgen.entries.computed_entries import ComputedStructureEntry  # noqa: F401
diff --git a/tests/test_type_map.py b/tests/test_type_map.py
index 2cc508654..92d25adac 100644
--- a/tests/test_type_map.py
+++ b/tests/test_type_map.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 from itertools import permutations
 
diff --git a/tests/test_vasp_outcar.py b/tests/test_vasp_outcar.py
index fb2ec1c94..832b0a91b 100644
--- a/tests/test_vasp_outcar.py
+++ b/tests/test_vasp_outcar.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_vasp_poscar_dump.py b/tests/test_vasp_poscar_dump.py
index a81cbe94b..62f215986 100644
--- a/tests/test_vasp_poscar_dump.py
+++ b/tests/test_vasp_poscar_dump.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_vasp_poscar_to_system.py b/tests/test_vasp_poscar_to_system.py
index dcb83bfdf..7457d33d2 100644
--- a/tests/test_vasp_poscar_to_system.py
+++ b/tests/test_vasp_poscar_to_system.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_vasp_unconverged_outcar.py b/tests/test_vasp_unconverged_outcar.py
index 7e1b35353..1f3b3d2d9 100644
--- a/tests/test_vasp_unconverged_outcar.py
+++ b/tests/test_vasp_unconverged_outcar.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_vasp_xml.py b/tests/test_vasp_xml.py
index cc0bbb41a..0b9177545 100644
--- a/tests/test_vasp_xml.py
+++ b/tests/test_vasp_xml.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import unittest
 
 import numpy as np
diff --git a/tests/test_water_ions.py b/tests/test_water_ions.py
index 788030f38..40c1c143c 100644
--- a/tests/test_water_ions.py
+++ b/tests/test_water_ions.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import os
 import unittest
 
diff --git a/tests/test_xyz.py b/tests/test_xyz.py
index a84ad28bc..d9bcf70ea 100644
--- a/tests/test_xyz.py
+++ b/tests/test_xyz.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import tempfile
 import unittest