From 1e65d7b044c0d85265acefec6b81921014d08c61 Mon Sep 17 00:00:00 2001 From: Edward Linscott Date: Tue, 21 May 2024 10:18:53 +0200 Subject: [PATCH] Adding link() method to workflows; WIP --- src/koopmans/calculators/_pw.py | 2 +- src/koopmans/calculators/_utils.py | 35 +++++- src/koopmans/references.bib | 138 ++++++++++++++-------- src/koopmans/settings/_utils.py | 6 +- src/koopmans/utils/_xml.py | 6 +- src/koopmans/utils/_xsf.py | 2 +- src/koopmans/workflows/_convergence_ml.py | 11 +- src/koopmans/workflows/_koopmans_dfpt.py | 7 +- src/koopmans/workflows/_wannierize.py | 36 +++--- src/koopmans/workflows/_workflow.py | 29 ++++- 10 files changed, 183 insertions(+), 89 deletions(-) diff --git a/src/koopmans/calculators/_pw.py b/src/koopmans/calculators/_pw.py index 9a3f539f5..b4a6c09c6 100644 --- a/src/koopmans/calculators/_pw.py +++ b/src/koopmans/calculators/_pw.py @@ -84,7 +84,7 @@ def vbm_energy(self) -> float: # Fetch the total number of electrons in the system nelec = nelec_from_pseudos(self.atoms, self.parameters.pseudopotentials, - self.parameters.pseudo_dir) + self.parameters.get('tot_charge', 0) + self.directory / self.parameters.pseudo_dir) + self.parameters.get('tot_charge', 0) # Determine the number of occupied bands in each spin channel if self.parameters.nspin == 1: diff --git a/src/koopmans/calculators/_utils.py b/src/koopmans/calculators/_utils.py index 1072e6510..8120bae28 100644 --- a/src/koopmans/calculators/_utils.py +++ b/src/koopmans/calculators/_utils.py @@ -24,7 +24,8 @@ class ExtendedCalc(CalculatorExt, ASECalc, CalculatorABC): import os from abc import ABC, abstractmethod, abstractproperty from pathlib import Path -from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union +from typing import (Any, Dict, Generic, List, Optional, Tuple, Type, TypeVar, + Union) import ase.io as ase_io import numpy as np @@ -84,6 +85,9 @@ def __init__(self, skip_qc: bool = False, **kwargs: Any): # skip_qc = True self.skip_qc = skip_qc + # Prepare a dictionary to store a record of linked files + self._linked_files: Dict[str, Tuple[CalculatorExt | None, Path]] = {} + def __repr__(self): entries = [] @@ -152,12 +156,33 @@ def _post_calculate(self): return + def _fetch_linked_files(self): + """Link all files provided in self._linked_files + + This function is called in _pre_calculate() i.e. immediately before a calculation is run. + """ + for dest_filename, (src_calc, src_filename) in self._linked_files.items(): + if src_calc is None: + src_filename = src_filename.resolve() + else: + src_filename = (src_calc.directory / src_filename).resolve() + if not src_filename.exists(): + raise FileNotFoundError( + f'Tried to link {src_filename} with the {self.prefix} calculator but it does not exist') + dest_filename = (self.directory / dest_filename).resolve() + if not dest_filename.exists(): + dest_filename.parent.mkdir(parents=True, exist_ok=True) + utils.symlink(src_filename, dest_filename) + def _pre_calculate(self): """Perform any necessary pre-calculation steps before running the calculation""" # By default, check the corresponding program is installed self.check_code_is_installed() + # Copy over all files linked to this calculation + self._fetch_linked_files() + return def _calculate(self): @@ -236,6 +261,14 @@ def fromdict(cls: Type[TCalc], dct: Any) -> TCalc: setattr(calc, k.lstrip('_'), v) return calc + def link_file(self, src_calc: CalculatorExt | None, src_filename: Path, dest_filename: Path): + if src_filename.is_absolute() and src_calc is not None: + raise ValueError(f'"src_filename" in {self.__class__.__name__}.link_file() must be a relative path if a ' + f'"src_calc" is provided') + if dest_filename.is_absolute(): + raise ValueError(f'"dest_filename" in {self.__class__.__name__}.link_file() must be a relative path') + self._linked_files[str(dest_filename)] = (src_calc, src_filename) + class CalculatorABC(ABC, Generic[TCalc]): diff --git a/src/koopmans/references.bib b/src/koopmans/references.bib index 0f1189ff0..ae4b2ddc9 100644 --- a/src/koopmans/references.bib +++ b/src/koopmans/references.bib @@ -7,7 +7,7 @@ @article{Anisimov2005 volume = {72}, number = {7}, pages = {075125}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, doi = {10.1103/PhysRevB.72.075125}, urldate = {2020-12-13}, abstract = {We propose a computational scheme for materials where standard local density approximation (LDA) fails to produce a satisfactory description of excitation energies. The method uses Slater's ``transition state'' approximation and Wannier functions basis set. We define a correction to LDA functional in such a way that its variation produces one-electron energies for Wannier functions equal to the energies obtained in transition state constrained LDA calculations. In the result eigenvalues of the proposed functional could be interpreted as excitation energies of the system under consideration. The method was applied to MgO, Si, NiO, and BaBiO3 and gave an improved agreement with experimental data of energy gap values comparing with LDA.} @@ -22,7 +22,7 @@ @article{Baroni2001 volume = {73}, number = {2}, pages = {515--562}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {0034-6861}, doi = {10.1103/RevModPhys.73.515}, urldate = {2018-11-06} @@ -37,7 +37,7 @@ @article{Borghi2014 volume = {90}, number = {7}, pages = {075135}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {1098-0121}, doi = {10.1103/PhysRevB.90.075135}, urldate = {2017-11-23} @@ -52,7 +52,7 @@ @article{Borghi2015 volume = {91}, number = {15}, pages = {155112}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {1550235X}, doi = {10.1103/PhysRevB.91.155112}, urldate = {2020-02-20}, @@ -68,7 +68,7 @@ @article{Colonna2018 volume = {14}, number = {5}, pages = {2549--2557}, - publisher = {{American Chemical Society}}, + publisher = {American Chemical Society}, issn = {15499626}, doi = {10.1021/acs.jctc.7b01116}, urldate = {2019-11-14}, @@ -84,7 +84,7 @@ @article{Colonna2019 volume = {15}, number = {3}, pages = {1905--1914}, - publisher = {{American Chemical Society}}, + publisher = {American Chemical Society}, issn = {1549-9618}, doi = {10.1021/acs.jctc.8b00976}, urldate = {2019-03-21}, @@ -93,16 +93,18 @@ @article{Colonna2019 @article{Colonna2022, title = {Koopmans {{Spectral Functionals}} in {{Periodic Boundary Conditions}}}, - author = {Colonna, Nicola and Gennaro, Riccardo De and Linscott, Edward and Marzari, Nicola}, + author = {Colonna, Nicola and De Gennaro, Riccardo and Linscott, Edward and Marzari, Nicola}, year = {2022}, - month = aug, + month = sep, journal = {J. Chem. Theory Comput.}, - publisher = {{American Chemical Society}}, + volume = {18}, + number = {9}, + pages = {5435--5448}, + publisher = {American Chemical Society}, + issn = {1549-9618}, doi = {10.1021/acs.jctc.2c00161}, - urldate = {2022-08-09}, - abstract = {Koopmans spectral functionals aim to describe simultaneously ground-state properties and charged excitations of atoms, molecules, nanostructures, and periodic crystals. This is achieved by augmenting standard density functionals with simple but physically motivated orbital-density-dependent corrections. These corrections act on a set of localized orbitals that, in periodic systems, resemble maximally localized Wannier functions. At variance with the original, direct supercell implementation (Phys. Rev. X 2018, 8, 021051), we discuss here (i) the complex but efficient formalism required for a periodic boundary code using explicit Brillouin zone sampling and (ii) the calculation of the screened Koopmans corrections with density functional perturbation theory. In addition to delivering improved scaling with system size, the present development makes the calculation of band structures with Koopmans functionals straightforward. The implementation in the open-source Quantum ESPRESSO distribution and the application to prototypical insulating and semiconducting systems are presented and discussed.}, - copyright = {{\textcopyright} 2022 American Chemical Society}, - langid = {english} + urldate = {2024-03-05}, + abstract = {Koopmans spectral functionals aim to describe simultaneously ground-state properties and charged excitations of atoms, molecules, nanostructures, and periodic crystals. This is achieved by augmenting standard density functionals with simple but physically motivated orbital-density-dependent corrections. These corrections act on a set of localized orbitals that, in periodic systems, resemble maximally localized Wannier functions. At variance with the original, direct supercell implementation (Phys. Rev. X 2018, 8, 021051), we discuss here (i) the complex but efficient formalism required for a periodic boundary code using explicit Brillouin zone sampling and (ii) the calculation of the screened Koopmans corrections with density functional perturbation theory. In addition to delivering improved scaling with system size, the present development makes the calculation of band structures with Koopmans functionals straightforward. The implementation in the open-source Quantum ESPRESSO distribution and the application to prototypical insulating and semiconducting systems are presented and discussed.} } @misc{Dabo2009, @@ -124,7 +126,7 @@ @article{Dabo2010 volume = {82}, number = {11}, pages = {115121}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {1098-0121}, doi = {10.1103/PhysRevB.82.115121}, urldate = {2017-11-23} @@ -142,7 +144,7 @@ @article{Dabo2013 issn = {14639076}, doi = {10.1039/c2cp43491a}, urldate = {2019-11-14}, - abstract = {Accurate and efficient approaches to predict the optical properties of organic semiconducting compounds could accelerate the search for efficient organic photovoltaic materials. Nevertheless, predicting the optical properties of organic semiconductors has been plagued by the inaccuracy or computational cost of conventional first-principles calculations. In this work, we demonstrate that orbital-dependent density-functional theory based upon Koopmans' condition [Phys. Rev. B, 2010, 82, 115121] is apt for describing donor and acceptor levels for a wide variety of organic molecules, clusters, and oligomers within a few tenths of an electron-volt relative to experiment, which is comparable to the predictive performance of many-body perturbation theory methods at a fraction of the computational cost. {\textcopyright} the Owner Societies 2013.} + abstract = {Accurate and efficient approaches to predict the optical properties of organic semiconducting compounds could accelerate the search for efficient organic photovoltaic materials. Nevertheless, predicting the optical properties of organic semiconductors has been plagued by the inaccuracy or computational cost of conventional first-principles calculations. In this work, we demonstrate that orbital-dependent density-functional theory based upon Koopmans' condition [Phys. Rev. B, 2010, 82, 115121] is apt for describing donor and acceptor levels for a wide variety of organic molecules, clusters, and oligomers within a few tenths of an electron-volt relative to experiment, which is comparable to the predictive performance of many-body perturbation theory methods at a fraction of the computational cost. {\copyright} the Owner Societies 2013.} } @article{DeGennaro2022, @@ -155,7 +157,7 @@ @article{DeGennaro2022 volume = {106}, number = {3}, pages = {035106}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, doi = {10.1103/PhysRevB.106.035106}, urldate = {2022-07-08}, abstract = {Koopmans-compliant functionals provide an orbital-density-dependent framework for an accurate evaluation of spectral properties; they are obtained by imposing a generalized piecewise-linearity condition on the total energy of the system with respect to the occupation of any orbital. In crystalline materials, due to the orbital-density-dependent nature of the functionals, minimization of the total energy to a ground state provides a set of minimizing variational orbitals that are localized and thus break the periodicity of the underlying lattice. Despite this, we show that Bloch symmetry can be preserved and it is possible to describe the electronic states with a band-structure picture, thanks to the Wannier-like character of the variational orbitals. We also present a method to unfold and interpolate the electronic bands from supercell ({$\Gamma$}-point) calculations, which enables us to calculate full band structures with Koopmans-compliant functionals. The results obtained for a set of benchmark semiconductors and insulators show very good agreement with state-of-the-art many-body perturbation theory and experiments, underscoring the reliability of these spectral functionals in predicting band structures.}, @@ -171,7 +173,7 @@ @article{Ferretti2014 volume = {89}, number = {19}, pages = {195134}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {1098-0121}, doi = {10.1103/PhysRevB.89.195134}, urldate = {2019-03-23} @@ -186,11 +188,11 @@ @article{Hamann2013 volume = {88}, number = {8}, pages = {085117}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {10980121}, doi = {10.1103/PhysRevB.88.085117}, urldate = {2020-06-23}, - abstract = {Fully nonlocal two-projector norm-conserving pseudopotentials are shown to be compatible with a systematic approach to the optimization of convergence with the size of the plane-wave basis. A reformulation of the optimization is developed, including the ability to apply it to positive-energy atomic scattering states and to enforce greater continuity in the pseudopotential. The generalization of norm conservation to multiple projectors is reviewed and recast for the present purposes. Comparisons among the results of all-electron and one- and two-projector norm-conserving pseudopotential calculations of lattice constants and bulk moduli are made for a group of solids chosen to represent a variety of types of bonding and a sampling of the periodic table. {\textcopyright} 2013 American Physical Society.} + abstract = {Fully nonlocal two-projector norm-conserving pseudopotentials are shown to be compatible with a systematic approach to the optimization of convergence with the size of the plane-wave basis. A reformulation of the optimization is developed, including the ability to apply it to positive-energy atomic scattering states and to enforce greater continuity in the pseudopotential. The generalization of norm conservation to multiple projectors is reviewed and recast for the present purposes. Comparisons among the results of all-electron and one- and two-projector norm-conserving pseudopotential calculations of lattice constants and bulk moduli are made for a group of solids chosen to represent a variety of types of bonding and a sampling of the periodic table. {\copyright} 2013 American Physical Society.} } @article{Kraisler2013, @@ -203,7 +205,7 @@ @article{Kraisler2013 volume = {110}, number = {12}, pages = {126403}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, doi = {10.1103/PhysRevLett.110.126403}, urldate = {2022-09-28}, abstract = {In the exact Kohn-Sham density-functional theory, the total energy versus the number of electrons is a series of linear segments between integer points. However, commonly used approximate density functionals produce total energies that do not exhibit this piecewise-linear behavior. As a result, the ionization potential theorem, equating the highest occupied eigenvalue with the ionization potential, is grossly disobeyed. Here, we show that, contrary to conventional wisdom, most of the required piecewise linearity of an arbitrary approximate density functional can be restored by careful consideration of the ensemble generalization of density-functional theory. Furthermore, the resulting formulation introduces the desired derivative discontinuity to any approximate exchange-correlation functional, even one that is explicitly density dependent. This opens the door to calculations of the ionization potential and electron affinity, even without explicit electron removal or addition. All these advances are achieved while neither introducing empiricism nor changing the underlying functional form. The power of the approach is demonstrated on benchmark systems using the local density approximation as an illustrative example.} @@ -218,11 +220,11 @@ @article{Kronik2012 volume = {8}, number = {5}, pages = {1515--1531}, - publisher = {{American Chemical Society}}, + publisher = {American Chemical Society}, issn = {1549-9618}, doi = {10.1021/ct2009363}, urldate = {2022-10-17}, - abstract = {Excitation gaps are of considerable significance in electronic structure theory. Two different gaps are of particular interest. The fundamental gap is defined by charged excitations, as the difference between the first ionization potential and the first electron affinity. The optical gap is defined by a neutral excitation, as the difference between the energies of the lowest dipole-allowed excited state and the ground state. Within many-body perturbation theory, the fundamental gap is the difference between the corresponding lowest quasi-hole and quasi-electron excitation energies, and the optical gap is addressed by including the interaction between a quasi-electron and a quasi-hole. A long-standing challenge has been the attainment of a similar description within density functional theory (DFT), with much debate on whether this is an achievable goal even in principle. Recently, we have constructed and applied a new approach to this problem. Anchored in the rigorous theoretical framework of the generalized Kohn{\textendash}Sham equation, our method is based on a range-split hybrid functional that uses exact long-range exchange. Its main novel feature is that the range-splitting parameter is not a universal constant but rather is determined from first principles, per system, based on satisfaction of the ionization potential theorem. For finite-sized objects, this DFT approach mimics successfully, to the best of our knowledge for the first time, the quasi-particle picture of many-body theory. Specifically, it allows for the extraction of both the fundamental and the optical gap from one underlying functional, based on the HOMO{\textendash}LUMO gap of a ground-state DFT calculation and the lowest excitation energy of a linear-response time-dependent DFT calculation, respectively. In particular, it produces the correct optical gap for the difficult case of charge-transfer and charge-transfer-like scenarios, where conventional functionals are known to fail. In this perspective, we overview the formal and practical challenges associated with gap calculations, explain our new approach and how it overcomes previous difficulties, and survey its application to a variety of systems.} + abstract = {Excitation gaps are of considerable significance in electronic structure theory. Two different gaps are of particular interest. The fundamental gap is defined by charged excitations, as the difference between the first ionization potential and the first electron affinity. The optical gap is defined by a neutral excitation, as the difference between the energies of the lowest dipole-allowed excited state and the ground state. Within many-body perturbation theory, the fundamental gap is the difference between the corresponding lowest quasi-hole and quasi-electron excitation energies, and the optical gap is addressed by including the interaction between a quasi-electron and a quasi-hole. A long-standing challenge has been the attainment of a similar description within density functional theory (DFT), with much debate on whether this is an achievable goal even in principle. Recently, we have constructed and applied a new approach to this problem. Anchored in the rigorous theoretical framework of the generalized Kohn--Sham equation, our method is based on a range-split hybrid functional that uses exact long-range exchange. Its main novel feature is that the range-splitting parameter is not a universal constant but rather is determined from first principles, per system, based on satisfaction of the ionization potential theorem. For finite-sized objects, this DFT approach mimics successfully, to the best of our knowledge for the first time, the quasi-particle picture of many-body theory. Specifically, it allows for the extraction of both the fundamental and the optical gap from one underlying functional, based on the HOMO--LUMO gap of a ground-state DFT calculation and the lowest excitation energy of a linear-response time-dependent DFT calculation, respectively. In particular, it produces the correct optical gap for the difficult case of charge-transfer and charge-transfer-like scenarios, where conventional functionals are known to fail. In this perspective, we overview the formal and practical challenges associated with gap calculations, explain our new approach and how it overcomes previous difficulties, and survey its application to a variety of systems.} } @article{Lejaeghere2016, @@ -234,7 +236,7 @@ @article{Lejaeghere2016 volume = {351}, number = {6280}, pages = {aad3000}, - publisher = {{American Association for the Advancement of Science}}, + publisher = {American Association for the Advancement of Science}, doi = {10.1126/science.aad3000}, urldate = {2022-02-17} } @@ -255,14 +257,16 @@ @article{Linscott2023 shorttitle = {Koopmans}, author = {Linscott, Edward B. and Colonna, Nicola and De Gennaro, Riccardo and Nguyen, Ngoc Linh and Borghi, Giovanni and Ferretti, Andrea and Dabo, Ismaila and Marzari, Nicola}, year = {2023}, - month = aug, + month = oct, journal = {J. Chem. Theory Comput.}, - publisher = {{American Chemical Society}}, + volume = {19}, + number = {20}, + pages = {7097--7111}, + publisher = {American Chemical Society}, issn = {1549-9618}, doi = {10.1021/acs.jctc.3c00652}, - urldate = {2023-08-24}, - abstract = {Over the past decade we have developed Koopmans functionals, a computationally efficient approach for predicting spectral properties with an orbital-density-dependent functional framework. These functionals impose a generalized piecewise linearity condition to the entire electronic manifold, ensuring that orbital energies match the corresponding electron removal/addition energy differences (in contrast to semilocal DFT, where a mismatch between the two lies at the heart of the band gap problem and, more generally, the unreliability of Kohn{\textendash}Sham orbital energies). This strategy has proven to be very powerful, yielding molecular orbital energies and solid-state band structures with comparable accuracy to many-body perturbation theory but at greatly reduced computational cost while preserving a functional formulation. This paper reviews the theory of Koopmans functionals, discusses the algorithms necessary for their implementation, and introduces koopmans, an open-source package that contains all of the code and workflows needed to perform Koopmans functional calculations and obtain reliable spectral properties of molecules and materials.}, - copyright = {All rights reserved} + urldate = {2024-03-05}, + abstract = {Over the past decade we have developed Koopmans functionals, a computationally efficient approach for predicting spectral properties with an orbital-density-dependent functional framework. These functionals impose a generalized piecewise linearity condition to the entire electronic manifold, ensuring that orbital energies match the corresponding electron removal/addition energy differences (in contrast to semilocal DFT, where a mismatch between the two lies at the heart of the band gap problem and, more generally, the unreliability of Kohn--Sham orbital energies). This strategy has proven to be very powerful, yielding molecular orbital energies and solid-state band structures with comparable accuracy to many-body perturbation theory but at greatly reduced computational cost while preserving a functional formulation. This paper reviews the theory of Koopmans functionals, discusses the algorithms necessary for their implementation, and introduces koopmans, an open-source package that contains all of the code and workflows needed to perform Koopmans functional calculations and obtain reliable spectral properties of molecules and materials.} } @article{Ma2016, @@ -274,7 +278,7 @@ @article{Ma2016 volume = {6}, number = {1}, pages = {24924}, - publisher = {{Nature Publishing Group}}, + publisher = {Nature Publishing Group}, issn = {2045-2322}, doi = {10.1038/srep24924}, urldate = {2020-11-25}, @@ -292,7 +296,7 @@ @article{Marzari2012 volume = {84}, number = {4}, pages = {1419--1475}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {0034-6861}, doi = {10.1103/RevModPhys.84.1419}, urldate = {2018-08-29} @@ -322,10 +326,10 @@ @article{Nguyen2015 volume = {114}, number = {16}, pages = {166405}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, doi = {10.1103/PhysRevLett.114.166405}, urldate = {2021-06-16}, - abstract = {The determination of spectral properties from first principles can provide powerful connections between microscopic theoretical predictions and experimental data, but requires complex electronic-structure formulations that fall outside the domain of applicability of common approaches, such as density-functional theory. We show here that Koopmans-compliant functionals, constructed to enforce piecewise linearity and the correct discontinuity derivative in energy functionals with respect to fractional occupation{\textemdash}i.e., with respect to charged excitations{\textemdash}provide molecular photoemission spectra and momentum maps of Dyson orbitals that are in excellent agreement with experimental ultraviolet photoemission spectroscopy and orbital tomography data. These results highlight the role of Koopmans-compliant functionals as accurate and inexpensive quasiparticle approximations to the spectral potential.} + abstract = {The determination of spectral properties from first principles can provide powerful connections between microscopic theoretical predictions and experimental data, but requires complex electronic-structure formulations that fall outside the domain of applicability of common approaches, such as density-functional theory. We show here that Koopmans-compliant functionals, constructed to enforce piecewise linearity and the correct discontinuity derivative in energy functionals with respect to fractional occupation---i.e., with respect to charged excitations---provide molecular photoemission spectra and momentum maps of Dyson orbitals that are in excellent agreement with experimental ultraviolet photoemission spectroscopy and orbital tomography data. These results highlight the role of Koopmans-compliant functionals as accurate and inexpensive quasiparticle approximations to the spectral potential.} } @article{Nguyen2016, @@ -337,7 +341,7 @@ @article{Nguyen2016 volume = {12}, number = {8}, pages = {3948--3958}, - publisher = {{American Chemical Society}}, + publisher = {American Chemical Society}, issn = {15499626}, doi = {10.1021/acs.jctc.6b00145}, urldate = {2020-04-20}, @@ -353,14 +357,14 @@ @article{Nguyen2018 volume = {8}, number = {2}, pages = {021051}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {2160-3308}, doi = {10.1103/PhysRevX.8.021051}, urldate = {2019-03-23} } @article{Pederson1984, - title = {Local-density {{Hartree}}{\textendash}{{Fock}} Theory of Electronic States of Molecules with Self-interaction Correction}, + title = {Local-density {{Hartree}}--{{Fock}} Theory of Electronic States of Molecules with Self-interaction Correction}, author = {Pederson, Mark R. and Heaton, Richard A. and Lin, Chun C.}, year = {1984}, month = mar, @@ -368,12 +372,12 @@ @article{Pederson1984 volume = {80}, number = {5}, pages = {1972--1975}, - publisher = {{American Institute of Physics}}, + publisher = {American Institute of Physics}, issn = {0021-9606}, doi = {10.1063/1.446959}, urldate = {2019-03-23}, - abstract = {A scheme for incorporating the self-interaction correction (SIC) to the local density approximation of the Hartree{\textendash}Fock theory of electronic structure of molecules is presented. This method is applied to the N2 molecule and the resulting orbital energies and total energy are in good agreement with the Hartree{\textendash}Fock values.}, - keywords = {ELECTRONIC STRUCTURE,HARTREE-FOCK METHOD,MOLECULES,NITROGEN} + abstract = {A scheme for incorporating the self-interaction correction (SIC) to the local density approximation of the Hartree--Fock theory of electronic structure of molecules is presented. This method is applied to the N2 molecule and the resulting orbital energies and total energy are in good agreement with the Hartree--Fock values.}, + keywords = {ELECTRONIC STRUCTURE,HARTREEFOCK METHOD,MOLECULES,NITROGEN} } @article{Prandini2018, @@ -385,7 +389,7 @@ @article{Prandini2018 volume = {4}, number = {1}, pages = {1--13}, - publisher = {{Nature Publishing Group}}, + publisher = {Nature Publishing Group}, issn = {2057-3960}, doi = {10.1038/s41524-018-0127-2}, urldate = {2022-02-17}, @@ -395,8 +399,42 @@ @article{Prandini2018 keywords = {Computational methods,Electronic structure} } +@article{Qiao2023, + title = {Projectability Disentanglement for Accurate and Automated Electronic-Structure {{Hamiltonians}}}, + author = {Qiao, Junfeng and Pizzi, Giovanni and Marzari, Nicola}, + year = {2023}, + month = nov, + journal = {npj Comput Mater}, + volume = {9}, + pages = {208}, + issn = {2057-3960}, + doi = {10.1038/s41524-023-01146-w}, + urldate = {2023-11-21}, + abstract = {Maximally-localized Wannier functions (MLWFs) are broadly used to characterize the electronic structure of materials. Generally, one can construct MLWFs describing isolated bands (e.g. valence bands of insulators) or entangled bands (e.g. valence and conduction bands of insulators, or metals). Obtaining accurate and compact MLWFs often requires chemical intuition and trial and error, a challenging step even for experienced researchers and a roadblock for high-throughput calculations. Here, we present an automated approach, projectability-disentangled Wannier functions (PDWFs), that constructs MLWFs spanning the occupied bands and their complement for the empty states, providing a tight-binding picture of optimized atomic orbitals in crystals. Key to the algorithm is a projectability measure for each Bloch state onto atomic orbitals, determining if that state should be kept identically, discarded, or mixed into the disentanglement. We showcase the accuracy on a test set of 200 materials, and the reliability by constructing 21,737 Wannier Hamiltonians.}, + copyright = {2023 The Author(s)}, + langid = {english}, + keywords = {Computational methods,Electronic structure} +} + +@article{Qiao2023a, + title = {Automated Mixing of Maximally Localized {{Wannier}} Functions into Target Manifolds}, + author = {Qiao, Junfeng and Pizzi, Giovanni and Marzari, Nicola}, + year = {2023}, + month = oct, + journal = {npj Comput Mater}, + volume = {9}, + pages = {206}, + issn = {2057-3960}, + doi = {10.1038/s41524-023-01147-9}, + urldate = {2023-11-23}, + abstract = {Maximally localized Wannier functions (MLWFs) are widely used in electronic-structure calculations. We have recently developed automated approaches to generate MLWFs that represent natural tight-binding sets of atomic-like orbitals; these describe accurately both the occupied states and the complementary unoccupied ones. For many applications, it is required to use MLWFs that describe instead certain target groups of bands: the valence or the conduction bands, or correlated manifolds. Here, we start from these tight-binding sets of MLWFs, and mix them using a combination of parallel transport and maximal localization to construct manifold-remixed Wannier functions (MRWFs): these are orthogonal sets of MLWFs that fully and only span desired target submanifolds. The algorithm is simple and robust, and is showcased here in reference applications (silicon, MoS2, and SrVO3) and in a mid-throughput study of 77 insulators.}, + copyright = {2023 The Author(s)}, + langid = {english}, + keywords = {Electronic properties and materials,Electronic structure} +} + @article{Scherpelz2016, - title = {Implementation and {{Validation}} of {{Fully Relativistic GW Calculations}}: {{Spin}}{\textendash}{{Orbit Coupling}} in {{Molecules}}, {{Nanocrystals}}, and {{Solids}}}, + title = {Implementation and {{Validation}} of {{Fully Relativistic GW Calculations}}: {{Spin}}--{{Orbit Coupling}} in {{Molecules}}, {{Nanocrystals}}, and {{Solids}}}, shorttitle = {Implementation and {{Validation}} of {{Fully Relativistic GW Calculations}}}, author = {Scherpelz, Peter and Govoni, Marco and Hamada, Ikutaro and Galli, Giulia}, year = {2016}, @@ -405,11 +443,11 @@ @article{Scherpelz2016 volume = {12}, number = {8}, pages = {3523--3544}, - publisher = {{American Chemical Society}}, + publisher = {American Chemical Society}, issn = {1549-9618}, doi = {10.1021/acs.jctc.6b00114}, urldate = {2022-02-17}, - abstract = {We present an implementation of G0W0 calculations including spin{\textendash}orbit coupling (SOC) enabling investigations of large systems, with thousands of electrons, and we discuss results for molecules, solids, and nanocrystals. Using a newly developed set of molecules with heavy elements (called GW-SOC81), we find that, when based upon hybrid density functional calculations, fully relativistic (FR) and scalar-relativistic (SR) G0W0 calculations of vertical ionization potentials both yield excellent performance compared to experiment, with errors below 1.9\%. We demonstrate that while SR calculations have higher random errors, FR calculations systematically underestimate the VIP by 0.1 to 0.2 eV. We further verify that SOC effects may be well approximated at the FR density functional level and then added to SR G0W0 results for a broad class of systems. We also address the use of different root-finding algorithms for the G0W0 quasiparticle equation and the significant influence of including d electrons in the valence partition of the pseudopotential for G0W0 calculations. Finally, we present statistical analyses of our data, highlighting the importance of separating definitive improvements from those that may occur by chance due to a limited number of samples. We suggest the statistical analyses used here will be useful in the assessment of the accuracy of a large variety of electronic structure methods.} + abstract = {We present an implementation of G0W0 calculations including spin--orbit coupling (SOC) enabling investigations of large systems, with thousands of electrons, and we discuss results for molecules, solids, and nanocrystals. Using a newly developed set of molecules with heavy elements (called GW-SOC81), we find that, when based upon hybrid density functional calculations, fully relativistic (FR) and scalar-relativistic (SR) G0W0 calculations of vertical ionization potentials both yield excellent performance compared to experiment, with errors below 1.9\%. We demonstrate that while SR calculations have higher random errors, FR calculations systematically underestimate the VIP by 0.1 to 0.2 eV. We further verify that SOC effects may be well approximated at the FR density functional level and then added to SR G0W0 results for a broad class of systems. We also address the use of different root-finding algorithms for the G0W0 quasiparticle equation and the significant influence of including d electrons in the valence partition of the pseudopotential for G0W0 calculations. Finally, we present statistical analyses of our data, highlighting the importance of separating definitive improvements from those that may occur by chance due to a limited number of samples. We suggest the statistical analyses used here will be useful in the assessment of the accuracy of a large variety of electronic structure methods.} } @article{Schlipf2015, @@ -423,7 +461,7 @@ @article{Schlipf2015 issn = {0010-4655}, doi = {10.1016/j.cpc.2015.05.011}, urldate = {2021-07-09}, - abstract = {We present an optimization algorithm to construct pseudopotentials and use it to generate a set of Optimized Norm-Conserving Vanderbilt (ONCV) pseudopotentials for elements up to Z=83 (Bi) (excluding Lanthanides). We introduce a quality function that assesses the agreement of a pseudopotential calculation with all-electron FLAPW results, and the necessary plane-wave energy cutoff. This quality function allows us to use a Nelder{\textendash}Mead optimization algorithm on a training set of materials to optimize the input parameters of the pseudopotential construction for most of the periodic table. We control the accuracy of the resulting pseudopotentials on a test set of materials independent of the training set. We find that the automatically constructed pseudopotentials (http://www.quantum-simulation.org) provide a good agreement with the all-electron results obtained using the FLEUR code with a plane-wave energy cutoff of approximately 60 Ry.}, + abstract = {We present an optimization algorithm to construct pseudopotentials and use it to generate a set of Optimized Norm-Conserving Vanderbilt (ONCV) pseudopotentials for elements up to Z=83 (Bi) (excluding Lanthanides). We introduce a quality function that assesses the agreement of a pseudopotential calculation with all-electron FLAPW results, and the necessary plane-wave energy cutoff. This quality function allows us to use a Nelder--Mead optimization algorithm on a training set of materials to optimize the input parameters of the pseudopotential construction for most of the periodic table. We control the accuracy of the resulting pseudopotentials on a test set of materials independent of the training set. We find that the automatically constructed pseudopotentials (http://www.quantum-simulation.org) provide a good agreement with the all-electron results obtained using the FLEUR code with a plane-wave energy cutoff of approximately 60 Ry.}, keywords = {All-electron calculation,Condensed matter,Density functional theory,Plane wave,Pseudopotential} } @@ -439,7 +477,7 @@ @article{Schubert2023 issn = {0021-9606}, doi = {10.1063/5.0138610}, urldate = {2023-04-20}, - abstract = {Koopmans spectral functionals are a class of orbital-density-dependent functionals designed to accurately predict spectroscopic properties. They do so markedly better than their Kohn{\textendash}Sham density-functional theory counterparts, as demonstrated in earlier works on benchmarks of molecules and bulk systems. This work is a complementary study where{\textemdash}instead of comparing against real, many-electron systems{\textemdash}we test Koopmans spectral functionals on Hooke's atom, a toy two-electron system that has analytical solutions for particular strengths of its harmonic confining potential. As these calculations clearly illustrate, Koopmans spectral functionals do an excellent job of describing Hooke's atom across a range of confining potential strengths. This work also provides broader insights into the features and capabilities of Koopmans spectral functionals more generally.} + abstract = {Koopmans spectral functionals are a class of orbital-density-dependent functionals designed to accurately predict spectroscopic properties. They do so markedly better than their Kohn--Sham density-functional theory counterparts, as demonstrated in earlier works on benchmarks of molecules and bulk systems. This work is a complementary study where---instead of comparing against real, many-electron systems---we test Koopmans spectral functionals on Hooke's atom, a toy two-electron system that has analytical solutions for particular strengths of its harmonic confining potential. As these calculations clearly illustrate, Koopmans spectral functionals do an excellent job of describing Hooke's atom across a range of confining potential strengths. This work also provides broader insights into the features and capabilities of Koopmans spectral functionals more generally.} } @article{Skone2016, @@ -451,7 +489,7 @@ @article{Skone2016 volume = {93}, number = {23}, pages = {235106}, - publisher = {{American Physical Society}}, + publisher = {American Physical Society}, issn = {24699969}, doi = {10.1103/PhysRevB.93.235106}, urldate = {2020-05-10}, @@ -476,7 +514,7 @@ @article{vanSetten2018 } @article{Wing2021, - title = {Band Gaps of Crystalline Solids from {{Wannier-localization}}{\textendash}Based Optimal Tuning of a Screened Range-Separated Hybrid Functional}, + title = {Band Gaps of Crystalline Solids from {{Wannier-localization}}--Based Optimal Tuning of a Screened Range-Separated Hybrid Functional}, author = {Wing, Dahvyd and Ohad, Guy and Haber, Jonah B. and Filip, Marina R. and Gant, Stephen E. and Neaton, Jeffrey B. and Kronik, Leeor}, year = {2021}, month = aug, @@ -484,7 +522,13 @@ @article{Wing2021 volume = {118}, number = {34}, pages = {e2104556118}, - publisher = {{Proceedings of the National Academy of Sciences}}, + publisher = {Proceedings of the National Academy of Sciences}, doi = {10.1073/pnas.2104556118}, urldate = {2022-03-22} } + +@misc{zotero-3342, + title = {Koopmans {{Spectral Functionals}} in {{Periodic Boundary Conditions}} {\textbar} {{Journal}} of {{Chemical Theory}} and {{Computation}}}, + url = {https://pubs.acs.org/doi/full/10.1021/acs.jctc.2c00161}, + urldate = {2024-03-05} +} diff --git a/src/koopmans/settings/_utils.py b/src/koopmans/settings/_utils.py index bd6b440a8..11f983987 100644 --- a/src/koopmans/settings/_utils.py +++ b/src/koopmans/settings/_utils.py @@ -130,14 +130,14 @@ def __setitem__(self, key: str, value: Any): self.pop(key, None) return - # Insisting that all values corresponding to paths are absolute and are Path objects + # Insisting that all values corresponding to paths are relative and are Path objects if key in self.are_paths: if isinstance(value, str): value = Path(value) elif not isinstance(value, Path): raise ValueError(f'{key} must be either a string or a Path') - if not value.is_absolute(): - value = (self.directory / value).resolve() + if value.is_absolute() and key not in ['pseudo_dir', 'pseudo_directory']: + raise ValueError(f'{key} must be a relative path') # Parse any units provided if key in self.physicals: diff --git a/src/koopmans/utils/_xml.py b/src/koopmans/utils/_xml.py index b15664d74..767b6cf0d 100644 --- a/src/koopmans/utils/_xml.py +++ b/src/koopmans/utils/_xml.py @@ -1,6 +1,6 @@ import xml.etree.ElementTree as ET from pathlib import Path -from typing import List, Tuple +from typing import List import numpy as np @@ -60,7 +60,7 @@ def read_xml_array( # Extract the array array_xml = np.zeros((nr_xml[2], nr_xml[1], nr_xml[0]), dtype=float) for k in range(nr_xml[2]): - current_name = 'z.' + str(k % (nr_xml[2]-1)+1) + current_name = 'z.' + str(k % (nr_xml[2] - 1) + 1) entry = branch.find(current_name) assert isinstance(entry, ET.Element) text = entry.text @@ -68,7 +68,7 @@ def read_xml_array( rho_tmp = np.array(text.split('\n')[1:-1], dtype=float) for j in range(nr_xml[1]): for i in range(nr_xml[0]): - array_xml[k, j, i] = rho_tmp[(j % (nr_xml[1]-1))*(nr_xml[0]-1)+(i % (nr_xml[0]-1))] + array_xml[k, j, i] = rho_tmp[(j % (nr_xml[1] - 1))*(nr_xml[0] - 1) + (i % (nr_xml[0] - 1))] array_xml *= norm_const if retain_final_element: diff --git a/src/koopmans/utils/_xsf.py b/src/koopmans/utils/_xsf.py index 84604a670..5fe9cafcf 100644 --- a/src/koopmans/utils/_xsf.py +++ b/src/koopmans/utils/_xsf.py @@ -17,7 +17,7 @@ def write_xsf(filename: Path, atoms: Atoms, arrays: List[np.ndarray], nr_xml: Tu for k in range(nr_xml[2]): for j in range(nr_xml[1]): for i in range(nr_xml[0]): - array_xsf[k, j, i] = array[k % (nr_xml[2]-1), j % (nr_xml[1]-1), i % (nr_xml[0]-1)] + array_xsf[k, j, i] = array[k % (nr_xml[2] - 1), j % (nr_xml[1] - 1), i % (nr_xml[0] - 1)] arrays_xsf.append(array_xsf) cell_parameters = atoms.get_cell() diff --git a/src/koopmans/workflows/_convergence_ml.py b/src/koopmans/workflows/_convergence_ml.py index 3d64d7d8b..ef783cf39 100644 --- a/src/koopmans/workflows/_convergence_ml.py +++ b/src/koopmans/workflows/_convergence_ml.py @@ -42,7 +42,7 @@ def _fromjsondct(cls, bigdct: Dict[str, Any], override: Dict[str, Any] = {}): """ try: snapshots_file = bigdct['atoms']['atomic_positions'].pop('snapshots') - except: + except KeyError: raise ValueError(f'To calculate a trajectory, please provide a xyz-file containing the atomic positions ' 'of the snapshots in the setup-block of the json-input file.') @@ -98,10 +98,11 @@ def inititalize_directories(self, grid_search_mode: bool): if not (grid_search_mode and convergence_point != self.convergence_points[-1]): self.dirs[f'convergence_{convergence_point}'] = self.dirs[f'convergence_pred'] / \ f"predicted_after_{convergence_point+1}" - # in case of the grid search we won't produce all the plots, hence we also don't need the corresponding folders + # in case of the grid search we won't produce all the plots, hence we also don't need the corresponding + # folders if not grid_search_mode: - self.dirs[f'convergence_final_results_{convergence_point}'] = self.dirs[f'convergence_final_results'] / \ - f"predicted_after_{convergence_point+1}" + self.dirs[f'convergence_final_results_{convergence_point}'] = \ + self.dirs[f'convergence_final_results'] / f"predicted_after_{convergence_point+1}" # create all directories for dir in self.dirs.values(): @@ -184,7 +185,7 @@ def convert_to_list(param, type): try: precompute_parameters_of_radial_basis( self.ml.n_max, self.ml.l_max, self.ml.r_min, self.ml.r_max) - except: + except ValueError: # skip this set of parameters if it is not possible to find the coefficients of the radial basis # function utils.warn( diff --git a/src/koopmans/workflows/_koopmans_dfpt.py b/src/koopmans/workflows/_koopmans_dfpt.py index eda421c47..0cf5223c1 100644 --- a/src/koopmans/workflows/_koopmans_dfpt.py +++ b/src/koopmans/workflows/_koopmans_dfpt.py @@ -167,13 +167,14 @@ def _run(self): base_outdir = self.calculator_parameters['pw'].outdir base_outdir.mkdir(exist_ok=True) scf_calcs = [c for c in self.calculations if isinstance(c, PWCalculator) and c.parameters.calculation == 'scf'] - init_outdir = scf_calcs[-1].parameters.outdir - if self.parameters.from_scratch and init_outdir != base_outdir: - utils.symlink(f'{init_outdir}/*', base_outdir) + init_scf = scf_calcs[-1] + # if self.parameters.from_scratch and init_outdir != base_outdir: + # utils.symlink(f'{init_outdir}/*', base_outdir) # Convert from wannier to KC self.print('Conversion to Koopmans format', style='subheading') wann2kc_calc = self.new_calculator('wann2kc') + self.link(init_scf, init_scf.parameters.outdir, wann2kc_calc, wann2kc_calc.parameters.outdir) self.run_calculator(wann2kc_calc) # Calculate screening parameters diff --git a/src/koopmans/workflows/_wannierize.py b/src/koopmans/workflows/_wannierize.py index 494d050dd..1f84c2213 100644 --- a/src/koopmans/workflows/_wannierize.py +++ b/src/koopmans/workflows/_wannierize.py @@ -119,18 +119,19 @@ def _run(self): # Run PW scf and nscf calculations # PWscf needs only the valence bands - calc_pw = self.new_calculator('pw') - calc_pw.parameters.pop('nbnd', None) - calc_pw.directory = 'wannier' - calc_pw.prefix = 'scf' + calc_scf = self.new_calculator('pw') + calc_scf.parameters.pop('nbnd', None) + calc_scf.directory = 'wannier' + calc_scf.prefix = 'scf' if self._scf_kgrid: - calc_pw.parameters.kpts = self._scf_kgrid - self.run_calculator(calc_pw) + calc_scf.parameters.kpts = self._scf_kgrid + self.run_calculator(calc_scf) - calc_pw = self.new_calculator('pw', calculation='nscf', nosym=True, noinv=True) - calc_pw.directory = 'wannier' - calc_pw.prefix = 'nscf' - self.run_calculator(calc_pw) + calc_nscf = self.new_calculator('pw', calculation='nscf', nosym=True, noinv=True) + calc_nscf.directory = 'wannier' + calc_nscf.prefix = 'nscf' + self.link(calc_scf, calc_scf.parameters.outdir, calc_nscf, calc_nscf.parameters.outdir) + self.run_calculator(calc_nscf) if self.parameters.init_orbitals in ['mlwfs', 'projwfs'] \ and self.parameters.init_empty_orbitals in ['mlwfs', 'projwfs']: @@ -170,8 +171,8 @@ def _run(self): # 2) standard pw2wannier90 calculation calc_p2w = self.new_calculator('pw2wannier', directory=w90_dir, - spin_component=block.spin, - outdir=calc_pw.parameters.outdir) + spin_component=block.spin) + self.link(calc_nscf, calc_nscf.parameters.outdir, calc_p2w, calc_p2w.parameters.outdir) calc_p2w.prefix = 'pw2wan' self.run_calculator(calc_p2w) @@ -231,17 +232,12 @@ def _run(self): calc_pw_bands.parameters.prefix += '_bands' # Link the save directory so that the bands calculation can use the old density - if self.parameters.from_scratch: - [src, dest] = [(c.parameters.outdir / c.parameters.prefix).with_suffix('.save') - for c in [calc_pw, calc_pw_bands]] - - if dest.exists(): - shutil.rmtree(str(dest)) - shutil.copytree(src, dest) + self.link(calc_nscf, (calc_nscf.parameters.outdir / calc_nscf.parameters.prefix).with_suffix('.save'), + calc_pw_bands, (calc_pw_bands.parameters.outdir / calc_pw_bands.parameters.prefix).with_suffix('.save')) self.run_calculator(calc_pw_bands) # Calculate a projected DOS - pseudos = [read_pseudo_file(calc_pw_bands.parameters.pseudo_dir / p) for p in + pseudos = [read_pseudo_file(calc_pw_bands.directory / calc_pw_bands.parameters.pseudo_dir / p) for p in self.pseudopotentials.values()] if all([p['header']['number_of_wfc'] > 0 for p in pseudos]): calc_dos = self.new_calculator('projwfc', filpdos=self.name) diff --git a/src/koopmans/workflows/_workflow.py b/src/koopmans/workflows/_workflow.py index 67f213533..384b47507 100644 --- a/src/koopmans/workflows/_workflow.py +++ b/src/koopmans/workflows/_workflow.py @@ -612,9 +612,9 @@ def new_calculator(self, calc_type: str, directory: Optional[Path] = None, kpts: Optional[Union[List[int], BandPath]] = None, - **kwargs) -> T: # type: ignore[type-var, misc] + **kwargs) -> calculators.Calc: # type: ignore[type-var, misc] - calc_class: Type[T] + calc_class: calculators.CalcType if calc_type == 'kcp': calc_class = calculators.KoopmansCPCalculator @@ -653,7 +653,7 @@ def new_calculator(self, all_kwargs['kpts'] = kpts if kpts is not None else self.kpoints.grid # Add further information to the calculator as required - for kw in ['pseudopotentials', 'pseudo_dir', 'gamma_only', 'kgrid', 'kpath', 'koffset', 'plotting']: + for kw in ['pseudopotentials', 'gamma_only', 'kgrid', 'kpath', 'koffset', 'plotting']: if kw not in all_kwargs and calculator_parameters.is_valid(kw): val: Any if kw == 'kgrid': @@ -668,8 +668,6 @@ def new_calculator(self, val = self.kpoints.offset elif kw == 'gamma_only': val = self.kpoints.gamma_only - elif kw == 'pseudo_dir': - val = self.parameters.pseudo_directory else: val = getattr(self, kw) all_kwargs[kw] = val @@ -681,6 +679,12 @@ def new_calculator(self, if directory is not None: calc.directory = directory + # Link the pseudopotentials if relevant + if calculator_parameters.is_valid('pseudo_dir'): + for pseudo in self.pseudopotentials.values(): + self.link(None, self.parameters.pseudo_directory / pseudo, calc, Path('pseudopotentials') / pseudo) + calc.parameters.pseudo_dir = 'pseudopotentials' + return calc def primitive_to_supercell(self, matrix: Optional[npt.NDArray[np.int_]] = None, **kwargs): @@ -882,6 +886,21 @@ def load_old_calculator(self, qe_calc: calculators.Calc) -> bool: return old_calc.is_complete() + def link(self, src_calc: calculators.Calc | None, src_path: Path, dest_calc: calculators.Calc, dest_path: Path) -> None: + """ + Link a file from one calculator to another + + Paths must be provided relative to the the calculator's directory i.e. calc.directory, unless src_calc is None + """ + + if src_path.is_absolute() and src_calc is not None: + raise ValueError(f'"src_path" in {self.__class__.__name__}.link() must be a relative path if a ' + f'"src_calc" is provided') + if dest_path.is_absolute(): + raise ValueError(f'"dest_path" in {self.__class__.__name__}.link() must be a relative path') + + dest_calc.link_file(src_calc, src_path, dest_path) + def print(self, text: str = '', style: str = 'body', **kwargs: Any): if style == 'body': utils.indented_print(str(text), self.print_indent + 1, **kwargs)