├── .github ├── CODEOWNERS └── workflows │ ├── check_release_tag.py │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── .style.yapf ├── CONTRIBUTING.md ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── README_GUI.md ├── docs ├── Makefile ├── make.bat └── source │ ├── CONTRIBUTING.md │ ├── EXAMPLE_NACL.md │ ├── EXAMPLE_SILICA.md │ ├── LAMMPS_SILICA.md │ ├── README.md │ ├── README_GUI.md │ ├── _templates │ └── autosummary │ │ ├── class.rst │ │ └── module.rst │ ├── conf.py │ ├── example_cepstrum_doublecomp_NaCl.ipynb │ ├── example_cepstrum_singlecomp_silica.ipynb │ ├── example_input_formats.ipynb │ ├── index.rst │ └── modules.rst ├── examples ├── 01_example_cepstrum_singlecomp_silica.ipynb ├── 02_example_cepstrum_doublecomp_NaCl.ipynb ├── 03_cmdline_example_silica │ ├── README.md │ ├── output_ref.cepstral.dat │ ├── output_ref.cepstrumfiltered_psd.dat │ ├── output_ref.log │ ├── output_ref.plots.pdf │ ├── output_ref.psd.dat │ ├── output_ref.resampled_psd.dat │ └── run_example.sh ├── 04_cmdline_example_NaCl │ ├── README.md │ ├── output_ref.cepstral.dat │ ├── output_ref.cepstrumfiltered_psd.dat │ ├── output_ref.cospectrum.dat │ ├── output_ref.cospectrum.filt.dat │ ├── output_ref.log │ ├── output_ref.plots.pdf │ ├── output_ref.psd.dat │ ├── output_ref.resampled_psd.dat │ └── run_example.sh ├── 05_example_input_formats.ipynb ├── README.md └── data │ ├── NaCl │ └── NaCl.dat │ └── Silica │ ├── Silica.dat │ ├── Silica.npy │ └── lammps │ ├── BKSwolf_0.3.potrsq │ ├── README.md │ ├── convert_lammps_log.sh │ ├── silica.in │ ├── silica.out │ └── silica_216_1000K.init ├── pyproject.toml ├── setup.json ├── setup.py ├── sportran ├── README.md ├── __init__.py ├── analysis.py ├── current │ ├── __init__.py │ ├── current.py │ ├── electric.py │ ├── generic.py │ ├── heat.py │ ├── stress.py │ ├── tools │ │ ├── __init__.py │ │ └── fstar_analysis.py │ └── units │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── electric.py │ │ ├── heat.py │ │ └── stress.py ├── i_o │ ├── __init__.py │ ├── _sportran_binary │ │ └── binary.py │ ├── extract_lammps_thermo.sh │ ├── read_lammps_datafile.py │ ├── read_lammps_dump.py │ ├── read_lammps_log.py │ └── read_tablefile.py ├── md │ ├── __init__.py │ ├── aic.py │ ├── cepstral.py │ ├── mdsample.py │ ├── resample.py │ └── tools │ │ ├── __init__.py │ │ ├── acf.py │ │ ├── armodel.py │ │ ├── filter.py │ │ ├── lpfilter.py │ │ ├── resample.py │ │ └── spectrum.py ├── plotter │ ├── __init__.py │ ├── cli.py │ ├── current.py │ ├── mdsample.py │ ├── plotter.py │ ├── style.py │ └── styles │ │ ├── __init__.py │ │ ├── api_style.mplstyle │ │ └── cli_style.mplstyle └── utils │ ├── __init__.py │ ├── attributedict.py │ ├── decorators.py │ ├── logger.py │ └── obsolete │ ├── blockanalysis.py │ └── blocks.py ├── sportran_gui ├── README_GUI.md ├── __init__.py ├── assets │ ├── __init__.py │ ├── icon.gif │ ├── languages.json │ └── window_icon.ico ├── core │ ├── __init__.py │ ├── control_unit.py │ └── settings.py ├── interfaces │ ├── __init__.py │ ├── fileManager.py │ ├── fstarSelector.py │ ├── headerSelector.py │ ├── otherVariables.py │ └── pstarSelector.py ├── main.py ├── thcp.ini └── utils │ ├── __init__.py │ ├── custom_widgets.py │ ├── tk_html_widgets │ ├── LICENSE │ ├── __init__.py │ └── html_parser.py │ └── utils.py └── tests ├── README.md ├── conftest.py ├── data ├── test_as_example.py ├── test_as_example ├── regenerate_results.py ├── test_example_NaCl.csv ├── test_example_NaCl.yml ├── test_example_SiO2.csv ├── test_example_SiO2.yml ├── test_example_SiO2_fixed_K.csv └── test_example_SiO2_fixed_K.yml ├── test_cli.py ├── test_cli ├── output.log.txt ├── output_no_w.log.txt ├── stderr.txt ├── stderr_no_w.txt ├── stdout.txt ├── stdout_no_w.txt ├── test_cli_NaCl.csv ├── test_cli_NaCl_no_w.csv └── test_cli_bin_output_NaCl.csv ├── test_notebooks.py └── test_notebooks ├── final_result1.txt └── final_result2.txt /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # The following owners will be the default owners for everything 2 | # in the repo unless a later match takes precedence. 3 | sportran/ @lorisercole 4 | sportran_gui/ @rikigigi 5 | -------------------------------------------------------------------------------- /.github/workflows/check_release_tag.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Check that the GitHub release tag matches the package version.""" 3 | import argparse 4 | import json 5 | 6 | if __name__ == '__main__': 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument('GITHUB_REF', help='The GITHUB_REF environmental variable') 9 | parser.add_argument('SETUP_PATH', help='Path to the setup.json') 10 | args = parser.parse_args() 11 | assert args.GITHUB_REF.startswith('refs/tags/v'), f'GITHUB_REF should start with "refs/tags/v": {args.GITHUB_REF}' 12 | tag_version = args.GITHUB_REF[11:] 13 | with open(args.SETUP_PATH, encoding='utf8') as handle: 14 | data = json.load(handle) 15 | pypi_version = data['version'] 16 | assert tag_version == pypi_version, f'The tag version {tag_version} != {pypi_version} specified in `setup.json`' 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: continuous-integration 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | pre-commit: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: Cache python dependencies 15 | id: cache-pip 16 | uses: actions/cache@v1 17 | with: 18 | path: ~/.cache/pip 19 | key: pip-pre-commit-${{ hashFiles('**/setup.json') }} 20 | restore-keys: 21 | pip-pre-commit- 22 | 23 | - name: Set up Python 3.8 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: '3.8' 27 | 28 | - name: Install python dependencies 29 | run: 30 | pip install -e .[pre-commit,tests] 31 | 32 | - name: Run pre-commit 33 | run: 34 | pre-commit run --all-files || ( git status --short ; git diff ; exit 1 ) 35 | 36 | tests: 37 | 38 | runs-on: ubuntu-latest 39 | 40 | strategy: 41 | matrix: 42 | python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] 43 | 44 | steps: 45 | - name: Checkout 46 | uses: actions/checkout@v2 47 | 48 | - name: Cache python dependencies 49 | id: cache-pip 50 | uses: actions/cache@v1 51 | with: 52 | path: ~/.cache/pip 53 | key: pip-pre-commit-${{ hashFiles('**/setup.json') }} 54 | restore-keys: 55 | pip-pre-commit- 56 | 57 | - name: Set up Python ${{ matrix.python-version }} 58 | uses: actions/setup-python@v2 59 | with: 60 | python-version: ${{ matrix.python-version }} 61 | 62 | - name: Install python dependencies 63 | run: | 64 | pip install --upgrade setuptools 65 | pip install -e .[tests] 66 | 67 | - name: Test with pytest 68 | run: | 69 | pytest -sv tests 70 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | # Automate deployment to PyPI when creating a release tag vX.Y.Z 4 | # will only be published to PyPI if the git tag matches the release version 5 | # and the pre-commit and tests pass 6 | 7 | on: 8 | push: 9 | tags: 10 | - "v[0-9]+.[0-9]+.[0-9]+*" 11 | jobs: 12 | 13 | check-release-tag: 14 | 15 | # Only run this job on the main repository and not on forks 16 | if: github.repository == 'sissaschool/sportran' 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Set up Python 3.8 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: '3.8' 25 | - run: python .github/workflows/check_release_tag.py $GITHUB_REF setup.json 26 | 27 | pre-commit: 28 | 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v2 34 | 35 | - name: Cache python dependencies 36 | id: cache-pip 37 | uses: actions/cache@v1 38 | with: 39 | path: ~/.cache/pip 40 | key: pip-pre-commit-${{ hashFiles('**/setup.json') }} 41 | restore-keys: 42 | pip-pre-commit- 43 | 44 | - name: Set up Python 3.8 45 | uses: actions/setup-python@v2 46 | with: 47 | python-version: '3.8' 48 | 49 | - name: Install python dependencies 50 | run: 51 | pip install -e .[pre-commit,tests] 52 | 53 | - name: Run pre-commit 54 | run: 55 | pre-commit run --all-files || ( git status --short ; git diff ; exit 1 ) 56 | 57 | tests: 58 | 59 | runs-on: ubuntu-latest 60 | 61 | strategy: 62 | matrix: 63 | python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] 64 | 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v2 68 | 69 | - name: Cache python dependencies 70 | id: cache-pip 71 | uses: actions/cache@v1 72 | with: 73 | path: ~/.cache/pip 74 | key: pip-pre-commit-${{ hashFiles('**/setup.json') }} 75 | restore-keys: 76 | pip-pre-commit- 77 | 78 | - name: Set up Python ${{ matrix.python-version }} 79 | uses: actions/setup-python@v2 80 | with: 81 | python-version: ${{ matrix.python-version }} 82 | 83 | - name: Install python dependencies 84 | run: | 85 | pip install --upgrade setuptools 86 | pip install -e .[tests] 87 | 88 | - name: Test with pytest 89 | run: | 90 | pytest -sv tests 91 | 92 | publish: 93 | 94 | name: Publish to PyPI 95 | 96 | needs: [check-release-tag, pre-commit, tests] 97 | 98 | runs-on: ubuntu-latest 99 | 100 | steps: 101 | - name: Checkout source 102 | uses: actions/checkout@v2 103 | - name: Set up Python 3.8 104 | uses: actions/setup-python@v2 105 | with: 106 | python-version: '3.8' 107 | - name: Build package 108 | run: | 109 | pip install wheel 110 | python setup.py sdist bdist_wheel 111 | - name: Publish to PyPI 112 | uses: pypa/gh-action-pypi-publish@v1.1.0 113 | with: 114 | user: __token__ 115 | password: ${{ secrets.PYPI_SPORTRAN }} 116 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | metadata.json 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | 50 | # Visual Studio Code 51 | .vscode/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # vi swap files 107 | *.swp 108 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Install pre-commit hooks via 2 | # pre-commit install 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/mirrors-yapf 6 | rev: v0.32.0 7 | hooks: 8 | - id: yapf 9 | name: yapf 10 | types: [python] 11 | args: ['-i'] 12 | exclude: &exclude_files > 13 | (?x)^( 14 | sportran/utils/obsolete/.*| 15 | sportran_gui/utils/tk_html_widgets/*.py| 16 | docs/.*| 17 | tests/test_cli/.*| 18 | examples/.*| 19 | setup.py| 20 | )$ 21 | additional_dependencies: ['toml'] 22 | 23 | - repo: https://github.com/pre-commit/pre-commit-hooks.git 24 | rev: v4.1.0 25 | hooks: 26 | - id: check-ast 27 | - id: check-builtin-literals 28 | - id: check-docstring-first 29 | - id: check-executables-have-shebangs 30 | - id: check-json 31 | - id: check-merge-conflict 32 | - id: check-shebang-scripts-are-executable 33 | - id: check-symlinks 34 | - id: check-toml 35 | - id: check-yaml 36 | - id: double-quote-string-fixer 37 | - id: fix-encoding-pragma 38 | - id: end-of-file-fixer 39 | exclude: > 40 | (?x)^( 41 | tests/test_cli/.*| 42 | sportran/utils/obsolete/.*| 43 | )$ 44 | - id: trailing-whitespace 45 | exclude: > 46 | (?x)^( 47 | sportran/utils/obsolete/.*| 48 | sportran_gui/utils/tk_html_widgets/.*py| 49 | docs/.*| 50 | tests/test_cli/.*| 51 | tests/test_notebooks/.*| 52 | examples/.*| 53 | )$ 54 | # 55 | #- repo: https://github.com/PyCQA/pylint 56 | # rev: v2.12.2 57 | # hooks: 58 | # - id: pylint 59 | # language: system 60 | # exclude: *exclude_files 61 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.9" 13 | # You can also specify other tool versions: 14 | # nodejs: "16" 15 | # rust: "1.55" 16 | # golang: "1.17" 17 | 18 | # Build documentation in the docs/ directory with Sphinx 19 | sphinx: 20 | configuration: docs/source/conf.py 21 | 22 | # If using Sphinx, optionally build your docs in additional formats such as PDF 23 | # formats: 24 | # - pdf 25 | 26 | # Optionally declare the Python requirements required to build your docs 27 | python: 28 | install: 29 | - method: pip 30 | path: . 31 | extra_requirements: 32 | - doc 33 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style=google 3 | column_limit=120 4 | spaces_before_comment=3 5 | disable_ending_comma_heuristic=True 6 | SPLIT_BEFORE_NAMED_ASSIGNS=False 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sissaschool/sportran/677d2bb5108ba05906cb3aa580d3a974efc77793/CONTRIBUTING.md -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include examples * 2 | include LICENSE.txt 3 | include setup.json 4 | include sportran/README.md 5 | include sportran/metadata.json 6 | exclude sportran/utils/obsolete/ 7 | include sportran_gui/README_GUI.md 8 | include sportran_gui/assets/icon.gif 9 | include sportran_gui/assets/languages.json 10 | include sportran/plotter/styles/api_style.mplstyle 11 | include sportran/plotter/styles/cli_style.mplstyle 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *SporTran* (FKA thermocepstrum) 2 | 3 | A code to estimate transport coefficients from the cepstral analysis of a multi-variate current stationary time series. 4 | 5 | [![PyPI version](https://badge.fury.io/py/sportran.svg)](https://badge.fury.io/py/sportran) 6 | [![Documentation Status](https://readthedocs.org/projects/sportran/badge/?version=latest)](https://sportran.readthedocs.io/en/latest/?badge=latest) 7 | 8 | ### Documentation 9 | https://sportran.readthedocs.io 10 | 11 | ### References 12 | - [Ercole L., Bertossa R., Bisacchi S., and Baroni S., "_SporTran: a code to estimate transport coefficients from the cepstral analysis of (multivariate) current time series_", *Comput. Phys. Commun.*, 108470](https://doi.org/10.1016/j.cpc.2022.108470), [*arXiv*:2202.11571 (2022)](https://arxiv.org/abs/2202.11571) 13 | - (cepstral analysis) [Ercole, Marcolongo, Baroni, *Sci. Rep.* **7**, 15835 (2017)](https://doi.org/10.1038/s41598-017-15843-2) 14 | - (multicomponent systems) [Bertossa, Grasselli, Ercole, Baroni, *Phys. Rev. Lett.* **122**, 255901 (2019)](https://doi.org/10.1103/PhysRevLett.122.255901) ([arXiv](https://arxiv.org/abs/1808.03341)) 15 | - (review) [Baroni, Bertossa, Ercole, Grasselli, Marcolongo, *Handbook of Materials Modeling* (2018)](https://doi.org/10.1007/978-3-319-50257-1_12-1) ([arXiv](https://arxiv.org/abs/1802.08006)) 16 | 17 | Developed by Loris Ercole, Riccardo Bertossa, Sebastiano Bisacchi under the supervision of prof. Stefano Baroni 18 | 19 | **Acknowledgment** The development of this software is part of the scientific program of the EU MaX Centre of Excellence for Supercomputing Applications (Grant No. 676598, 824143) and has been partly funded through it. 20 | 21 | --- 22 | 23 | ### Usage 24 | There is a [**GUI**](README_GUI.md) that you can try after installing the package. Click [here](README_GUI.md) for instructions. 25 | 26 | The code can be used as a **library**, for example in a Jupyter notebook. 27 | In the [`examples`](examples/) folder you can find some examples. 28 | 29 | Alternatively, you can run the code `analysis.py` from the **command line** without any installation procedure. 30 | It can execute most of the cepstral analysis routines, returning the results in a series of data files and PDF plots. 31 | See the [`examples/example_commandline_NaCl`](examples/example_commandline_NaCl/) folder and the help (`python analysis.py --help`) for more information. 32 | 33 | ### Requirements 34 | - numpy 35 | - scipy 36 | - matplotlib 37 | - tkinter 38 | - markdown2 39 | - pillow 40 | 41 | 42 | ### Installation 43 | You can simply pip-install SporTran downloading it from PyPI with `pip install sportran`. 44 | 45 | Alternatively: 46 | 47 | 1. Clone this repository: `git clone https://github.com/sissaschool/sportran.git` 48 | 2. Install the package with pip (dependencies will be automatically downloaded). For example: 49 | ``` 50 | cd sportran 51 | pip install . 52 | ``` 53 | You are all set! You can check that the installation is working by trying to run the command `sportran-analysis`. 54 | 55 | The Graphical User Interface can be started with the command `sportran-gui`. 56 | 57 | ### Issues 58 | You are strongly encouraged to report any issue on the [official](https://github.com/sissaschool/sportran/issues) GitHub issues page. 59 | -------------------------------------------------------------------------------- /README_GUI.md: -------------------------------------------------------------------------------- 1 | # *SporTran* GUI 2 | 3 | 4 | ### Installation 5 | See [README.md](README.md) 6 | 7 | ### Usage 8 | 1. Run `sportran-gui` from the command line 9 | 2. Select the input file with the "`...`" button. After you select the file a preview will be shown if possible. If a binary file was selected the preview will not update, and a message will pop up. 10 | 3. Select the input format: 11 | - "`table`" is the standard column-formatted data file, the first line contains the names of each column. In order to be recognized, vector data (e.g. the 3 Cartesian components of a current) must be named as "`name_[1] name_[2] name_[3]`". 12 | - "`dict`" is a numpy dictionary (`numpy.save()`) containing binary data. This type of file can be generated when you press "export data" in the "File" menu. A binary file is faster to load. 13 | 4. "`Next`". If the selected file type is wrong, an error message will pop up and the program will return to the previous step. 14 | 5. Read the instructions and select the type of data corresponding to each column. 15 | 6. Select the units. 16 | 7. "`Next`". 17 | 8. Input the required values (if not already filled with the information retrieved in the input file). 18 | 9. "`Filter width`" can be left as is, it is used for visualization purposes and can be changed later. 19 | 10. Select the "`Resampling frequency`" with the cursor. You can press "`Resample`" to see the effect. This is necessary to not run the analysis on useless portions of the spectrum. You can select the first important feature of the plot near zero-frequency (see manual and docs for reference). 20 | - "`Next`". 21 | - The result is shown. It is possible to tweak the default number of cepstral coefficients and visualize the effect. 22 | 23 | At any time it is possible to save the current status by pressing "`Export data`" from the menu. All the parameters can be automatically reloaded if you select the generated file as input. 24 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ../../CONTRIBUTING.md -------------------------------------------------------------------------------- /docs/source/EXAMPLE_NACL.md: -------------------------------------------------------------------------------- 1 | ../../examples/04_cmdline_example_NaCl/README.md -------------------------------------------------------------------------------- /docs/source/EXAMPLE_SILICA.md: -------------------------------------------------------------------------------- 1 | ../../examples/03_cmdline_example_silica/README.md -------------------------------------------------------------------------------- /docs/source/LAMMPS_SILICA.md: -------------------------------------------------------------------------------- 1 | ../../examples/data/Silica/lammps/README.md -------------------------------------------------------------------------------- /docs/source/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /docs/source/README_GUI.md: -------------------------------------------------------------------------------- 1 | ../../README_GUI.md -------------------------------------------------------------------------------- /docs/source/_templates/autosummary/class.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. autoclass:: {{ objname }} 6 | :members: 7 | :show-inheritance: 8 | :inherited-members: 9 | 10 | {% block methods %} 11 | .. automethod:: __init__ 12 | 13 | {% if methods %} 14 | .. rubric:: {{ _('Methods') }} 15 | 16 | .. autosummary:: 17 | {% for item in methods %} 18 | ~{{ name }}.{{ item }} 19 | {%- endfor %} 20 | {% endif %} 21 | {% endblock %} 22 | 23 | {% block attributes %} 24 | {% if attributes %} 25 | .. rubric:: {{ _('Attributes') }} 26 | 27 | .. autosummary:: 28 | {% for item in attributes %} 29 | ~{{ name }}.{{ item }} 30 | {%- endfor %} 31 | {% endif %} 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /docs/source/_templates/autosummary/module.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. automodule:: {{ fullname }} 4 | 5 | {% block attributes %} 6 | {% if attributes %} 7 | .. rubric:: {{ _('Module Attributes') }} 8 | 9 | .. autosummary:: 10 | {% for item in attributes %} 11 | {{ item }} 12 | {%- endfor %} 13 | {% endif %} 14 | {% endblock %} 15 | 16 | {% block functions %} 17 | {% if functions %} 18 | .. rubric:: {{ _('Functions') }} 19 | 20 | .. autosummary:: 21 | {% for item in functions %} 22 | {{ item }} 23 | {%- endfor %} 24 | {% endif %} 25 | {% endblock %} 26 | 27 | {% block classes %} 28 | {% if classes %} 29 | .. rubric:: {{ _('Classes') }} 30 | 31 | .. autosummary:: 32 | :toctree: 33 | :recursive: 34 | {% for item in classes %} 35 | {{ item }} 36 | {%- endfor %} 37 | {% endif %} 38 | {% endblock %} 39 | 40 | {% block exceptions %} 41 | {% if exceptions %} 42 | .. rubric:: {{ _('Exceptions') }} 43 | 44 | .. autosummary:: 45 | {% for item in exceptions %} 46 | {{ item }} 47 | {%- endfor %} 48 | {% endif %} 49 | {% endblock %} 50 | 51 | 52 | {% for item in functions %} 53 | .. autofunction:: {{ item }} 54 | {% endfor %} 55 | 56 | {% block modules %} 57 | {% if modules %} 58 | .. rubric:: Modules 59 | 60 | .. autosummary:: 61 | :toctree: 62 | :recursive: 63 | {% for item in modules %} 64 | {{ item }} 65 | {%- endfor %} 66 | {% endif %} 67 | {% endblock %} 68 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Configuration file for the Sphinx documentation builder. 3 | # 4 | # This file only contains a selection of the most common options. For a full 5 | # list see the documentation: 6 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 7 | 8 | # -- Path setup -------------------------------------------------------------- 9 | 10 | # If extensions (or modules to document with autodoc) are in another directory, 11 | # add these directories to sys.path here. If the directory is relative to the 12 | # documentation root, use os.path.abspath to make it absolute, like shown here. 13 | # 14 | # import os 15 | # import sys 16 | # sys.path.insert(0, os.path.abspath('.')) 17 | 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = 'SporTran' 22 | copyright = '2021, Loris Ercole, Riccardo Bertossa, Sebastiano Bisacchi' 23 | author = 'Loris Ercole, Riccardo Bertossa, Sebastiano Bisacchi' 24 | 25 | 26 | # -- General configuration --------------------------------------------------- 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.napoleon', 33 | 'sphinx.ext.autodoc', 34 | 'sphinx.ext.autosummary', 35 | 'myst_parser', 36 | 'nbsphinx', 37 | ] 38 | 39 | napoleon_google_docstring = False 40 | napoleon_use_param = False 41 | napoleon_use_ivar = True 42 | autosummary_generate = True # Turn on sphinx.ext.autosummary 43 | 44 | # Add any paths that contain templates here, relative to this directory. 45 | templates_path = ['_templates'] 46 | 47 | # List of patterns, relative to source directory, that match files and 48 | # directories to ignore when looking for source files. 49 | # This pattern also affects html_static_path and html_extra_path. 50 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 51 | 52 | source_suffix = { 53 | '.rst': 'restructuredtext', 54 | '.md': 'markdown', 55 | } 56 | 57 | # -- Options for HTML output ------------------------------------------------- 58 | 59 | # The theme to use for HTML and HTML Help pages. See the documentation for 60 | # a list of builtin themes. 61 | # 62 | #html_theme = 'sphinx_rtd_theme' 63 | 64 | # Add any paths that contain custom static files (such as style sheets) here, 65 | # relative to this directory. They are copied after the builtin static files, 66 | # so a file named "default.css" will overwrite the builtin "default.css". 67 | #html_static_path = ['_static'] 68 | -------------------------------------------------------------------------------- /docs/source/example_cepstrum_doublecomp_NaCl.ipynb: -------------------------------------------------------------------------------- 1 | ../../examples/02_example_cepstrum_doublecomp_NaCl.ipynb -------------------------------------------------------------------------------- /docs/source/example_cepstrum_singlecomp_silica.ipynb: -------------------------------------------------------------------------------- 1 | ../../examples/01_example_cepstrum_singlecomp_silica.ipynb -------------------------------------------------------------------------------- /docs/source/example_input_formats.ipynb: -------------------------------------------------------------------------------- 1 | ../../examples/05_example_input_formats.ipynb -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. SporTran documentation master file, created by 2 | sphinx-quickstart on Sun Mar 22 22:31:28 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to SporTran's documentation! 7 | ==================================== 8 | 9 | 10 | Introduction 11 | ============ 12 | .. toctree:: 13 | README.md 14 | README_GUI.md 15 | CONTRIBUTING.md 16 | 17 | Examples 18 | ======== 19 | 20 | Jupyter notebook examples 21 | ------------------------- 22 | 23 | These examples show how to use the sportran package in a Python script, step by step, to analyse a time series and perform cepstral analysis. Some of the tools and plot functions of the code are presented. 24 | 25 | .. toctree:: 26 | example_cepstrum_singlecomp_silica 27 | example_cepstrum_doublecomp_NaCl 28 | example_input_formats 29 | 30 | Command line examples 31 | --------------------- 32 | 33 | These examples show how to use the command line program sportran-analysis (analysis.py) to perform cepstral analysis in a straightforward way. This is an expedient tool to analyse a time series using predefined parameters. Results are produced in the form of several text/binary files and pdf plots. 34 | You can find the examples in the `repository `_. 35 | 36 | .. toctree:: 37 | EXAMPLE_SILICA.md 38 | EXAMPLE_NACL.md 39 | 40 | LAMMPS input files for energy current generation 41 | ------------------------------------------------ 42 | 43 | .. toctree:: 44 | LAMMPS_SILICA.md 45 | 46 | API 47 | === 48 | .. toctree:: 49 | modules 50 | 51 | 52 | Indices and tables 53 | ================== 54 | 55 | * :ref:`genindex` 56 | * :ref:`modindex` 57 | * :ref:`search` 58 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | Library API 2 | ============== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | 8 | .. autosummary:: 9 | :toctree: _autosummary 10 | :recursive: 11 | 12 | sportran 13 | -------------------------------------------------------------------------------- /examples/03_cmdline_example_silica/README.md: -------------------------------------------------------------------------------- 1 | # Example 3: command line (silica) 2 | 3 | In this example we perform the same analysis that is performed in the correspondent [example notebook](../01_example_cepstrum_singlecomp_silica.ipynb), but using the command-line interface. 4 | 5 | Simply run, after installing the package, the following command (see [`run_example.sh`](run_example.sh)): 6 | 7 | ```bash 8 | sportran-analysis ../data/Silica/Silica.dat --input-format table -k flux1 -C heat -u metal -t 1.0 --VOLUME 3130.431110818 --param-from-input-file-column Temp TEMPERATURE -w 0.1 --FSTAR 28.0 -r 9 | ``` 10 | 11 | The options have the following meaning: 12 | 13 | | command | explanation | 14 | | --- | --- | 15 | | `--input-format table` | input is a plain-text table-formatted file, column headers are used as keys | 16 | | `-k flux1` | use the columns with header `flux1` as the main (energy) flux | 17 | | `-C heat` | the flux is a heat current | 18 | | `-u metal` | use LAMMPS metal units | 19 | | `-t 1.0` | set the timestep to 1.0fs | 20 | | `--VOLUME 3130.431110818` | set the volume of the system | 21 | | `--param-from-input-file-column Temp TEMPERATURE` | use the column with header `Temp` as the temperature of the system | 22 | | `-w 0.1` | set the width of the moving average filter to 0.1THz, that is used only to visualize the spectrum | 23 | | `--FSTAR 28.0` | set the $f^*$ cutoff frequency to 28.0THz | 24 | | `-r` | resample the time-series according to the value of $f^*$ specified with `--FSTAR` | 25 | 26 | The [output](output_ref.log) of the program in the terminal is: 27 | 28 | ```text 29 | Input file (table): ../data/Silica/Silica.dat 30 | Units: metal 31 | Time step: 1.0 fs 32 | # Solid Silica - BKS potential, melted and quenched 33 | # 216 atoms, T~1000K, dens~2.295g/cm^3 34 | # NVE, dt = 1.0 fs, 100 ps, print_step = 1.0 fs 35 | # Temperature = 983.172635 K, Volume = 3130.431110818 A^3 36 | # LAMMPS metal units 37 | Temp c_flux1[1] c_flux1[2] c_flux1[3] 38 | ##################################### 39 | all_ckeys = [('Temp', [0]), ('flux1', array([1, 2, 3]))] 40 | ##################################### 41 | Data length = 100001 42 | ckey = [('Temp', [0]), ('flux1', array([1, 2, 3]))] 43 | step = 100000 - 100.00% completed 44 | ( 100000 ) steps read. 45 | DONE. Elapsed time: 0.6583120822906494seconds 46 | VOLUME (input): 3130.431110818 47 | Mean TEMPERATURE (computed): 983.1726353043 +/- 39.36090003953625 48 | Time step (input): 1.0 fs 49 | currents shape is (1, 100000, 3) 50 | snippet: 51 | [[[ -265.30586 1520.6107 67.461829] 52 | [ -168.68352 1377.4459 101.82146 ] 53 | [ -93.688306 1180.375 117.20939 ] 54 | ... 55 | [ 1226.9778 212.0939 -1126.4643 ] 56 | [ 1223.8753 186.93836 -881.39541 ] 57 | [ 1232.7723 141.30647 -620.41895 ]]] 58 | Using single component code. 59 | Number of currents = 1 60 | Number of equivalent components = 3 61 | KAPPA_SCALE = 6.144312221539281e-06 62 | Nyquist_f = 500.0 THz 63 | Using single component code. 64 | ----------------------------------------------------- 65 | RESAMPLE TIME SERIES 66 | ----------------------------------------------------- 67 | Original Nyquist freq f_Ny = 500.00000 THz 68 | Resampling freq f* = 27.77778 THz 69 | Sampling time TSKIP = 18 steps 70 | = 18.000 fs 71 | Original n. of frequencies = 50001 72 | Resampled n. of frequencies = 2778 73 | PSD @cutoff (pre-filter&sample) ~ 2802468.65938 74 | (post-filter&sample) ~ 2455132.46201 75 | log(PSD) @cutoff (pre-filter&sample) ~ 14.59530 76 | (post-filter&sample) ~ 14.38333 77 | min(PSD) (pre-filter&sample) = 4.03008 78 | min(PSD) (post-filter&sample) = 60168.84968 79 | % of original PSD Power f Silica.dat 12 | 13 | # convert to numpy binary 14 | python ${SPORTRAN_DIR}/i_o/read_lammps_log.py ${LAMMPS_FILE} Silica.npy -s silica_216_1000K.init -d 'NVE RUN' 15 | -------------------------------------------------------------------------------- /examples/data/Silica/lammps/silica.in: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | ### Computes heat flux and thermal conductivity of glass silica 3 | ######################################################################### 4 | # 5 | # run with e.g.: mpirun -np 12 lmp_mpi -in silica.in -log silica.out 6 | # 7 | 8 | variable kB equal 1.3806504e-23 # [J/K] 9 | variable NA equal 6.02214e23 # [1/mol] 10 | variable massunit equal 1.660538921e-27 # [kg] 11 | variable V equal vol 12 | 13 | variable T_eq equal 1000. # [K] -- Temperature 14 | variable dt equal 0.001 # [ps] -- MD time step 15 | variable nvt_tau equal 0.200 # [ps] -- NVT thermostat coupling time 16 | 17 | variable thermo_print_interval equal 1 # [timesteps] 18 | variable traj_print_interval equal 100 # [timesteps] 19 | 20 | 21 | ####################################### 22 | ### Output style 23 | ####################################### 24 | 25 | units metal 26 | atom_style charge 27 | 28 | 29 | ####################################### 30 | ### Read Structure 31 | ####################################### 32 | 33 | read_data silica_216_1000K.init 34 | 35 | group oxygen type 2 36 | group silicon type 1 37 | 38 | ####################################### 39 | ### Pair style 40 | ####################################### 41 | 42 | # BKS potential [B.W.H. van Beest et al., Phys. Rev. Lett. 64 (1990) 1955-1958] 43 | # + Wolf truncation and smoothing [A. Carré, J. Chem. Phys. 127 (11) (2007)] 44 | # + 1/r^12 core repulsion [as parameterized by N.S. Shcheblanov et al., J. Non-Cryst. Sol. 428 (2015) 6-19] 45 | pair_style table linear 12000 46 | pair_coeff 1 1 ./BKSwolf_0.3.potrsq BKSWOLF_SI_SI 47 | pair_coeff 1 2 ./BKSwolf_0.3.potrsq BKSWOLF_SI_O 48 | pair_coeff 2 2 ./BKSwolf_0.3.potrsq BKSWOLF_O_O 49 | 50 | timestep ${dt} 51 | 52 | ####################################### 53 | ### Output settings 54 | ####################################### 55 | 56 | # Compute heat flux 57 | compute KEat all ke/atom 58 | compute PEat all pe/atom 59 | compute Stressat all stress/atom NULL virial 60 | compute flux1 all heat/flux KEat PEat Stressat 61 | 62 | # Equilibrating CSVR 63 | fix NVE_RELAX all nve 64 | fix CSVR_RELAX all temp/csvr ${T_eq} ${T_eq} ${nvt_tau} 942057 65 | run 20000 66 | unfix CSVR_RELAX 67 | 68 | # Equilibrating NVE 69 | run 10000 70 | unfix NVE_RELAX 71 | 72 | ######################################################################### 73 | ### Simulation: production 74 | ######################################################################### 75 | 76 | thermo ${thermo_print_interval} 77 | thermo_style custom step time temp pe ke etotal press c_flux1[1] c_flux1[2] c_flux1[3] 78 | 79 | reset_timestep 0 80 | 81 | ## The following line can be uncommented to print the trajectory. 82 | ## sportran.i_o.LAMMPSLogFile will detect this line containing "DUMP_RUN" in the log.lammps file, 83 | ## it will extract the desired columns, and save the data in numpy binary format 84 | #dump DUMP_RUN all custom ${traj_print_interval} silica-run.lammpstrj id type xu yu zu vx vy vz 85 | 86 | # NVE 87 | fix NVE_RUN all nve 88 | run 100000 89 | unfix NVE_RUN 90 | #undump DUMP_RUN 91 | 92 | write_restart silica-run.rst 93 | write_data silica-run.init 94 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel" 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | -------------------------------------------------------------------------------- /setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sportran", 3 | "version": "1.0.0rc4", 4 | "author": "Loris Ercole, Riccardo Bertossa, Sebastiano Bisacchi", 5 | "author_email": "loris.ercole@epfl.ch", 6 | "description": "Cepstral Data Analysis of current time series for Green-Kubo transport coefficients", 7 | "license": "GPL 3", 8 | "url": "https://github.com/sissaschool/sportran", 9 | "keywords": "cepstral data analysis thermal conductivity transport coefficients physics green-kubo", 10 | "python_requires": ">=3.7, <4", 11 | "classifiers": [ 12 | "Development Status :: 5 - Production/Stable", 13 | "Programming Language :: Python", 14 | "Programming Language :: Python :: 3.7", 15 | "Programming Language :: Python :: 3.8", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 20 | "Operating System :: OS Independent", 21 | "Intended Audience :: Science/Research", 22 | "Intended Audience :: Developers", 23 | "Natural Language :: English", 24 | "Topic :: Scientific/Engineering :: Information Analysis", 25 | "Topic :: Scientific/Engineering :: Physics", 26 | "Environment :: Console" 27 | ], 28 | "install_requires": [ 29 | "numpy>=1.18.0", 30 | "scipy>=1.3.2", 31 | "matplotlib>=3.1.2", 32 | "markdown2>=2.0.0", 33 | "pillow>=5.4.0" 34 | ], 35 | "extras_require": { 36 | "pre-commit": [ 37 | "pre-commit>=1.11.0", 38 | "yapf==0.32.0" 39 | ], 40 | "notebook": [ 41 | "jupyter" 42 | ], 43 | "tests": [ 44 | "pytest>=5.1.0", 45 | "pytest-regressions>=2.4.2", 46 | "pandas>=1.1.0", 47 | "testbook", 48 | "ipykernel" 49 | ], 50 | "doc": [ 51 | "sphinx==4.2.0", 52 | "myst_parser==0.15.2", 53 | "nbsphinx==0.8.7" 54 | ] 55 | }, 56 | "entry_points": { 57 | "console_scripts": [ 58 | "sportran-analysis=sportran.analysis:main", 59 | "sportran-gui=sportran_gui.main:run" 60 | ] 61 | }, 62 | "package_data": { 63 | "sportran": ["plotter/styles/*.mplstyle"] 64 | }, 65 | "gui_version" : "0.1.2", 66 | "credits" : "developed at SISSA, Via Bonomea, 265 - 34136 Trieste ITALY" 67 | } 68 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup, find_packages 4 | from os import path 5 | import sys 6 | import json 7 | 8 | if __name__ == '__main__': 9 | THIS_FOLDER = path.split(path.abspath(__file__))[0] 10 | 11 | with open(path.join(THIS_FOLDER, 'README.md'), 'r') as fh: 12 | long_description = fh.read() 13 | 14 | with open(path.join(THIS_FOLDER, 'setup.json'), 'r') as info: 15 | SETUP_JSON = json.load(info) 16 | 17 | with open(path.join(THIS_FOLDER, 'sportran/metadata.json'), 'w') as fh: 18 | json.dump(SETUP_JSON, fh) 19 | 20 | setup(include_package_data=True, 21 | packages=find_packages(), 22 | long_description=long_description, 23 | long_description_content_type='text/markdown', 24 | **SETUP_JSON) 25 | -------------------------------------------------------------------------------- /sportran/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /sportran/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ========================== 4 | Sportran library 5 | ========================== 6 | 7 | This is the core library containing all the code necessary to perform a nice 8 | and fast cepstral analysis on your time series. 9 | """ 10 | 11 | from . import md 12 | from . import current 13 | from .current import * 14 | from . import utils 15 | from . import plotter 16 | from . import i_o 17 | 18 | __all__ = [current.__all__ + md.__all__] 19 | 20 | __license__ = 'GPL-3.0 license, see LICENSE.txt file.' 21 | __version__ = '1.0.0rc1' 22 | __authors__ = 'Loris Ercole, Riccardo Bertossa, Sebastiano Bisacchi' 23 | __paper__ = ( 24 | 'L. Ercole, R. Bertossa, S. Bisacchi, S.Baroni, "SporTran: a code to estimate transport coefficients from the ' 25 | 'cepstral analysis of (multivariate) current time series", arXiV:2202.11571 (2022), submitted to Computer' 26 | 'Physics Communications, https://doi.org/10.48550/arXiv.2202.11571') 27 | __paper_short__ = 'L. Ercole et al., arXiv:2202.117571 (2022)' 28 | -------------------------------------------------------------------------------- /sportran/current/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This module handles all the various type of currents and units of measure that can be handled natively by this library. 4 | 5 | The currently supported units and currents can be viewed by calling the function :py:func:`build_currents_units_table` 6 | 7 | To add a new current type and/or unit you have to do the following. 8 | 1. define in a new file a class derived from :py:class:`.current.Current` with the following class variables: 9 | - `_current_type` that you can set to the name of the current that will be used in all the user interfaces 10 | - `_input_parameters` is the set of the parameters that your current needs. Usually `{'DT_FS', 'UNITS', 'TEMPERATURE', 'VOLUME'}` 11 | - `_KAPPA_SI_UNITS` string that describes the units of the final result to be displayed where appropriate 12 | 2. define `__init__` like 13 | `` 14 | def __init__(self, traj, **params): 15 | super().__init__(traj, **params) 16 | `` 17 | 3. define `_get_builder` that must return the current class type and a list of parameters that applied to the class constructor generate an instance identical to the current one, so that it can be used like 18 | `` 19 | CurrentType, builder = self._get_builder() 20 | new_ts = CurrentType(**builder) 21 | `` 22 | 4. define the units for your class. The units are automatically retrieved from the module :py:mod:`.current.units`. You must do the following: 23 | - add a file named `_current_type`.py (a file with the same name of your class attribute value) 24 | - write inside the file some functions `scale_kappa_*` the part of the name after `scale_kappa_` will be the name of the unit used in the user interfaces. 25 | The parameters must be consistent with `_input_parameters`. The parameters (with the same name) will be provided by the user in the class constructor or in the various interfaces 26 | 27 | """ 28 | from .current import Current 29 | from .generic import * 30 | from .heat import * 31 | from .electric import * 32 | from .stress import * 33 | 34 | __all__ = ['GenericCurrent', 'HeatCurrent', 'ElectricCurrent', 'StressCurrent'] 35 | 36 | # define list of all classes with units defined 37 | import inspect 38 | 39 | _all = dir() 40 | 41 | 42 | def _get_currents_with_units(): 43 | """ 44 | Inspect all the classes accessible from this module, and detect the ones that contains the attribute `_current_type`. 45 | Then call the `get_units` method to inspect the units implemented for each discovered class. 46 | :return: ( {'_current_type': (CurrentClass, ['unit_list'], ['parameter_list'])} ) 47 | """ 48 | currents_with_units = {} 49 | all_units = [] 50 | all_parameters = [] 51 | for k in _all: 52 | v = globals()[k] 53 | att = getattr(v, '_current_type', None) 54 | if att is not None: 55 | parameters = [] 56 | units = [] 57 | for unit, funct in v._get_units().items(): 58 | param = list(inspect.signature(funct).parameters.keys()) 59 | units.append(unit) 60 | parameters += param 61 | parameters = list(set(parameters)) 62 | currents_with_units[att] = (v, units, parameters) 63 | all_units += units 64 | all_parameters += parameters 65 | all_units = list(set(all_units)) 66 | all_parameters = list(set(all_parameters)) 67 | return currents_with_units, all_units, all_parameters 68 | 69 | 70 | def _list_of_currents_and_units(verbose=False): 71 | s = '' 72 | for k, v_ in all_currents.items(): 73 | v = v_[0] 74 | s += f"'{k}': {v._input_parameters}\n" 75 | for u in v.get_units_list(): 76 | s += f" - '{u}'\n" 77 | if verbose: 78 | s += v._get_units()[u].__doc__ 79 | return s 80 | 81 | 82 | # list of currents classes, units implemented and parameters that are found dynamically when the module is imported 83 | all_currents, all_units, all_parameters = _get_currents_with_units() 84 | 85 | 86 | def build_currents_units_table(col=9): 87 | """Print a table with the Current classes and the units implemented for each class""" 88 | table = '' 89 | table += ' ' * col 90 | for u in all_units: 91 | table += u[:col].ljust(col) 92 | table += '\n' 93 | for k, v in all_currents.items(): 94 | table += k[:col].ljust(col) 95 | for unit in all_units: 96 | if unit in v[1]: 97 | table += 'X'.ljust(col) 98 | else: 99 | table += ' ' * col 100 | table += '\n' 101 | return table 102 | -------------------------------------------------------------------------------- /sportran/current/electric.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import Current 4 | 5 | __all__ = ['ElectricCurrent'] 6 | 7 | 8 | class ElectricCurrent(Current): 9 | """ 10 | ElectricCurrent API for thermo-cepstral analysis. 11 | Defines an ElectricCurrent object with useful tools to perform analysis. 12 | 13 | INPUT parameters: 14 | - traj the current time series (N, N_EQUIV_COMPONENTS) array 15 | For a multi-component fluid use a (N_CURRENTS, N, N_EQUIV_COMPONENTS) array 16 | - DT_FS MD time step [fs] 17 | - UNITS the units of current ('metal', 'real', ...) - use the method `get_units_list()` to get a list of supported units 18 | - TEMPERATURE average temperature [K] 19 | - VOLUME simulation cell volume [A^3] 20 | 21 | OPTIONAL parameters: 22 | - PSD_FILTER_W PSD filter window [freq_units] (optional) 23 | - FREQ_UNITS frequency units [THz or red] (optional) 24 | - MAIN_CURRENT_INDEX for a multi-current time series, the index of the "main" current (e.g. energy) [0] 25 | - MAIN_CURRENT_FACTOR factor to be multiplied by the main current [1.0] 26 | """ 27 | _current_type = 'electric' 28 | _input_parameters = {'DT_FS', 'UNITS', 'TEMPERATURE', 'VOLUME'} 29 | _KAPPA_SI_UNITS = 'S/m' 30 | # _optional_parameters = {'PSD_FILTER_W', 'FREQ_UNITS', 'MAIN_CURRENT_INDEX', 'MAIN_CURRENT_FACTOR'} 31 | 32 | @property 33 | def _builder(self): 34 | """ 35 | Returns a dictionary of all keyworded parameters needed to rebuild an identical object of the same class. 36 | The trajectory is excluded. Used by self._get_builder(). 37 | """ 38 | return dict(DT_FS=self.DT_FS, UNITS=self.UNITS, TEMPERATURE=self.TEMPERATURE, VOLUME=self.VOLUME, 39 | PSD_FILTER_W=self.PSD_FILTER_W_THZ, FREQ_UNITS='THz') 40 | -------------------------------------------------------------------------------- /sportran/current/generic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import Current 4 | 5 | __all__ = ['GenericCurrent'] 6 | 7 | 8 | class GenericCurrent(Current): 9 | """ 10 | GenericCurrent API for thermo-cepstral analysis. 11 | Defines a HeatCurrent object with useful tools to perform analysis. 12 | 13 | INPUT parameters: 14 | - traj the current time series (N, N_EQUIV_COMPONENTS) array 15 | For a multi-component fluid use a (N_CURRENTS, N, N_EQUIV_COMPONENTS) array 16 | - DT_FS MD time step [fs] 17 | - KAPPA_SCALE the GK conversion factor, multiplies the GK integral 18 | 19 | OPTIONAL parameters: 20 | - PSD_FILTER_W PSD filter window [freq_units] (optional) 21 | - FREQ_UNITS frequency units [THz or red] (optional) 22 | - MAIN_CURRENT_INDEX for a multi-current time series, the index of the "main" current (e.g. energy) [0] 23 | - MAIN_CURRENT_FACTOR factor to be multiplied by the main current [1.0] 24 | """ 25 | _current_type = None 26 | _input_parameters = {'DT_FS', 'KAPPA_SCALE'} 27 | _KAPPA_SI_UNITS = '' 28 | # _optional_parameters = {'PSD_FILTER_W', 'FREQ_UNITS', 'MAIN_CURRENT_INDEX', 'MAIN_CURRENT_FACTOR'} 29 | 30 | @property 31 | def _builder(self): 32 | """ 33 | Returns a dictionary of all keyworded parameters needed to rebuild an identical object of the same class. 34 | The trajectory is excluded. Used by self._get_builder(). 35 | """ 36 | return dict(DT_FS=self.DT_FS, KAPPA_SCALE=self.KAPPA_SCALE, PSD_FILTER_W=self.PSD_FILTER_W_THZ, 37 | FREQ_UNITS='THz') 38 | -------------------------------------------------------------------------------- /sportran/current/heat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import Current 4 | 5 | __all__ = ['HeatCurrent'] 6 | 7 | 8 | class HeatCurrent(Current): 9 | """ 10 | HeatCurrent API for thermo-cepstral analysis. 11 | Defines a HeatCurrent object with useful tools to perform analysis. 12 | 13 | INPUT parameters: 14 | - traj the current time series (N, N_EQUIV_COMPONENTS) array 15 | For a multi-component fluid use a (N_CURRENTS, N, N_EQUIV_COMPONENTS) array 16 | - DT_FS MD time step [fs] 17 | - UNITS the units of current ('metal', 'real', ...) - use the method `get_units_list()` to get a list of supported units 18 | - TEMPERATURE average temperature [K] 19 | - VOLUME simulation cell volume [A^3] 20 | 21 | OPTIONAL parameters: 22 | - PSD_FILTER_W PSD filter window [freq_units] (optional) 23 | - FREQ_UNITS frequency units [THz or red] (optional) 24 | - MAIN_CURRENT_INDEX for a multi-current time series, the index of the "main" current (e.g. energy) [0] 25 | - MAIN_CURRENT_FACTOR factor to be multiplied by the main current [1.0] 26 | """ 27 | _current_type = 'heat' 28 | _input_parameters = {'DT_FS', 'UNITS', 'TEMPERATURE', 'VOLUME'} 29 | _KAPPA_SI_UNITS = 'W/m/K' 30 | # _optional_parameters = {'PSD_FILTER_W', 'FREQ_UNITS', 'MAIN_CURRENT_INDEX', 'MAIN_CURRENT_FACTOR'} 31 | 32 | @property 33 | def _builder(self): 34 | """ 35 | Returns a dictionary of all keyworded parameters needed to rebuild an identical object of the same class. 36 | The trajectory is excluded. Used by self._get_builder(). 37 | """ 38 | return dict(DT_FS=self.DT_FS, UNITS=self.UNITS, TEMPERATURE=self.TEMPERATURE, VOLUME=self.VOLUME, 39 | PSD_FILTER_W=self.PSD_FILTER_W_THZ, FREQ_UNITS='THz') 40 | -------------------------------------------------------------------------------- /sportran/current/stress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import Current 4 | 5 | __all__ = ['StressCurrent'] 6 | 7 | 8 | class StressCurrent(Current): 9 | """ 10 | StressCurrent API for thermo-cepstral analysis. 11 | Defines a StressCurrent object with useful tools to perform analysis. 12 | 13 | INPUT parameters: 14 | - traj the current time series (N, N_EQUIV_COMPONENTS) array 15 | For a multi-component fluid use a (N_CURRENTS, N, N_EQUIV_COMPONENTS) array 16 | - DT_FS MD time step [fs] 17 | - UNITS the units of current ('metal', 'real', ...) - use the method `get_units_list()` to get a list of supported units 18 | - TEMPERATURE average temperature [K] 19 | - VOLUME simulation cell volume [A^3] 20 | 21 | OPTIONAL parameters: 22 | - PSD_FILTER_W PSD filter window [freq_units] (optional) 23 | - FREQ_UNITS frequency units [THz or red] (optional) 24 | - MAIN_CURRENT_INDEX for a multi-current time series, the index of the "main" current (e.g. energy) [0] 25 | - MAIN_CURRENT_FACTOR factor to be multiplied by the main current [1.0] 26 | """ 27 | _current_type = 'stress' 28 | _input_parameters = {'DT_FS', 'UNITS', 'TEMPERATURE', 'VOLUME'} 29 | _KAPPA_SI_UNITS = 'Pa*s' 30 | # _optional_parameters = {'PSD_FILTER_W', 'FREQ_UNITS', 'MAIN_CURRENT_INDEX', 'MAIN_CURRENT_FACTOR'} 31 | 32 | @property 33 | def _builder(self): 34 | """ 35 | Returns a dictionary of all keyworded parameters needed to rebuild an identical object of the same class. 36 | The trajectory is excluded. Used by self._get_builder(). 37 | """ 38 | return dict(DT_FS=self.DT_FS, UNITS=self.UNITS, TEMPERATURE=self.TEMPERATURE, VOLUME=self.VOLUME, 39 | PSD_FILTER_W=self.PSD_FILTER_W_THZ, FREQ_UNITS='THz') 40 | -------------------------------------------------------------------------------- /sportran/current/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .fstar_analysis import fstar_analysis 4 | -------------------------------------------------------------------------------- /sportran/current/tools/fstar_analysis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from sportran.current import Current 4 | from sportran.utils import log 5 | from sportran.plotter.plotter import plot_fstar_analysis 6 | 7 | __all__ = ['fstar_analysis'] 8 | 9 | def fstar_analysis(x, TSKIP_LIST, aic_type='aic', aic_Kmin_corrfactor=1.0, manual_cutoffK=None, plot=True, axes=None, 10 | FIGSIZE=None, verbose=False, **plot_kwargs): # yapf: disable 11 | """ 12 | Perform cepstral analysis on a set of resampled time series, to study the effect of f*. 13 | For each TSKIP in TSKIP_LIST, the HeatCurrent x is filtered & resampled, and then cesptral-analysed. 14 | 15 | Parameters 16 | ---------- 17 | TSKIP_LIST = list of sampling times [steps] 18 | aic_type = the Akaike Information Criterion function used to choose the cutoff ('aic', 'aicc') 19 | aic_Kmin_corrfactor = correction factor multiplied by the AIC cutoff (cutoffK = aic_Kmin * aic_Kmin_corrfactor) 20 | manual_cutoffK = (P*-1) = manual cutoff. If set, the AIC cutoff will be ignored. 21 | 22 | plot = plot the PSD (default: True) 23 | axes = matplotlib.axes.Axes object (if None, create one) 24 | FIGSIZE = plot figure size 25 | verbose = verbose output (default: False) 26 | **plot_kwargs = other parameters passed to plot function 27 | 28 | Returns 29 | ------- 30 | xf : array_like 31 | an array of HeatCurrents, corresponding the values of TSKIP_LIST 32 | ax : array_like, optional (if plot=True) 33 | array of plot axes 34 | fig : figure, optional (if axes=None) 35 | plot figure 36 | """ 37 | 38 | if not isinstance(x, Current): 39 | raise ValueError('x must be a Current object or a subclass.') 40 | 41 | xf = [] 42 | for TSKIP in TSKIP_LIST: 43 | log.write_log('TSKIP = {:4d} - FSTAR = {:8g} THz'.format(TSKIP, x.Nyquist_f_THz / TSKIP)) 44 | xff = x.resample(TSKIP=TSKIP, plot=False, verbose=verbose) 45 | xff.cepstral_analysis(aic_type=aic_type, aic_Kmin_corrfactor=aic_Kmin_corrfactor, manual_cutoffK=manual_cutoffK) 46 | xf.append(xff) 47 | FSTAR_THZ_LIST = [xff.Nyquist_f_THz for xff in xf] 48 | 49 | if plot: 50 | try: 51 | return plot_fstar_analysis(xf, FSTAR_THZ_LIST, original_current=x, axes=axes, FIGSIZE=FIGSIZE, 52 | **plot_kwargs) 53 | except AttributeError: 54 | print('Plotter does not support the plot_resample method') 55 | else: 56 | return xf 57 | -------------------------------------------------------------------------------- /sportran/current/units/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __all__ = ['heat', 'electric', 'stress', 'constants'] 4 | 5 | from . import * 6 | -------------------------------------------------------------------------------- /sportran/current/units/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | In this file you can find the conversion factors used in the code. 4 | ''' 5 | 6 | kB = 1.3806504 7 | NA = 6.02214 8 | massunit = 1.660538921 9 | charge = 1.6021765 10 | kcal = 4184. # Joule 11 | Ha = 27.211386 # eV 12 | Ry = Ha / 2.0 # eV 13 | bohr = 0.529177 # A 14 | tau_PW = 4.8378e-5 # ps 15 | atm = 1.01325 # bars 16 | 17 | J_PWtoMETAL = bohr / tau_PW # [Bohr/tau_PW] --> [A/ps] 18 | Ry_per_bohr3 = Ry / bohr**3 # [Ry/bohr^3] --> [eV/A^3] 19 | dlpoly_press = massunit * 1.0e2 # bars 20 | 21 | A_to_m = 1e-10 # [A] --> [m] 22 | A_per_fs_to_A_per_ps = 1.0e-3 # [A/fs] --> [A/ps] 23 | A_per_ps_to_m_per_s = 1.0e2 # [A/ps] --> [m/s] 24 | -------------------------------------------------------------------------------- /sportran/current/units/electric.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import constants 4 | 5 | # Note: the factor 10000 appearing in metal and qepw units is due to the following: 6 | # 1e(2*(-19) + 2*(-10) -2*(-12) -(-23) -3*(-10) -15) = 1e4 7 | # e_SI ang ps kB ang fs 8 | # since the multiplicative factor would be 9 | # [(charge*1e-19)**2] [velocity**2] / (kB*1e-23) / [TEMPERATURE] / [VOLUME] * [integration DT_FS] 10 | # 11 | # The additional "1.0e6" factor appearing in real units is due to the velocity units, 12 | # which are ang/fs in real units, rather than ang/ps as in metal units. 13 | 14 | 15 | def scale_kappa_real(TEMPERATURE, VOLUME): 16 | """ 17 | Conversion factor for the electrical conductivity from REAL LAMMPS units to SI units. 18 | INPUT: 19 | TEMPERATURE [K] 20 | VOLUME cell VOLUME [A^3] 21 | Input current is in units of electrons_charge * Angstrom/femtosecond. Current is EXTENSIVE. 22 | """ 23 | return constants.charge**2 / TEMPERATURE / constants.kB / VOLUME * 10000. * 1.0e6 24 | 25 | 26 | def scale_kappa_metal(TEMPERATURE, VOLUME): 27 | """ 28 | Conversion factor for the thermal conductivity from METAL LAMMPS units to SI units. 29 | INPUT: 30 | TEMPERATURE [K] 31 | VOLUME cell VOLUME [A^3] 32 | Input current is in units of electrons_charge * Angstrom/picosecond. Current is EXTENSIVE. 33 | """ 34 | return constants.charge**2 / TEMPERATURE / constants.kB / VOLUME * 10000. 35 | 36 | 37 | def scale_kappa_qepw(TEMPERATURE, VOLUME): 38 | """ 39 | Conversion factor for the electrical conductivity from Quantum Espresso PW units to SI units. 40 | INPUT: 41 | TEMPERATURE [K] 42 | VOLUME cell VOLUME [A^3] 43 | Input current is in units of electrons_charge * a_0 / tau_{a.u.} . Current is EXTENSIVE. 44 | a0 = 5.2918 10^{-11}m 45 | tau_{a.u.} = 4.8378 10^{-17} s 46 | 47 | """ 48 | return constants.charge**2 / TEMPERATURE / constants.kB / VOLUME * 10000. * constants.J_PWtoMETAL**2 49 | 50 | 51 | def scale_kappa_gpumd(TEMPERATURE, VOLUME): 52 | """ 53 | Conversion factor for the thermal conductivity from GPUMD units to SI units. 54 | Note that units for time are derived from energy [eV], mass [amu] and position [A]. 55 | Therefore, units for square velocity are [eV/amu]. 56 | INPUT: 57 | TEMPERATURE [K] 58 | VOLUME cell VOLUME [A^3] 59 | """ 60 | return constants.charge**3 / TEMPERATURE / constants.massunit / constants.kB / VOLUME * 1.0e8 61 | -------------------------------------------------------------------------------- /sportran/current/units/heat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import constants 4 | 5 | 6 | def scale_kappa_real(TEMPERATURE, VOLUME): 7 | """ 8 | Conversion factor for the thermal conductivity from REAL LAMMPS units to SI units. 9 | INPUT: 10 | TEMPERATURE [K] 11 | VOLUME cell VOLUME [A^3] 12 | The input current is in units of kcal/mole * Angstrom/femtosecond. Current is EXTENSIVE 13 | """ 14 | return (constants.kcal / constants.NA / TEMPERATURE)**2 / constants.kB / VOLUME * 100. 15 | 16 | 17 | def scale_kappa_metal(TEMPERATURE, VOLUME): 18 | """ 19 | Conversion factor for the thermal conductivity from METAL LAMMPS units to SI units. 20 | INPUT: 21 | TEMPERATURE [K] 22 | VOLUME cell VOLUME [A^3] 23 | The input current is in units of eV * Angstrom/picosecond. Current is EXTENSIVE 24 | """ 25 | return (constants.charge / TEMPERATURE)**2 / constants.kB / VOLUME * 10000. 26 | 27 | 28 | def scale_kappa_qepw(TEMPERATURE, VOLUME): 29 | """ 30 | Conversion factor for the thermal conductivity from Quantum Espresso PW/HARTREE-HEATCURRENT units to SI units. 31 | INPUT: 32 | TEMPERATURE [K] 33 | VOLUME cell VOLUME [A^3] 34 | The input current is in units of Rydberg atomic units. Current is EXTENSIVE 35 | Rydberg atomic units for the energy current are: Ry * a_0 / tau_{a.u.} where 36 | Ry = 13.606eV = 2.1799 10^{-18} J 37 | a0 = 5.2918 10^{-11}m 38 | tau_{a.u.} = 4.8378 10^{-17} s 39 | """ 40 | return (constants.charge / TEMPERATURE)**2 / constants.kB / VOLUME * 10000. * (constants.Ry * 41 | constants.J_PWtoMETAL)**2 42 | 43 | 44 | def scale_kappa_gpumd(TEMPERATURE, VOLUME): 45 | """ 46 | Conversion factor for the thermal conductivity from GPUMD units to SI units. 47 | INPUT: 48 | TEMPERATURE [K] 49 | VOLUME cell VOLUME [A^3] 50 | Note that units for time are derived from energy [eV], mass [amu] and position [A]. 51 | Therefore, units for square velocity are [eV/amu]. 52 | The input current is in units of eV ^ {3/2} / atomic_mass_unit ^ {1/2}. Current is EXTENSIVE 53 | """ 54 | return (constants.charge)**3 / (TEMPERATURE)**2 / constants.massunit / constants.kB / VOLUME * 1.0e8 55 | -------------------------------------------------------------------------------- /sportran/current/units/stress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import constants 4 | 5 | 6 | def scale_kappa_GPa(TEMPERATURE, VOLUME): 7 | """ 8 | Conversion factor for the viscosity to SI units when stress is in GPa. 9 | Notice that GPa are the units used for stress in cp.x *.str file. 10 | INPUT: 11 | TEMPERATURE [K] 12 | VOLUME cell VOLUME [A^3] 13 | Input stress in GPa. 14 | """ 15 | return 1.0e-4 * VOLUME / TEMPERATURE / constants.kB 16 | 17 | 18 | def scale_kappa_real(TEMPERATURE, VOLUME): 19 | """ 20 | Conversion factor for the viscosity from REAL LAMMPS units to SI units. 21 | Stress is computed with https://docs.lammps.org/compute_pressure.html 22 | INPUT: 23 | TEMPERATURE [K] 24 | VOLUME cell VOLUME [A^3] 25 | Input stress in atmospheres. 26 | """ 27 | return constants.atm**2 * 1.0e-12 * VOLUME / TEMPERATURE / constants.kB 28 | 29 | 30 | def scale_kappa_metal(TEMPERATURE, VOLUME): 31 | """ 32 | Conversion factor for the viscosity from METAL LAMMPS units to SI units. 33 | Stress is computed with https://docs.lammps.org/compute_pressure.html 34 | INPUT: 35 | TEMPERATURE [K] 36 | VOLUME cell VOLUME [A^3] 37 | Input stress in bars. 38 | """ 39 | return 1.0e-12 * VOLUME / TEMPERATURE / constants.kB 40 | 41 | 42 | def scale_kappa_qepw(TEMPERATURE, VOLUME): 43 | """ 44 | Conversion factor for the viscosity from Quantum Espresso PW units to SI units. 45 | INPUT: 46 | TEMPERATURE [K] 47 | VOLUME cell VOLUME [A^3] 48 | Input stress is in units of Ry / bohr_radius^3 . 49 | """ 50 | return (constants.charge * constants.Ry_per_bohr3)**2 * VOLUME / TEMPERATURE / constants.kB 51 | 52 | 53 | def scale_kappa_gpumd(TEMPERATURE, VOLUME): 54 | """ 55 | Conversion factor for the viscosity from GPUMD units to SI units. 56 | Note that units for stress and pressure, derived from energy [eV] and volume [A^3], 57 | are [eV/A^3]. 58 | INPUT: 59 | TEMPERATURE [K] 60 | VOLUME cell VOLUME [A^3] 61 | Input stress is in units of eV / Angstrom^3 . 62 | """ 63 | return constants.charge**2 * VOLUME / TEMPERATURE / constants.kB 64 | -------------------------------------------------------------------------------- /sportran/i_o/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This module includes some utilities to read files in various formats and return numpy arrays 4 | """ 5 | from .read_tablefile import TableFile 6 | from .read_lammps_dump import LAMMPS_Dump 7 | from .read_lammps_log import LAMMPSLogFile 8 | from . import read_lammps_datafile 9 | 10 | __all__ = ['TableFile', 'LAMMPS_Dump', 'LAMMPSLogFile'] 11 | -------------------------------------------------------------------------------- /sportran/i_o/_sportran_binary/binary.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This module is supposed to implement a SportranBinaryFile, but it is not ready. 4 | See: https://github.com/sissaschool/sportran/issues/37 5 | """ 6 | 7 | from sportran.utils.attributedict import AttributeDict 8 | from sportran.current import Current 9 | import pickle 10 | 11 | __all__ = ['SportranBinaryFile', 'SportranInput', 'SportranOutput', 'SportranSettings'] 12 | 13 | 14 | def multi_pickle_dump(self, filename, **objs): 15 | # dump a dictionary of all the passed objects 16 | pickle.dump(objs, open(filename, 'wb')) 17 | 18 | 19 | def multi_pickle_load(self, filename): 20 | # return a dictionary of all the stored objects 21 | return pickle.load(open(filename, 'rb')) 22 | 23 | 24 | class SportranInput(AttributeDict): 25 | 26 | pass 27 | 28 | 29 | class SportranOutput(AttributeDict): 30 | 31 | pass 32 | 33 | 34 | class SportranSettings(AttributeDict): 35 | 36 | pass 37 | 38 | 39 | class SportranBinaryFile(): 40 | """ 41 | A SporTran Binary File object, that stores information about a SporTran calculation, its inputs, data, and outputs. 42 | The data stored can be used to restore a calculation that was previously run. 43 | 44 | Attributes: 45 | input_parameters a SportranInput object, the input parameters of the calculation 46 | settings a SportranSettings object, the calculation settings 47 | current a Current object, the original current 48 | current_resampled a Current object, the current obtained by resampling current 49 | output_results a SportranOutput object, the calculation outputs 50 | 51 | Methods: 52 | :classmethod: load(filename) 53 | :method: dump(filename) 54 | """ 55 | 56 | _storable_attrs = ('input_parameters', 'settings', 'current', 'current_resampled', 'output_results') 57 | 58 | def __init__(self, **kwargs): 59 | for key in self._storable_attrs: 60 | self.__setattr__(key, kwargs.pop(key, None)) 61 | if kwargs: 62 | raise ValueError('Keys {} are not valid.'.format(kwargs.keys())) 63 | 64 | @classmethod 65 | def load(cls, filename): 66 | """Load SporTran data from a binary file.""" 67 | return cls(**multi_pickle_load(filename)) 68 | 69 | def dump(self, filename): 70 | """Dump SporTran data to a binary file.""" 71 | multi_pickle_dump(filename, **{key: self.key for key in self._storable_attrs}) 72 | print(f'Binary file "{filename}" successfully created, with the following data:') 73 | print([key for key in self._storable_attrs if key is not None]) 74 | 75 | @property 76 | def input_parameters(self): 77 | return self._input_parameters 78 | 79 | @input_parameters.setter 80 | def input_parameters(self, value): 81 | if value is None: 82 | self._input_parameters = None 83 | elif isinstance(value, SportranInput): 84 | self._input_parameters = value 85 | else: 86 | raise TypeError() 87 | 88 | @property 89 | def settings(self): 90 | return self._settings 91 | 92 | @settings.setter 93 | def settings(self, value): 94 | if value is None: 95 | self._settings = None 96 | elif isinstance(value, SportranSettings): 97 | self._settings = value 98 | else: 99 | raise TypeError() 100 | 101 | @property 102 | def current(self): 103 | return self._current 104 | 105 | @current.setter 106 | def current(self, value): 107 | if value is None: 108 | self._current = None 109 | elif isinstance(value, Current): 110 | self._current = value 111 | else: 112 | raise TypeError() 113 | 114 | @property 115 | def current_resampled(self): 116 | return self._current_resampled 117 | 118 | @current_resampled.setter 119 | def current_resampled(self, value): 120 | if value is None: 121 | self._current_resampled = None 122 | elif isinstance(value, Current): 123 | self._current_resampled = value 124 | else: 125 | raise TypeError() 126 | 127 | @property 128 | def output_results(self): 129 | return self._output_results 130 | 131 | @output_results.setter 132 | def output_results(self, value): 133 | if value is None: 134 | self._output_results = None 135 | elif isinstance(value, SportranOutput): 136 | self._output_results = value 137 | else: 138 | raise TypeError() 139 | -------------------------------------------------------------------------------- /sportran/i_o/extract_lammps_thermo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function usage 4 | { 5 | echo -e "This script extracts the thermo log from a lammps log file. 6 | It assumes that a line such as 7 | dump DUMP_RUN_KEY all custom file.lammpstrj id type x y z 8 | or simply 9 | #dump DUMP_RUN_KEY 10 | is present in the file before the 'run' command. 11 | DUMP_RUN can be set with the -d flag. 12 | Note: DUMP_RUN cannot contain spaces 13 | 14 | Usage: extract_current.sh -f LOG_FILE [-h] 15 | -f LOG_FILE LAMMPS log file name 16 | -d DUMP_RUN_KEY label - used to identify the production run 17 | --start-time initial time to add to the Time column 18 | --start-step initial step to add to the Step column 19 | --last-step last step to read 20 | --skip-first skip header and first step (useful to concatenate files) 21 | -h --help print this help 22 | Example: extract_current.sh -f water.log -d DUMP_RUN > thermo.dat 23 | " 24 | } 25 | 26 | LOG_FILE= 27 | DUMP_RUN_KEY='DUMP_RUN' 28 | VERBOSITY=1 29 | START_STEP=0 30 | START_TIME=0 31 | SKIP_FIRST=0 32 | LAST_STEP=-1 33 | 34 | while [ $# -gt 0 ]; do 35 | case $1 in 36 | -f ) shift; LOG_FILE=$1 37 | ;; 38 | -d ) shift; DUMP_RUN_KEY=$1 39 | ;; 40 | -v ) VERBOSITY=0 41 | ;; 42 | --start-time ) shift; START_TIME=$1 43 | ;; 44 | --start-step ) shift; START_STEP=$1 45 | ;; 46 | --last-step ) shift; LAST_STEP=$1 47 | ;; 48 | --skip-first ) SKIP_FIRST=1 49 | ;; 50 | -h | --help ) usage 51 | exit 52 | ;; 53 | * ) usage 54 | exit 1 55 | esac 56 | shift 57 | done 58 | 59 | if [[ ( (-z "$LOG_FILE") ) ]]; then 60 | usage 61 | exit 1 62 | fi 63 | 64 | echo " Reading file: "${LOG_FILE} > "/dev/stderr" 65 | echo " DUMP RUN key: "${DUMP_RUN_KEY} > "/dev/stderr" 66 | 67 | awk -vDUMPKEY=${DUMP_RUN_KEY} -vVERB=${VERBOSITY} -vSTART_TIME=${START_TIME} -vSTART_STEP=${START_STEP} -vSKIP_FIRST=${SKIP_FIRST} -vLAST_STEP=${LAST_STEP} ' 68 | BEGIN { 69 | flag = 0 # flag = 1 read column headers, flag = 2 read data 70 | nfluxcol = 0 71 | stepcol = 0 72 | timecol = 0 73 | count = 0 74 | if (LAST_STEP >= 0 ) 75 | laststepflag = 1 76 | else 77 | laststepflag = 0 78 | } 79 | (($1 == "dump" || $1 == "#dump") && $2 == DUMPKEY) || (($2 == "dump" && $1 == "#") && $3 == DUMPKEY){ 80 | ## production run found 81 | printf(" dump %s found at line %d.\n", DUMPKEY, NR) > "/dev/stderr" 82 | flag = 1 83 | } 84 | flag == 1 && $1 == "Step" { 85 | ## here you can read column headers 86 | flag = 2 87 | for (i=1; i<=NF; i++) { 88 | printf("Found: %16s at line %d, column %d\n", $i, NR, i) > "/dev/stderr" 89 | if ($i == "Step") { 90 | stepcol = i 91 | } 92 | else if ($i == "Time") { 93 | timecol = i 94 | } 95 | } 96 | if (!SKIP_FIRST) 97 | print $0 98 | next 99 | } 100 | flag == 2 { 101 | if (count==0 && SKIP_FIRST) { 102 | count++ 103 | next 104 | } 105 | else { 106 | ## read data until Loop 107 | if ($1 == "Loop") { 108 | flag = 0 109 | exit 0 110 | } 111 | else if ($1 == "WARNING:") { 112 | flag = 0 113 | printf("WARNING found at line %d.\n", NR) > "/dev/stderr" 114 | } 115 | else if (laststepflag && ($stepcol > LAST_STEP)) { # Last step reached 116 | exit 0 117 | } 118 | else { ## print all 119 | for (i=1; i<=NF; i++) { 120 | if (i == stepcol) { 121 | printf("%d ", $i + START_STEP) 122 | } 123 | else if (i == timecol) { 124 | printf("%g ", $i + START_TIME) 125 | } 126 | else { 127 | printf("%s ", $i) 128 | } 129 | } 130 | printf("\n") 131 | count++ 132 | } 133 | } 134 | } 135 | END { 136 | if ( count == 0 ) 137 | printf("ERROR: no steps found.\n") > "/dev/stderr" 138 | else 139 | printf("DONE: %d steps of data read.\n", count) > "/dev/stderr" 140 | } 141 | ' $LOG_FILE 142 | -------------------------------------------------------------------------------- /sportran/i_o/read_lammps_datafile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ### 5 | ### ReadLAMMPSdatafile 6 | ### 7 | ################################################################################ 8 | 9 | import numpy as np 10 | 11 | 12 | def get_box(filename): 13 | """Return the box edges and volume from a LAMMPS Data file.""" 14 | box = np.zeros((2, 3)) 15 | with open(filename, 'r') as f: 16 | while True: 17 | line = f.readline().split() 18 | if 'xlo' in line: 19 | box[:, 0] = [float(line[0]), float(line[1])] 20 | if 'ylo' in line: 21 | box[:, 1] = [float(line[0]), float(line[1])] 22 | if 'zlo' in line: 23 | box[:, 2] = [float(line[0]), float(line[1])] 24 | break 25 | volume = np.prod(box[1, :] - box[0, :]) 26 | return box, volume 27 | 28 | 29 | ## ** SHOULD ADD ADDITIONAL OF READ FUNCTIONS 30 | -------------------------------------------------------------------------------- /sportran/md/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Basic classes and facilities to perform a complete cepstral analysis 4 | """ 5 | 6 | from .cepstral import * 7 | from .mdsample import * 8 | 9 | __all__ = ['CepstralFilter', 'MDSample'] 10 | -------------------------------------------------------------------------------- /sportran/md/aic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | 5 | 6 | def dct_AIC(yk, theory_var=None): 7 | """AIC[K] = sum_{k>K} c_k^2/theory_var + 2*(K+1) 8 | Assumiamo di tenere tutti i k <= K.""" 9 | aic = np.zeros(yk.size) 10 | if theory_var is None: 11 | N = 2 * (yk.size - 1) 12 | theory_var1 = np.pi**2 / 3. / N # k = {0, N/2} 13 | theory_var2 = np.pi**2 / 6. / N # otherwise 14 | for K in range(yk.size - 1): 15 | # aic[K] = np.sum(yk[K+1:]**2)/theory_var + 2.*K 16 | aic[K] = ((1. / theory_var2) * np.sum(yk[K + 1:-1]**2) + (1. / theory_var1) * yk[-1]**2) + 2. * (K + 1) 17 | aic[-1] = 2. * yk.size 18 | else: 19 | aic[-1] = 0. # + (2*(yk.size+1)) 20 | for K in range(yk.size - 2, -1, -1): # N-2, N-3, ..., 0 21 | aic[K] = aic[K + 1] + yk[K + 1]**2 / theory_var[K + 1] 22 | aic = aic + 2. * (np.arange(yk.size) + 1) 23 | return aic 24 | 25 | 26 | def dct_AICc(yk, theory_var=None): 27 | """AICc[K] = AIC[K] + 2*(K+1)*(K+2)/(NF-K-2) 28 | Assumiamo di tenere tutti i k <= K.""" 29 | aic = dct_AIC(yk, theory_var) 30 | KK = np.arange(yk.size - 2) 31 | aic[:-2] = aic[:-2] + 2. * (KK + 1) * (KK + 2) / (yk.size - KK - 2.) 32 | aic[-2] = aic[-3] # not defined 33 | aic[-1] = aic[-3] # not defined 34 | return aic 35 | 36 | 37 | def dct_aic_ab(yk, theory_var, A=1.0, B=2.0): 38 | """AIC[K] = sum_{k>K} c_k^2/theory_var + 2*K 39 | Assumiamo di tenere tutti i k <= K.""" 40 | aic = np.zeros(yk.size) 41 | aic[-1] = 0. 42 | for K in range(yk.size - 2, -1, -1): # N-2, N-3, ..., 0 43 | aic[K] = aic[K + 1] + yk[K + 1]**2 / theory_var[K + 1] 44 | aic = A * aic + B * (np.arange(yk.size) + 1) 45 | return aic 46 | 47 | 48 | ################################################################################ 49 | #### Bayesian method 50 | ################################################################################ 51 | # 52 | # Produce_density takes as inputs the following numpy arrays: 53 | # 54 | # INPUT : 55 | # 56 | # - aic: The vector with the estimate of the Akaike information aic(k), with k ranging from k_beg:k_max. 57 | # I think it is important to have values till k_max but the first k_beg can be different from one ; 58 | # - sigma, mean : for the k_beg:k_max, in the same order, the mean and sigmas of the estimate 59 | # of the transport coefficient ; 60 | # - method : a string equal to 'one' or 'two' with two different methods of inizializing p[k], the estimate probability 61 | # of a given k. I hope in real cases the two methods should be equivalent. 62 | # 63 | # RETURNS: 64 | # 65 | # The probability p[ik], a grid and a density for the probability of the transport coefficient on that grid, obtained as: 66 | # density[igrid] \sim Sum_ik p[ik] * N[ mean[ik],sigma[ik] ]. 67 | # All pi factors are never inserted and taken care I hope by final normalization. 68 | # 69 | # The functions produce_p and produce_p_density distinguish the two conceptual steps of producing p and using this quantity 70 | # to provide the final estimate on the transport coefficient 71 | # 72 | 73 | 74 | def produce_p(aic, method='ba', force_normalize=False): 75 | k0 = np.argmin(aic) 76 | kM = len(aic) 77 | p = np.zeros(kM) 78 | 79 | if (method == 'min'): 80 | # just the min(aic) 81 | p[k0] = 1.0 82 | 83 | elif (method == 'baroni'): 84 | for ik in range(kM): 85 | delta_aic = aic[ik] - aic[k0] 86 | GAMMA = 3.9215536345675050924567623117545 # (epsilon/sigma)^2 87 | p[ik] = np.exp(-0.25 * np.log(1. + GAMMA) * delta_aic) 88 | 89 | elif ((method == 'burnham-anderson') or (method == 'ba')): 90 | delta_aic = aic - aic[k0] 91 | p = np.exp(-0.5 * delta_aic) 92 | p = p / np.sum(p) 93 | 94 | elif ((method == 'burnham-anderson2') or (method == 'ba2')): 95 | delta_aic = aic - aic[k0] 96 | p = np.exp(-delta_aic) 97 | p = p / np.sum(p) 98 | 99 | elif (method == 'two'): 100 | for ik in range(kM): 101 | delta_aic = aic[ik] - aic[k0] 102 | p[ik] = np.exp(-delta_aic**2 / (2.0 * (kM - k0))) / np.sqrt(kM - k0) 103 | 104 | elif (method == 'four'): 105 | for ik in range(kM): 106 | delta_aic = aic[ik] - aic[k0] 107 | p[ik] = np.exp(-delta_aic**2 / (2.0 * np.abs(ik - k0))) 108 | p[k0] = 1.0 109 | 110 | else: 111 | raise KeyError('P_AIC METHOD not valid.') 112 | #p[ik] = np.exp(- delta_aic ** 2 / ( 2.0 * ( kM - ik) ) ) / np.sqrt(kM - ik) 113 | #p[ik] = np.exp(-delta_aic ** 2 / ( 2.0 * np.abs(ik - k0) )) / np.sqrt(np.abs(ik - k0)) 114 | 115 | # normalize p 116 | if force_normalize: 117 | integral = np.trapz(p) 118 | for ik in range(kM): 119 | p[ik] = p[ik] / integral 120 | 121 | # checks 122 | if any(np.isnan(p)): 123 | raise Warning('WARNING: P contains NaN.') 124 | if (np.min(p < 0)): 125 | raise Warning('WARNING: P < 0.') 126 | return p 127 | 128 | 129 | def produce_p_density(p, sigma, mean, grid=None, grid_size=1000): 130 | kM = len(mean) 131 | dm = np.min(mean) 132 | dM = np.max(mean) 133 | argdm = np.argmin(mean) 134 | argdM = np.argmax(mean) 135 | sigmam = sigma[argdm] 136 | sigmaM = sigma[argdM] 137 | if grid is None: 138 | return_grid = True 139 | # generate grid with grid_size points 140 | delta = ((dM + 5. * sigmaM) - (dm - 5. * sigmam)) / float(grid_size - 1) 141 | grid = np.linspace(dm - 5. * sigmam, dM + 5. * sigmaM, grid_size) 142 | else: 143 | return_grid = False 144 | delta = grid[1] - grid[0] 145 | density = np.zeros(len(grid)) 146 | for ik in range(kM): 147 | density = density + p[ik] * np.exp(-(grid - mean[ik])**2 / (2.0 * (sigma[ik]**2))) / sigma[ik] 148 | somma = np.trapz(density) * delta 149 | density = density / somma 150 | if return_grid: 151 | return density, grid 152 | else: 153 | return density 154 | 155 | 156 | def grid_statistics(grid, density, grid2=None): 157 | """Compute distribution mean and std. 158 | media = \\sum_i (density[i] * grid[i]) 159 | std = sqrt( \\sum_i (density[i] * grid[i]^2) - media^2 ) 160 | oppure = sqrt( \\sum_i (density[i] * grid2[i]) - media^2 )""" 161 | somma = np.sum(density) 162 | media = np.dot(density, grid) / somma 163 | if grid2 is None: 164 | var = np.dot(density, grid**2) / somma - media**2 165 | else: 166 | var = np.dot(density, grid2) / somma - media**2 167 | std = np.sqrt(var) 168 | return media, std 169 | -------------------------------------------------------------------------------- /sportran/md/resample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | from .tools.resample import filter_and_sample 5 | from sportran.utils import log 6 | 7 | 8 | def resample_timeseries(x, TSKIP=None, fstar_THz=None, FILTER_W=None, plot=False, PSD_FILTER_W=None, freq_units='THz', 9 | FIGSIZE=None, verbose=True): # yapf: disable 10 | """ 11 | Simulate the resampling of a time series x. 12 | 13 | Parameters 14 | ---------- 15 | x = the time series object, a (subclass of) MDSample 16 | TSKIP = sampling time [steps] 17 | fstar_THz = target cutoff frequency [THz] 18 | TSKIP and fstar_THZ are mutually exclusive. 19 | 20 | FILTER_W = pre-sampling filter window width [steps] 21 | plot = plot the PSD [False] 22 | PSD_FILTER_W = PSD filtering window width [chosen frequency units] 23 | freq_units = 'thz' [THz] 24 | 'red' [omega*DT/(2*pi)] 25 | FIGSIZE = plot figure size 26 | verbose = print log [True] 27 | 28 | Returns 29 | ------- 30 | xf : a filtered & resampled time series object 31 | ax : an array of plot axes, optional (if plot=True) 32 | """ 33 | from .mdsample import MDSample 34 | 35 | if not isinstance(x, MDSample): 36 | raise ValueError('x must be a MDSample object or a subclass.') 37 | if (TSKIP is not None) and (fstar_THz is not None): 38 | raise ValueError('Please specify either TSKIP or fstar_THz.') 39 | elif (TSKIP is None) and (fstar_THz is None): 40 | raise ValueError('Please specify either TSKIP or fstar_THz.') 41 | if x.psd is None or PSD_FILTER_W: 42 | x.compute_psd(PSD_FILTER_W, freq_units) # this ensures that Nyquist_f_THz is defined 43 | if fstar_THz: 44 | # find TSKIP that gives the closest fstar 45 | TSKIP = int(round(x.Nyquist_f_THz / fstar_THz)) 46 | if FILTER_W is None: 47 | FILTER_W = TSKIP 48 | if plot: 49 | raise NotImplementedError() 50 | 51 | fstar_THz = x.Nyquist_f_THz / TSKIP 52 | fstar_idx = np.argmin(x.freqs_THz < fstar_THz) 53 | 54 | # get the builder of the original time series object 55 | # builder is a dictionary containing the parameters to define the new time series 56 | TimeSeries, builder = x._get_builder() 57 | trajectory = builder['traj'] 58 | 59 | # filter & resample time series -- for 3D time series, apply to each row 60 | if (len(trajectory.shape) == 3): 61 | new_trajectory = np.array([filter_and_sample(traj, FILTER_W, TSKIP, 'rectangular') for traj in trajectory]) 62 | elif (len(trajectory.shape) < 3): 63 | new_trajectory = filter_and_sample(trajectory, FILTER_W, TSKIP, 'rectangular') 64 | else: 65 | raise ValueError('Trajectory shape not valid.') 66 | 67 | # define new time series by updating the builder 68 | builder.update(traj=new_trajectory, DT_FS=(x.DT_FS * TSKIP)) 69 | xf = TimeSeries(**builder) 70 | 71 | # define new filtering window width to be equal to the original one 72 | new_PSD_FILTER_W = x.PSD_FILTER_W_THZ if freq_units in ('THz', 'thz') else x.PSD_FILTER_W * TSKIP 73 | if xf.psd is None: 74 | xf.compute_psd(new_PSD_FILTER_W, freq_units) # this ensures that Nyquist_f_THz is defined 75 | 76 | # write log 77 | xf.resample_log = \ 78 | '-----------------------------------------------------\n' +\ 79 | ' RESAMPLE TIME SERIES\n' +\ 80 | '-----------------------------------------------------\n' +\ 81 | ' Original Nyquist freq f_Ny = {:12.5f} THz\n'.format(x.Nyquist_f_THz) +\ 82 | ' Resampling freq f* = {:12.5f} THz\n'.format(fstar_THz) +\ 83 | ' Sampling time TSKIP = {:12d} steps\n'.format(TSKIP) +\ 84 | ' = {:12.3f} fs\n'.format(TSKIP * x.DT_FS) +\ 85 | ' Original n. of frequencies = {:12d}\n'.format(x.NFREQS) +\ 86 | ' Resampled n. of frequencies = {:12d}\n'.format(xf.NFREQS) 87 | if x.fpsd is not None and xf.fpsd is not None: # TODO: maybe substitute with if x.PSD_FILTER_W != 0 88 | xf.resample_log += \ 89 | ' PSD @cutoff (pre-filter&sample) ~ {:12.5f}\n'.format(x.fpsd[fstar_idx]) +\ 90 | ' (post-filter&sample) ~ {:12.5f}\n'.format(xf.fpsd[-1]) +\ 91 | ' log(PSD) @cutoff (pre-filter&sample) ~ {:12.5f}\n'.format(x.flogpsd[fstar_idx]) +\ 92 | ' (post-filter&sample) ~ {:12.5f}\n'.format(xf.flogpsd[-1]) 93 | xf.resample_log += \ 94 | ' min(PSD) (pre-filter&sample) = {:12.5f}\n'.format(x.psd_min) +\ 95 | ' min(PSD) (post-filter&sample) = {:12.5f}\n'.format(xf.psd_min) +\ 96 | ' % of original PSD Power f X.shape[0] - 1: 17 | W = X.shape[0] - 1 18 | WF = 2 * W 19 | print('Warning: reducing filtering window') 20 | 21 | Y = np.concatenate((X[W:0:-1], X, X[-2:-W - 2:-1])) 22 | return np.convolve(Y, np.array([1.0 / WF] * WF), 'valid') 23 | -------------------------------------------------------------------------------- /sportran/md/tools/lpfilter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | from sportran.utils import log 5 | 6 | 7 | class LowPassFilter(object): 8 | 9 | def __init__(self, *args, **kwargs): 10 | if (len(args) < 1): 11 | self.filtertype = kwargs.get('filtertype', None) 12 | if self.filtertype is None: 13 | raise ValueError('Not enough input arguments.') 14 | else: 15 | self.filtertype = args[0] 16 | if (self.filtertype == 'exp'): 17 | if (len(args) == 5): 18 | self.freqs = args[1] 19 | self.f0 = args[2] 20 | self.alpha = args[3] 21 | self.minatt = args[4] 22 | else: 23 | self.freqs = kwargs.get('freqs', None) 24 | self.f0 = kwargs.get('f0', None) 25 | self.alpha = kwargs.get('alpha', None) 26 | self.minatt = kwargs.get('minatt', None) 27 | return 28 | 29 | def __repr__(self): 30 | msg = '{} low-pass filter:\n'.format(self.filtertype) + \ 31 | ' f0 = {}\n'.format(self.f0) + \ 32 | ' alpha = {}\n'.format(self.alpha) 33 | return msg 34 | 35 | def compute_response(self, freqs=None): 36 | if freqs is not None: 37 | self.freqs = freqs 38 | if self.freqs is None: 39 | raise ValueError('freqs not set.') 40 | if (self.filtertype == 'exp'): 41 | self.response = self.exp_filter() 42 | self.logresponse = np.log(self.response) 43 | return 44 | 45 | def exp_filter(self): 46 | if self.f0 is None: 47 | raise ValueError('f0 not set.') 48 | if self.alpha is None: 49 | raise ValueError('alpha not set.') 50 | if self.minatt is None: 51 | self.minatt = 1.0e-3 52 | lf = np.zeros(self.freqs.size) 53 | for i in range(self.freqs.size): 54 | if (0. <= self.freqs[i] < 0.5): 55 | lf[i] = np.exp(-(self.freqs[i] / self.f0)**self.alpha) * (1.0 - self.minatt) + self.minatt 56 | elif (0.5 <= self.freqs[i] < 1.): 57 | lf[i] = np.exp(-(np.abs(self.freqs[i] - 1.) / self.f0)**self.alpha) * (1.0 - self.minatt) + self.minatt 58 | else: 59 | log.write_log('ERROR: frequency out of range!') 60 | return lf 61 | -------------------------------------------------------------------------------- /sportran/md/tools/resample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | from scipy.signal import lfilter 5 | 6 | 7 | def filter_and_sample(y_big, W, DT, window='rectangular', even_NSTEPS=True, detrend=False, drop_first=True): 8 | """Filter signal with moving average window of width W and then sample it 9 | with time step DT.""" 10 | 11 | if (W > 1): 12 | if (window == 'rectangular'): 13 | y_f = lfilter((1. / W) * np.ones(W), 1., y_big, axis=0) 14 | else: 15 | raise NotImplementedError('Not implemented window type.') 16 | 17 | # drop first W steps (initial conditions) 18 | if drop_first: 19 | y = y_f[(W - 1)::DT] 20 | else: 21 | y = y_f[::DT] 22 | else: 23 | y = y_big[::DT] 24 | 25 | # remove the mean 26 | if detrend: 27 | y = y - np.mean(y, axis=0) 28 | 29 | # keeps an even number of points 30 | if even_NSTEPS: 31 | if (y.shape[0] % 2 == 1): 32 | return y[:-1] 33 | return y 34 | 35 | 36 | def resample_psd(freqs, psd, cutfrequency): 37 | if (cutfrequency >= freqs[-1]): 38 | return freqs, psd 39 | NFREQS = freqs.size - 1 40 | cutidx = (np.abs(freqs - cutfrequency)).argmin() 41 | if (NFREQS % cutidx == 0): # cut frequency is sub-multiple of max freq 42 | DT = NFREQS / cutidx 43 | if (DT > 2): 44 | raise Warning('DT Not implemented.') 45 | newpsd = psd.copy()[:cutidx + 1] 46 | newpsd = newpsd + psd[:-cutidx - 2:-1] 47 | newfreqs = freqs[:cutidx + 1] 48 | #log.write_log(cutidx, DT, freqs[cutidx], newpsd.size) 49 | else: 50 | raise NotImplementedError('Not implemented.') 51 | return newfreqs, newpsd 52 | -------------------------------------------------------------------------------- /sportran/md/tools/spectrum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | 5 | 6 | def freq_THz_to_red(f_THz, DT_FS): 7 | """ 8 | Converts THz to reduced frequency units. 9 | f[red] = f[THz] * dt[fs] / 1000 10 | """ 11 | return f_THz / 1000. * DT_FS 12 | 13 | 14 | def freq_red_to_THz(f_red, DT_FS): 15 | """ 16 | Converts reduced frequency units to THz. 17 | f[THz] = f[red] * 1000 / dt[fs] 18 | """ 19 | return f_red * 1000. / DT_FS 20 | 21 | 22 | def logtau_to_tau(logtau, logtau_mean, logtau_var, correct_mean=True): 23 | if correct_mean: 24 | logtau = logtau - logtau_mean 25 | tau = np.exp(logtau) 26 | tau_std = np.sqrt(tau * logtau_var) 27 | else: 28 | raise NotImplementedError() 29 | return tau, tau_std 30 | 31 | 32 | def generate_empirical_spectrum(psd): 33 | """Add noise to a periodogram and generate a complex spectrum.""" 34 | ### Probabilmente ci vuole un fattore 1/N, in maniera da tirar via il fattore N 35 | ### in compute_trajectory, cosi' diventa consistente con compute_spectrum 36 | spectr = np.random.normal(0., np.sqrt(0.5*psd*(psd.size-1)*2)) + \ 37 | 1.0J*np.random.normal(0., np.sqrt(0.5*psd*(psd.size-1)*2)) 38 | spectr[0] = spectr[0].real 39 | spectr[-1] = spectr[-1].real 40 | return spectr 41 | -------------------------------------------------------------------------------- /sportran/plotter/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utilities to pretty-plot the results 4 | """ 5 | 6 | __all__ = ['plt', 'use_plot_style', 'Plotter', 'MDSamplePlotter', 'CurrentPlotter', 'CLIPlotter'] 7 | 8 | import matplotlib 9 | # matplotlib.use('Agg') # if needed use force=True, warn=False 10 | import matplotlib.pyplot as plt 11 | 12 | from .plotter import Plotter, addPlotToPdf, PdfPages 13 | from .style import use_plot_style 14 | from .mdsample import MDSamplePlotter 15 | from .current import CurrentPlotter 16 | from .cli import CLIPlotter 17 | -------------------------------------------------------------------------------- /sportran/plotter/cli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Defines the CLIPlotter class. 4 | """ 5 | 6 | __all__ = ['CLIPlotter'] 7 | 8 | from . import plotter 9 | 10 | 11 | class CLIPlotter(plotter.Plotter): 12 | """ 13 | A Plotter subclass containing the plot functions used by the command-line interface. 14 | """ 15 | from .plotter import (plot_periodogram, plot_ck, plot_L0_Pstar, plot_kappa_Pstar, plot_cepstral_spectrum, 16 | plot_fstar_analysis, plot_resample, plot_psd, plot_cospectrum_component) 17 | _plot_style = 'cli_style.mplstyle' # TODO define style for CLI 18 | pass 19 | 20 | 21 | # probably we should decorate all these functions with addPlotToPdf and other decorators that allow any changes of 22 | # style needed 23 | -------------------------------------------------------------------------------- /sportran/plotter/current.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Defines the CurrentPlotter class. 4 | """ 5 | 6 | __all__ = ['CurrentPlotter'] 7 | 8 | from . import plotter 9 | 10 | 11 | class CurrentPlotter(plotter.Plotter): 12 | """ 13 | A Plotter subclass containing the plot functions used by an object of type Current. 14 | """ 15 | from .plotter import (plot_trajectory, plot_periodogram, plot_cospectrum_component, plot_ck, plot_L0_Pstar, 16 | plot_kappa_Pstar, plot_cepstral_spectrum, plot_resample) 17 | _plot_style = 'api_style.mplstyle' 18 | pass 19 | 20 | 21 | # # alternative method: 22 | #for funcname in ( 23 | # 'plot_periodogram', 24 | # 'plot_ck', 25 | # 'plot_L0_Pstar', 26 | # 'plot_kappa_Pstar', 27 | # 'plot_cepstral_spectrum', 28 | # 'plot_resample'): 29 | # 30 | # # get the function from the plotter module, and make it an attribute of CurrentPlotter 31 | # setattr(CurrentPlotter, funcname, getattr(plotter, funcname)) 32 | -------------------------------------------------------------------------------- /sportran/plotter/mdsample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Defines the MDSamplePlotter class. 4 | """ 5 | 6 | __all__ = ['MDSamplePlotter'] 7 | 8 | from . import plotter 9 | 10 | 11 | class MDSamplePlotter(plotter.Plotter): 12 | """ 13 | A Plotter subclass containing the plot functions used by an object of type MDSample. 14 | """ 15 | from .plotter import (plot_trajectory, plot_resample) 16 | _plot_style = 'api_style.mplstyle' 17 | 18 | def plot_periodogram(current, PSD_FILTER_W=None, *, freq_units='THz', freq_scale=1.0, axes=None, FIGSIZE=None, 19 | mode='log', **plot_kwargs): 20 | """ 21 | Plots the current's periodogram (psd) 22 | :param current: current object to plot periodogram 23 | :param PSD_FILTER_W: width of the filtering window 24 | :param freq_units: 'thz' [THz] 25 | 'red' [omega*DT/(2*pi)] 26 | :param freq_scale: rescale red frequencies by this factor (e.g. 2 --> freq = [0, 0.25]) 27 | :param axes: plot periodograms in units of kappa (default: True) - NB: log-psd not converted 28 | :param FIGSIZE: size of the plot 29 | 30 | :return: a matplotlib.axes.Axes object 31 | """ 32 | # kappa_units is not supported by MDSample 33 | from .plotter import plot_periodogram 34 | plot_kwargs.pop('kappa_units') 35 | return plot_periodogram(current, PSD_FILTER_W=PSD_FILTER_W, freq_units=freq_units, freq_scale=freq_scale, 36 | axes=axes, kappa_units=False, FIGSIZE=FIGSIZE, mode=mode, **plot_kwargs) 37 | 38 | pass 39 | -------------------------------------------------------------------------------- /sportran/plotter/style.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from os.path import isfile 4 | from . import plt 5 | from warnings import warn 6 | 7 | DEFAULT_PLOT_STYLE = 'api_style.mplstyle' 8 | 9 | 10 | def use_plot_style(plot_style_filename=None): 11 | """ 12 | Use a matplotlib plot style file. 13 | """ 14 | if plot_style_filename is None: 15 | plot_style_filename = DEFAULT_PLOT_STYLE 16 | #print('Using {} plot style.'.format(plot_style_filename)) 17 | 18 | # try to import matplotlib style settings 19 | if isfile(plot_style_filename): 20 | pltstyle_file = plot_style_filename 21 | else: 22 | try: 23 | import pkg_resources 24 | pltstyle_file = pkg_resources.resource_filename('sportran.plotter.styles', plot_style_filename) 25 | except: 26 | # fallback (if sportran is not installed...) 27 | try: 28 | abs_path = os.path.abspath(__file__) 29 | tc_path = abs_path[:abs_path.rfind('/')] 30 | os.path.append(tc_path[:tc_path.rfind('/')]) 31 | except: 32 | abs_path = '.' 33 | pltstyle_file = tc_path + '/plotter/styles/' + plot_style_filename 34 | 35 | try: 36 | # print('using style ', plot_style_filename) 37 | plt.style.use(pltstyle_file) 38 | except: 39 | warn('The plot style {} could not be loaded.'.format(pltstyle_file)) 40 | -------------------------------------------------------------------------------- /sportran/plotter/styles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sissaschool/sportran/677d2bb5108ba05906cb3aa580d3a974efc77793/sportran/plotter/styles/__init__.py -------------------------------------------------------------------------------- /sportran/plotter/styles/api_style.mplstyle: -------------------------------------------------------------------------------- 1 | # Customized matplotlib style for paper publication. 2 | # see http://matplotlib.org/users/customizing.html 3 | 4 | figure.facecolor : 1.0 5 | figure.autolayout : False 6 | figure.figsize : 3.8, 2.3 7 | figure.dpi : 300 8 | 9 | #text.usetex : True 10 | font.size : 9.0 11 | font.family : sans-serif 12 | 13 | lines.linewidth : 0.75 14 | lines.markersize : 3 15 | lines.markeredgewidth : 0. 16 | #lines.solid_capstyle : round 17 | lines.dashed_pattern : 2.8, 1.2 18 | patch.linewidth: 0. 19 | hatch.linewidth: 0.2 20 | 21 | axes.xmargin: 0 22 | axes.ymargin: 0 23 | axes.formatter.limits: -3, 3 24 | axes.labelpad : 1.5 25 | #axes.titlesize : 24 26 | #axes.labelsize : 20 27 | 28 | #axes.prop_cycle: cycler('color', 'bgrcmyk') 29 | #axes.prop_cycle : cycler('color', ['1f77b4', 'ff7f0e', '2ca02c', 'd62728', '9467bd', '8c564b', 'e377c2', '7f7f7f', 'bcbd22', '17becf']) 30 | 31 | #xtick.labelsize : 16 32 | #ytick.labelsize : 16 33 | xtick.direction : in 34 | ytick.direction : in 35 | xtick.major.size : 4 36 | ytick.major.size : 4 37 | xtick.minor.size : 2 38 | ytick.minor.size : 2 39 | 40 | #grid.color: 0.8 41 | #grid.linestyle: : 42 | grid.linewidth: 0.1 43 | 44 | savefig.dpi : 300 45 | savefig.format : 'pdf' 46 | savefig.bbox : tight 47 | -------------------------------------------------------------------------------- /sportran/plotter/styles/cli_style.mplstyle: -------------------------------------------------------------------------------- 1 | # Customized matplotlib style for paper publication. 2 | # see http://matplotlib.org/users/customizing.html 3 | # TODO: customize this 4 | 5 | figure.facecolor : 1.0 6 | figure.autolayout : False 7 | #figure.figsize : 3.8, 2.3 8 | figure.figsize : 6, 4 9 | figure.dpi : 300 10 | 11 | #text.usetex : True 12 | font.size : 9.0 13 | font.family : sans-serif 14 | 15 | lines.linewidth : 0.75 16 | lines.markersize : 3 17 | lines.markeredgewidth : 0. 18 | #lines.solid_capstyle : round 19 | lines.dashed_pattern : 2.8, 1.2 20 | patch.linewidth: 0. 21 | hatch.linewidth: 0.2 22 | 23 | axes.xmargin: 0 24 | axes.ymargin: 0 25 | axes.formatter.limits: -3, 3 26 | axes.labelpad : 1.5 27 | #axes.titlesize : 24 28 | #axes.labelsize : 20 29 | 30 | #axes.prop_cycle: cycler('color', 'bgrcmyk') 31 | #axes.prop_cycle : cycler('color', ['1f77b4', 'ff7f0e', '2ca02c', 'd62728', '9467bd', '8c564b', 'e377c2', '7f7f7f', 'bcbd22', '17becf']) 32 | 33 | #xtick.labelsize : 16 34 | #ytick.labelsize : 16 35 | xtick.direction : in 36 | ytick.direction : in 37 | xtick.major.size : 4 38 | ytick.major.size : 4 39 | xtick.minor.size : 2 40 | ytick.minor.size : 2 41 | 42 | #grid.color: 0.8 43 | #grid.linestyle: : 44 | grid.linewidth: 0.1 45 | 46 | savefig.dpi : 300 47 | savefig.format : 'pdf' 48 | savefig.bbox : tight 49 | -------------------------------------------------------------------------------- /sportran/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Various utilities like for example a global logger instance 4 | """ 5 | 6 | from .logger import PrintMethod 7 | 8 | try: 9 | log = PrintMethod() 10 | except: 11 | raise RuntimeError('PrintMethod not defined.') 12 | 13 | __all__ = ['log'] 14 | -------------------------------------------------------------------------------- /sportran/utils/attributedict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class AttributeDict(dict): # pylint: disable=too-many-instance-attributes 5 | """ 6 | This class internally stores values in a dictionary, but exposes 7 | the keys also as attributes, i.e. asking for attrdict.key 8 | will return the value of attrdict['key'] and so on. 9 | 10 | Raises an AttributeError if the key does not exist, when called as an attribute, 11 | while the usual KeyError if the key does not exist and the dictionary syntax is 12 | used. 13 | """ 14 | 15 | def __init__(self, dictionary=None): 16 | """Recursively turn the `dict` and all its nested dictionaries into `AttributeDict` instance.""" 17 | super().__init__() 18 | if dictionary is None: 19 | dictionary = {} 20 | 21 | for key, value in dictionary.items(): 22 | if isinstance(value, Mapping): 23 | self[key] = AttributeDict(value) 24 | else: 25 | self[key] = value 26 | 27 | def __repr__(self): 28 | """Representation of the object.""" 29 | return f'{self.__class__.__name__}({dict.__repr__(self)})' 30 | 31 | def __getattr__(self, attr): 32 | """Read a key as an attribute. 33 | 34 | :raises AttributeError: if the attribute does not correspond to an existing key. 35 | """ 36 | try: 37 | return self[attr] 38 | except KeyError: 39 | errmsg = f"'{self.__class__.__name__}' object has no attribute '{attr}'" 40 | raise AttributeError(errmsg) 41 | 42 | def __setattr__(self, attr, value): 43 | """Set a key as an attribute.""" 44 | try: 45 | self[attr] = value 46 | except KeyError: 47 | raise AttributeError( 48 | f"AttributeError: '{attr}' is not a valid attribute of the object '{self.__class__.__name__}'") 49 | 50 | def __delattr__(self, attr): 51 | """Delete a key as an attribute. 52 | 53 | :raises AttributeError: if the attribute does not correspond to an existing key. 54 | """ 55 | try: 56 | del self[attr] 57 | except KeyError: 58 | errmsg = f"'{self.__class__.__name__}' object has no attribute '{attr}'" 59 | raise AttributeError(errmsg) 60 | 61 | def __deepcopy__(self, memo=None): 62 | """Deep copy.""" 63 | from copy import deepcopy 64 | 65 | if memo is None: 66 | memo = {} 67 | retval = deepcopy(dict(self)) 68 | return self.__class__(retval) 69 | 70 | def __getstate__(self): 71 | """Needed for pickling this class.""" 72 | return self.__dict__.copy() 73 | 74 | def __setstate__(self, dictionary): 75 | """Needed for pickling this class.""" 76 | self.__dict__.update(dictionary) 77 | 78 | def __dir__(self): 79 | return self.keys() 80 | -------------------------------------------------------------------------------- /sportran/utils/decorators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from functools import wraps 4 | 5 | 6 | def add_method(cls): 7 | """A decorator to dynamically add a method to a class.""" 8 | 9 | def decorator(func): 10 | 11 | @wraps(func) 12 | def wrapper(self, *args, **kwargs): 13 | return func(self, *args, **kwargs) 14 | 15 | setattr(cls, func.__name__, wrapper) 16 | # Note we are not binding func, but wrapper which accepts self but does exactly the same as func 17 | return func # returning func means func can still be used normally 18 | 19 | return decorator 20 | 21 | 22 | ## Example: 23 | ## let A be a class 24 | ## 25 | ## Non-decorator way (note the function must accept self) 26 | ## def foo(self): 27 | ## print('hello world!') 28 | ## setattr(A, 'foo', foo) 29 | # 30 | ## def bar(self, s): 31 | ## print(f'Message: {s}') 32 | ## setattr(A, 'bar', bar) 33 | # 34 | ## Decorator can be written to take normal functions and make them methods 35 | #@add_method(A) 36 | #def foo(): 37 | # print('hello world!') 38 | # 39 | #@add_method(A) 40 | #def bar(s): 41 | # print(f'Message: {s}') 42 | -------------------------------------------------------------------------------- /sportran/utils/logger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class PrintMethod: 5 | """ 6 | This class is intended to manage all the messages that should be put in a file, 7 | in the stdout with print, or in any other place. Is has to be used as a global thing. 8 | It is just a routing point. In the whole library please use this. 9 | By default it is equivalent to a call to print() 10 | """ 11 | 12 | _print_func = None #: print function that can be called by :func:`write_log` 13 | _METHOD = ['bash'] #: specify the list of methods to be called by :func:`write_log` 14 | 15 | def __init__(self): 16 | pass 17 | 18 | @classmethod 19 | def open_file(cls, fname): 20 | """opens the log file that will be used globally""" 21 | cls.lfile = open(fname, 'w') 22 | 23 | @classmethod 24 | def close_file(cls): 25 | cls.lfile.close() 26 | 27 | @classmethod 28 | def write_log(cls, *args, **kwargs): 29 | """ 30 | Calls all the methods added by :func:`append_method` 31 | """ 32 | if 'bash' in cls._METHOD: 33 | print(*args, **kwargs) 34 | if 'file' in cls._METHOD: 35 | s = '' 36 | for a in args: 37 | s += str(a) 38 | cls.lfile.write(s + '\n') 39 | if 'other' in cls._METHOD: 40 | if cls._print_func: 41 | cls._print_func(*args, **kwargs) 42 | else: 43 | print(*args, **kwargs) 44 | 45 | @classmethod 46 | def set_func(cls, func): 47 | """Set the function to call when :func:`write_log` is called. 48 | The function is called only if the 'other' method is setted by :func:`append_method` 49 | or :func:`set_method`""" 50 | cls._print_func = func 51 | 52 | @classmethod 53 | def append_method(cls, method): 54 | """append the method to the list. 55 | :param method: the method to be added to the method list, can be any of 'bash', 'file' or 'other' 56 | :type method: str 57 | if 'file' remember to call :func:`open_file` somewhere 58 | """ 59 | cls._METHOD.append(method) 60 | 61 | @classmethod 62 | def set_method(cls, method): 63 | """Removes all method and set only the provided one""" 64 | cls._METHOD = [method] 65 | -------------------------------------------------------------------------------- /sportran_gui/README_GUI.md: -------------------------------------------------------------------------------- 1 | ../README_GUI.md -------------------------------------------------------------------------------- /sportran_gui/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ========================================== 4 | Sportran graphic user interface 5 | ========================================== 6 | 7 | This is a GUI written in Tk, great for making fast experiments. The GUI can be called from the command line with 8 | `sportrain-gui` if this package is installed. 9 | """ 10 | 11 | __version__ = '0.1.2' 12 | -------------------------------------------------------------------------------- /sportran_gui/assets/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from pkg_resources import resource_stream, resource_string 4 | import json 5 | 6 | with resource_stream('sportran', 'metadata.json') as JS: 7 | METADATA = json.load(JS) 8 | dev_state = '' 9 | if 'classifiers' in METADATA: 10 | dev_state = [x for x in METADATA['classifiers'] if 'Development Status ::' in x] 11 | if len(dev_state) > 0: 12 | dev_state = dev_state[0].split('::')[1] 13 | else: 14 | dev_state = '' 15 | 16 | with resource_stream(__name__, 'languages.json') as JS: 17 | LANGUAGES = json.load(JS) 18 | 19 | ICON = resource_string(__name__, 'icon.gif') 20 | 21 | README_MD = resource_string('sportran', 'README.md') 22 | README_GUI_MD = resource_string('sportran_gui', 'README_GUI.md') 23 | -------------------------------------------------------------------------------- /sportran_gui/assets/icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sissaschool/sportran/677d2bb5108ba05906cb3aa580d3a974efc77793/sportran_gui/assets/icon.gif -------------------------------------------------------------------------------- /sportran_gui/assets/languages.json: -------------------------------------------------------------------------------- 1 | { 2 | "en-EN": { 3 | "exit": "Exit", 4 | "back": "Back", 5 | "next": "Next", 6 | "resample": "Resample", 7 | "recalculate": "Recalculate", 8 | "zoom_in": "Zoom-in", 9 | "reset_view": "Reset view", 10 | "help": "Help", 11 | "version": "Version", 12 | "developers": "Developers", 13 | "contacts": "Contacts", 14 | "about": "About", 15 | "info": "Info", 16 | "view": "View", 17 | "s_log": "Show logs", 18 | "s_inf": "Show Info", 19 | "f_nm": "File name", 20 | "f_tp": "File type", 21 | "size": "Size", 22 | 23 | "stp1": "Select a file", 24 | "f_list": "File list in the current folder:", 25 | "f_prv": "File preview", 26 | "in_frm": "Input format:", 27 | "slct": "Selected file:", 28 | 29 | "stp2": "Define what the headers/keys correspond to", 30 | "nn": "None", 31 | "en_cr": "Main current", 32 | "oth_cr": "Additional current", 33 | "tmp": "Temperature", 34 | "timestep": "Time step", 35 | "volume": "Volume", 36 | 37 | "df1": "None: the header will not be used.", 38 | "df2": "Temperature: this column will be used to calculate the mean temperature.", 39 | "df3": "Main current: this column corresponds to first flux in the onsager matrix (e.g. the energy current).", 40 | "df4": "Additional current: Additional current for multi-component fluids. If the fluid has N diffusive components, you must provide N-1 independent currents. The units are not important, since they simplify in the formula. For example, the center of mass velocity of N-1 components. Non-diffusive currents can be useful to reduce noise by making the spectrum less powerful.", 41 | 42 | "stp3": "Set variables", 43 | "e_v": "Environment variables", 44 | "vl": "Volume", 45 | "fl_v": "Plot filter variables", 46 | "fl_w": "Plot filter width", 47 | 48 | "stp4": "F* selector", 49 | "fslide": "F* selector: slide to select F* or enter the value, then click Resample", 50 | "slct_v": "F* Selected value", 51 | "stp5": "P* selector", 52 | "pstar_cmt": "The default value is obtained from the Akaike Information Criterion", 53 | "inc": "change by", 54 | 55 | "lst_rl": "Last release", 56 | "new_a": "New analysis", 57 | "fs": "Font size", 58 | "pl": "Preview lines", 59 | "save": "Save", 60 | "lang": "Language", 61 | "export_data": "Export data", 62 | "settings": "Settings", 63 | "export_success": "Export success", 64 | "export_success_t": "Numpy data exported to \"{}\"", 65 | "export_fail": "Export fail", 66 | "export_fail_t": "Cannot save numpy data to file \"{}\"", 67 | "description_loaded": "{} description loaded from input file ({})", 68 | "last_release": "Last release: {}", 69 | "display_error": "Display error", 70 | "display_error_t" : "Unable to show a preview of this file.", 71 | "swich_to_dict_t" : "Set the file type to 'dict'?", 72 | "invalid_format": "Invalid format!", 73 | "invalid_format_t": "The file that you have selected has an invalid format!", 74 | "file_not_exist": "File doesn't exists!", 75 | "file_not_exist_t": "The file that you have selected doesn't exists!", 76 | "no_file": "No file selected!", 77 | "no_file_t": "You must select a data file!", 78 | "select_units": "Select the unit to use", 79 | "units": "Units :", 80 | "value_error": "Value error", 81 | "only_one_T": "You can't assign more than one time the value \"Temperature\"", 82 | "only_one_E": "You must assign only one \"Energy current\" value", 83 | "only_one_Volume": "You must assign only one \"Volume\" value", 84 | "only_one_DT": "You must assign only one \"DT_FS\" value", 85 | "no_key": "No keys selected", 86 | "no_key_t": "ou must select almost one header key!", 87 | "units_loaded": "units loaded from input file ({})", 88 | "read_error": "Read error", 89 | "read_error_t": "Unable to read this file", 90 | "temp_low": "Temperature can't be less than 0", 91 | "temp_void": "Temperature can't be void", 92 | "vol_low": "Volume cant be less than 0", 93 | "vol_void": "Volume cant be void", 94 | "DT_low": "DT_FS can't be less than 0", 95 | "DT_void": "DT_FS can't be void", 96 | "fw_low": "Filter width can't be less than 0", 97 | "fw_void": "Filter width can't be void", 98 | "automatic_T": "The temperature will be automatically calculated", 99 | "automatic_V": "The volume will be automatically calculated", 100 | "automatic_DT": "The DT_FS will be automatically calculated", 101 | "back_reset": "Go back?", 102 | "back_reset_t": "Save changes?\nIf reopen the same file\nthe values that you chosed will not be deleted!", 103 | "fs_loaded": "F* loaded from input file ({})", 104 | "Errors": {} 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /sportran_gui/assets/window_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sissaschool/sportran/677d2bb5108ba05906cb3aa580d3a974efc77793/sportran_gui/assets/window_icon.ico -------------------------------------------------------------------------------- /sportran_gui/core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __all__ = ['control_unit', 'settings'] 4 | 5 | from . import * 6 | -------------------------------------------------------------------------------- /sportran_gui/core/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | =========================================== 4 | Sportran graphic user interface 5 | =========================================== 6 | --------------------------- 7 | Settings file 8 | --------------------------- 9 | 10 | This file contains the main settings and preferences of the Sportran GUI 11 | ''' 12 | 13 | import os 14 | 15 | # -------- FILE SETTINGS -------- 16 | 17 | BASE_PATH = '.' 18 | DATA_PATH = '' 19 | OUTPUT_PATH = '' 20 | LOG_PATH = '' 21 | ASSETS_PATH = os.path.join(BASE_PATH, 'assets') 22 | 23 | # todo: Add/Remove extensions 24 | FILE_EXTENSIONS = ['dat', 'log', 'txt', 'bin', 'npy'] 25 | 26 | # -------- GUI SETTINGS -------- 27 | 28 | STATUS = ['Loading', 'Configuring', 'Calculating', 'Idle', 'Sleep'] 29 | STATUS_NOW = STATUS[0] 30 | PREVIEW_LINES = 10 31 | OVERWRITE = True 32 | 33 | LANGUAGE = 'en-EN' 34 | 35 | X_RESIZE = True 36 | Y_RESIZE = True 37 | 38 | X_SIZE = 1240 39 | Y_SIZE = 720 40 | 41 | X_SPACING = 300 42 | Y_SPACING = 200 43 | 44 | # -------- GRAPHIC SETTINGS -------- 45 | 46 | BG_COLOR = '#ffffff' 47 | FONT = 'Arial' 48 | FONT_SIZE = 11 49 | -------------------------------------------------------------------------------- /sportran_gui/interfaces/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from sportran_gui.interfaces.fileManager import FileManager 4 | from sportran_gui.interfaces.headerSelector import HeaderSelector 5 | from sportran_gui.interfaces.otherVariables import OtherVariables 6 | from sportran_gui.interfaces.fstarSelector import FStarSelector 7 | from sportran_gui.interfaces.pstarSelector import PStarSelector 8 | -------------------------------------------------------------------------------- /sportran_gui/interfaces/fstarSelector.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from sportran_gui.utils.custom_widgets import * 4 | 5 | 6 | class FStarSelector(Frame): 7 | 8 | def __init__(self, parent, main): 9 | Frame.__init__(self, parent) 10 | 11 | self.main = main 12 | 13 | self.next_frame = None 14 | self.prev_frame = None 15 | 16 | self.parent = parent 17 | self.main_frame_scroll = ScrollFrame(self, self) 18 | self.main_frame = self.main_frame_scroll.viewPort 19 | 20 | self.filter_width_value = DoubleVar() 21 | # self.main_frame.grid(column=0, row=0, sticky='nsew') 22 | 23 | self.sections = Frame(self.main_frame, pady=20) 24 | self.sections.grid(row=0, column=0, sticky='nsew') 25 | 26 | self.graph = GraphWidget(self.sections, self.sections, size=(7, 4), toolbar=True) 27 | 28 | self.slider_locked = False 29 | 30 | slider_frame = Frame(self.sections) 31 | slider_frame.pack(side=TOP, anchor='w', padx=20, fill=BOTH) 32 | 33 | Label(slider_frame, text=LANGUAGES[settings.LANGUAGE]['fslide'], 34 | font='Arial 12').grid(row=0, column=1, sticky='w', padx=20) 35 | 36 | self.slider = ttk.Scale(slider_frame, from_=0, to_=0.1) 37 | self.slider.grid(row=1, column=1, sticky='we', columnspan=1, padx=20, pady=5) 38 | slider_frame.columnconfigure(0, weight=2) 39 | slider_frame.columnconfigure(1, weight=11) 40 | slider_frame.columnconfigure(2, weight=2) 41 | 42 | slider_options_frame = Frame(slider_frame) 43 | slider_options_frame.grid(row=2, column=1, sticky='w', padx=20, pady=2) 44 | 45 | lock_slider = Button(slider_options_frame, command=lambda: self._lock_unlock_slider(), bd=1, relief=SOLID) 46 | lock_slider.grid(row=0, column=0, padx=2, sticky='w') 47 | 48 | self.change_view_button = Button(slider_options_frame, text='Zoom-in', command=lambda: self._change_view()) 49 | self.change_view_button.grid(row=0, column=1, sticky='w', padx=2) 50 | 51 | value_frame = Frame(self.sections) 52 | value_frame.pack(side=TOP, pady=10, padx=20, fill=BOTH, expand=1) 53 | 54 | Label(value_frame, text=LANGUAGES[settings.LANGUAGE]['stp4'], 55 | font='Arial 12 bold').grid(row=0, column=0, sticky='w') 56 | 57 | ttk.Separator(value_frame, orient=HORIZONTAL).grid(row=0, column=0, sticky='we', columnspan=4, pady=5, padx=20) 58 | 59 | Label(value_frame, text=LANGUAGES[settings.LANGUAGE]['slct_v'] + ': ')\ 60 | .grid(row=1, column=0, sticky='w', pady=4, padx=20) 61 | 62 | self.value_entry = Entry(value_frame, bd=1, relief=SOLID) 63 | self.value_entry.grid(row=1, column=1, sticky='we', padx=20) 64 | 65 | self.graph.attach_entry(self.value_entry) 66 | self.graph.attach_slider(self.slider) 67 | 68 | value_frame.columnconfigure(1, weight=1, minsize=150) 69 | value_frame.columnconfigure(2, weight=1, minsize=10) 70 | value_frame.columnconfigure(3, weight=1, minsize=300) 71 | value_frame.columnconfigure(4, weight=1, minsize=150) 72 | 73 | Label(value_frame, text=LANGUAGES[settings.LANGUAGE]['fl_w']+': ')\ 74 | .grid(row=2, column=0, sticky='w', padx=20) 75 | self.filter_width = Spinbox(value_frame, from_=0.1, to=10, increment=0.1, bd=1, relief=SOLID, 76 | textvariable=self.filter_width_value) 77 | self.filter_width.grid(row=2, column=1, sticky='we', pady=10, padx=20) 78 | 79 | self.fstar_screen = Label(value_frame, text='F*: ', font='Arial 14 bold', width=20, bd=1, relief=SOLID) 80 | self.fstar_screen.grid(row=1, column=3, sticky='we', padx=50) 81 | 82 | Button(value_frame, text=LANGUAGES[settings.LANGUAGE]['resample'], font='Arial 12 bold', bd=1, relief=SOLID, 83 | command=self.resample, width=20).grid(row=2, column=3, sticky='wens', rowspan=1, padx=50) 84 | 85 | value_frame.rowconfigure(3, weight=1) 86 | button_frame = Frame(self.main_frame) 87 | button_frame.grid(row=3, column=0, padx=20, pady=10, sticky='w') 88 | 89 | back_button = Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['back'], bd=1, relief=SOLID, 90 | command=lambda: self.back(), width=10) 91 | back_button.grid(row=0, column=0, sticky='we', padx=5) 92 | 93 | next_button = Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['next'], bd=1, relief=SOLID, 94 | command=lambda: self.next(), width=10) 95 | next_button.grid(row=0, column=1, sticky='we', padx=5) 96 | 97 | self.main_frame.rowconfigure(0, weight=1) 98 | self.main_frame.columnconfigure(0, weight=1) # , minsize=720) 99 | 100 | self.setted = False 101 | 102 | if cu.info: 103 | cu.update_info(cu.info) 104 | 105 | def _lock_unlock_slider(self, force=False): 106 | if self.slider_locked or not force: 107 | self.slider_locked = False 108 | self.slider.state(['!disabled']) 109 | self.value_entry.config(state=NORMAL) 110 | else: 111 | self.slider_locked = True 112 | self.slider.state(['disabled']) 113 | self.value_entry.config(state=DISABLED) 114 | 115 | def _change_view(self): 116 | if self.graph.cut_line > 0: 117 | if self.graph.show_selected_area: 118 | self.graph.show_selected_area = False 119 | self.change_view_button.config(text='Zoom-in') 120 | else: 121 | self.graph.show_selected_area = True 122 | self.change_view_button.config(text=LANGUAGES[settings.LANGUAGE]['reset_view']) 123 | 124 | self.graph.change_view() 125 | self.graph.update_cut() 126 | 127 | def resample(self): 128 | cu.data.fstar = float(self.value_entry.get()) 129 | cu.data.psd_filter_width = float(self.filter_width_value.get()) 130 | if cu.data.fstar > 0: 131 | if cu.data.changes: 132 | cu.data.xf = cu.data.j.resample(fstar_THz=cu.data.fstar, PSD_FILTER_W=cu.data.psd_filter_width, 133 | plot=False) 134 | self.graph.add_graph(cu.gm.plot_resample, 'resample', xf=cu.data.xf, mode='linear', x=cu.data.j, 135 | PSD_FILTER_W=cu.data.psd_filter_width) 136 | self.graph.update_cut() 137 | 138 | self.graph.cut_line = cu.data.xf.Nyquist_f_THz 139 | self.fstar_screen.config(text='F*: {}'.format(round(cu.data.xf.Nyquist_f_THz, 3))) 140 | cu.data.changes = False 141 | else: 142 | msg.showwarning('Value error', 'F* must be greater than zero') 143 | return False 144 | 145 | if self.graph.show_selected_area: 146 | self.graph.show_selected_area = True 147 | self.graph.change_view() 148 | 149 | self.update() 150 | return True 151 | 152 | def set_next_frame(self, frame): 153 | self.next_frame = frame 154 | 155 | def set_prev_frame(self, frame): 156 | self.prev_frame = frame 157 | 158 | def back(self): 159 | cu.data.psd_filter_width = self.filter_width_value.get() 160 | cu.data.fstar = float(self.value_entry.get()) 161 | cu.Data.loaded = True 162 | if self.prev_frame: 163 | self.main.show_frame(self.prev_frame) 164 | else: 165 | raise ValueError('Prev frame isn\'t defined') 166 | 167 | def next(self): 168 | if (self.resample()): 169 | #cu.data.fstar = cu.data.xf.Nyquist_f_THz 170 | 171 | if self.next_frame: 172 | self.main.show_frame(self.next_frame) 173 | else: 174 | raise ValueError('Next frame isn\'t defined') 175 | 176 | def recalculate(self, slider_config=None): 177 | cu.data.psd_filter_width = float(self.filter_width_value.get()) 178 | self.graph.other_graph.clear() 179 | self.graph.graph.clear() 180 | self.graph.show(cu.gm.plot_periodogram, mode='linear', current=cu.data.j, PSD_FILTER_W=cu.data.psd_filter_width, 181 | slider_config=slider_config, kappa_units=True) 182 | if float(self.value_entry.get()): 183 | print('resampled') 184 | self.resample() 185 | if cu.info: 186 | cu.update_info(cu.info) 187 | 188 | def update_data(self): 189 | self.filter_width_value.set(cu.data.psd_filter_width) 190 | 191 | def update(self): 192 | super().update() 193 | cu.data.fstar = float(self.value_entry.get()) 194 | 195 | if cu.data.first_fstar and '_FSTAR' in cu.data.jdata.keys(): 196 | cu.data.fstar = cu.data.jdata['_FSTAR'] 197 | self.value_entry.delete(0, END) 198 | self.value_entry.insert(0, cu.data.fstar) 199 | cu.log.write_log(LANGUAGES[settings.LANGUAGE]['fs_loaded'].format(cu.data.jdata['_FSTAR'])) 200 | self.recalculate(slider_config=cu.data.fstar) 201 | elif cu.data.changes: 202 | self.recalculate() 203 | 204 | if cu.info: 205 | cu.update_info(cu.info) 206 | 207 | self.main_frame_scroll.update_view() 208 | -------------------------------------------------------------------------------- /sportran_gui/interfaces/headerSelector.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from tkinter import messagebox as msg 4 | from sportran_gui.utils.custom_widgets import * 5 | from sportran_gui.core.control_unit import Current, select_current 6 | import sportran as st 7 | import traceback 8 | 9 | 10 | class HeaderSelector(Frame): 11 | 12 | def __init__(self, parent, main): 13 | Frame.__init__(self, parent) 14 | 15 | self.main = main 16 | self.next_frame = None 17 | self.prev_frame = None 18 | 19 | self.main_frame = ScrollFrame(self, self) 20 | 21 | header_frame = self.main_frame.viewPort 22 | #header_frame.grid(row=0, column=0, sticky='nswe', padx=20, pady=5) 23 | 24 | Label(header_frame, text=LANGUAGES[settings.LANGUAGE]['stp2'], 25 | font='Arial 14 bold').grid(row=0, column=0, sticky='w') 26 | 27 | definitions_frame = Frame(header_frame) 28 | definitions_frame.grid(row=1, column=1, sticky='wn', padx=20) 29 | Label(definitions_frame, text=LANGUAGES[settings.LANGUAGE]['df1'], wraplengt=500, justify=LEFT)\ 30 | .grid(row=0, column=0, sticky='wn') 31 | Label(definitions_frame, text=LANGUAGES[settings.LANGUAGE]['df2'], wraplengt=500, justify=LEFT)\ 32 | .grid(row=1, column=0, sticky='wn') 33 | # todo: put definition 34 | Label(definitions_frame, text=LANGUAGES[settings.LANGUAGE]['df3'], wraplengt=500, justify=LEFT)\ 35 | .grid(row=2, column=0, sticky='wn') 36 | Label(definitions_frame, text=LANGUAGES[settings.LANGUAGE]['df4'], wraplengt=500, justify=LEFT)\ 37 | .grid(row=3, column=0, sticky='wn') 38 | 39 | definitions_frame.columnconfigure(0, weight=1) 40 | for i in range(0, 3): 41 | definitions_frame.rowconfigure(i, weight=1) 42 | 43 | header_list_frame = Frame(header_frame) 44 | header_list_frame.grid(row=1, column=0, sticky='nswe', pady=10) 45 | 46 | self.scrollable_header_list = ScrollFrame(header_list_frame, header_list_frame, bd=1) 47 | self.check_list = CheckList(self.scrollable_header_list.viewPort, self.scrollable_header_list.viewPort, 48 | start_row=1) 49 | 50 | Label(header_frame, text=LANGUAGES[settings.LANGUAGE]['select_units'], font='Arial 14 bold') \ 51 | .grid(row=2, column=0, sticky='w', pady=10) 52 | self.units_selector_frame = Frame(header_frame) 53 | self.current_selector_value = StringVar() 54 | 55 | def on_current_sel(index, value, op): 56 | current_type = self.current_selector_value.get() 57 | select_current(current_type) 58 | self.units_selector['values'] = st.current.all_currents[current_type][1] 59 | self.units_selector.current(0) 60 | 61 | self.current_selector_value.trace('w', on_current_sel) 62 | self.current_selector = ttk.Combobox(self.units_selector_frame, values=list(st.current.all_currents.keys()), 63 | state='readonly', textvar=self.current_selector_value) 64 | self.units_selector = ttk.Combobox(self.units_selector_frame, values=[], state='readonly') 65 | Label(self.units_selector_frame, 66 | text=LANGUAGES[settings.LANGUAGE]['units']).grid(row=0, column=0, sticky='we', pady=2) 67 | self.current_selector.grid(row=0, column=1, sticky='we', pady=2) 68 | self.units_selector.grid(row=1, column=1, sticky='we', pady=2) 69 | self.units_selector_frame.grid(row=3, column=0, sticky='nswe', pady=2) 70 | 71 | button_frame = Frame(header_frame) 72 | button_frame.grid(row=4, column=0, sticky='w') 73 | 74 | header_frame.config(padx=20) 75 | header_frame.rowconfigure(0, weight=1, minsize=50) 76 | header_frame.rowconfigure(1, weight=10, minsize=150) 77 | header_frame.rowconfigure(2, weight=1, minsize=50) 78 | header_frame.rowconfigure(3, weight=10, minsize=50) 79 | header_frame.rowconfigure(4, weight=1, minsize=50) 80 | header_frame.columnconfigure(0, weight=1, minsize=300) 81 | header_frame.columnconfigure(1, weight=1, minsize=500) 82 | 83 | Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['back'], bd=1, relief=SOLID, font='Arial 12', 84 | command=lambda: self.back(), width=10).grid(row=0, column=0, sticky='we') 85 | 86 | Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['next'], bd=1, relief=SOLID, font='Arial 12', 87 | command=lambda: self.next(), width=10).grid(row=0, column=1, padx=5, sticky='we') 88 | 89 | def set_next_frame(self, frame): 90 | self.next_frame = frame 91 | 92 | def set_prev_frame(self, frame): 93 | self.prev_frame = frame 94 | 95 | def next(self): 96 | keys, description = self.check_list.get_list() 97 | if cu.Data.options[1] in description: #energy current 98 | if description.count(cu.Data.options[1]) == 1: 99 | if description.count(cu.Data.options[3]) <= 1: #temperature 100 | if description.count(cu.Data.options[4]) <= 1: #volume 101 | if description.count(cu.Data.options[5]) <= 1: #DT 102 | cu.data.keys = keys 103 | cu.data.description = description 104 | cu.Data.loaded = True 105 | cu.data.units = self.units_selector.get() 106 | cu.data.current_type = self.current_selector.get() 107 | if self.next_frame: 108 | self.main.show_frame(self.next_frame) 109 | else: 110 | raise ValueError('Next frame isn\'t defined') 111 | else: 112 | msg.showerror(LANGUAGES[settings.LANGUAGE]['value_error'], 113 | LANGUAGES[settings.LANGUAGE]['only_one_DT']) 114 | else: 115 | msg.showerror(LANGUAGES[settings.LANGUAGE]['value_error'], 116 | LANGUAGES[settings.LANGUAGE]['only_one_Volume']) 117 | else: 118 | msg.showerror(LANGUAGES[settings.LANGUAGE]['value_error'], 119 | LANGUAGES[settings.LANGUAGE]['only_one_T']) 120 | else: 121 | msg.showerror(LANGUAGES[settings.LANGUAGE]['value_error'], LANGUAGES[settings.LANGUAGE]['only_one_E']) 122 | else: 123 | msg.showerror(LANGUAGES[settings.LANGUAGE]['no_key'], LANGUAGES[settings.LANGUAGE]['no_key_t']) 124 | 125 | def back(self): 126 | response = msg.askyesno(LANGUAGES[settings.LANGUAGE]['back_reset'], 127 | LANGUAGES[settings.LANGUAGE]['back_reset_t']) 128 | 129 | if response: 130 | keys, description = self.check_list.get_list() 131 | cu.data.keys = keys 132 | cu.data.description = description 133 | if self.prev_frame: 134 | self.main.show_frame(self.prev_frame) 135 | else: 136 | raise ValueError('Prev frame isn\'t defined') 137 | 138 | elif response == 0: 139 | # cu.data.changes = False 140 | cu.data.fstar = 0.0 141 | cu.Data.loaded = False 142 | cu.data.temperature = 0.1 143 | cu.data.volume = 0.1 144 | cu.data.DT_FS = 0.1 145 | cu.data.psd_filter_width = 0.1 146 | if self.prev_frame: 147 | self.main.show_frame(self.prev_frame) 148 | else: 149 | raise ValueError('Prev frame isn\'t defined') 150 | else: 151 | pass 152 | 153 | def update_data(self): 154 | pass 155 | 156 | def update(self): 157 | super().update() 158 | self.main_frame.update_view() 159 | 160 | try: 161 | print('cu.Data.loaded={}'.format(cu.Data.loaded)) 162 | if not cu.Data.loaded: 163 | keys = cu.load_keys(cu.data.CURRENT_FILE) 164 | else: 165 | keys = {key: i for i, key in enumerate(cu.data.keys) if key[0] != '_'} 166 | 167 | print(cu.data.jdata) 168 | self.check_list.set_list(keys) 169 | 170 | #try to set units (if given) 171 | try: 172 | if '_CURRENT' in cu.data.jdata: 173 | current_type = cu.data.jdata['_CURRENT'] 174 | else: 175 | current_type = 'heat' 176 | select_current(current_type) 177 | self.current_selector_value.set(current_type) 178 | self.units_selector.current(st.current.all_currents[current_type][1].index(cu.data.jdata['_UNITS'])) 179 | cu.log.write_log(LANGUAGES[settings.LANGUAGE]['units_loaded'].format(cu.data.jdata['_UNITS'])) 180 | except BaseException as e: 181 | print(e) 182 | try: 183 | self.current_selector.current(list(st.current.all_currents.keys()).index(cu.data.current_type)) 184 | self.units_selector.current(st.current.all_currents[cu.data.current_type][1].index(cu.data.units)) 185 | except BaseException as e: 186 | print(e) 187 | pass 188 | 189 | if cu.Data.loaded: 190 | for i, check in enumerate(self.check_list.controller.winfo_children()): 191 | check.winfo_children()[1].current(cu.Data.options.index(cu.data.description[i])) 192 | except Exception as e: 193 | cu.Data.loaded = False 194 | cu.log.write_log(str(e)) 195 | traceback.print_exc() 196 | msg.showerror(LANGUAGES[settings.LANGUAGE]['read_error'], LANGUAGES[settings.LANGUAGE]['read_error_t']) 197 | if self.prev_frame: 198 | self.main.show_frame(self.prev_frame) 199 | else: 200 | raise ValueError('Prev frame isn\'t defined') 201 | -------------------------------------------------------------------------------- /sportran_gui/interfaces/otherVariables.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from tkinter.ttk import Separator 4 | from tkinter import messagebox as msg 5 | from sportran_gui.utils.custom_widgets import * 6 | from sportran_gui.assets import LANGUAGES 7 | 8 | 9 | class OtherVariables(Frame): 10 | 11 | def __init__(self, parent, main): 12 | Frame.__init__(self, parent) 13 | 14 | self.main = main 15 | self.next_frame = None 16 | self.prev_frame = None 17 | 18 | self.main_frame = ScrollFrame(self, self) 19 | self.main_frame.grid(row=0, column=0, sticky='nswe') 20 | 21 | self.temperature_value = DoubleVar(value=0.1) 22 | self.volume_value = DoubleVar(value=0.1) 23 | self.DT_FS_value = DoubleVar(value=0.1) 24 | self.filter_width_value = DoubleVar(value=0.1) 25 | 26 | variable_frame = Frame(self.main_frame.viewPort) 27 | 28 | variable_frame.grid(column=0, row=0, sticky='nswe', padx=20, pady=5) 29 | 30 | Label(variable_frame, text=LANGUAGES[settings.LANGUAGE]['stp3'], 31 | font='Arial 14 bold').grid(row=0, column=0, pady=2, sticky='w') 32 | 33 | variable_frame.columnconfigure(0, weight=0, minsize=150) 34 | variable_frame.columnconfigure(1, weight=1, minsize=200) 35 | 36 | Label(variable_frame, text=LANGUAGES[settings.LANGUAGE]['e_v'], 37 | font='Arial 11').grid(row=1, column=0, pady=20, sticky='w') 38 | Separator(variable_frame, orient=HORIZONTAL).grid(row=2, sticky='we', columnspan=3) 39 | 40 | Label(variable_frame, text=LANGUAGES[settings.LANGUAGE]['tmp'] + ' (K):').grid(row=3, column=0, sticky='w') 41 | self.temperature_entry = Spinbox(variable_frame, from_=0.1, to=10000, increment=0.1, bd=1, relief=SOLID, 42 | textvariable=self.temperature_value) 43 | self.temperature_entry.grid(row=3, column=1, padx=2, sticky='w', pady=10) 44 | self.temp_advertise = Label(variable_frame, text='', font='Arial 10') 45 | self.temp_advertise.grid(row=3, column=2, sticky='w') 46 | 47 | Label(variable_frame, 48 | text=LANGUAGES[settings.LANGUAGE]['volume'] + ' (Angstrom^3):').grid(row=4, column=0, sticky='w') 49 | self.volume_entry = Spinbox(variable_frame, from_=0.1, to=10000, increment=0.1, bd=1, relief=SOLID, 50 | textvariable=self.volume_value) 51 | self.volume_entry.grid(row=4, column=1, padx=2, sticky='w') 52 | self.volume_advertise = Label(variable_frame, text='', font='Arial 10') 53 | self.volume_advertise.grid(row=4, column=2, sticky='w') 54 | 55 | Label(variable_frame, 56 | text=LANGUAGES[settings.LANGUAGE]['timestep'] + ' (fs):').grid(row=5, column=0, sticky='w') 57 | self.DT_FS_entry = Spinbox(variable_frame, from_=0.1, to=10000, increment=0.1, bd=1, relief=SOLID, 58 | textvariable=self.DT_FS_value) 59 | self.DT_FS_entry.grid(row=5, column=1, padx=2, sticky='w', pady=10) 60 | self.DT_FS_advertise = Label(variable_frame, text='', font='Arial 10') 61 | self.DT_FS_advertise.grid(row=5, column=2, sticky='w') 62 | 63 | Label(variable_frame, text=LANGUAGES[settings.LANGUAGE]['fl_v'], 64 | font='Arial 11').grid(row=6, column=0, pady=20, sticky='w') 65 | Separator(variable_frame, orient=HORIZONTAL).grid(row=7, sticky='we', columnspan=3) 66 | 67 | Label(variable_frame, text=LANGUAGES[settings.LANGUAGE]['fl_w'] + ' (THz):').grid(row=8, column=0, sticky='w') 68 | self.filter_width_entry = Spinbox(variable_frame, from_=0.1, to=10.0, increment=0.1, bd=1, relief=SOLID, 69 | textvariable=self.filter_width_value) 70 | self.filter_width_entry.grid(row=8, column=1, padx=2, sticky='w', pady=10) 71 | 72 | variable_frame.rowconfigure(9, weight=1) 73 | button_frame = Frame(variable_frame) 74 | button_frame.grid(row=9, column=0, sticky='ws', pady=20) 75 | 76 | Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['back'], bd=1, relief=SOLID, font='Arial 12', 77 | command=lambda: self.back(), width=10).grid(row=0, column=0, sticky='we') 78 | 79 | Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['next'], bd=1, relief=SOLID, font='Arial 12', 80 | command=lambda: self.next(), width=10).grid(row=0, column=1, padx=5, sticky='we') 81 | 82 | self.update_data() 83 | 84 | def set_next_frame(self, frame): 85 | self.next_frame = frame 86 | 87 | def set_prev_frame(self, frame): 88 | self.prev_frame = frame 89 | 90 | def get_entry_data(self): 91 | if self.filter_width_value.get(): 92 | cu.data.psd_filter_width = float(self.filter_width_value.get()) 93 | if self.temperature_value.get(): 94 | cu.data.temperature = float(self.temperature_value.get()) 95 | if self.volume_value.get(): 96 | cu.data.volume = float(self.volume_value.get()) 97 | if self.DT_FS_value.get(): 98 | cu.data.DT_FS = float(self.DT_FS_value.get()) 99 | 100 | def next(self): 101 | self.get_entry_data() 102 | 103 | er = False 104 | msgs = [] 105 | if cu.data.temperature: 106 | if cu.data.temperature < 0 and 'Temperature' not in cu.data.description: 107 | msgs.append(LANGUAGES[settings.LANGUAGE]['temp_low']) 108 | er = True 109 | else: 110 | msgs.append(LANGUAGES[settings.LANGUAGE]['temp_void']) 111 | 112 | if cu.data.volume: 113 | if cu.data.volume < 0: 114 | msgs.append(LANGUAGES[settings.LANGUAGE]['vol_low']) 115 | er = True 116 | else: 117 | msgs.append(LANGUAGES[settings.LANGUAGE]['vol_void']) 118 | er = True 119 | 120 | if cu.data.DT_FS: 121 | if cu.data.DT_FS < 0: 122 | msgs.append(LANGUAGES[settings.LANGUAGE]['DT_low']) 123 | er = True 124 | else: 125 | msgs.append(LANGUAGES[settings.LANGUAGE]['DT_void']) 126 | er = True 127 | 128 | if cu.data.psd_filter_width: 129 | if cu.data.psd_filter_width < 0: 130 | msgs.append(LANGUAGES[settings.LANGUAGE]['fw_low']) 131 | er = True 132 | else: 133 | msgs.append(LANGUAGES[settings.LANGUAGE]['fw_void']) 134 | er = True 135 | 136 | if not er: 137 | cu.load_data(cu.data.CURRENT_FILE, cu.data.inputformat, _selected_keys=cu.data.keys, 138 | _descriptions=cu.data.description, temperature=cu.data.temperature, units=cu.data.units, 139 | volume=cu.data.volume, psd_filter_w=cu.data.psd_filter_width, DT_FS=cu.data.DT_FS) 140 | 141 | if self.next_frame: 142 | self.main.show_frame(self.next_frame) 143 | else: 144 | raise ValueError('Next frame isn\'t defined') 145 | else: 146 | ermsg = '\n'.join(mser for mser in msgs) 147 | msg.showerror(LANGUAGES[settings.LANGUAGE]['value_error'], ermsg) 148 | 149 | def back(self): 150 | self.get_entry_data() 151 | 152 | if self.prev_frame: 153 | self.main.show_frame(self.prev_frame) 154 | else: 155 | raise ValueError('Prev frame isn\'t defined') 156 | 157 | def update_data(self): 158 | self.temperature_value.set(cu.data.temperature) 159 | self.DT_FS_value.set(cu.data.DT_FS) 160 | self.volume_value.set(cu.data.volume) 161 | self.filter_width_value.set(cu.data.psd_filter_width) 162 | 163 | def update(self): 164 | super().update() 165 | 166 | self.temperature_entry.config(state=NORMAL) 167 | self.DT_FS_entry.config(state=NORMAL) 168 | self.volume_entry.config(state=NORMAL) 169 | 170 | if cu.Data.options[3] in cu.data.description: 171 | self.temp_advertise.config(text=LANGUAGES[settings.LANGUAGE]['automatic_T'], fg='red') 172 | if cu.data.inputformat == 'dict': #only for dict I have the data here 173 | temp = cu.data.jdata[cu.data.keys[cu.data.description.index(cu.Data.options[3])]] 174 | if type(temp) == float: 175 | cu.data.temperature = temp 176 | else: 177 | cu.data.temperature = temp.mean() 178 | cu.data.temperature_std = temp.std() 179 | self.temperature_entry.config(state=DISABLED) 180 | else: 181 | self.temperature_entry.config(state=NORMAL) 182 | self.temp_advertise.config(text='') 183 | if cu.Data.options[4] in cu.data.description: 184 | self.volume_advertise.config(text=LANGUAGES[settings.LANGUAGE]['automatic_V'], fg='red') 185 | if cu.data.inputformat == 'dict': #only for dict I have the data here 186 | vol = cu.data.jdata[cu.data.keys[cu.data.description.index(cu.Data.options[4])]] 187 | if type(vol) == float: 188 | cu.data.volume = vol 189 | else: 190 | cu.data.volume = vol.mean() 191 | else: 192 | raise RuntimeError('NOT IMPLEMENTED') 193 | self.volume_entry.config(state=DISABLED) 194 | else: 195 | self.volume_advertise.config(text='') 196 | self.volume_entry.config(state=NORMAL) 197 | if cu.Data.options[5] in cu.data.description: 198 | self.DT_FS_advertise.config(text=LANGUAGES[settings.LANGUAGE]['automatic_DT'], fg='red') 199 | if cu.data.inputformat == 'dict': #only for dict I have the data here 200 | cu.data.DT_FS = cu.data.jdata[cu.data.keys[cu.data.description.index(cu.Data.options[5])]] 201 | else: 202 | raise RuntimeError('NOT IMPLEMENTED') 203 | self.DT_FS_entry.config(state=DISABLED) 204 | else: 205 | self.DT_FS_advertise.config(text='') 206 | self.DT_FS_entry.config(state=NORMAL) 207 | 208 | self.update_data() 209 | 210 | self.main_frame.viewPort.columnconfigure(0, weight=1) 211 | self.main_frame.viewPort.rowconfigure(0, weight=1) 212 | self.main_frame.update_view() 213 | -------------------------------------------------------------------------------- /sportran_gui/interfaces/pstarSelector.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from sportran_gui.utils.custom_widgets import * 4 | from sportran_gui.core import control_unit as cu 5 | 6 | INDENT = 0 7 | 8 | 9 | def print_name(func): 10 | 11 | def inner(*args, **kwargs): 12 | global INDENT 13 | print('{}BEGIN {}'.format(' ' * INDENT, func.__name__)) 14 | INDENT = INDENT + 1 15 | func(*args, **kwargs) 16 | INDENT = INDENT - 1 17 | print('{}END {}'.format(' ' * INDENT, func.__name__)) 18 | 19 | return inner 20 | 21 | 22 | class PStarSelector(Frame): 23 | 24 | def __init__(self, parent, main): 25 | Frame.__init__(self, parent) 26 | 27 | self.main = main 28 | 29 | self.next_frame = None 30 | self.prev_frame = None 31 | 32 | self.parent = parent 33 | self.main_frame_scroll = ScrollFrame(self, self) 34 | self.main_frame = self.main_frame_scroll.viewPort 35 | 36 | #self.main_frame.grid(column=0, row=0, sticky='nsew') 37 | 38 | sections = Frame(self.main_frame) 39 | sections.grid(row=0, column=0, sticky='nsew', padx=20, columnspan=2) 40 | 41 | self.graph = GraphWidget(sections, sections, size=(7, 4), toolbar=True) 42 | 43 | self.container_frame = Frame(self.main_frame) 44 | self.container_frame.grid(row=1, column=0, sticky='nsew') 45 | 46 | variable_frame = Frame(self.container_frame, bd=1, relief=SOLID) 47 | variable_frame.pack(side=TOP, anchor='w', padx=20, fill='x', expand=1, pady=20) 48 | 49 | self.fstar_label = Label(variable_frame, text='', font=('Arial 24')) 50 | self.fstar_label.grid(row=0, column=0, sticky='w') 51 | self.kmin_label = Label(variable_frame, text='', font=('Arial 24')) 52 | self.kmin_label.grid(row=1, column=0, sticky='w') 53 | 54 | value_frame = Frame(self.container_frame) 55 | value_frame.pack(side=TOP, anchor='w', padx=20, fill=BOTH, expand=1) 56 | 57 | Label(value_frame, text=LANGUAGES[settings.LANGUAGE]['stp5'], 58 | font='Arial 12 bold').grid(row=0, column=0, sticky='w') 59 | ttk.Separator(value_frame, orient=HORIZONTAL).grid(row=1, column=0, sticky='we', pady=10, columnspan=5) 60 | 61 | Label(value_frame, text=LANGUAGES[settings.LANGUAGE]['pstar_cmt'], 62 | font='Arial 12').grid(row=2, column=0, sticky='w') 63 | Label(value_frame, text='P*: ', font='Arial 12').grid(row=3, column=0, sticky='w') 64 | self.value_entry = Spinbox(value_frame, bd=1, relief=SOLID, increment=1) 65 | self.value_entry.grid(row=3, column=1, sticky='w') 66 | 67 | self.increment = IntVar() 68 | Label(value_frame, text=LANGUAGES[settings.LANGUAGE]['inc'], 69 | font='Arial 12').grid(row=4, column=0, sticky='w', pady=10) 70 | 71 | rdbt_frame = Frame(value_frame) 72 | rdbt_frame.grid(row=4, column=1, sticky='w') 73 | 74 | Radiobutton(rdbt_frame, text='1', font='Arial 11 bold', variable=self.increment, value=1, 75 | command=self._change_increment).pack(side=LEFT) 76 | Radiobutton(rdbt_frame, text='10', font='Arial 11 bold', variable=self.increment, value=10, 77 | command=self._change_increment).pack(side=LEFT) 78 | Radiobutton(rdbt_frame, text='100', font='Arial 11 bold', variable=self.increment, value=100, 79 | command=self._change_increment).pack(side=LEFT) 80 | 81 | Button(value_frame, text=LANGUAGES[settings.LANGUAGE]['recalculate'], font='Arial 12 bold', bd=1, relief=SOLID, 82 | command=self._recalc, width=20).grid(row=2, column=2, sticky='wens', rowspan=2, padx=50) 83 | 84 | value_frame.columnconfigure(0, weight=1, minsize=110) 85 | value_frame.columnconfigure(1, weight=1, minsize=150) 86 | value_frame.columnconfigure(2, weight=1, minsize=1) 87 | 88 | self.main_frame.rowconfigure(1, weight=1) 89 | button_frame = Frame(self.main_frame) 90 | button_frame.grid(row=2, column=0, sticky='w', padx=10, pady=20) 91 | 92 | back_button = Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['back'], bd=1, relief=SOLID, 93 | command=lambda: self.back(), width=10) 94 | back_button.grid(row=0, column=0, sticky='we', padx=5) 95 | 96 | new_a = Button(button_frame, text=LANGUAGES[settings.LANGUAGE]['new_a'], bd=1, relief=SOLID, 97 | command=lambda: cu.new(self.main.root), width=10) 98 | new_a.grid(row=0, column=1, sticky='we', padx=5) 99 | 100 | self.main_frame.columnconfigure(0, weight=1, minsize=500) 101 | 102 | self.setted = False 103 | 104 | if cu.info: 105 | cu.update_info(cu.info) 106 | 107 | def set_prev_frame(self, frame): 108 | self.prev_frame = frame 109 | 110 | def back(self): 111 | cu.data.pstar = int(self.value_entry.get()) 112 | if self.prev_frame: 113 | self.main.show_frame(self.prev_frame) 114 | else: 115 | raise ValueError('Prev frame isn\'t defined') 116 | 117 | def _get_pstar(self, aic_type='aic', cutoffK=None): 118 | cu.data.xf.cepstral_analysis(aic_type=aic_type, manual_cutoffK=((cutoffK - 1) if cutoffK is not None else None)) 119 | 120 | def _pstar(self): 121 | self.value_entry.config(from_=2, to=cu.data.xf.NFREQS) 122 | 123 | if cu.data.xf.cepf: 124 | self.value_entry.delete(0, END) 125 | self.value_entry.insert(0, (cu.data.xf.cepf.cutoffK + 1)) 126 | self.fstar_label.config(text='f*: {:.3f} P*: {}'.format(cu.data.fstar, cu.data.xf.cepf.cutoffK + 1)) 127 | self.kmin_label.config(text=u'\u03f0: {:18f} +/- {:8f} {}\n'.format(cu.data.xf.kappa, cu.data.xf.kappa_std, 128 | cu.data.xf._KAPPA_SI_UNITS)) 129 | 130 | def _change_increment(self): 131 | self.value_entry.config(increment=int(self.increment.get())) 132 | 133 | def _recalc(self): 134 | if self.value_entry.get(): 135 | kmin_c = int(self.value_entry.get()) 136 | else: 137 | kmin_c = 0 138 | self._get_pstar(aic_type='aic', cutoffK=kmin_c) 139 | self.graph.add_graph(cu.gm.plot_cepstral_spectrum, 'cepstral', mode='linear', current=cu.data.xf, 140 | kappa_units=True) 141 | xf = cu.data.xf 142 | self.graph.update_cut() 143 | cu.data.xf = xf 144 | self._pstar() 145 | 146 | def _setup_pstar(self): 147 | cu.data.xf.cepstral_analysis(aic_type='aic', manual_cutoffK=None) 148 | self._pstar() 149 | cu.data.pstar = int(self.value_entry.get()) 150 | 151 | def _draw_graph(self): 152 | self.graph.graph.clear() 153 | self.graph.show(cu.gm.plot_periodogram, current=cu.data.j, mode='linear', kappa_units=True) 154 | cu.data.xf = cu.data.j.resample(fstar_THz=cu.data.fstar, PSD_FILTER_W=cu.data.psd_filter_width, plot=False) 155 | self.graph.add_graph(cu.gm.plot_resample, 'resample', xf=cu.data.xf, mode='linear', x=cu.data.j, 156 | PSD_FILTER_W=cu.data.psd_filter_width) 157 | 158 | def recalculate(self): 159 | self._setup_pstar() 160 | self._draw_graph() 161 | self._recalc() 162 | 163 | if cu.info: 164 | cu.update_info(cu.info) 165 | 166 | def update_data(self): 167 | pass 168 | 169 | def update(self): 170 | super().update() 171 | 172 | self.setted = cu.data.recalc_pstar 173 | 174 | if self.setted: 175 | self.recalculate() 176 | 177 | if cu.info: 178 | cu.update_info(cu.info) 179 | 180 | self._recalc() 181 | self.graph.update_cut() 182 | 183 | self.main_frame_scroll.update_view() 184 | -------------------------------------------------------------------------------- /sportran_gui/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | =========================================== 4 | Sportran graphic user interface 5 | =========================================== 6 | 7 | Developers: Sebastiano Bisacchi, Riccardo Bertossa 8 | 9 | This file contains the GUI of the Sportran project developed at SISSA. 10 | """ 11 | 12 | # todo: Put an accurate description of the project? 13 | 14 | from sportran_gui.interfaces import * 15 | from sportran_gui.utils.custom_widgets import * 16 | from sportran_gui.assets import ICON, METADATA, LANGUAGES, dev_state 17 | # Verify that sportran is installed 18 | try: 19 | import sportran 20 | except ImportError: 21 | raise ImportError('Couldn\'t find sportran') 22 | 23 | 24 | # Main app 25 | class SportranGUI(Tk): 26 | """ 27 | This class is used to initialize all 28 | the interfaces and to setup the multi frame functionality. 29 | 30 | SportranGUI is a subclass of Tk that is the root window. 31 | """ 32 | 33 | # Class variables to store some main parameters 34 | open_windows = [] 35 | frames = [] 36 | frame = None 37 | root = None 38 | container = None 39 | home = FileManager 40 | 41 | def __init__(self, *args, **kwargs): 42 | Tk.__init__(self, *args, **kwargs) 43 | 44 | SportranGUI.root = self 45 | 46 | # Setup the default colors and font to use in the interface 47 | self.option_add('*Font', '{} {}'.format(settings.FONT, settings.FONT_SIZE)) 48 | self.option_add('*Background', '{}'.format(settings.BG_COLOR)) 49 | self.option_add('*selectBackground', 'light blue') 50 | self.option_add('*selectForeground', 'black') 51 | 52 | self.show_software_info() 53 | 54 | # Add the main window to the open windows 55 | SportranGUI.open_windows.insert(0, self) 56 | 57 | # Configure the window 58 | 59 | window_icon = PhotoImage(master=self, data=ICON) 60 | self.iconphoto(True, window_icon) 61 | #self.tk.call('wm', 'iconphoto', self._w, window_icon) 62 | #self.iconbitmap(bitmap=window_icon) 63 | self.title('Sportran') 64 | self.geometry('{}x{}+{}+{}'.format(settings.X_SIZE, settings.Y_SIZE, settings.X_SPACING, settings.Y_SPACING)) 65 | 66 | self.resizable(settings.X_RESIZE, settings.Y_RESIZE) 67 | 68 | # Define the exit function 69 | self.protocol('WM_DELETE_WINDOW', func=lambda: cu.secure_exit(SportranGUI)) 70 | 71 | # Creating the main frame 72 | container = Frame(self) 73 | SportranGUI.container = container 74 | container.grid(row=0, column=0, sticky='nsew') 75 | 76 | self.grid_rowconfigure(0, weight=1) 77 | self.grid_columnconfigure(0, weight=1) 78 | 79 | container.grid_rowconfigure(0, weight=10) 80 | container.grid_columnconfigure(0, weight=10) 81 | 82 | SportranGUI.root.grid_propagate(True) 83 | 84 | # ## Setting up multiple window system ## # 85 | self.topbar = TopBar(self, self, SportranGUI) 86 | SportranGUI.frames = {} 87 | frame_order = [FileManager, HeaderSelector, OtherVariables, FStarSelector, PStarSelector] 88 | 89 | # Load and setup the interfaces 90 | for n, F in enumerate(frame_order): 91 | SportranGUI.frame = F(container, SportranGUI) 92 | try: 93 | SportranGUI.frame.set_next_frame(frame_order[n + 1]) 94 | except: 95 | pass 96 | try: 97 | SportranGUI.frame.set_prev_frame(frame_order[n - 1]) 98 | except: 99 | pass 100 | 101 | SportranGUI.frames[F] = SportranGUI.frame 102 | 103 | # Init the main interface 104 | self.show_frame(SportranGUI.home) 105 | 106 | @staticmethod 107 | def show_software_info(): 108 | """ 109 | This function displays some software info at the startup. 110 | 111 | The data displayed are took from METADATA 112 | """ 113 | print('------------------- Sportran GUI -------------------') 114 | print('') 115 | print('\t\t\tGUI version: {}'.format(METADATA['gui_version'])) 116 | print('\t\t\tSportran version: {}'.format(METADATA['version'])) 117 | print('\t\t\tDev state: {}'.format(dev_state)) 118 | #print('\t\t\tLast release: {}'.format(METADATA['release_date'])) 119 | print('\t\t\tDevelopers: {}'.format(METADATA['author'])) 120 | print('\t\t\tURL: {}'.format(METADATA['url'])) 121 | print('') 122 | print('This software is an open-source project licensed under {}'.format(METADATA['license'])) 123 | print(METADATA['credits']) 124 | print('') 125 | print(METADATA['description']) # todo: Add other project infos 126 | print('----------------------------------------------------------') 127 | 128 | @staticmethod 129 | def show_frame(frame): 130 | """ 131 | This function is used to display a frame. 132 | 133 | :param frame: the frame to be displayed. Must be a Frame object. 134 | """ 135 | SportranGUI.container.grid_rowconfigure(0, weight=0) 136 | SportranGUI.container.grid_columnconfigure(0, weight=0) 137 | 138 | SportranGUI.frame = SportranGUI.frames[frame] 139 | SportranGUI.frame.grid(row=0, column=0, sticky='nsew') 140 | 141 | SportranGUI.container.grid_rowconfigure(0, weight=1) 142 | SportranGUI.container.grid_columnconfigure(0, weight=1) 143 | 144 | SportranGUI.frame.tkraise() 145 | SportranGUI.frame.update_data() 146 | SportranGUI.frame.update() 147 | 148 | 149 | def run(): 150 | """ 151 | This function is called only one time at the 152 | startup and it load the .ini file and start the 153 | software. 154 | """ 155 | 156 | # Load data 157 | cu.load_settings() 158 | 159 | # Start the software 160 | app = SportranGUI() 161 | app.mainloop() 162 | 163 | 164 | if __name__ == '__main__': 165 | 166 | # Set the output method 167 | cu.log.set_method('other') 168 | run() 169 | -------------------------------------------------------------------------------- /sportran_gui/thcp.ini: -------------------------------------------------------------------------------- 1 | DP:../examples/data 2 | LP:./logs 3 | OP:./outputs 4 | FS:10 5 | PL:15 6 | LANG:en-EN 7 | -------------------------------------------------------------------------------- /sportran_gui/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import * 4 | -------------------------------------------------------------------------------- /sportran_gui/utils/tk_html_widgets/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 paolo-gurisatti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sportran_gui/utils/tk_html_widgets/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tkinter HTML text widgets 4 | """ 5 | import sys 6 | import tkinter as tk 7 | from tkinter import scrolledtext 8 | from tkinter import font 9 | from . import html_parser 10 | 11 | VERSION = '0.4.0' 12 | 13 | 14 | class _ScrolledText(tk.Text): 15 | #---------------------------------------------------------------------------------------------- 16 | def __init__(self, master=None, **kw): 17 | self.frame = tk.Frame(master) 18 | 19 | self.vbar = tk.Scrollbar(self.frame) 20 | kw.update({'yscrollcommand': self.vbar.set}) 21 | self.vbar.pack(side=tk.RIGHT, fill=tk.Y) 22 | self.vbar['command'] = self.yview 23 | 24 | tk.Text.__init__(self, self.frame, **kw) 25 | self.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) 26 | 27 | text_meths = vars(tk.Text).keys() 28 | methods = vars(tk.Pack).keys() | vars(tk.Grid).keys() | vars(tk.Place).keys() 29 | methods = methods.difference(text_meths) 30 | 31 | for m in methods: 32 | if m[0] != '_' and m != 'config' and m != 'configure': 33 | setattr(self, m, getattr(self.frame, m)) 34 | 35 | def __str__(self): 36 | return str(self.frame) 37 | 38 | 39 | class HTMLScrolledText(_ScrolledText): 40 | #---------------------------------------------------------------------------------------------- 41 | """ 42 | HTML scrolled text widget 43 | """ 44 | 45 | def __init__(self, *args, html=None, **kwargs): 46 | #------------------------------------------------------------------------------------------ 47 | super().__init__(*args, **kwargs) 48 | self._w_init(kwargs) 49 | self.html_parser = html_parser.HTMLTextParser() 50 | if isinstance(html, str): 51 | self.set_html(html) 52 | 53 | def _w_init(self, kwargs): 54 | #------------------------------------------------------------------------------------------ 55 | if not 'wrap' in kwargs.keys(): 56 | self.config(wrap='word') 57 | if not 'background' in kwargs.keys(): 58 | if sys.platform.startswith('win'): 59 | self.config(background='SystemWindow') 60 | else: 61 | self.config(background='white') 62 | 63 | def fit_height(self): 64 | #------------------------------------------------------------------------------------------ 65 | """ 66 | Fit widget height to wrapped lines 67 | """ 68 | for h in range(1, 4): 69 | self.config(height=h) 70 | self.master.update() 71 | if self.yview()[1] >= 1: 72 | break 73 | else: 74 | self.config(height=0.5 + 3 / self.yview()[1]) 75 | 76 | def set_html(self, html, strip=True): 77 | #------------------------------------------------------------------------------------------ 78 | """ 79 | Set HTML widget text. If strip is enabled (default) it ignores spaces and new lines. 80 | 81 | """ 82 | prev_state = self.cget('state') 83 | self.config(state=tk.NORMAL) 84 | self.delete('1.0', tk.END) 85 | self.tag_delete(self.tag_names) 86 | self.html_parser.w_set_html(self, html, strip=strip) 87 | self.config(state=prev_state) 88 | 89 | 90 | class HTMLText(HTMLScrolledText): 91 | #---------------------------------------------------------------------------------------------- 92 | """ 93 | HTML text widget 94 | """ 95 | 96 | def _w_init(self, kwargs): 97 | #------------------------------------------------------------------------------------------ 98 | super()._w_init(kwargs) 99 | self.vbar.pack_forget() 100 | 101 | def fit_height(self): 102 | #------------------------------------------------------------------------------------------ 103 | super().fit_height() 104 | #self.master.update() 105 | self.vbar.pack_forget() 106 | 107 | 108 | class HTMLLabel(HTMLText): 109 | #---------------------------------------------------------------------------------------------- 110 | """ 111 | HTML label widget 112 | """ 113 | 114 | def _w_init(self, kwargs): 115 | #------------------------------------------------------------------------------------------ 116 | super()._w_init(kwargs) 117 | if not 'background' in kwargs.keys(): 118 | if sys.platform.startswith('win'): 119 | self.config(background='SystemButtonFace') 120 | else: 121 | self.config(background='#d9d9d9') 122 | 123 | if not 'borderwidth' in kwargs.keys(): 124 | self.config(borderwidth=0) 125 | 126 | if not 'padx' in kwargs.keys(): 127 | self.config(padx=3) 128 | 129 | def set_html(self, *args, **kwargs): 130 | #------------------------------------------------------------------------------------------ 131 | super().set_html(*args, **kwargs) 132 | self.config(state=tk.DISABLED) 133 | -------------------------------------------------------------------------------- /sportran_gui/utils/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ========================================== 4 | Sportran graphic user interface 5 | ========================================== 6 | -------------------------------------------- 7 | Utils file 8 | -------------------------------------------- 9 | 10 | This file contains utility functions. 11 | """ 12 | 13 | 14 | class PrintMethod: 15 | """ 16 | PrintMethod is used to define and change 17 | the output method. 18 | """ 19 | 20 | _print_func = None 21 | _METHOD = 'bash' 22 | 23 | @classmethod 24 | def write_log(cls, s, s2='', *args, **kwargs): 25 | if cls._METHOD == 'bash': 26 | print(s, *args, **kwargs) 27 | elif cls._METHOD == 'other': 28 | if cls._print_func: 29 | cls._print_func(s, s2, *args, **kwargs) 30 | else: 31 | print(s, s2, *args, **kwargs) 32 | else: 33 | print(s, s2, *args, **kwargs) 34 | 35 | @classmethod 36 | def set_func(cls, func): 37 | """ 38 | This function sets the output function. 39 | :param func: The output function to be used. 40 | It must have at least one input string. 41 | 42 | Will be called in this way 43 | _print_func(s, s2, *args, **kwargs) 44 | """ 45 | cls._print_func = func 46 | 47 | @classmethod 48 | def set_method(cls, method): 49 | """ 50 | This function sets the output method. 51 | Two output methods are allowed: bash and other. 52 | The default value is bash that uses the normal print() function. 53 | 54 | :param method: the method used to output the data. 55 | bash - prints the output on the console using the print built-in function. 56 | other - allows you to define a custom output function using set_func() 57 | """ 58 | 59 | if method is 'bash' or method is 'other': 60 | cls._METHOD = method 61 | else: 62 | raise ValueError('Invalid input method') 63 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | This folder contains various tests. 2 | They are run by running 'tox' in the main folder of the project, where tox.ini is located. 'tox' is installed with 'pip install tox'. 3 | If you have issues running the tests with errors such 'You are using pip version 8.1.1, however version 19.2.1 is available.', please consider upgrading virtualenv with 'pip install virtualenv --upgrade'. Simply upgrading pip in this case does not work. 4 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | import os 5 | import numpy as np 6 | 7 | pytest_plugins = 'pytester' 8 | 9 | 10 | @pytest.fixture(scope='session') 11 | def filepath_tests(): 12 | """Return the absolute filepath of the `tests` folder. 13 | 14 | .. warning:: if this file moves with respect to the `tests` folder, the implementation should change. 15 | 16 | :return: absolute filepath of `tests` folder which is the basepath for all test resources. 17 | """ 18 | return os.path.dirname(os.path.abspath(__file__)) 19 | 20 | 21 | @pytest.fixture(scope='session') 22 | def data_NaCl_path(filepath_tests): 23 | return filepath_tests + '/data/NaCl/NaCl.dat' 24 | 25 | 26 | @pytest.fixture(scope='session') 27 | def data_SiO2_path(filepath_tests): 28 | return filepath_tests + '/data/Silica/Silica.dat' 29 | 30 | 31 | @pytest.fixture(scope='session') 32 | def data_NaCl(data_NaCl_path): 33 | import sportran as st 34 | import numpy as np 35 | 36 | jfile = st.i_o.TableFile(data_NaCl_path, group_vectors=True) 37 | jfile.read_datalines(start_step=0, NSTEPS=0, select_ckeys=['Temp', 'flux', 'vcm[1]']) 38 | DT_FS = 5.0 # time step [fs] 39 | TEMPERATURE = np.mean(jfile.data['Temp']) # temperature [K] 40 | VOLUME = 40.21**3 # volume [A^3] 41 | print('T = {:f} K'.format(TEMPERATURE)) 42 | print('V = {:f} A^3'.format(VOLUME)) 43 | return jfile, DT_FS, TEMPERATURE, VOLUME 44 | 45 | 46 | @pytest.fixture(scope='session') 47 | def data_SiO2(data_SiO2_path): 48 | import sportran as st 49 | import numpy as np 50 | 51 | jfile = st.i_o.TableFile(data_SiO2_path, group_vectors=True) 52 | jfile.read_datalines(start_step=0, NSTEPS=0, select_ckeys=['flux1', 'Temp']) 53 | DT_FS = 1.0 # time step [fs] 54 | TEMPERATURE = np.mean(jfile.data['Temp']) # temperature [K] 55 | VOLUME = 3130.431110818 # volume [A^3] 56 | print('T = {:f} K'.format(TEMPERATURE)) 57 | return jfile, DT_FS, TEMPERATURE, VOLUME 58 | 59 | 60 | @pytest.fixture 61 | def check_reg(num_regression, data_regression): 62 | 63 | def _check(jf): 64 | num_regression.check({ 65 | 'psd': jf.psd, 66 | 'logpsdK': jf.cepf.logpsdK, 67 | 'logpsdK_THEORY_std': jf.cepf.logpsdK_THEORY_std, 68 | 'logtau': jf.cepf.logtau, 69 | 'logtau_THEORY_std': jf.cepf.logtau_THEORY_std, 70 | 'KAPPA_SCALE': np.array([float(jf.KAPPA_SCALE)]), 71 | 'Nyquist_f_THz': np.array([float(jf.Nyquist_f_THz)]), 72 | 'kappa_Kmin': np.array([float(jf.kappa)]), 73 | 'kappa_Kmin_std': np.array([float(jf.kappa_std)]), 74 | 'aic_min': np.array([float(jf.cepf.aic_min)]), 75 | }) 76 | data_regression.check({'aic_Kmin': int(jf.cepf.aic_Kmin)}) 77 | 78 | return _check 79 | -------------------------------------------------------------------------------- /tests/data: -------------------------------------------------------------------------------- 1 | ../examples/data -------------------------------------------------------------------------------- /tests/test_as_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | import numpy as np 4 | 5 | 6 | def test_example_NaCl(data_NaCl, check_reg): 7 | import sportran as st 8 | 9 | jfile, DT_FS, TEMPERATURE, VOLUME = data_NaCl 10 | j = st.HeatCurrent([jfile.data['flux'], jfile.data['vcm[1]']], DT_FS=DT_FS, UNITS='metal', TEMPERATURE=TEMPERATURE, 11 | VOLUME=VOLUME) 12 | print(j.Nyquist_f_THz) 13 | FSTAR_THZ = 14.0 14 | jf = j.resample(fstar_THz=FSTAR_THZ, plot=False, freq_units='thz') 15 | jf.cepstral_analysis() 16 | print(jf.cepstral_log) 17 | check_reg(jf) 18 | 19 | 20 | def test_example_SiO2(data_SiO2, check_reg): 21 | import sportran as st 22 | 23 | jfile, DT_FS, TEMPERATURE, VOLUME = data_SiO2 24 | j = st.HeatCurrent(jfile.data['flux1'], DT_FS=DT_FS, UNITS='metal', TEMPERATURE=TEMPERATURE, VOLUME=VOLUME) 25 | print(j.Nyquist_f_THz) 26 | FSTAR_THZ = 28.0 27 | jf = j.resample(fstar_THz=FSTAR_THZ, plot=False, freq_units='thz') 28 | jf.cepstral_analysis() 29 | print(jf.cepstral_log) 30 | check_reg(jf) 31 | 32 | 33 | def test_example_SiO2_fixed_K(data_SiO2, check_reg): 34 | import sportran as st 35 | 36 | jfile, DT_FS, TEMPERATURE, VOLUME = data_SiO2 37 | j = st.HeatCurrent(jfile.data['flux1'], DT_FS=DT_FS, UNITS='metal', TEMPERATURE=TEMPERATURE, VOLUME=VOLUME) 38 | print(j.Nyquist_f_THz) 39 | FSTAR_THZ = 28.0 40 | jf = j.resample(fstar_THz=FSTAR_THZ, plot=False, freq_units='thz') 41 | jf.cepstral_analysis(manual_cutoffK=42) 42 | print(jf.cepstral_log) 43 | check_reg(jf) 44 | 45 | 46 | if __name__ == '__main__': 47 | test_example_NaCl() 48 | test_example_SiO2() 49 | -------------------------------------------------------------------------------- /tests/test_as_example/regenerate_results.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sportran as st 3 | import numpy as np 4 | import yaml, pandas as pd 5 | 6 | 7 | def write_results(jf, filename): 8 | d = { 9 | 'psd': jf.psd, 10 | 'logpsdK': jf.cepf.logpsdK, 11 | 'logpsdK_THEORY_std': jf.cepf.logpsdK_THEORY_std, 12 | 'logtau': jf.cepf.logtau, 13 | 'logtau_THEORY_std': jf.cepf.logtau_THEORY_std, 14 | 'KAPPA_SCALE': np.array([float(jf.KAPPA_SCALE)]), 15 | 'Nyquist_f_THz': np.array([float(jf.Nyquist_f_THz)]), 16 | 'kappa_Kmin': np.array([float(jf.kappa)]), 17 | 'kappa_Kmin_std': np.array([float(jf.kappa_std)]), 18 | 'aic_min': np.array([float(jf.cepf.aic_min)]), 19 | } 20 | df = pd.DataFrame({k: pd.Series(v) for k, v in d.items()}) 21 | df.to_csv(filename + '.csv') 22 | 23 | d = {'aic_Kmin': int(jf.cepf.aic_Kmin)} 24 | with open(filename + '.yml', 'w') as f: 25 | f.write(yaml.dump(d)) 26 | 27 | 28 | # NaCl 29 | jfile = st.i_o.TableFile('../data/NaCl/NaCl.dat', group_vectors=True) 30 | jfile.read_datalines(start_step=0, NSTEPS=0, select_ckeys=['Temp', 'flux', 'vcm[1]']) 31 | DT_FS = 5.0 # time step [fs] 32 | TEMPERATURE = np.mean(jfile.data['Temp']) # temperature [K] 33 | VOLUME = 40.21**3 # volume [A^3] 34 | print('T = {:f} K'.format(TEMPERATURE)) 35 | print('V = {:f} A^3'.format(VOLUME)) 36 | 37 | j = st.HeatCurrent([jfile.data['flux'], jfile.data['vcm[1]']], DT_FS=DT_FS, UNITS='metal', TEMPERATURE=TEMPERATURE, 38 | VOLUME=VOLUME) 39 | print(j.Nyquist_f_THz) 40 | FSTAR_THZ = 14.0 41 | jf = j.resample(fstar_THz=FSTAR_THZ, plot=False, freq_units='thz') 42 | jf.cepstral_analysis() 43 | print(jf.cepstral_log) 44 | 45 | write_results(jf, 'test_example_NaCl') 46 | 47 | # SiO2 48 | jfile = st.i_o.TableFile('../data/Silica/Silica.dat', group_vectors=True) 49 | jfile.read_datalines(start_step=0, NSTEPS=0, select_ckeys=['flux1', 'Temp']) 50 | DT_FS = 1.0 # time step [fs] 51 | TEMPERATURE = np.mean(jfile.data['Temp']) # temperature [K] 52 | VOLUME = 3130.431110818 # volume [A^3] 53 | print('T = {:f} K'.format(TEMPERATURE)) 54 | 55 | j = st.HeatCurrent(jfile.data['flux1'], DT_FS=DT_FS, UNITS='metal', TEMPERATURE=TEMPERATURE, VOLUME=VOLUME) 56 | print(j.Nyquist_f_THz) 57 | FSTAR_THZ = 28.0 58 | jf = j.resample(fstar_THz=FSTAR_THZ, plot=False, freq_units='thz') 59 | jf.cepstral_analysis() 60 | print(jf.cepstral_log) 61 | 62 | write_results(jf, 'test_example_SiO2') 63 | 64 | j = st.HeatCurrent(jfile.data['flux1'], DT_FS=DT_FS, UNITS='metal', TEMPERATURE=TEMPERATURE, VOLUME=VOLUME) 65 | print(j.Nyquist_f_THz) 66 | FSTAR_THZ = 28.0 67 | jf = j.resample(fstar_THz=FSTAR_THZ, plot=False, freq_units='thz') 68 | jf.cepstral_analysis(manual_cutoffK=42) 69 | print(jf.cepstral_log) 70 | 71 | write_results(jf, 'test_example_SiO2_fixed_K') 72 | -------------------------------------------------------------------------------- /tests/test_as_example/test_example_NaCl.yml: -------------------------------------------------------------------------------- 1 | aic_Kmin: 3 2 | -------------------------------------------------------------------------------- /tests/test_as_example/test_example_SiO2.yml: -------------------------------------------------------------------------------- 1 | aic_Kmin: 33 2 | -------------------------------------------------------------------------------- /tests/test_as_example/test_example_SiO2_fixed_K.yml: -------------------------------------------------------------------------------- 1 | aic_Kmin: 33 2 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | import numpy as np 5 | 6 | 7 | @pytest.fixture 8 | def run_cli(testdir, filepath_tests): 9 | 10 | def do_run(*args): 11 | args = [filepath_tests + '/../sportran/analysis.py'] + list(args) 12 | return testdir.run(*args) 13 | 14 | return do_run 15 | 16 | 17 | def test_cli_NaCl(tmpdir, run_cli, data_NaCl_path, num_regression, file_regression): 18 | fileout_pref = tmpdir.join('output') 19 | output = run_cli(data_NaCl_path, '-k', 'flux', '-j', 'vcm[1]', '-t', '5.0', '--VOLUME', '65013.301261', 20 | '--param-from-input-file-column', 'Temp', 'TEMPERATURE', '-w', '0.1', '--FSTAR', '14.0', '-r', 21 | '--test-suite-run', '-o', fileout_pref) 22 | file_list = [ 23 | 'output.cepstral.dat', 'output.cepstrumfiltered_psd.dat', 'output.cospectrum.dat', 'output.cospectrum.filt.dat', 24 | 'output.psd.dat', 'output.resampled_psd.dat' 25 | ] 26 | with open(str(tmpdir.join('output.log'))) as l: 27 | file_regression.check(l.read(), basename='output.log') 28 | file_regression.check(output.stdout.str(), basename='stdout') 29 | file_regression.check(output.stderr.str(), basename='stderr') 30 | readed = {} 31 | for f in file_list: 32 | file_out = tmpdir.join(f) 33 | readed[f] = np.loadtxt(file_out).flatten() 34 | 35 | num_regression.check(readed) 36 | 37 | 38 | def test_cli_NaCl_no_w(tmpdir, run_cli, data_NaCl_path, num_regression, file_regression): 39 | fileout_pref = tmpdir.join('output_no_w') 40 | output = run_cli(data_NaCl_path, '-k', 'flux', '-j', 'vcm[1]', '-t', '5.0', '--VOLUME', '65013.301261', 41 | '--param-from-input-file-column', 'Temp', 'TEMPERATURE', '--FSTAR', '14.0', '-r', 42 | '--test-suite-run', '-o', fileout_pref) 43 | file_list = [ 44 | 'output_no_w.cepstral.dat', 'output_no_w.cepstrumfiltered_psd.dat', 'output_no_w.cospectrum.dat', 45 | 'output_no_w.psd.dat', 'output_no_w.resampled_psd.dat' 46 | ] 47 | with open(str(tmpdir.join('output_no_w.log'))) as l: 48 | file_regression.check(l.read(), basename='output_no_w.log') 49 | file_regression.check(output.stdout.str(), basename='stdout_no_w') 50 | file_regression.check(output.stderr.str(), basename='stderr_no_w') 51 | readed = {} 52 | for f in file_list: 53 | file_out = tmpdir.join(f) 54 | readed[f] = np.loadtxt(file_out).flatten() 55 | 56 | num_regression.check(readed) 57 | 58 | 59 | def test_cli_bin_output_NaCl(tmpdir, run_cli, data_NaCl_path, num_regression, file_regression): 60 | fileout_pref = tmpdir.join('output') 61 | output = run_cli(data_NaCl_path, '-k', 'flux', '-j', 'vcm[1]', '-t', '5.0', '--VOLUME', '65013.301261', 62 | '--param-from-input-file-column', 'Temp', 'TEMPERATURE', '-w', '0.1', '--FSTAR', '14.0', '-r', 63 | '--test-suite-run', '-O', '--bin-output-old', '-o', fileout_pref) 64 | file_list = [ 65 | 'output.cepstral.npy', 'output.cepstrumfiltered_psd.npy', 'output.cospectrum.npy', 'output.cospectrum.filt.npy', 66 | 'output.psd.npy', 'output.resampled_psd.npy' 67 | ] 68 | with open(str(tmpdir.join('output.log'))) as l: 69 | file_regression.check(l.read(), basename='output.log') 70 | file_regression.check(output.stdout.str(), basename='stdout') 71 | file_regression.check(output.stderr.str(), basename='stderr') 72 | readed = {} 73 | for f in file_list: 74 | file_out = tmpdir.join(f) 75 | readed[f] = np.load(str(file_out), allow_pickle=True).flatten().real 76 | # NOTE: TODO: imaginary part of cospectrum is not checked! 77 | num_regression.check(readed) 78 | -------------------------------------------------------------------------------- /tests/test_cli/output.log.txt: -------------------------------------------------------------------------------- 1 | Units: metal 2 | Time step: 5.0 fs 3 | # Molten NaCl - Fumi-Tosi potential 4 | # https://journals.aps.org/prl/supplemental/10.1103/PhysRevLett.122.255901/Bertossa_et_al_Supplemental_Material.pdf 5 | # 1728 atoms, T~1400K 6 | # NVE, dt = 1.0 fs, 100 ps, print_step = 5.0 fs 7 | # Volume = 65013.301261 A^3 8 | # LAMMPS metal units 9 | Temp c_flux[1] c_flux[2] c_flux[3] c_vcm[1][1] c_vcm[1][2] c_vcm[1][3] 10 | ##################################### 11 | all_ckeys = [('Temp', [0]), ('flux', array([1, 2, 3])), ('vcm[1]', array([4, 5, 6]))] 12 | ##################################### 13 | Data length = 20000 14 | ckey = [('Temp', [0]), ('flux', array([1, 2, 3])), ('vcm[1]', array([4, 5, 6]))] 15 | ( 20000 ) steps read. 16 | VOLUME (input): 65013.301261 17 | Mean TEMPERATURE (computed): 1399.3477811999999 +/- 19.318785820942594 18 | Time step (input): 5.0 fs 19 | currents shape is (2, 20000, 3) 20 | snippet: 21 | [[[ 2.5086549e+02 2.0619423e+01 2.0011500e+02] 22 | [ 1.9622265e+02 8.2667342e+01 2.8433250e+02] 23 | [ 1.2639441e+02 1.6075472e+02 3.4036769e+02] 24 | ... 25 | [ 1.7991856e+02 1.8612706e+01 -1.3265623e+02] 26 | [ 2.0471193e+02 -4.6643315e-01 -2.0401650e+02] 27 | [ 2.4123318e+02 -1.8295461e+01 -2.5246475e+02]] 28 | 29 | [[-1.5991832e-01 -7.1370426e-02 2.0687917e-02] 30 | [-1.3755206e-01 -7.1002931e-02 -1.1279876e-02] 31 | [-1.0615044e-01 -6.2381243e-02 -4.1568120e-02] 32 | ... 33 | [-9.1939899e-02 -8.4778292e-02 6.0011385e-02] 34 | [-1.3384949e-01 -1.1474530e-01 8.9323546e-02] 35 | [-1.8385053e-01 -1.3693430e-01 1.1434060e-01]]] 36 | Using multicomponent code. 37 | Number of currents = 2 38 | Number of equivalent components = 3 39 | KAPPA_SCALE = 1.4604390788939313e-07 40 | Nyquist_f = 100.0 THz 41 | Using multicomponent code. 42 | ----------------------------------------------------- 43 | RESAMPLE TIME SERIES 44 | ----------------------------------------------------- 45 | Original Nyquist freq f_Ny = 100.00000 THz 46 | Resampling freq f* = 14.28571 THz 47 | Sampling time TSKIP = 7 steps 48 | = 35.000 fs 49 | Original n. of frequencies = 10001 50 | Resampled n. of frequencies = 1429 51 | PSD @cutoff (pre-filter&sample) ~ 443152.37265 52 | (post-filter&sample) ~ 564877.86516 53 | log(PSD) @cutoff (pre-filter&sample) ~ 12.89638 54 | (post-filter&sample) ~ 13.05597 55 | min(PSD) (pre-filter&sample) = 0.31536 56 | min(PSD) (post-filter&sample) = 22166.11934 57 | % of original PSD Power f