Coverage for /builds/kinetik161/ase/ase/nomad.py: 73.42%

79 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1import json 

2 

3import numpy as np 

4 

5import ase.units as units 

6from ase import Atoms 

7from ase.data import chemical_symbols 

8 

9nomad_api_template = ('https://labdev-nomad.esc.rzg.mpg.de/' 

10 'api/resolve/{hash}?format=recursiveJson') 

11 

12 

13def nmd2https(uri): 

14 """Get https URI corresponding to given nmd:// URI.""" 

15 assert uri.startswith('nmd://') 

16 return nomad_api_template.format(hash=uri[6:]) 

17 

18 

19def download(uri): 

20 """Download data at nmd:// URI as a NomadEntry object.""" 

21 try: 

22 from urllib2 import urlopen 

23 except ImportError: 

24 from urllib.request import urlopen 

25 

26 httpsuri = nmd2https(uri) 

27 response = urlopen(httpsuri) 

28 txt = response.read().decode('utf8') 

29 return json.loads(txt, object_hook=NomadEntry) 

30 

31 

32def read(fd, _includekeys=lambda key: True): 

33 """Read NomadEntry object from file.""" 

34 # _includekeys can be used to strip unnecessary keys out of a 

35 # downloaded nomad file so its size is suitable for inclusion 

36 # in the test suite. 

37 

38 def hook(dct): 

39 d = {k: dct[k] for k in dct if _includekeys(k)} 

40 return NomadEntry(d) 

41 

42 dct = json.load(fd, object_hook=hook) 

43 return dct 

44 

45 

46def section_system_to_atoms(section): 

47 """Covnert section_system into an Atoms object.""" 

48 assert section['name'] == 'section_system' 

49 numbers = section['atom_species'] 

50 numbers = np.array(numbers, int) 

51 numbers[numbers < 0] = 0 # We don't support Z < 0 

52 numbers[numbers >= len(chemical_symbols)] = 0 

53 positions = section['atom_positions']['flatData'] 

54 positions = np.array(positions).reshape(-1, 3) * units.m 

55 atoms = Atoms(numbers, positions=positions) 

56 atoms.info['nomad_uri'] = section['uri'] 

57 

58 pbc = section.get('configuration_periodic_dimensions') 

59 if pbc is not None: 

60 assert len(pbc) == 1 

61 pbc = pbc[0] # it's a list?? 

62 pbc = pbc['flatData'] 

63 assert len(pbc) == 3 

64 atoms.pbc = pbc 

65 

66 # celldisp? 

67 cell = section.get('lattice_vectors') 

68 if cell is not None: 

69 cell = cell['flatData'] 

70 cell = np.array(cell).reshape(3, 3) * units.m 

71 atoms.cell = cell 

72 

73 return atoms 

74 

75 

76def nomad_entry_to_images(section): 

77 """Yield the images from a Nomad entry. 

78 

79 The entry must contain a section_run. 

80 One atoms object will be yielded for each section_system.""" 

81 

82 

83class NomadEntry(dict): 

84 """An entry from the Nomad database. 

85 

86 The Nomad entry is represented as nested dictionaries and lists. 

87 

88 ASE converts each dictionary into a NomadEntry object which supports 

89 different actions. Some actions are only available when the NomadEntry 

90 represents a particular section.""" 

91 

92 def __init__(self, dct): 

93 # assert dct['type'] == 'nomad_calculation_2_0' 

94 # assert dct['name'] == 'calculation_context' 

95 # We could implement NomadEntries that represent sections. 

96 dict.__init__(self, dct) 

97 

98 @property 

99 def hash(self): 

100 # The hash is a string, so not __hash__ 

101 assert self['uri'].startswith('nmd://') 

102 return self['uri'][6:] 

103 

104 def toatoms(self): 

105 """Convert this NomadEntry into an Atoms object. 

106 

107 This NomadEntry must represent a section_system.""" 

108 return section_system_to_atoms(self) 

109 

110 def iterimages(self): 

111 """Yield Atoms object contained within this NomadEntry. 

112 

113 This NomadEntry must represent or contain a section_run.""" 

114 

115 if 'section_run' in self: 

116 run_sections = self['section_run'] 

117 else: 

118 assert self['name'] == 'section_run' 

119 run_sections = [self] # We assume that we are the section_run 

120 

121 for run in run_sections: 

122 systems = run['section_system'] 

123 for system in systems: 

124 atoms = section_system_to_atoms(system) 

125 atoms.info['nomad_run_gIndex'] = run['gIndex'] 

126 atoms.info['nomad_system_gIndex'] = system['gIndex'] 

127 

128 if self.get('name') == 'calculation_context': 

129 atoms.info['nomad_calculation_uri'] = self['uri'] 

130 yield atoms 

131 

132 

133def main(): 

134 uri = "nmd://N9Jqc1y-Bzf7sI1R9qhyyyoIosJDs/C74RJltyQeM9_WFuJYO49AR4gKuJ2" 

135 print(nmd2https(uri)) 

136 entry = download(uri) 

137 from ase.visualize import view 

138 view(list(entry.iterimages())) 

139 

140 

141if __name__ == '__main__': 

142 main()