diff --git a/doc/command-options.md b/doc/command-options.md index 9851d73e..e48a393a 100644 --- a/doc/command-options.md +++ b/doc/command-options.md @@ -27,13 +27,13 @@ CELL_FILENAME = POSCAR-unitcell where the setting tag names are case insensitive. This is run by ```bash -% phono3py setting.conf [comannd options] +% phono3py setting.conf [command options] ``` or ```bash -% phono3py [comannd options] -- setting.conf +% phono3py [command options] -- setting.conf ``` ```{contents} @@ -48,7 +48,7 @@ or This specifies input unit cell filename. ```bash -% phono3py -c POSCAR-unitcell ... (many options) +% phono3py -c POSCAR-unitcell ... (options) ``` ## Calculator interface @@ -174,6 +174,25 @@ created from `FORCES_FC2` and `phono3py_disp.yaml` instead of `FORCES_FC3` and % phono3py --cfs --dim-fc2="x x x" ``` +### `--sp` or `--save-params` + +Instead of `FORCES_FC3`, `phono3py_params.yaml` is generated. This option must +be used with `--cf3`, and optionally with `--cf2`. If the force calculator +supports reading energy of supercell, those are written into +`phono3y_params.yaml`. These energies are necessary for using `--pypolymlp` +option. + +```bash +% phono3py --cf3 disp-{00001..00755}/vasprun.xml --sp +``` + +When using with `--cf2`, `--cf3` has to be specified simultaneously as below, + +```bash +% phono3py --cf3 disp-{00001..00755}/vasprun.xml --cf2 disp_fc2-{00001..00002}/vasprun.xml --sp +``` + + ## Supercell, primitive cell, masses, magnetic moments (dim_option)= @@ -250,7 +269,6 @@ web page](https://phonopy.github.io/phonopy/setting-tags.html#magmom). ## Displacement creation (create_displacements_option)= - ### `-d` (`CREATE_DISPLACEMENTS = .TRUE.`) Supercell with displacements are created. Using with `--amplitude` option, @@ -259,8 +277,8 @@ supercells with displacements and `phono3py_disp.yaml` file are created. `--pa` should be specified if the input unit cell structure is not a primitive cell, e.g., `--pa="F"` if the input unit cell has F-centring. -(amplitude_option)= +(random_displacements_option)= ### `--rd` (`RANDOM_DISPLACEMENTS`), `--rd-fc2` (`RANDOM_DISPLACEMENTS_FC2`) and `--random-seed` (`RANDOM_SEED`) Random directional displacements are generated for fc3 and fc2 supercells by @@ -268,6 +286,7 @@ Random directional displacements are generated for fc3 and fc2 supercells by may be used together. These are used in the equivalent way to [`--rd` of phonopy](https://phonopy.github.io/phonopy/setting-tags.html#random-displacements). +(amplitude_option)= ### `--amplitude` (`DISPLACEMENT_DISTANCE`) Atomic displacement distance is specified. This value may be increased for the @@ -277,6 +296,43 @@ very accurate. The default value depends on calculator. See {ref}`default_displacement_distance_for_calculator`. +(fc_calculator_options_option)= +### `--fc-calc`, `--fc-calculator` (`FC_CALCULATOR`) + +Choice of force constants calculator. + +``` +% phono3py --fc-calc symfc ... +``` + +To use different force constants calculators for fc2 and fc3 +``` +% phono3py --fc-calc "symfc|" ... +``` +Those for fc2 and fc3 are seprated by `|` such as `symfc|` . Blank means to +employ the finite difference method for systematic displacements generated by +the option `-d`. + +### `--fc-calc-opt`, `--fc-calculator-options` (`FC_CALCULATOR_OPTIONS`) + +Special options for force constants calculators. + +``` +% phono3py --fc-calc-opt "cutoff=8" ... +``` + +Similarly to `--fc-calc`, `|` can be used to separated those for fc2 and fc3. + +#### Options for symfc + +* cutoff : cutoff pair distance beyond that third-order force constants are zero + (fc3 only). +* use_mkl : sparse_dot_mkl is employed when it is available. + +### `--symfc` and `--alm` + +These are shortcuts of `--fc-calc symfc` and `--fc-calc alm`, respectively. + ## Force constants (compact_fc_option)= @@ -331,14 +387,16 @@ supercell size and the second choice is using `--cutoff-pair` option. ### `--cutoff-pair` or `--cutoff-pair-distance` (`CUTOFF_PAIR_DISTANCE`) -This option is only used together with `-d` option. +This option works differently for the `-d` and `--rd` options. -A cutoff pair-distance in a supercell is used to reduce the number of necessary -supercells with displacements to obtain third order force constants. As the -drawback, a certain number of third-order-force-constants elements are abandoned -or computed with less numerical accuracy. More details are found at +For `-d`, A cutoff pair-distance in a supercell is used to reduce the number of +necessary supercells with displacements to obtain third order force constants. +As the drawback, a certain number of third-order-force-constants elements are +abandoned or computed with less numerical accuracy. More details are found at {ref}`command_cutoff_pair`. +For `--rd`, `--cutoff-pair VAL` is equivalent to `--fc-calc-opt "cutoff=VAL"`. + ### `--alm` This invokes ALM as the force constants calculator for fc2 and fc3. See the diff --git a/doc/index.md b/doc/index.md index 961ad249..1e077455 100644 --- a/doc/index.md +++ b/doc/index.md @@ -58,6 +58,7 @@ auxiliary-tools direct-solution wigner-solution workload-distribution +random-displacements cutoff-pair external-tools phono3py-api diff --git a/doc/random-displacements.md b/doc/random-displacements.md new file mode 100644 index 00000000..89b746b9 --- /dev/null +++ b/doc/random-displacements.md @@ -0,0 +1,36 @@ +(random-displacements)= +# Randan displacements + +Random displacements and corresponding forces in supercells can be employed as a +displacement-force dataset for computing force constants. This requires an +external force constants calculator, e.g., symfc or ALM. Here, examples are +presented with using symfc that can be installed via pip or conda easily. + +## Related setting tags + +- {ref}`random_displacements_option` (`--rd`, `--random-seed`) +- {ref}`fc_calculator_option` (`--fc-calc`) +- {ref}`fc_calculator_options_option` (`--fc-calc-opt`) + +## Generation of random directional displacements + +The option `--rd NUM` is used instead of `-d` in generating displacements as follows: + +```bash +% phono3py --rd 100 --dim 2 2 2 --pa auto -c POSCAR-unitcell +``` + +`NUM` means the number of supercells with random directional displacements. This +must be specified, and the initial guess may be from around the number of +supecells generated for the systematic displacements by `-d`. In the case of the +`NaCl-rd` example, 146 supercells are generated with `-d`, so similar +number `--rd 100` was chosen here. + +If random directional displacements for fc2 are expected, `--rd-fc2` and +`--dim-fc2` have to be specified: + +```bash +% phono3py --rd 100 --dim 2 2 2 --rd-fc2 2 --dim-fc2 4 4 4 --pa auto -c POSCAR-unitcell +``` + +where `--dim` is necessary but `--rd` is not. diff --git a/example/NaCl-rd/README.md b/example/NaCl-rd/README.md new file mode 100644 index 00000000..761570c0 --- /dev/null +++ b/example/NaCl-rd/README.md @@ -0,0 +1,80 @@ +# Example of using random directional displacements + +## How to use symfc + +This example utilizes an NaCl calculation result from A. Togo and A. Seko, J. +Chem. Phys. 160, 211001 (2024). Supercells of 2x2x2 and 4x4x4 conventional unit +cells are chosen for the third-order force constants (fc3) and second-order +force constants (fc2), respectively. Displacement-force datasets consisting of +100 supercells for fc3 and 2 supercells for fc2 are extracted and stored in +`phono3py_params_NaCl.yaml.xz`. Random directional displacements of a constant +0.03 Angstrom are used. + +To calculate force constants, an external force constants calculator is +necessary. Here, the symfc tool (available at https://github.com/symfc/symfc) is +used, which can be easily installed via pip or conda. + +The `fc3.hdf5` and `fc2.hdf5` are computed using the command: + +``` +% phono3py-load phono3py_params_NaCl.yaml.xz --symfc -v +``` + +Lattice thermal conductivity (LTC) is calculated with the following command: + +``` +% phono3py-load phono3py_params_NaCl.yaml.xz --br --ts 300 --mesh 50 +``` + +By this, LTC is obtained around 8.3 W/m-k. + + +## How to use pypolymlp + +When supercell energies are included in `phono3py_params.yaml` like file, the +polynomial machine learning potential (poly-MLP) by pypolymlp can be used to +calculate fc3 by the following command: + +```bash +% phono3py-load phono3py_params_NaCl.yaml.xz --pypolymlp --symfc --rd 400 -v +``` + +the procedure below is performed: + +1. Poly-MLPs are computed from the displacement-force dataset for fc3. This is + activated by `--pypolymlp` option. +2. 800=400+400 supercells for random directional displacements are generated, + where 400+400 means 400 supercells with random displacements (u) and 400 + supercells with opposite displacement vectors (-u). This is activated by + `--rd 400` option. The default displacement distance is 0.001 Angstrom in + `--pypolymlp` mode. Since random displacements are generated `--symfc` has to + be specified for fc3. In this example, random displacements are used for fc2, + too, `--symfc` is applied to both of fc3 and fc2. Without `--rd` option, + systematic displacements are generated, for which the option `--fc-calc "symfc|"` + has to be specified instead of `--symfc` (equivalent to `--fc-calc "symfc|symfc")`). +3. Forces on atoms in these 800 supercells are calculated using poly-MLP. +4. Force constants are calculated. + + +The `fc3.hdf5` and `fc2.hdf5` are obtained. Using these force constants, LTC is +calculated by + +```bash +% phono3py-load phono3py_params_NaCl.yaml.xz --br --ts 300 --mesh 50 +``` + +and the LTC value of around 8.2 W/m-k is obtained. + +## Generating phono3py_params.yaml from vasprun.xml's + +`phono3py_params.yaml` is generated from + +```bash +% phono3py phono3py_disp.yaml --cf3 NaCl-vasprun/vasprun-{00001..00100}.xml --cf2 NaCl-vasprun/vasprun-ph0000{1,2}.xml --sp +``` + +This command reads electronic energies of supercells from `vasprun.xml`s and +writes them into `phono3py_params.yaml`, too. Here, `phono3py_disp.yaml` is not +included in this example, but `phono3py_params_NaCl.yaml.xz` can be used to run +this example since corresponding information of displacements is included in +this file, too. diff --git a/example/NaCl-rd/phono3py_params_NaCl.yaml.xz b/example/NaCl-rd/phono3py_params_NaCl.yaml.xz new file mode 100644 index 00000000..105413ca Binary files /dev/null and b/example/NaCl-rd/phono3py_params_NaCl.yaml.xz differ diff --git a/example/NaCl-rd/vasprun_xmls.tar.xz b/example/NaCl-rd/vasprun_xmls.tar.xz new file mode 100644 index 00000000..2bb74deb Binary files /dev/null and b/example/NaCl-rd/vasprun_xmls.tar.xz differ diff --git a/phono3py/cui/create_supercells.py b/phono3py/cui/create_supercells.py index a09f3dcf..9c1e5a57 100644 --- a/phono3py/cui/create_supercells.py +++ b/phono3py/cui/create_supercells.py @@ -35,8 +35,10 @@ # POSSIBILITY OF SUCH DAMAGE. from phonopy.interface.calculator import write_supercells_with_displacements +from phonopy.structure.cells import print_cell from phono3py import Phono3py +from phono3py.cui.show_log import print_supercell_matrix from phono3py.interface.calculator import ( get_additional_info_to_write_fc2_supercells, get_additional_info_to_write_supercells, @@ -92,6 +94,16 @@ def create_phono3py_supercells( if log_level: print("") print('Unit cell was read from "%s".' % optional_structure_info[0]) + print("-" * 32 + " unit cell " + "-" * 33) # 32 + 11 + 33 = 76 + print_cell(phono3py.unitcell) + print("-" * 76) + print_supercell_matrix( + phono3py.supercell_matrix, phono3py.phonon_supercell_matrix + ) + if phono3py.primitive_matrix is not None: + print("Primitive matrix:") + for v in phono3py.primitive_matrix: + print(" %s" % v) print("Displacement distance: %s" % distance) ids = [] @@ -125,6 +137,7 @@ def create_phono3py_supercells( print("Number of displacement supercell files created: %d" % num_disp_files) if phono3py.phonon_supercell_matrix is not None: + num_disps = len(phono3py.phonon_supercells_with_displacements) additional_info = get_additional_info_to_write_fc2_supercells( interface_mode, phono3py.phonon_supercell_matrix ) diff --git a/phono3py/cui/load.py b/phono3py/cui/load.py index a5acd263..d100afdb 100644 --- a/phono3py/cui/load.py +++ b/phono3py/cui/load.py @@ -588,9 +588,9 @@ def _get_dataset_phonon_dataset_or_fc2( ) elif ( forces_fc2_filename is not None or pathlib.Path("FORCES_FC2").exists() - ) and ph3py.phonon_supercell_matrix: + ) and ph3py.phonon_supercell_matrix is not None: if forces_fc2_filename is None: - force_filename = forces_fc2_filename + force_filename = "FORCES_FC2" else: force_filename = forces_fc2_filename phonon_dataset = _get_dataset_for_fc2( diff --git a/phono3py/cui/phono3py_script.py b/phono3py/cui/phono3py_script.py index 3bf870bf..5ba25f55 100644 --- a/phono3py/cui/phono3py_script.py +++ b/phono3py/cui/phono3py_script.py @@ -36,8 +36,10 @@ from __future__ import annotations +import argparse import datetime import sys +from typing import Optional import numpy as np from phonopy.cui.collect_cell_info import collect_cell_info @@ -75,7 +77,7 @@ set_dataset_and_force_constants, ) from phono3py.cui.phono3py_argparse import get_parser -from phono3py.cui.settings import Phono3pyConfParser +from phono3py.cui.settings import Phono3pyConfParser, Phono3pySettings from phono3py.cui.show_log import ( show_general_settings, show_phono3py_cells, @@ -212,7 +214,7 @@ def get_run_mode(settings): return run_mode -def start_phono3py(**argparse_control): +def start_phono3py(**argparse_control) -> tuple[argparse.Namespace, int]: """Parse arguments and set some basic parameters.""" parser, deprecated = get_parser(**argparse_control) args = parser.parse_args() @@ -310,7 +312,9 @@ def get_input_output_filenames_from_args(args): return input_filename, output_filename -def get_cell_info(settings, cell_filename, log_level): +def get_cell_info( + settings: Phono3pySettings, cell_filename: str, log_level: int +) -> dict: """Return calculator interface and crystal structure information.""" cell_info = collect_cell_info( supercell_matrix=settings.supercell_matrix, @@ -513,6 +517,46 @@ def grid_addresses_to_grid_points(grid_addresses, bz_grid): return bz_grid.grg2bzg[grid_points] +def create_supercells( + settings: Phono3pySettings, + cell_info: dict, + confs_dict: dict, + interface_mode: Optional[str], + symprec: float, + log_level: int, +): + """Create supercells and write displacements.""" + if ( + settings.create_displacements + or settings.random_displacements + or settings.random_displacements_fc2 + ): + phono3py = create_phono3py_supercells( + cell_info, + settings, + symprec, + interface_mode=interface_mode, + log_level=log_level, + ) + + if log_level: + if phono3py.supercell.magnetic_moments is None: + print("Spacegroup: %s" % phono3py.symmetry.get_international_table()) + else: + print( + "Number of symmetry operations in supercell: %d" + % len(phono3py.symmetry.symmetry_operations["rotations"]) + ) + + finalize_phono3py( + phono3py, + confs_dict, + log_level, + write_displacements=True, + filename="phono3py_disp.yaml", + ) + + def store_force_constants( phono3py: Phono3py, settings, @@ -853,7 +897,7 @@ def main(**argparse_control): # warnings.simplefilter("error") load_phono3py_yaml = argparse_control.get("load_phono3py_yaml", False) - if "args" in argparse_control: # For pytest + if "args" in argparse_control: # This is for pytest. args = argparse_control["args"] log_level = args.log_level else: @@ -926,29 +970,9 @@ def main(**argparse_control): ###################################################### # Create supercells with displacements and then exit # ###################################################### - if settings.create_displacements: - phono3py = create_phono3py_supercells( - cell_info, - settings, - symprec, - interface_mode=interface_mode, - log_level=log_level, - ) - - if phono3py.supercell.magnetic_moments is None: - print("Spacegroup: %s" % phono3py.symmetry.get_international_table()) - else: - print( - "Number of symmetry operations in supercell: %d" - % len(phono3py.symmetry.symmetry_operations["rotations"]) - ) - - finalize_phono3py( - phono3py, - confs_dict, - log_level, - write_displacements=True, - filename="phono3py_disp.yaml", + if not settings.use_pypolymlp: + create_supercells( + settings, cell_info, confs_dict, interface_mode, symprec, log_level ) ####################### diff --git a/phono3py/cui/show_log.py b/phono3py/cui/show_log.py index bc731832..650a5657 100644 --- a/phono3py/cui/show_log.py +++ b/phono3py/cui/show_log.py @@ -35,6 +35,8 @@ # POSSIBILITY OF SUCH DAMAGE. import sys +from collections.abc import Sequence +from typing import Optional, Union import numpy as np from phonopy.structure.cells import print_cell @@ -68,6 +70,23 @@ def show_general_settings( print("Calculator interface: %s" % phono3py.calculator) print('Crystal structure was read from "%s".' % cell_filename) + print_supercell_matrix(supercell_matrix, phonon_supercell_matrix) + + if is_primitive_axes_auto: + print("Primitive matrix (Auto):") + for v in primitive_matrix: + print(" %s" % v) + elif primitive_matrix is not None: + print("Primitive matrix:") + for v in primitive_matrix: + print(" %s" % v) + + +def print_supercell_matrix( + supercell_matrix: Union[Sequence, np.ndarray], + phonon_supercell_matrix: Optional[Union[Sequence, np.ndarray]] = None, +): + """Print supercell matrix.""" if (np.diag(np.diag(supercell_matrix)) - supercell_matrix).any(): print("Supercell matrix (dim):") for v in supercell_matrix: @@ -81,14 +100,6 @@ def show_general_settings( print(" %s" % v) else: print("Phonon supercell (dim-fc2): %s" % np.diag(phonon_supercell_matrix)) - if is_primitive_axes_auto: - print("Primitive matrix (Auto):") - for v in primitive_matrix: - print(" %s" % v) - elif primitive_matrix is not None: - print("Primitive matrix:") - for v in primitive_matrix: - print(" %s" % v) def show_phono3py_cells(phono3py: Phono3py): diff --git a/phono3py/file_IO.py b/phono3py/file_IO.py index 58a94ffc..0b9066b7 100644 --- a/phono3py/file_IO.py +++ b/phono3py/file_IO.py @@ -200,16 +200,26 @@ def write_FORCES_FC2(disp_dataset, forces_fc2=None, fp=None, filename="FORCES_FC else: w = fp - for i, disp1 in enumerate(disp_dataset["first_atoms"]): - w.write("# File: %-5d\n" % (i + 1)) - w.write("# %-5d " % (disp1["number"] + 1)) - w.write("%20.16f %20.16f %20.16f\n" % tuple(disp1["displacement"])) - if "forces" in disp1 and forces_fc2 is None: - force_set = disp1["forces"] + if "first_atoms" in disp_dataset: + for i, disp1 in enumerate(disp_dataset["first_atoms"]): + w.write("# File: %-5d\n" % (i + 1)) + w.write("# %-5d " % (disp1["number"] + 1)) + w.write("%20.16f %20.16f %20.16f\n" % tuple(disp1["displacement"])) + if "forces" in disp1 and forces_fc2 is None: + force_set = disp1["forces"] + else: + force_set = forces_fc2[i] + for forces in force_set: + w.write("%15.10f %15.10f %15.10f\n" % tuple(forces)) + else: + if "forces" in disp_dataset: + write_FORCE_SETS(disp_dataset, filename="FORCES_FC2") else: - force_set = forces_fc2[i] - for forces in force_set: - w.write("%15.10f %15.10f %15.10f\n" % tuple(forces)) + if forces_fc2 is None: + raise RuntimeError("No forces are found.") + dataset = disp_dataset.copy() + dataset["forces"] = forces_fc2 + write_FORCE_SETS(dataset, filename="FORCES_FC2") if fp is None: w.close()