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

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

import time 

import logging 

import shutil 

import os 

 

from grond.config import read_config, write_config 

from grond import meta, run_info 

from grond.problems.base import load_optimiser_info, load_problem_info, \ 

ModelHistory 

 

op = os.path 

 

logger = logging.getLogger('grond.environment') 

 

 

class GrondEnvironmentError(meta.GrondError): 

pass 

 

 

class EventSelectionFailed(GrondEnvironmentError): 

pass 

 

 

class NoCurrentEventAvailable(GrondEnvironmentError): 

def __init__(self, message='no current event available'): 

GrondEnvironmentError.__init__(self, message) 

 

 

class NoEventSelectionAvailable(GrondEnvironmentError): 

def __init__(self, message='no event selection available'): 

GrondEnvironmentError.__init__(self, message) 

 

 

class NoRundirAvailable(GrondEnvironmentError): 

def __init__(self, message='no rundir available'): 

GrondEnvironmentError.__init__(self, message) 

 

 

class NoPlotCollectionManagerAvailable(GrondEnvironmentError): 

def __init__(self, message='no plot collection manager available'): 

GrondEnvironmentError.__init__(self, message) 

 

 

class Environment(object): 

 

def __init__(self, args=None, config=None, event_names=None): 

 

self._current_event_name = None 

self._selected_event_names = None 

self._config = None 

self._plot_collection_manager = None 

 

if isinstance(args, str): 

args = [args] 

 

if not args and not config: 

raise GrondEnvironmentError('missing arguments') 

 

if config and event_names: 

self._config_path = None 

self._rundir_path = None 

self._config = config 

 

if isinstance(event_names, str): 

event_names = [event_names] 

self.set_selected_event_names(event_names) 

 

elif op.isdir(args[0]): 

self._rundir_path = args[0] 

self._config_path = op.join(self._rundir_path, 'config.yaml') 

 

else: 

self._rundir_path = None 

self._config_path = args[0] 

self.set_selected_event_names(args[1:]) 

 

self.reset() 

 

@classmethod 

def discover(cls, rundir): 

running_fn = op.join(rundir, '.running') 

while op.exists(running_fn): 

try: 

cls.verify_rundir(rundir) 

return cls([rundir]) 

except GrondEnvironmentError: 

time.sleep(.25) 

raise GrondEnvironmentError('could not discover rundir') 

 

@staticmethod 

def verify_rundir(rundir_path): 

files = [ 

'config.yaml', 

'problem.yaml', 

'optimiser.yaml', 

'misfits' 

] 

for fn in files: 

if not op.exists(op.join(rundir_path, fn)): 

raise GrondEnvironmentError('inconsistent rundir') 

 

def copy(self, destination, force=False): 

''' Copy the environment and return it ''' 

files = [ 

'config.yaml', 

'problem.yaml', 

'optimiser.yaml', 

'misfits', 

'models', 

'choices', 

'chains' 

] 

 

if op.exists(destination) and not force: 

raise OSError('Directory %s already exists' % destination) 

 

destination = op.abspath(destination) 

os.makedirs(destination, exist_ok=True) 

 

for file in files: 

src = op.join(self._rundir_path, file) 

dest = op.join(destination, file) 

 

if not op.isfile(src): 

logger.debug('Cannot find file %s', src) 

continue 

logger.debug('Copying %s to %s', src, dest) 

 

shutil.copy(src, dest) 

 

cls = self.__class__ 

return cls(destination) 

 

def reset(self): 

self._histories = {} 

self._dataset = None 

self._optimiser = None 

self._problem = None 

 

def get_config(self): 

if self._config is None: 

self._config = read_config(self._config_path) 

 

return self._config 

 

def write_config(self): 

write_config(self.get_config(), self.get_config_path()) 

 

def get_available_event_names(self): 

return self.get_config().get_event_names() 

 

def set_current_event_name(self, event_name): 

self._current_event_name = event_name 

self.reset() 

 

def get_current_event_name(self): 

if self._current_event_name is None: 

try: 

self.get_rundir_path() 

self._current_event_name = self.get_problem().base_source.name 

except NoRundirAvailable: 

try: 

event_names = self.get_selected_event_names() 

if len(event_names) == 1: 

self._current_event_name = event_names[0] 

else: 

raise NoCurrentEventAvailable() 

 

except NoEventSelectionAvailable: 

raise NoCurrentEventAvailable() 

 

return self._current_event_name 

 

def set_selected_event_names(self, args): 

event_names = self.get_available_event_names() 

if len(args) == 0: 

if len(event_names) == 1: 

self._selected_event_names = event_names 

else: 

if not event_names: 

raise EventSelectionFailed( 

'No event file found, check your config!') 

raise EventSelectionFailed( 

'Ambiguous event selection. Select from available events:' 

'\n %s\n or \'all\' to use all available events' 

% '\n '.join(event_names)) 

 

elif len(args) == 1 and args[0] == 'all': 

self._selected_event_names = event_names 

 

else: 

self._selected_event_names = [] 

for event_name in args: 

if event_name not in event_names: 

self._selected_event_names = None 

raise EventSelectionFailed( 

'No such event: %s' % event_name) 

 

self._selected_event_names.append(event_name) 

 

@property 

def nevents_selected(self): 

return len(self.get_selected_event_names()) 

 

def get_selected_event_names(self): 

if self._selected_event_names is None: 

raise NoEventSelectionAvailable() 

 

return self._selected_event_names 

 

def get_dataset(self): 

if self._dataset is None: 

event_name = self.get_current_event_name() 

self._dataset = self.get_config().get_dataset(event_name) 

 

return self._dataset 

 

def set_rundir_path(self, path): 

self._rundir_path = path 

 

def get_rundir_path(self): 

if self._rundir_path is None: 

raise NoRundirAvailable() 

 

return self._rundir_path 

 

def have_rundir(self): 

return self._rundir_path is not None 

 

def get_run_info_path(self): 

return op.join(self.get_rundir_path(), 'run_info.yaml') 

 

def get_run_info(self): 

run_info_path = self.get_run_info_path() 

if not op.exists(run_info_path): 

info = run_info.RunInfo() 

return info 

else: 

return run_info.read_info(run_info_path) 

 

def set_run_info(self, info): 

run_info_path = self.get_run_info_path() 

run_info.write_info(info, run_info_path) 

 

def get_optimiser(self): 

if self._optimiser is None: 

try: 

self._optimiser = load_optimiser_info(self.get_rundir_path()) 

except NoRundirAvailable: 

self._optimiser = \ 

self.get_config().optimiser_config.get_optimiser() 

 

return self._optimiser 

 

def get_problem(self): 

if self._problem is None: 

try: 

self._problem = load_problem_info(self.get_rundir_path()) 

except NoRundirAvailable: 

self._problem = \ 

self.get_config().get_problem( 

self.get_dataset().get_event()) 

 

return self._problem 

 

def get_history(self, subset=None): 

if subset not in self._histories: 

self._histories[subset] = \ 

ModelHistory( 

self.get_problem(), 

nchains=self.get_optimiser().nchains, 

path=meta.xjoin(self.get_rundir_path(), subset)) 

 

self._histories[subset].ensure_bootstrap_misfits( 

self.get_optimiser()) 

 

return self._histories[subset] 

 

def set_plot_collection_manager(self, pcm): 

self._plot_collection_manager = pcm 

 

def get_plot_collection_manager(self): 

if self._plot_collection_manager is None: 

raise NoPlotCollectionManagerAvailable() 

 

return self._plot_collection_manager 

 

def setup_modelling(self): 

'''Must be called before any modelling can be done.''' 

logger.debug('Setting up modelling...') 

self.get_config().setup_modelling_environment(self.get_problem()) 

ds = self.get_dataset() 

for target in self.get_problem().targets: 

target.set_dataset(ds) 

 

def get_plot_classes(self): 

'''Discover all plot classes relevant for the setup.''' 

 

plots = set() 

try: 

plots.update(self.get_problem().get_plot_classes()) 

except GrondEnvironmentError: 

pass 

 

try: 

plots.update(self.get_optimiser().get_plot_classes()) 

except GrondEnvironmentError: 

pass 

 

try: 

for target in self.get_problem().targets: 

plots.update(target.get_plot_classes()) 

except GrondEnvironmentError: 

pass 

 

return sorted(list(plots), key=lambda plot: plot.name) 

 

def get_plots_path(self): 

try: 

return op.join(self.get_rundir_path(), 'plots') 

except NoRundirAvailable: 

return 'plots' 

 

def get_config_path(self): 

return self._config_path 

 

def is_running(self): 

return op.exists(self.get_rundir_path, '.running') 

 

 

__all__ = [ 

'GrondEnvironmentError', 

'EventSelectionFailed', 

'NoCurrentEventAvailable', 

'NoRundirAvailable', 

'NoPlotCollectionManagerAvailable', 

'Environment', 

]