Coverage for eminus/config.py: 85.39%

89 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-08 12:59 +0000

1# SPDX-FileCopyrightText: 2021 The eminus developers 

2# SPDX-License-Identifier: Apache-2.0 

3"""Consolidated configuration module.""" 

4 

5import numbers 

6import os 

7import pathlib 

8import sys 

9 

10from .logger import log 

11 

12 

13class ConfigClass: 

14 """Configuration class holding user specifiable variables. 

15 

16 An instance of this class will be set as the same name as this module. This will effectively 

17 make this module a singleton data class. 

18 """ 

19 

20 def __init__(self): 

21 """Initialize the ConfigClass object.""" 

22 self.use_torch = True # Use the faster Torch FFTs if available 

23 self.use_gpu = False # Disable GPU by default, since it is slower in my tests 

24 self.use_pylibxc = True # Use Libxc over PySCF if available since it is faster 

25 self.threads = None # Read threads from environment variables by default 

26 self.verbose = "INFO" # Only display warnings (and worse) by default 

27 

28 # ### Class properties ### 

29 

30 @property 

31 def use_torch(self): 

32 """Whether to use Torch or SciPy if Torch is installed.""" 

33 # Add the logic in the getter method so it does not run on initialization since importing 

34 # Torch is rather slow 

35 if self._use_torch: 

36 try: 

37 import torch # noqa: F401 

38 

39 return True # noqa: TRY300 

40 except ImportError: 

41 pass 

42 return False 

43 

44 @use_torch.setter 

45 def use_torch(self, value): 

46 self._use_torch = value 

47 

48 @property 

49 def use_gpu(self): 

50 """Whether to use Torch on the GPU if available.""" 

51 # Only use GPU if Torch is available 

52 if self.use_torch and self._use_gpu: 

53 import torch 

54 

55 return torch.cuda.is_available() 

56 return False 

57 

58 @use_gpu.setter 

59 def use_gpu(self, value): 

60 self._use_gpu = value 

61 

62 @property 

63 def use_pylibxc(self): 

64 """Whether to use pylibxc or PySCF for functionals if both are installed.""" 

65 if self._use_pylibxc: 

66 try: 

67 import pylibxc # noqa: F401 

68 

69 return True # noqa: TRY300 

70 except ImportError: 

71 pass 

72 return False 

73 

74 @use_pylibxc.setter 

75 def use_pylibxc(self, value): 

76 self._use_pylibxc = value 

77 

78 @property 

79 def threads(self): 

80 """Number of threads used in FFT calculations.""" 

81 if self._threads is None: 

82 try: 

83 if self.use_torch: 

84 import torch 

85 

86 return torch.get_num_threads() 

87 # Read the OMP threads for the default operators 

88 return int(os.environ["OMP_NUM_THREADS"]) 

89 except KeyError: 

90 return None 

91 return int(self._threads) 

92 

93 @threads.setter 

94 def threads(self, value): 

95 self._threads = value 

96 if isinstance(value, numbers.Integral): 

97 if self.use_torch: 

98 import torch 

99 

100 return torch.set_num_threads(value) 

101 os.environ["OMP_NUM_THREADS"] = str(value) 

102 return None 

103 

104 @property 

105 def verbose(self): 

106 """Logger verbosity level.""" 

107 return log.verbose 

108 

109 @verbose.setter 

110 def verbose(self, value): 

111 # Logic in setter to run it on initialization 

112 log.verbose = value 

113 

114 # ### Class methods ### 

115 

116 def info(self): 

117 """Print configuration and performance information.""" 

118 sys.stdout.write("--- Configuration infos ---\n") 

119 sys.stdout.write(f"Global verbosity : {self.verbose}\n") 

120 # Only print if PySCF or pylibxc is installed 

121 if not self.use_pylibxc: 

122 try: 

123 import pyscf # noqa: F401 

124 

125 sys.stdout.write("Libxc backend : PySCF\n") 

126 except ImportError: 

127 pass 

128 else: 

129 sys.stdout.write("Libxc backend : pylibxc\n") 

130 

131 sys.stdout.write( 

132 "\n--- Performance infos ---\n" 

133 f"FFT backend : {'Torch' if self.use_torch else 'SciPy'}\n" 

134 f"FFT device : {'GPU' if self.use_gpu else 'CPU'}\n" 

135 ) 

136 # Do not print threading information when using GPU 

137 if self.use_gpu: 

138 return 

139 # Check FFT threads 

140 if self.threads is None: 

141 sys.stdout.write( 

142 "FFT threads : 1\n" 

143 "INFO: No OMP_NUM_THREADS environment variable was found.\nTo improve " 

144 'performance, add "export OMP_NUM_THREADS=n" to your ".bashrc".\nMake sure to ' 

145 'replace "n", typically with the number of cores your CPU.\nTemporarily, you can ' 

146 'set them in your Python environment with "eminus.config.threads=n".\n' 

147 ) 

148 else: 

149 sys.stdout.write(f"FFT threads : {self.threads}\n") 

150 

151 

152# Do not initialize the class when Sphinx is running 

153# Since we set the class instance to the module name Sphinx will only document the main docstring of 

154# the class without the properties 

155if "sphinx-build" not in pathlib.Path(sys.argv[0]).name: 

156 sys.modules[__name__] = ConfigClass()