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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

#!/usr/bin/env python 

 

"""Module to register applications to libEnsemble""" 

 

import os 

import logging 

from mpi4py import MPI 

 

logger = logging.getLogger(__name__) 

#For debug messages in this module - uncomment (see libE.py to change root logging level) 

#logger.setLevel(logging.DEBUG) 

 

class RegistrationException(Exception): pass 

 

 

class Application: 

 

'''An application is an executable user-program (e.g. Implementing a sim/gen function).''' 

 

def __init__(self, full_path, calc_type='sim', desc=None, default=True): 

'''Instantiate a new Application instance.''' 

self.full_path = full_path 

self.calc_type = calc_type 

self.default = default 

self.calc_dir, self.exe = os.path.split(full_path) 

 

#Dont change:Why? - cos will use this name to delete jobs in database - see del_apps(), del_jobs() 

self.name = self.exe + '.' + self.calc_type + 'func' 

self.desc = desc or (self.exe + ' ' + self.calc_type + ' function') 

 

#May merge this into job_controller 

class Register(): 

 

'''Registers and stores user applications 

 

Attributes 

---------- 

default_registry : Obj: Register or inherited class. 

A class attribute holding the default registry. 

 

''' 

 

default_registry = None 

 

@property 

def sim_default_app(self): 

"""Return the default simulation app.""" 

return self._default_apps['sim'] 

 

@property 

def gen_default_app(self): 

"""Return the default generator app.""" 

return self._default_apps['gen'] 

 

def __init__(self, default=True): 

'''Instantiate a new Register instance 

 

Parameters 

---------- 

 

default: Boolean, optional 

Make this the default_registry (Default is True) 

 

 

Note: Currently, only a default registry is supported. 

 

''' 

self._default_apps = {'sim' : None, 'gen': None} 

69 ↛ exitline 69 didn't return from function '__init__', because the condition on line 69 was never false if default: 

Register.default_registry = self 

 

def register_calc(self, full_path, calc_type='sim', desc=None, default=True): 

'''Registers a user application to libEnsemble 

 

Parameters 

---------- 

 

full_path: String 

The full path of the user application to be registered. 

 

calc_type: String 

Calculation type: Is this application part of a 'sim' or 'gen' function. 

 

desc: String, optional 

Description of this application. 

 

default: Boolean, optional 

Register to the default_registry (Default is True). 

 

 

''' 

92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true if not default: 

return # Always default currently 

94 ↛ 95line 94 didn't jump to line 95, because the condition on line 94 was never true if calc_type not in self._default_apps: 

raise RegistrationException("Unrecognized calculation type", calc_type) 

96 ↛ 97line 96 didn't jump to line 97, because the condition on line 96 was never true if self._default_apps[calc_type] is not None: 

raise RegistrationException("Default {} app already set".format(calc_type)) 

self._default_apps[calc_type] = Application(full_path, calc_type, desc, default) 

 

 

class BalsamRegister(Register): 

 

'''Registers and stores user applications in libEnsemble and Balsam''' 

 

@staticmethod 

def del_apps(): 

""" Deletes all Balsam apps whose names contains substring .simfunc or .genfunc""" 

import balsam.launcher.dag 

from balsam.service import models 

AppDef = models.ApplicationDefinition 

 

#Some error handling on deletes.... is it internal 

for app_type in ['.simfunc', '.genfunc']: 

deletion_objs = AppDef.objects.filter(name__contains=app_type) 

if deletion_objs: 

for del_app in deletion_objs.iterator(): 

logger.debug("Deleting app {}".format(del_app.name)) 

deletion_objs.delete() 

 

@staticmethod 

def del_jobs(): 

""" Deletes all Balsam jobs whose names contains substring .simfunc or .genfunc""" 

import balsam.launcher.dag 

from balsam.service import models 

Job = models.BalsamJob 

 

for app_type in ['.simfunc', '.genfunc']: 

deletion_objs = Job.objects.filter(name__contains=app_type) 

if deletion_objs: 

for del_job in deletion_objs.iterator(): 

logger.debug("Deleting job {}".format(del_job.name)) 

deletion_objs.delete() 

 

##May be able to use union function - to combine - see queryset help. Eg (not tested) 

#del_simfuncs = Job.objects.filter(name__contains='.simfunc') 

#del_genfuncs = Job.objects.filter(name__contains='.genfunc') 

#deletion_objs = deletion_objs.union() 

 

@staticmethod 

def add_app(name, exepath, desc): 

""" Add application to Balsam database """ 

import balsam.launcher.dag 

from balsam.service import models 

AppDef = models.ApplicationDefinition 

app = AppDef() 

app.name = name 

app.executable = exepath 

app.description = desc 

#app.default_preprocess = '' # optional 

#app.default_postprocess = '' # optional 

app.save() 

logger.debug("Added App {}".format(app.name)) 

 

def __init__(self, default=True): 

'''Instantiate a new BalsamRegister instance 

 

Parameters 

---------- 

 

default: Boolean, optional 

Make this the default_registry (Default is True) 

 

 

Note: Currently, only a default registry is supported. 

 

''' 

 

super().__init__(default) 

#Check for empty database if poss 

#And/or compare with whats in database and only empty if I need to 

 

#Currently not deleting as will delete the top level job - ie. the one running. 

 

#Will put MPI_MODE in a settings module... 

if MPI.COMM_WORLD.Get_rank() == 0: 

BalsamRegister.del_apps() 

BalsamRegister.del_jobs() 

 

 

def register_calc(self, full_path, calc_type='sim', desc=None, default=True): 

'''Registers a user applications to libEnsemble and Balsam 

 

Parameters 

---------- 

 

full_path: String 

The full path of the user application to be registered. 

 

calc_type: String 

Calculation type: Is this application part of a 'sim' or 'gen' function. 

 

desc: String, optional 

Description of this application. 

 

default: Boolean, optional 

Register to the default_registry (Default is True). 

 

 

''' 

super().register_calc(full_path, calc_type, desc, default) 

#Req python 3 to exclude args - but as Balsam requires 3.6+ I may do - or is it only __init__() 

 

#Get from one place - so always matches 

if calc_type in self._default_apps: 

calc_name = self._default_apps[calc_type].name 

desc = self._default_apps[calc_type].desc 

else: 

raise RegistrationException("Unrecognized calculation type", calc_type) 

 

if MPI.COMM_WORLD.Get_rank() == 0: 

self.add_app(calc_name, full_path, desc)