Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from os import PathLike 

2from pathlib import Path 

3from typing import Iterable, Mapping, Any 

4from abc import ABC, abstractmethod 

5 

6from ase.calculators.abc import GetOutputsMixin 

7from ase.calculators.calculator import BaseCalculator 

8 

9 

10def read_stdout(args, createfile=None): 

11 """Run command in tempdir and return standard output. 

12 

13 Helper function for getting version numbers of DFT codes. 

14 Most DFT codes don't implement a --version flag, so in order to 

15 determine the code version, we just run the code until it prints 

16 a version number.""" 

17 import tempfile 

18 from subprocess import Popen, PIPE 

19 with tempfile.TemporaryDirectory() as directory: 

20 if createfile is not None: 

21 path = Path(directory) / createfile 

22 path.touch() 

23 proc = Popen(args, 

24 stdout=PIPE, 

25 stderr=PIPE, 

26 stdin=PIPE, 

27 cwd=directory, 

28 encoding='ascii') 

29 stdout, _ = proc.communicate() 

30 # Exit code will be != 0 because there isn't an input file 

31 return stdout 

32 

33 

34class CalculatorTemplate(ABC): 

35 def __init__(self, name: str, implemented_properties: Iterable[str]): 

36 self.name = name 

37 self.implemented_properties = frozenset(implemented_properties) 

38 

39 @abstractmethod 

40 def write_input(self, directory, atoms, parameters, properties): 

41 ... 

42 

43 @abstractmethod 

44 def execute(self, directory, profile): 

45 ... 

46 

47 @abstractmethod 

48 def read_results(self, directory: PathLike) -> Mapping[str, Any]: 

49 ... 

50 

51 

52class GenericFileIOCalculator(BaseCalculator, GetOutputsMixin): 

53 def __init__(self, *, template, profile, directory, parameters=None): 

54 self.template = template 

55 self.profile = profile 

56 

57 # Maybe we should allow directory to be a factory, so 

58 # calculators e.g. produce new directories on demand. 

59 self.directory = Path(directory) 

60 

61 super().__init__(parameters) 

62 

63 def set(self, *args, **kwargs): 

64 raise RuntimeError('No setting parameters for now, please. ' 

65 'Just create new calculators.') 

66 

67 def __repr__(self): 

68 return '{}({})'.format(type(self).__name__, self.template.name) 

69 

70 @property 

71 def implemented_properties(self): 

72 return self.template.implemented_properties 

73 

74 @property 

75 def name(self): 

76 return self.template.name 

77 

78 def write_inputfiles(self, atoms, properties): 

79 # SocketIOCalculators like to write inputfiles 

80 # without calculating. 

81 self.directory.mkdir(exist_ok=True, parents=True) 

82 self.template.write_input( 

83 atoms=atoms, 

84 parameters=self.parameters, 

85 properties=properties, 

86 directory=self.directory) 

87 

88 def calculate(self, atoms, properties, system_changes): 

89 self.write_inputfiles(atoms, properties) 

90 self.template.execute(self.directory, self.profile) 

91 self.results = self.template.read_results(self.directory) 

92 # XXX Return something useful? 

93 

94 def _outputmixin_get_results(self): 

95 return self.results