source: issm/trunk-jpl/src/m/classes/clusters/fram.py@ 27262

Last change on this file since 27262 was 27262, checked in by jdquinn, 3 years ago

CHG: Custom cluster class for AWS ISSM solution server instance; typos; clean up

File size: 7.0 KB
Line 
1import subprocess
2
3import numpy as np
4
5from fielddisplay import fielddisplay
6try:
7 from fram_settings import fram_settings
8except ImportError:
9 print('You need fram_settings.py to proceed, check presence and sys.path')
10from helpers import *
11from pairoptions import pairoptions
12from IssmConfig import IssmConfig
13from issmscpin import issmscpin
14from issmscpout import issmscpout
15from issmssh import issmssh
16from QueueRequirements import QueueRequirements
17
18
19class fram(object):
20 """FRAM cluster class definition
21
22 This is a SLURM queue
23 The priorities are based on a point system, reservation when reaching 20000 and earning 1 point per min.
24 - Devel queue starts at 19990
25 - Normal starts at 19940
26 - Normal unpri atarts at 19400
27
28 Jobs can be:
29 - normal (4 to 30 nodes, more if asked, 48h max walltime, 60Gb per nodes)
30 - bigmem for big memory nodes (8 512Gb nodes and 2 6Tb nodes, shared nodes, 14days max walltime
31
32 Usage:
33 cluster = fram()
34 """
35
36 def __init__(self, *args): # {{{
37 self.name = 'fram'
38 self.login = ''
39 self.numnodes = 2
40 self.cpuspernode = 20
41 self.mem = 1.6
42 self.queue = 'normal'
43 self.time = 2 * 60
44 self.codepath = ''
45 self.executionpath = ''
46 self.interactive = 0
47 self.port = []
48 self.accountname = ''
49 self.profiling = 0
50 # Use provided options to change fields
51 options = pairoptions(*args)
52
53 # Initialize cluster using user settings if provided
54 self = fram_settings(self)
55
56 # OK get other fields
57 self = options.AssignObjectFields(self)
58 self.np = self.numnodes * self.cpuspernode
59 # }}}
60
61 def __repr__(self): # {{{
62 # Display the object
63 s = "class fram object:"
64 s = "%s\n%s" % (s, fielddisplay(self, 'name', 'name of the cluster'))
65 s = "%s\n%s" % (s, fielddisplay(self, 'login', 'login'))
66 s = "%s\n%s" % (s, fielddisplay(self, 'numnodes', 'number of nodes'))
67 s = "%s\n%s" % (s, fielddisplay(self, 'cpuspernode', 'number of nodes per CPUs'))
68 s = "%s\n%s" % (s, fielddisplay(self, 'mem', 'memory per CPU'))
69 s = "%s\n%s" % (s, fielddisplay(self, 'queue', 'name of the queue (normal (D), short, singlenode, multinode, devel)'))
70 s = "%s\n%s" % (s, fielddisplay(self, 'time', 'walltime requested in minutes'))
71 s = "%s\n%s" % (s, fielddisplay(self, 'codepath', 'code path on the cluster'))
72 s = "%s\n%s" % (s, fielddisplay(self, 'executionpath', 'execution path on the cluster'))
73 s = "%s\n%s" % (s, fielddisplay(self, 'interactive', ''))
74 s = "%s\n%s" % (s, fielddisplay(self, 'accountname', 'your cluster account'))
75 s = "%s\n%s" % (s, fielddisplay(self, 'profiling', 'enable profiling if 1 default is 0'))
76 return s
77 # }}}
78
79 def checkconsistency(self, md, solution, analyses): # {{{
80 # Queue dictionary gives queue name as key and max walltime and CPUs as var
81 queuedict = {'normal': [2 * 24 * 60, 2048],
82 'devel': [4 * 60, 2048]}
83 QueueRequirements(queuedict, self.queue, self.np, self.time)
84
85 # Miscellaneous
86 if not self.login:
87 md = md.checkmessage('login empty')
88 if not self.codepath:
89 md = md.checkmessage('codepath empty')
90 if not self.executionpath:
91 md = md.checkmessage('executionpath empty')
92 if self.interactive == 1:
93 md = md.checkmessage('interactive mode not implemented')
94 return self
95 # }}}
96
97 def BuildQueueScript(self, dirname, modelname, solution, io_gather, isvalgrind, isgprof, isdakota, isoceancoupling): # {{{
98 executable = 'issm.exe'
99 if isdakota:
100 version = IssmConfig('_DAKOTA_VERSION_')[0:2]
101 version = float(version)
102 if version >= 6:
103 executable = 'issm_dakota.exe'
104 if isoceancoupling:
105 executable = 'issm_ocean.exe'
106 # Write queuing script
107 shortname = modelname[0:min(12, len(modelname))]
108 fid = open(modelname + '.queue', 'w')
109
110 fid.write('#!/bin/bash -l\n')
111 fid.write('#SBATCH --job-name=%s \n' % shortname)
112 fid.write('#SBATCH --partition %s \n' % self.queue)
113 fid.write('#SBATCH --nodes=%i' % self.numnodes)
114 fid.write('#SBATCH --ntasks-per-nodes==%i \n' % self.cpuspernode)
115 fid.write('#SBATCH --time=%s\n' % self.time) #walltime is minutes
116 fid.write('#SBATCH --mem-per-cpu=%iGB\n' % self.mem) # mem is in GB
117 if (np.mod(self.np, 16) + np.mod(self.np, 20)) == 0:
118 fid.write('#SBATCH --ntask=%i\n' % self.np)
119 fid.write('#SBATCH --account=%s\n' % self.accountname)
120 fid.write('#SBATCH --output %s/%s /%s.outlog \n' % (self.executionpath, dirname, modelname))
121 fid.write('#SBATCH --error %s/%s /%s.errlog \n\n' % (self.executionpath, dirname, modelname))
122
123 fid.write('export ISSM_DIR="%s/../ "\n' % self.codepath)
124 fid.write('module restore system\n')
125 fid.write('module load load Automake/1.15.1-GCCcore-6.3.0\n')
126 fid.write('module load libtool/2.4.6-GCCcore-6.3.0\n')
127 fid.write('module load CMake/3.9.1\n')
128 fid.write('module load PETSc/3.8.0-intel-2017a-Python-2.7.13\n')
129 fid.write('module load ParMETIS/4.0.3-intel-2017a\n')
130 fid.write('cd %s/%s/ \n\n' % (self.executionpath, dirname))
131 if self.profiling:
132 fid.write('module load perf-report\n')
133 fid.write('perf-report mpirun -np %i %s/%s %s %s/%s %s\n' % (self.np, self.codepath, executable, str(solution), self.executionpath, dirname, modelname))
134 else:
135 fid.write('mpirun -np %i %s/%s %s %s/%s %s\n' % (self.np, self.codepath, executable, str(solution), self.executionpath, dirname, modelname))
136 fid.close()
137 # }}}
138
139 def UploadQueueJob(self, modelname, dirname, filelist): # {{{
140 # Compress the files into one zip
141 compressstring = 'tar -zcf %s.tar.gz ' % dirname
142 for file in filelist:
143 compressstring += ' {}'.format(file)
144 subprocess.call(compressstring, shell=True)
145
146 print('uploading input file and queuing script')
147 issmscpout(self.name, self.executionpath, self.login, self.port, [dirname + '.tar.gz'])
148
149 # }}}
150 def LaunchQueueJob(self, modelname, dirname, filelist, restart, batch): # {{{
151 print('launching solution sequence on remote cluster')
152 if not isempty(restart):
153 launchcommand = 'cd %s && cd %s && sbatch %s.queue' % (self.executionpath, dirname, modelname)
154 else:
155 launchcommand = 'cd %s && rm -rf ./%s && mkdir %s && cd %s && mv ../%s.tar.gz ./ && tar -zxf %s.tar.gz && sbatch %s.queue' % (self.executionpath, dirname, dirname, dirname, dirname, dirname, modelname)
156 issmssh(self.name, self.login, self.port, launchcommand)
157 # }}}
158 def Download(self, dirname, filelist): # {{{
159 # Copy files from cluster to current directory
160 directory = '%s/%s/' % (self.executionpath, dirname)
161 issmscpin(self.name, self.login, self.port, directory, filelist)
162 # }}}
Note: See TracBrowser for help on using the repository browser.