diff --git a/nexus/executables/ntest b/nexus/executables/ntest index 0fa1de0b66..84304d9b1e 100755 --- a/nexus/executables/ntest +++ b/nexus/executables/ntest @@ -97,6 +97,8 @@ def value_diff(v1,v2,tol=1e-6): diff = v1!=v2 elif v1 is None and v2 is None: diff = False + elif hasattr(v1,'__len__') and hasattr(v2,'__len__') and len(v1)==0 and len(v2)==0: + None else: diff = True # unsupported types #end if @@ -1283,6 +1285,12 @@ def generic_extensions(): nassert(object_eq(o,osmall)) nassert(object_eq(o2,osmall2)) + o2 = oref.copy() + o = obj() + o.move_from_optional(o2,keys=['b','c',(3,4,5),'alpha','beta']) + nassert(object_eq(o,osmall)) + nassert(object_eq(o2,osmall2)) + o2 = oref.copy() o = obj() try: @@ -1311,6 +1319,12 @@ def generic_extensions(): nassert(object_eq(o,osmall)) nassert(object_eq(o2,osmall2)) + o2 = oref.copy() + o = obj() + o2.move_to_optional(o,keys=['b','c',(3,4,5),'alpha','beta']) + nassert(object_eq(o,osmall)) + nassert(object_eq(o2,osmall2)) + o2 = oref.copy() o = obj() try: @@ -1370,7 +1384,63 @@ def generic_extensions(): npass() #end try - + # test extract + nlabel('extract') + o = oref.copy() + o2 = o.extract() + nassert(len(o)==0) + nassert(object_eq(o2,oref)) + + o = oref.copy() + o2 = o.extract(['b','c',(3,4,5)]) + nassert(object_eq(o2,osmall)) + nassert(object_eq(o,osmall2)) + + o = oref.copy() + o2 = o.extract_optional(['b','c',(3,4,5),'alpha','beta']) + nassert(object_eq(o2,osmall)) + nassert(object_eq(o,osmall2)) + + + # test check_required + nlabel('check_required') + oref.check_required(['a','d',(3,4,5)]) + try: + oref.check_required(['alpha','beta']) + nfail() + except: + npass() + #end try + + # test check_types + nlabel('check_types') + types = dict( + a = int, + b = str, + c = tuple, + d = dict, + ) + types[3,4,5] = tuple + oref.check_types(types) + + types['b'] = int + try: + oref.check_types(types) + nfail() + except: + npass() + #end try + + types['b'] = str + types['alpha'] = float + types['beta'] = list + try: + oref.check_types_optional(types) + npass() + except: + nfail() + #end try + # test shallow_copy nlabel('shallow_copy') class DerivedObj(obj): @@ -1392,6 +1462,7 @@ def generic_extensions(): oi = do.inverse() nassert(sorted(oi.keys())==sorted(do.values())) nassert(sorted(oi.values())==sorted(do.keys())) + nassert(oi[1]=='a') nassert(oi.b=='b') nassert(oi[1,1,1]=='c') @@ -1694,6 +1765,573 @@ def settings_operation(): +def machines(): + # divert logging function + nlog_divert() + + nlabel('imports') + from random import randint + from generic import obj + from machines import Machine,Workstation,InteractiveCluster,Supercomputer + from machines import Job + from machines import get_machine,get_machine_name + + + nlabel('unimplemented_base_interface') + arg0 = None + arg1 = None + try: + Machine.query_queue(arg0) + nfail() + except: + npass() + #end try + try: + Machine.submit_jobs(arg0) + nfail() + except: + npass() + #end try + try: + Machine.process_job(arg0,arg1) + nfail() + except: + npass() + #end try + try: + Machine.process_job_options(arg0,arg1) + nfail() + except: + npass() + #end try + try: + Machine.write_job(arg0,arg1,file=False) + nfail() + except: + npass() + #end try + try: + Machine.submit_job(arg0,arg1) + nfail() + except: + npass() + #end try + + + nlabel('file_setup') + nassert(len(Machine.machines)>0) + for m in Machine.machines: + nassert(isinstance(m,Machine)) + exists = m.name in Machine.machines + nassert(exists) + nassert(Machine.exists(m.name)) + nassert(Machine.is_unique(m)) + m.validate() + #end for + + + nlabel('add') + mtest = Machine.machines.first() + nassert(isinstance(mtest,Machine)) + try: + Machine.add(mtest) + nfail() + except: + npass() + #end try + try: + Machine.add('my_machine') + nfail() + except: + npass() + #end try + + + nlabel('get') + m = Machine.get(mtest.name) + nassert(isinstance(m,Machine)) + nassert(id(m)==id(mtest)) + try: + Machine.get(m) + nfail() + except: + npass() + #end try + try: + Machine.get('some_nonexistant_machine') + nfail() + except: + npass() + #end try + + + nlabel('intantiation') + # test guards against empty/invalid instantiation + try: + Machine() + nfail() + except: + npass() + #end try + try: + Machine(123) + nfail() + except: + npass() + #end try + # test creation of a new machine + test_name = 'test_machine' + nassert(not Machine.exists(test_name)) + m = Machine(name=test_name) + nassert(isinstance(m,Machine)) + nassert(Machine.exists(m.name)) + nassert(Machine.is_unique(m)) + m.validate() + + # test guards against multiple instantiation + try: + Machine(name=test_name) + nfail() + except: + npass() + #end try + + # remove test machine + del Machine.machines.test_machine + nassert(not Machine.exists(test_name)) + + + # sort known machines + workstations = obj() + supercomputers = obj() + for machine in Machine.machines: + if isinstance(machine,Workstation): + workstations.append(machine) + elif isinstance(machine,Supercomputer): + supercomputers[machine.name] = machine + else: + Machine.class_error('unknown machine type encountered: {0}'.format(machine.__class__.__name__),'check_process_job_idempotency') + #end if + #end for + + + nlabel('process_job_idempotency') + def check_process_job_idempotency(nw=10,nwj=10,nsj=10,nij=10): + allow_warn = Machine.allow_warnings + Machine.allow_warnings = False + + not_idempotent = obj() + + # check workstations + nworkstations = nw + if nworkstations is None: + nworkstations=len(workstations) + #end if + nworkstations = min(nworkstations,len(workstations)) + njobs = nwj + for nm in range(nworkstations): + if nworkstations0: + mlist = '' + for name in sorted(not_idempotent.keys()): + mlist+= '\n '+name + #end for + Machine.class_error('\n\nsome machines failed process_job idempotency test:{0}'.format(mlist)) + #end if + Machine.class_log('done checking idempotency') + Machine.allow_warnings = allow_warn + #end def check_process_job_idempotency + + check_process_job_idempotency() + + + # test Job.run_command + nlabel('job_run_command') + def parse_job_command(command): + tokens = command.replace(':',' ').split() + launcher = tokens[0] + exe = tokens[-1] + args = [] + options = obj() + last_option = None + for t in tokens[1:-1]: + if t.startswith('-'): + options[t] = None + last_option = t + elif last_option is not None: + options[last_option] = t + last_option = None + else: + args.append(t) + #end if + #end for + jc = obj( + launcher = launcher, + executable = exe, + args = args, + options = options, + ) + return jc + #end def parse_job_command + + def job_commands_equal(c1,c2): + jc1 = parse_job_command(c1) + jc2 = parse_job_command(c2) + return object_eq(jc1,jc2) + #end def job_command_equal + + job_run_ref = obj({ + ('amos' , 'n1' ) : 'srun test.x', + ('amos' , 'n1_p1' ) : 'srun test.x', + ('amos' , 'n2' ) : 'srun test.x', + ('amos' , 'n2_t2' ) : 'srun test.x', + ('amos' , 'n2_t2_p2' ) : 'srun test.x', + ('bluewaters_xe' , 'n1' ) : 'aprun -n 32 test.x', + ('bluewaters_xe' , 'n1_p1' ) : 'aprun -n 1 test.x', + ('bluewaters_xe' , 'n2' ) : 'aprun -n 64 test.x', + ('bluewaters_xe' , 'n2_t2' ) : 'aprun -d 2 -n 32 test.x', + ('bluewaters_xe' , 'n2_t2_p2' ) : 'aprun -d 2 -n 4 test.x', + ('bluewaters_xk' , 'n1' ) : 'aprun -n 16 test.x', + ('bluewaters_xk' , 'n1_p1' ) : 'aprun -n 1 test.x', + ('bluewaters_xk' , 'n2' ) : 'aprun -n 32 test.x', + ('bluewaters_xk' , 'n2_t2' ) : 'aprun -d 2 -n 16 test.x', + ('bluewaters_xk' , 'n2_t2_p2' ) : 'aprun -d 2 -n 4 test.x', + ('cetus' , 'n1' ) : 'runjob --np 16 -p 16 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('cetus' , 'n1_p1' ) : 'runjob --np 1 -p 1 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('cetus' , 'n2' ) : 'runjob --np 32 -p 16 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('cetus' , 'n2_t2' ) : 'runjob --np 16 -p 8 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=2 : test.x', + ('cetus' , 'n2_t2_p2' ) : 'runjob --np 4 -p 2 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=2 : test.x', + ('chama' , 'n1' ) : 'srun test.x', + ('chama' , 'n1_p1' ) : 'srun test.x', + ('chama' , 'n2' ) : 'srun test.x', + ('chama' , 'n2_t2' ) : 'srun test.x', + ('chama' , 'n2_t2_p2' ) : 'srun test.x', + ('cooley' , 'n1' ) : 'mpirun -np 12 -f $COBALT_NODEFILE test.x', + ('cooley' , 'n1_p1' ) : 'mpirun -np 1 -f $COBALT_NODEFILE test.x', + ('cooley' , 'n2' ) : 'mpirun -np 24 -f $COBALT_NODEFILE test.x', + ('cooley' , 'n2_t2' ) : 'mpirun -np 12 -f $COBALT_NODEFILE test.x', + ('cooley' , 'n2_t2_p2' ) : 'mpirun -np 4 -f $COBALT_NODEFILE test.x', + ('cori' , 'n1' ) : 'srun test.x', + ('cori' , 'n1_p1' ) : 'srun test.x', + ('cori' , 'n2' ) : 'srun test.x', + ('cori' , 'n2_t2' ) : 'srun test.x', + ('cori' , 'n2_t2_p2' ) : 'srun test.x', + ('edison' , 'n1' ) : 'srun test.x', + ('edison' , 'n1_p1' ) : 'srun test.x', + ('edison' , 'n2' ) : 'srun test.x', + ('edison' , 'n2_t2' ) : 'srun test.x', + ('edison' , 'n2_t2_p2' ) : 'srun test.x', + ('eos' , 'n1' ) : 'aprun -n 16 test.x', + ('eos' , 'n1_p1' ) : 'aprun -n 1 test.x', + ('eos' , 'n2' ) : 'aprun -n 32 test.x', + ('eos' , 'n2_t2' ) : 'aprun -ss -cc numa_node -d 2 -n 16 test.x', + ('eos' , 'n2_t2_p2' ) : 'aprun -ss -cc numa_node -d 2 -n 4 test.x', + ('jaguar' , 'n1' ) : 'aprun -n 16 test.x', + ('jaguar' , 'n1_p1' ) : 'aprun -n 1 test.x', + ('jaguar' , 'n2' ) : 'aprun -n 32 test.x', + ('jaguar' , 'n2_t2' ) : 'aprun -d 2 -n 16 test.x', + ('jaguar' , 'n2_t2_p2' ) : 'aprun -d 2 -n 4 test.x', + ('komodo' , 'n1' ) : 'mpirun -np 12 test.x', + ('komodo' , 'n1_p1' ) : 'mpirun -np 1 test.x', + ('komodo' , 'n2' ) : 'mpirun -np 24 test.x', + ('komodo' , 'n2_t2' ) : 'mpirun -np 12 test.x', + ('komodo' , 'n2_t2_p2' ) : 'mpirun -np 4 test.x', + ('kraken' , 'n1' ) : 'aprun -n 12 test.x', + ('kraken' , 'n1_p1' ) : 'aprun -n 1 test.x', + ('kraken' , 'n2' ) : 'aprun -n 24 test.x', + ('kraken' , 'n2_t2' ) : 'aprun -d 2 -n 12 test.x', + ('kraken' , 'n2_t2_p2' ) : 'aprun -d 2 -n 4 test.x', + ('lonestar' , 'n1' ) : 'ibrun -n 12 -o 0 test.x', + ('lonestar' , 'n1_p1' ) : 'ibrun -n 1 -o 0 test.x', + ('lonestar' , 'n2' ) : 'ibrun -n 24 -o 0 test.x', + ('lonestar' , 'n2_t2' ) : 'ibrun -n 12 -o 0 test.x', + ('lonestar' , 'n2_t2_p2' ) : 'ibrun -n 4 -o 0 test.x', + ('matisse' , 'n1' ) : 'mpirun -np 16 test.x', + ('matisse' , 'n1_p1' ) : 'mpirun -np 1 test.x', + ('matisse' , 'n2' ) : 'mpirun -np 32 test.x', + ('matisse' , 'n2_t2' ) : 'mpirun -np 16 test.x', + ('matisse' , 'n2_t2_p2' ) : 'mpirun -np 4 test.x', + ('mira' , 'n1' ) : 'runjob --np 16 -p 16 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('mira' , 'n1_p1' ) : 'runjob --np 1 -p 1 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('mira' , 'n2' ) : 'runjob --np 32 -p 16 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('mira' , 'n2_t2' ) : 'runjob --np 16 -p 8 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=2 : test.x', + ('mira' , 'n2_t2_p2' ) : 'runjob --np 4 -p 2 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=2 : test.x', + ('oic5' , 'n1' ) : 'mpirun -np 32 test.x', + ('oic5' , 'n1_p1' ) : 'mpirun -np 1 test.x', + ('oic5' , 'n2' ) : 'mpirun -np 64 test.x', + ('oic5' , 'n2_t2' ) : 'mpirun -np 32 test.x', + ('oic5' , 'n2_t2_p2' ) : 'mpirun -np 4 test.x', + ('redsky' , 'n1' ) : 'srun test.x', + ('redsky' , 'n1_p1' ) : 'srun test.x', + ('redsky' , 'n2' ) : 'srun test.x', + ('redsky' , 'n2_t2' ) : 'srun test.x', + ('redsky' , 'n2_t2_p2' ) : 'srun test.x', + ('serrano' , 'n1' ) : 'srun test.x', + ('serrano' , 'n1_p1' ) : 'srun test.x', + ('serrano' , 'n2' ) : 'srun test.x', + ('serrano' , 'n2_t2' ) : 'srun test.x', + ('serrano' , 'n2_t2_p2' ) : 'srun test.x', + ('skybridge' , 'n1' ) : 'srun test.x', + ('skybridge' , 'n1_p1' ) : 'srun test.x', + ('skybridge' , 'n2' ) : 'srun test.x', + ('skybridge' , 'n2_t2' ) : 'srun test.x', + ('skybridge' , 'n2_t2_p2' ) : 'srun test.x', + ('solo' , 'n1' ) : 'srun test.x', + ('solo' , 'n1_p1' ) : 'srun test.x', + ('solo' , 'n2' ) : 'srun test.x', + ('solo' , 'n2_t2' ) : 'srun test.x', + ('solo' , 'n2_t2_p2' ) : 'srun test.x', + ('stampede2' , 'n1' ) : 'ibrun -n 68 -o 0 test.x', + ('stampede2' , 'n1_p1' ) : 'ibrun -n 1 -o 0 test.x', + ('stampede2' , 'n2' ) : 'ibrun -n 136 -o 0 test.x', + ('stampede2' , 'n2_t2' ) : 'ibrun -n 68 -o 0 test.x', + ('stampede2' , 'n2_t2_p2' ) : 'ibrun -n 4 -o 0 test.x', + ('supermuc' , 'n1' ) : 'mpiexec -n 40 test.x', + ('supermuc' , 'n1_p1' ) : 'mpiexec -n 1 test.x', + ('supermuc' , 'n2' ) : 'mpiexec -n 80 test.x', + ('supermuc' , 'n2_t2' ) : 'mpiexec -n 40 test.x', + ('supermuc' , 'n2_t2_p2' ) : 'mpiexec -n 4 test.x', + ('taub' , 'n1' ) : 'mpirun -np 12 test.x', + ('taub' , 'n1_p1' ) : 'mpirun -np 1 test.x', + ('taub' , 'n2' ) : 'mpirun -np 24 test.x', + ('taub' , 'n2_t2' ) : 'mpirun -np 12 test.x', + ('taub' , 'n2_t2_p2' ) : 'mpirun -np 4 test.x', + ('theta' , 'n1' ) : 'aprun -e OMP_NUM_THREADS=1 -cc depth -n 64 -j 1 -d 1 -N 64 test.x', + ('theta' , 'n1_p1' ) : 'aprun -e OMP_NUM_THREADS=1 -cc depth -n 1 -j 1 -d 1 -N 1 test.x', + ('theta' , 'n2' ) : 'aprun -e OMP_NUM_THREADS=1 -cc depth -n 128 -j 1 -d 1 -N 64 test.x', + ('theta' , 'n2_t2' ) : 'aprun -e OMP_NUM_THREADS=2 -cc depth -n 64 -j 1 -d 2 -N 32 test.x', + ('theta' , 'n2_t2_p2' ) : 'aprun -e OMP_NUM_THREADS=2 -cc depth -n 4 -j 1 -d 2 -N 2 test.x', + ('titan' , 'n1' ) : 'aprun -n 16 test.x', + ('titan' , 'n1_p1' ) : 'aprun -n 1 test.x', + ('titan' , 'n2' ) : 'aprun -n 32 test.x', + ('titan' , 'n2_t2' ) : 'aprun -d 2 -n 16 test.x', + ('titan' , 'n2_t2_p2' ) : 'aprun -d 2 -n 4 test.x', + ('uno' , 'n1' ) : 'srun test.x', + ('uno' , 'n1_p1' ) : 'srun test.x', + ('uno' , 'n2' ) : 'srun test.x', + ('uno' , 'n2_t2' ) : 'srun test.x', + ('uno' , 'n2_t2_p2' ) : 'srun test.x', + ('vesta' , 'n1' ) : 'runjob --np 16 -p 16 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('vesta' , 'n1_p1' ) : 'runjob --np 1 -p 1 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('vesta' , 'n2' ) : 'runjob --np 32 -p 16 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=1 : test.x', + ('vesta' , 'n2_t2' ) : 'runjob --np 16 -p 8 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=2 : test.x', + ('vesta' , 'n2_t2_p2' ) : 'runjob --np 4 -p 2 $LOCARGS --verbose=INFO --envs OMP_NUM_THREADS=2 : test.x', + }) + + job_inputs = obj( + n1 = obj(nodes=1), + n1_p1 = obj(nodes=1,processes_per_node=1), + n2 = obj(nodes=2), + n2_t2 = obj(nodes=2,threads=2), + n2_t2_p2 = obj(nodes=2,threads=2,processes_per_node=2), + ) + for name in sorted(supercomputers.keys()): + m = supercomputers[name] + if m.requires_account: + acc = 'ABC123' + else: + acc = None + #end if + for jtype in sorted(job_inputs.keys()): + job = Job(app_command = 'test.x', + machine = name, + account = acc, + **job_inputs[jtype] + ) + command = job.run_command() + # uncomment below to get lines for job_run_ref table above + #sname = "'{0}'".format(name) + #stype = "'{0}'".format(jtype) + #print " ({0:<16} , {1:<16}) : '{2}',".format(sname,stype,command) + ref_command = job_run_ref[name,jtype] + if not job_commands_equal(command,ref_command): + nfail('Job.run_command for machine "{0}" does not match the reference\njob inputs: {1}\nreference command: {2}\nincorrect command: {3}'.format(name,job_inputs[jtype],ref_command,command)) + #end for + #end for + #end for + + + nlabel('Job.split_nodes') + for name in sorted(supercomputers.keys()): + m = supercomputers[name] + if m.app_launcher=='srun': # no slurm support yet + continue + #end if + if m.requires_account: + acc = 'ABC123' + else: + acc = None + #end if + # make a 4 node job + job = Job(app_command = 'test.x', + machine = name, + account = acc, + nodes = 4, + threads = m.cores_per_node, + ) + # split the job into 1 and 3 nodes + job1,job2 = job.split_nodes(1) + # get the split run commands + rc = job.run_command() + rc1 = job1.run_command() + rc2 = job2.run_command() + ns = ' {0} '.format(job.nodes) + ns1 = ' {0} '.format(job1.nodes) + ns2 = ' {0} '.format(job2.nodes) + # verify that node count is in each command + nassert(ns in rc ) + nassert(ns1 in rc1) + nassert(ns2 in rc2) + # verify that text on either side of node count + # agrees for original and split commands + rcl ,rcr = rc.split(ns,1) + rc1l,rc1r = rc1.split(ns1,1) + rc2l,rc2r = rc2.split(ns2,1) + rcf = rcl+' '+rcr + rc1f = rc1l+' '+rc1r + rc2f = rc2l+' '+rc2r + nassert(job_commands_equal(rcf,rc1f)) + nassert(job_commands_equal(rcf,rc2f)) + #end for + + + # restore logging function + nlog_restore() +#end def machines + + + example_information = dict( pwscf_relax_Ge_T = dict( path = 'quantum_espresso/relax_Ge_T_vs_kpoints', @@ -1936,6 +2574,7 @@ NexusTest( generic_intrinsics ) NexusTest( generic_extensions ) NexusTest( nexus_imports ) NexusTest( settings_operation , 'settings' ) +NexusTest( machines ) for label in example_information.keys(): NexusTest( diff --git a/nexus/library/bundle.py b/nexus/library/bundle.py index 56d45573b2..1585342e24 100644 --- a/nexus/library/bundle.py +++ b/nexus/library/bundle.py @@ -54,11 +54,13 @@ def __init__(self,*sims,**kwargs): #end if sims = list(sims) # make a copy if len(sims)==0: - self.error('attempted to bundle 0 simulations\n at least one simulation must be provided to bundle') + self.error('attempted to bundle 0 simulations\nat least one simulation must be provided to bundle') #end if for sim in sims: if not isinstance(sim,Simulation): self.error('attempted to bundle non-simulation object: '+sim.__class__.__name__) + elif not sim.bundleable: + self.error('attempted to bundle simulation that does not support bundling\nsim type: {0}\nsim identifier: {1}\nsim directory: {2}'.format(sim.__class__.__name__,sim.identifier,sim.locdir)) #end if sim.bundled = True sim.bundler = self diff --git a/nexus/library/generic.py b/nexus/library/generic.py index d027c57941..d8ab76e1a3 100644 --- a/nexus/library/generic.py +++ b/nexus/library/generic.py @@ -750,7 +750,7 @@ def add_optional(self,key,value): #end def add_optional def transfer_from(self,other,keys=None,copy=False,overwrite=True): - if keys==None: + if keys is None: if isinstance(other,object_interface): keys = other._keys() else: @@ -776,7 +776,7 @@ def transfer_from(self,other,keys=None,copy=False,overwrite=True): #end def transfer_from def transfer_to(self,other,keys=None,copy=False,overwrite=True): - if keys==None: + if keys is None: keys = self._keys() #end if if copy: @@ -797,30 +797,56 @@ def transfer_to(self,other,keys=None,copy=False,overwrite=True): #end if #end def transfer_to - def move_from(self,other,keys=None): - if keys==None: + def move_from(self,other,keys=None,optional=False): + if keys is None: if isinstance(other,object_interface): keys = other._keys() else: keys = other.keys() #end if #end if - for k in keys: - self[k]=other[k] - del other[k] - #end for + if not optional: + for k in keys: + self[k]=other[k] + del other[k] + #end for + else: + for k in keys: + if k in other: + self[k]=other[k] + del other[k] + #end if + #end for + #end if #end def move_from - def move_to(self,other,keys=None): - if keys==None: + def move_to(self,other,keys=None,optional=False): + if keys is None: keys = self._keys() #end if - for k in keys: - other[k]=self[k] - del self[k] - #end for + if not optional: + for k in keys: + other[k]=self[k] + del self[k] + #end for + else: + for k in keys: + if k in self: + other[k]=self[k] + del self[k] + #end if + #end for + #end if #end def move_to + def move_from_optional(self,other,keys=None): + self.move_from(other,keys,optional=True) + #end def move_from_optional + + def move_to_optional(self,other,keys=None): + self.move_to(other,keys,optional=True) + #end def move_to_optional + def copy_from(self,other,keys=None,deep=True): obj.transfer_from(self,other,keys,copy=deep) #end def copy_from @@ -829,6 +855,57 @@ def copy_to(self,other,keys=None,deep=True): obj.transfer_to(self,other,keys,copy=deep) #end def copy_to + def extract(self,keys=None,optional=False): + ext = obj() + ext.move_from(self,keys,optional=optional) + return ext + #end def extract + + def extract_optional(self,keys=None): + return self.extract(keys,optional=True) + #end def extract_optional + + def check_required(self,keys,exit=True): + if not isinstance(keys,set): + keys = set(keys) + #end if + missing = keys-set(self.keys()) + if exit and len(missing)>0: + self._error('required keys are missing\nmissing keys: {0}'.format(sorted(missing))) + #end if + return missing + #end def check_required + + def check_types(self,types,optional=False,exit=True): + kfail = None + tfail = None + if not optional: + for k,t in types.iteritems(): + if not isinstance(self[k],t): + kfail = k + tfail = t + break + #end if + #end for + else: + for k,t in types.iteritems(): + if k in self and not isinstance(self[k],t): + kfail = k + tfail = t + break + #end if + #end for + #end if + if exit and kfail is not None: + self._error('incorrect type encountered for key value\ntype required: {0}\ntype encountered: {1}\ninvalid key: {2}'.format(tfail.__name__,self[kfail].__class__.__name__,kfail)) + #end if + return kfail,tfail + #end def check_types + + def check_types_optional(self,types,exit=True): + return self.check_types(types,exit=exit,optional=True) + #end def check_types_optional + def shallow_copy(self): new = self.__class__() for k,v in self._iteritems(): @@ -917,7 +994,6 @@ def serial(self,s=None,path=None): #end def serial - # access preserving functions # list interface def _append(self,*args,**kwargs): @@ -973,10 +1049,24 @@ def _move_from(self,*args,**kwargs): obj.move_from(self,*args,**kwargs) def _move_to(self,*args,**kwargs): obj.move_to(self,*args,**kwargs) + def _move_from_optional(self,*args,**kwargs): + obj.move_from_optional(self,*args,**kwargs) + def _move_to_optional(self,*args,**kwargs): + obj.move_to_optional(self,*args,**kwargs) def _copy_from(self,*args,**kwargs): obj.copy_from(self,*args,**kwargs) def _copy_to(self,*args,**kwargs): obj.copy_to(self,*args,**kwargs) + def _extract(self,*args,**kwargs): + obj.extract(self,*args,**kwargs) + def _extract_optional(self,*args,**kwargs): + obj.extract_optional(self,*args,**kwargs) + def _check_required(self,*args,**kwargs): + obj.check_required(self,*args,**kwargs) + def _check_types(self,*args,**kwargs): + obj.check_types(self,*args,**kwargs) + def _check_types_optional(self,*args,**kwargs): + obj.check_types_optional(self,*args,**kwargs) def _shallow_copy(self,*args,**kwargs): obj.shallow_copy(self,*args,**kwargs) def _inverse(self,*args,**kwargs): diff --git a/nexus/library/machines.py b/nexus/library/machines.py index ed1ca49042..6e53fef1ed 100644 --- a/nexus/library/machines.py +++ b/nexus/library/machines.py @@ -48,7 +48,6 @@ #from multiprocessing import cpu_count from socket import gethostname from subprocess import Popen,PIPE -from random import randint from numpy import array,mod,floor,ceil,round,log,empty from generic import obj from developer import DevBase @@ -69,6 +68,7 @@ def cpu_count(): return multiprocessing.cpu_count() except (ImportError,NotImplementedError): None + #end try # POSIX try: @@ -76,8 +76,10 @@ def cpu_count(): if res > 0: return res + #end if except (AttributeError,ValueError): None + #end try #end def cpu_count @@ -176,123 +178,123 @@ def zero_time(cls): def __init__(self, - name = 'jobname', - type = None, - directory = None, - sub_dir = None, - app_name = None, # name of/path to application - app_flags = None, - app_command = None, - app_props = None, - app = None, # name of/path to application - env = None, - user_env = True, # import user environment - presub = '', # shell text executed just prior to submission - postsub = '', # shell text executed just after submission - outfile = None, - errfile = None, - mode = None, - machine = None, - account = None, - queue = None, - bundled_jobs = None, - relative = False, - cores = None, # number of cores for the job - nodes = None, # number of nodes for the job - threads = 1, # number of openmp threads for the job - hyperthreads = None, - ppn = None, - compiler = None, - override = None, - options = None, - app_options = None, - run_options = None, - sub_options = None, - serial = False, # run job serially, no mpi - local = False, # run job locally, no queue submission - days = 0, - hours = 0, - minutes = 0, - seconds = 0, - subfile = None, - grains = None, - procs = None, - processes = None, + name = 'jobname', + type = None, + directory = None, + sub_dir = None, + app_name = None, # name of/path to application + app_flags = None, + app_command = None, # command used to launch application + app_props = None, + app = None, # name of/path to application + full_command = None, # custom command including e.g. mpirun + env = None, + user_env = True, # import user environment + presub = '', # shell text executed just prior to submission + postsub = '', # shell text executed just after submission + outfile = None, + errfile = None, + mode = None, + machine = None, + account = None, + queue = None, + bundled_jobs = None, + relative = False, + cores = None, # number of cores for the job + nodes = None, # number of nodes for the job + threads = 1, # number of openmp threads for the job + hyperthreads = None, + ppn = None, + compiler = None, + options = None, + app_options = None, + run_options = None, + sub_options = None, + serial = False, # run job serially, no mpi + local = False, # run job locally, no queue submission + days = 0, + hours = 0, + minutes = 0, + seconds = 0, + subfile = None, + grains = None, + procs = None, + processes = None, processes_per_proc = None, processes_per_node = None, - email = None, - constraint = None, # slurm specific, Cori - fake = False, + email = None, + constraint = None, # slurm specific, Cori + fake = False, ): - self.directory = directory - self.subdir = sub_dir - self.app_name = app_name - self.app_command = app_command - self.app_props = app_props - self.outfile = outfile - self.errfile = errfile - self.user_env = user_env - self.presub = presub - self.postsub = postsub - self.name = name - self.type = type - self.queue = queue - self.bundled_jobs= bundled_jobs - self.relative = relative - self.cores = cores - self.nodes = nodes - self.threads = threads - self.hyperthreads= hyperthreads - self.ppn = ppn - self.compiler = compiler - self.override = override - self.app_options = Options() - self.run_options = Options() - self.sub_options = Options() - self.serial = serial - self.local = local - self.days = days - self.hours = hours - self.minutes = minutes - self.seconds = seconds - self.subfile = subfile - self.grains = grains - self.procs = procs - self.processes = processes + self.directory = directory + self.subdir = sub_dir + self.app_name = app_name + self.app_command = app_command + self.app_props = app_props + self.full_command = full_command + self.outfile = outfile + self.errfile = errfile + self.user_env = user_env + self.presub = presub + self.postsub = postsub + self.name = name + self.type = type + self.queue = queue + self.bundled_jobs = bundled_jobs + self.relative = relative + self.cores = cores + self.nodes = nodes + self.threads = threads + self.hyperthreads = hyperthreads + self.ppn = ppn + self.compiler = compiler + self.app_options = Options() + self.run_options = Options() + self.sub_options = Options() + self.serial = serial + self.local = local + self.days = days + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.subfile = subfile + self.grains = grains + self.procs = procs + self.processes = processes self.processes_per_proc = processes_per_proc self.processes_per_node = processes_per_node - self.account = account - self.email = email - self.constraint = constraint - self.internal_id = None - self.system_id = None - self.tot_cores = None - self.identifier = None - self.submitted = False - self.status = self.states.none - self.crashed = False - self.overtime = False - self.successful = False - self.finished = False - self.fake_job = fake - - if app != None: + self.account = account + self.email = email + self.constraint = constraint + self.internal_id = None + self.system_id = None + self.tot_cores = None + self.identifier = None + self.submitted = False + self.status = self.states.none + self.crashed = False + self.overtime = False + self.successful = False + self.finished = False + self.fake_job = fake + + if app is not None: self.app_name = app #end if - if app_options != None: + if app_options is not None: self.app_options.read(app_options) #end if - if run_options != None: + if run_options is not None: self.run_options.read(run_options) #end if - if sub_options != None: + if sub_options is not None: self.sub_options.read(sub_options) #end if - if app_flags != None: + if app_flags is not None: self.app_options.read(app_flags) #end if - if options != None: + if options is not None: self.run_options.read(options) #end if if env is None: @@ -301,10 +303,10 @@ def __init__(self, self.set_environment(**env) #end if - if app_props==None: + if app_props is None: self.app_props = [] #end if - if compiler!=None: + if compiler is not None: if compiler in self.intel_compilers: self.compiler = 'intel' elif compiler in self.gnu_compilers: @@ -321,7 +323,7 @@ def __init__(self, self.nodes = None #end if - if machine!=None: + if machine is not None: self.machine = machine #end if #check that the machine exists and have it complete the job info @@ -330,7 +332,7 @@ def __init__(self, machine = self.get_machine() self.batch_mode = machine.in_batch_mode() - if bundled_jobs!=None and not machine.batch_capable: + if bundled_jobs is not None and not machine.batch_capable: self.error('running batched/bundled jobs on {0} is either not possible or not yet implemented, sorry.'.format(machine.name)) #end if @@ -351,6 +353,14 @@ def process(self,machine=None): #end def process + def process_options(self,machine=None): + if machine is None: + machine = self.get_machine() + #end if + machine.process_job_options(self) + #end def process_options + + def initialize(self,sim): self.set_id() self.identifier = sim.identifier @@ -361,7 +371,7 @@ def initialize(self,sim): #end if if self.directory is None: self.directory = sim.locdir - self.abs_dir = os.path.abspath(sim.locdir) + self.abs_dir = os.path.abspath(sim.locdir) elif self.abs_dir is None: self.abs_dir = os.path.abspath(self.directory) #end if @@ -394,10 +404,10 @@ def initialize(self,sim): #end if sim.set_app_name(app_name) self.set( - name = sim.identifier, - simid = sim.simid, - outfile = sim.outfile, - errfile = sim.errfile + name = sim.identifier, + simid = sim.simid, + outfile = sim.outfile, + errfile = sim.errfile ) if self.app_command is None: self.app_command = sim.app_command() @@ -417,7 +427,7 @@ def set_id(self): def set_processes(self): if self.processes is None: - self.error('processes should have been set before now\n contact the developers and have them fix this','Developer') + self.error('processes should have been set before now\ncontact the developers and have them fix this','Developer') self.processes = int(ceil(float(self.cores)/self.threads)) #end if #end def set_processes @@ -443,6 +453,15 @@ def set_environment(self,limited_env=False,clear_env=False,**env): #end def set_environment + def divert_out_err(self): + self.identifier += '_divert' + #prefix,ext = self.outfile.split('.',1) + #self.outfile = prefix+'_divert.'+ext + #prefix,ext = self.errfile.split('.',1) + #self.errfile = prefix+'_divert.'+ext + #end def divert_out_err + + def get_time(self): time = obj( days = self.days, @@ -507,11 +526,15 @@ def reenter_queue(self): #end def reenter_queue - def run_command(self,launcher,redirect=False): + def run_command(self,launcher=None,redirect=False): + if launcher is None: + machine = self.get_machine() + launcher = machine.app_launcher + #end if c = '' if self.bundled_jobs is None: - if isinstance(self.override,str): - c = self.override + if self.full_command is not None: + c = self.full_command else: if self.app_command is None: self.error('app_command has not been provided') @@ -616,6 +639,34 @@ def total_hours(self): def total_days(self): return int(self.total_seconds()/(24*3600)) #end def total_days + + + def clone(self): + job = self.copy() + job.set_id() + return job + #end def clone + + + def split_nodes(self,n): + run_options = self.run_options + if not isinstance(n,int): + self.error('cannot split job by nodes\nrequested split value must be an integer\nreceived type: {0}\nwith value: {1}'.format(n.__class__.__name__,n)) + elif n<1 or n>=self.nodes: + self.error('cannot split job by nodes\nrequested split must be in the range [1,{0})\nrequested split: {1}'.format(self.nodes,n)) + #end if + m = self.get_machine() + if m.app_launcher=='srun': + self.error('splitting jobs by nodes is not currently supported on machine "{0}" (SLURM)'.format(m.name)) + #end if + job1 = self.clone() + job2 = self.clone() + job1.nodes = n + job2.nodes = self.nodes - n + m.process_job(job1) + m.process_job(job2) + return job1,job2 + #end def split_nodes #end class Job @@ -663,7 +714,7 @@ def exists(machine_name): @staticmethod def is_unique(machine): return id(machine)==id(Machine.machines[machine.name]) - #end def is_valid + #end def is_unique @staticmethod @@ -695,176 +746,12 @@ def get(machine_name): else: machs = Machine.machines.keys() machs.sort() - Machine.class_error('attempted to get machine '+machine_name+', but it is unknown\n known options are '+str(machs)) + Machine.class_error('attempted to get machine '+machine_name+', but it is unknown\nknown options are '+str(machs)) #end if return machine #end def get - @staticmethod - def check_process_job_idempotency(nw=10,nwj=10,nsj=10,nij=10): - allow_warn = Machine.allow_warnings - Machine.allow_warnings = False - # sort known machines - workstations = obj() - supercomputers = [] - for machine in Machine.machines: - if isinstance(machine,Workstation): - workstations.append(machine) - elif isinstance(machine,Supercomputer): - supercomputers.append(machine) - else: - Machine.class_error('unknown machine type encountered: {0}'.format(machine.__class__.__name__),'check_process_job_idempotency') - #end if - #end for - - not_idempotent = obj() - - # check workstations - nworkstations = nw - if nworkstations is None: - nworkstations=len(workstations) - #end if - nworkstations = min(nworkstations,len(workstations)) - njobs = nwj - for nm in range(nworkstations): - if nworkstations0: - mlist = '' - for name in sorted(not_idempotent.keys()): - mlist+= '\n '+name - #end for - Machine.class_error('\n\nsome machines failed process_job idempotency test:{0}'.format(mlist)) - #end if - Machine.class_log('done checking idempotency') - exit() - Machine.allow_warnings = allow_warn - #end def check_process_job_idempotency - - def warn(self,*args,**kwargs): if Machine.allow_warnings: NexusCore.warn(self,*args,**kwargs) @@ -901,6 +788,10 @@ def process_job(self,job): self.not_implemented() #end def process_job + def process_job_options(self,job): + self.not_implemented() + #end def process_job_options + def write_job(self,job,file=False): self.not_implemented() #end def write_job @@ -926,7 +817,7 @@ def __init__(self,name,queue_size=0): self.app_directories = None if not isinstance(name,str): - self.error('machine name must be a string\n you provided '+str(name)) + self.error('machine name must be a string\nyou provided '+str(name)) #end if Machine.add(self) @@ -967,12 +858,12 @@ def incorporate_user_info(self,infoin): vars = set(info.keys()) invalid = vars-self.allowed_user_info if len(invalid)>0: - self.error('invalid inputs encountered in incorporate_user_info\n allowed inputs: {0}\n invalid inputs: {1}'.format(list(self.allowed_user_info),list(invalid))) + self.error('invalid inputs encountered in incorporate_user_info\nallowed inputs: {0}\n invalid inputs: {1}'.format(list(self.allowed_user_info),list(invalid))) #end if if 'app_directories' in info: ad = info.app_directories if not isinstance(ad,dict) and not isinstance(ad,obj): - self.error('app_directories must be of type dict or obj\n you provided '+ad.__class__.__name__) + self.error('app_directories must be of type dict or obj\nyou provided '+ad.__class__.__name__) #end if #end if self.transfer_from(info) @@ -988,7 +879,12 @@ class Workstation(Machine): batch_capable = False - def __init__(self,name='workstation',cores=None,app_launcher='mpirun',process_granularity=1): + def __init__(self, + name = 'workstation', + cores = None, + app_launcher = 'mpirun', + process_granularity = 1 + ): Machine.__init__(self,name) self.app_launcher = app_launcher if cores==None: @@ -1019,10 +915,15 @@ def process_job(self,job): job.grains = grains job.cores = grains*self.process_granularity - job.run_options.add(np='-np '+str(job.processes)) + self.process_job_options(job) #end def process_job + def process_job_options(self,job): + job.run_options.add(np='-np '+str(job.processes)) + #end def process_job_options + + def write_job_states(self,title=''): self.log(title,n=2) n=3 @@ -1063,6 +964,7 @@ def write_job_states(self,title=''): self.log('end job states',n=1) #end def write_job_states + def query_queue(self): #self.write_job_states('query queue') self.validate() @@ -1094,7 +996,6 @@ def query_queue(self): #end def query_queue - def submit_jobs(self): cores_used = 0 for process in self.processes: @@ -1127,7 +1028,7 @@ def submit_jobs(self): for job in job_req: if job.cores>self.cores and not nexus_core.generate_only: - self.error('job '+str(job.internal_id)+' is too large to run on this machine\n cores requested: '+str(job.cores)+'\n machine cores: '+str(self.cores)) + self.error('job '+str(job.internal_id)+' is too large to run on this machine\ncores requested: '+str(job.cores)+'\nmachine cores: '+str(self.cores)) #end if if job.cores<=cores_available: iid = job.internal_id @@ -1136,7 +1037,7 @@ def submit_jobs(self): self.submit_job(job) cores_available-=job.cores elif job.cores>self.cores: - self.error('job requested more cores than are present on '+self.name+'\n cores requested: {0}\n cores present: {1}'.format(job.cores,self.cores)) + self.error('job requested more cores than are present on '+self.name+'\ncores requested: {0}\ncores present: {1}'.format(job.cores,self.cores)) else: break #end if @@ -1199,8 +1100,8 @@ def submit_job(self,job): class InteractiveCluster(Workstation): + def __init__(self,*args,**kwargs): - if len(args)==0 or not isinstance(args[0],Supercomputer): self.init_from_args(*args,**kwargs) else: @@ -1212,9 +1113,15 @@ def __init__(self,*args,**kwargs): #end def __init__ - def init_from_args(self,name='icluster',nodes=None,procs_per_node=None, - cores_per_proc=None,process_granularity=None,ram_per_node=None, - app_launcher=None): + def init_from_args(self, + name = 'icluster', + nodes = None, + procs_per_node = None, + cores_per_proc = None, + process_granularity = None, + ram_per_node = None, + app_launcher = None + ): self.name = name self.nodes = nodes self.procs_per_node = procs_per_node @@ -1280,12 +1187,32 @@ class Supercomputer(Machine): aprun_options = set(['n','d']) - def __init__(self,nodes=None,procs_per_node=None, - cores_per_proc=None,ram_per_node=None,queue_size=0, - app_launcher=None,sub_launcher=None,queue_querier=None, - job_remover=None,name=None): + required_inputs = [ + 'nodes', + 'procs_per_node', + 'cores_per_proc', + 'ram_per_node', + 'queue_size', + 'app_launcher', + 'sub_launcher', + 'queue_querier', + 'job_remover' + ] + + def __init__(self, + nodes = None, + procs_per_node = None, + cores_per_proc = None, + ram_per_node = None, + queue_size = 0, + app_launcher = None, + sub_launcher = None, + queue_querier = None, + job_remover = None, + name = None + ): if name is None: - if self.name!=None: + if self.name is not None: name = self.name else: name = self.__class__.__name__.lower() @@ -1302,9 +1229,8 @@ def __init__(self,nodes=None,procs_per_node=None, self.queue_querier = queue_querier self.job_remover = job_remover - required = ['nodes','procs_per_node','cores_per_proc','ram_per_node','queue_size','app_launcher','sub_launcher','queue_querier','job_remover'] - for var in required: - if self[var]==None: + for var in Supercomputer.required_inputs: + if self[var] is None: self.error('input variable '+var+' is required to initialize Supercomputer object.') #end if #end for @@ -1425,8 +1351,8 @@ def process_job(self,job): return #end if job.subfile = job.name+'.'+self.sub_launcher+'.in' - no_cores = job.cores==None - no_nodes = job.nodes==None + no_cores = job.cores is None + no_nodes = job.nodes is None if no_cores and no_nodes: self.error('job did not specify cores or nodes\nAt least one must be provided') elif no_cores: @@ -1439,8 +1365,8 @@ def process_job(self,job): else: job.cores = min(job.cores,job.nodes*self.cores_per_node) #end if - if job.processes_per_node!=None: - job.processes=job.nodes*job.processes_per_node + if job.processes_per_node is not None: + job.processes = job.nodes*job.processes_per_node else: job.processes = max(1,int(float(job.cores)/job.threads)) #end if @@ -1474,6 +1400,11 @@ def process_job(self,job): job.set_environment(OMP_NUM_THREADS=job.threads) + self.process_job_options(job) + #end def process_job + + + def process_job_options(self,job): launcher = self.app_launcher if launcher=='mpirun': job.run_options.add(np='-np '+str(job.processes)) @@ -1486,19 +1417,25 @@ def process_job(self,job): if 'd' in self.aprun_options and job.threads>1: job.run_options.add(d='-d '+str(job.threads)) #end if - if 'N' in self.aprun_options and job.processes_per_node!=None: + if 'N' in self.aprun_options and job.processes_per_node is not None: job.run_options.add(N='-N '+str(job.processes_per_node)) #end if - if 'S' in self.aprun_options and job.processes_per_proc!=None: + if 'S' in self.aprun_options and job.processes_per_proc is not None: job.run_options.add(S='-S '+str(job.processes_per_proc)) #end if elif launcher=='runjob': #bypass setup_environment - envs='--envs' - for name,value in job.env.iteritems(): - envs+=' {0}={1}'.format(name,value) - #end for - job.env = None + if job.env is not None: + envs='--envs' + for name,value in job.env.iteritems(): + envs+=' {0}={1}'.format(name,value) + #end for + job.env = None + elif 'envs' in job.run_options: + envs = job.run_options.envs + else: + self.error('failed to set env options for runjob') + #end if job.run_options.add( np = '--np '+str(job.processes), p = '-p '+str(job.processes_per_node), @@ -1506,9 +1443,6 @@ def process_job(self,job): verbose = '--verbose=INFO', envs = envs ) - if job.processes_per_node is None: - self.error('please specify processes_per_node in each job to launched with runjob') - #end if elif launcher=='srun': # Amos contribution from Ryan McAvoy None # anything needed here? elif launcher=='ibrun': # Lonestar contribution from Paul Young @@ -1519,7 +1453,7 @@ def process_job(self,job): else: self.error(launcher+' is not yet implemented as an application launcher') #end if - #end def process_job + #end def process_job_options def process_job_extra(self,job): @@ -1847,6 +1781,8 @@ def read_process_id(self,output): #end class Supercomputer + + # Load local class for local cluster's setting from ~/.nexus/local.py # The following is an example of this file (see machines.py for more examples): ''' @@ -1858,6 +1794,7 @@ class Clustername(Supercomputer): def write_job_header(self,job): if job.queue is None: job.queue='batch' + #end if c= '#!/bin/bash\n' c+='#PBS -l nodes={0}:ppn={1}\n'.format(job.nodes,job.ppn) c+='#PBS -l walltime='+job.pbs_walltime()+'\n' @@ -1866,10 +1803,9 @@ def write_job_header(self,job): c+='#PBS -e '+job.errfile+'\n' c+='#PBS -V\n' c+='#PBS -q '+job.queue+'\n' - - #end if c+=' \n ' return c + #end def write_job_header #end class Clustername # nodes sockets cores ram qslots qlaunch qsubmit qstatus qdelete Clustername( 4, 1, 16, 24, 4, 'mpirun', 'qsub', 'qstat', 'qdel') @@ -1880,6 +1816,8 @@ def write_job_header(self,job): pass except: raise +#end try + class Kraken(Supercomputer): @@ -1908,410 +1846,108 @@ def write_job_header(self,job): ''' return c #end def write_job_header - - - def read_process_id(self,output): - self.not_implemented() - #end def read_process_id #end class Kraken -# SANDIA test -class Chama(Supercomputer): - name = 'chama' - - requires_account = True - batch_capable = True - #executable_subfile = True +class Jaguar(Supercomputer): + name = 'jaguar' - prefixed_output = True - outfile_extension = '.output' - errfile_extension = '.error' + requires_account = True def write_job_header(self,job): if job.queue is None: job.queue='batch' - #end if - job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 - if job.total_hours > 96: # warn if job will take more than 96 hrs. - self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) - job.hours = max_time - job.minutes =0 - job.seconds =0 - #end if - #end if c='#!/bin/bash\n' - c+='#SBATCH --job-name '+str(job.name)+'\n' - c+='#SBATCH --account='+str(job.account)+'\n' - c+='#SBATCH -N '+str(job.nodes)+'\n' - c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) - c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) - c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) - #c+='#SBATCH --export=ALL\n' # equiv to PBS -V - c+='#SBATCH -o {0}\n'.format(job.outfile) - c+='#SBATCH -e {0}\n'.format(job.errfile) - c+='\n' - c+='module purge\n' - c+='module add intel/intel-16.0.1.150\n' - c+='module add libraries/intel-mkl-16.0.1.150\n' - c+='module add mvapich2-intel-psm/1.7\n' + c+='#PBS -A '+str(job.account)+'\n' + c+='#PBS -q '+job.queue+'\n' + c+='#PBS -N '+str(job.name)+'\n' + c+='#PBS -l walltime='+job.pbs_walltime()+'\n' + c+='#PBS -l size='+str(job.tot_cores)+'\n' + c+='#PBS -l gres=widow2%widow3\n' + c+='#PBS -o '+job.outfile+'\n' + c+='#PBS -e '+job.errfile+'\n' + if job.user_env: + c+='#PBS -V\n' + #end if + c+=''' +echo $PBS_O_WORKDIR +cd $PBS_O_WORKDIR +export MPICH_PTL_SEND_CREDITS=-1 +export MPICH_MAX_SHORT_MSG_SIZE=1024 +export MPICH_PTL_UNEX_EVENTS=800000 +export MPICH_UNEX_BUFFER_SIZE=16M +export MPI_MSGS_PER_PROC=32768 +''' return c #end def write_job_header -#end class Chama -##### SANDIA test - +#end class Jaguar -class Uno(Supercomputer): - name = 'uno' - requires_account = True - batch_capable = True - #executable_subfile = True - prefixed_output = True - outfile_extension = '.output' - errfile_extension = '.error' +class Taub(Supercomputer): + name = 'taub' + def write_job_header(self,job): if job.queue is None: - job.queue='batch' - - job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 - if job.total_hours > 96: # warn if job will take more than 96 hrs. - self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) - job.hours = max_time - job.minutes =0 - job.seconds =0 + job.queue='cse' #end if - + c='' + c+='#PBS -q '+job.queue+'\n' + c+='#PBS -N '+job.name+'\n' + c+='#PBS -l nodes={0}:ppn={1}\n'.format(job.nodes,job.ppn) + c+='#PBS -l walltime='+job.pbs_walltime()+'\n' + c+='#PBS -e '+job.errfile+'\n' + c+='#PBS -o '+job.outfile+'\n' + if job.user_env: + c+='#PBS -V\n' #end if - c='#!/bin/bash\n' - c+='#SBATCH --job-name '+str(job.name)+'\n' - c+='#SBATCH --account='+str(job.account)+'\n' - c+='#SBATCH -N '+str(job.nodes)+'\n' - c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) - c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) - c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) - #c+='#SBATCH --export=ALL\n' # equiv to PBS -V - c+='#SBATCH -o {0}\n'.format(job.outfile) - c+='#SBATCH -e {0}\n'.format(job.errfile) - c+='#SBATCH -p quad\n' - c+='\n' - c+='module purge\n' - c+='module add intel/intel-16.0.1.150\n' - c+='module add libraries/intel-mkl-16.0.1.150\n' - c+='module add mvapich2-intel-psm/1.7\n' + c+=''' +cd ${PBS_O_WORKDIR} + +''' return c #end def write_job_header -#end class Uno -##### SANDIA test +#end class Taub -## Added 09/23/2016 by JP Townsend -class Serrano(Supercomputer): - name = 'serrano' - requires_account = True - batch_capable = True - #executable_subfile = True +class OIC5(Supercomputer): - prefixed_output = True - outfile_extension = '.output' - errfile_extension = '.error' + name = 'oic5' + batch_capable = True def write_job_header(self,job): if job.queue is None: - job.queue='batch' - - job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 - if job.total_hours > 96: # warn if job will take more than 96 hrs. - self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) - job.hours = max_time - job.minutes =0 - job.seconds =0 + job.queue = 'mstqmc13q' #end if - #end if + ppn = job.processes_per_node + #ppn = 32/job.threads + #if ppn*job.threads!=32: + # self.error('ppn is not being set properly for OIC5\n perhaps the number of threads requested does not evenly divide the 32 cores\n you requested {0} threads'.format(job.threads)) + ##end if + c='#!/bin/bash\n' - c+='#SBATCH --job-name '+str(job.name)+'\n' - c+='#SBATCH --account='+str(job.account)+'\n' - c+='#SBATCH -N '+str(job.nodes)+'\n' - c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) - c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) - c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) - #c+='#SBATCH --export=ALL\n' # equiv to PBS -V - c+='#SBATCH -o {0}\n'.format(job.outfile) - c+='#SBATCH -e {0}\n'.format(job.errfile) - #c+='#SBATCH -p quad\n' - c+='\n' - c+='module purge\n' - c+='module add intel/16.0.3\n' - c+='module add mkl/16.0\n' - c+='module add mvapich2-intel-psm2/2.2rc1\n' - return c - #end def write_job_header -#end class Serrano -##### SANDIA test - - - - -## Added 09/23/2016 by JP Townsend -class Skybridge(Supercomputer): - name = 'skybridge' - - requires_account = True - batch_capable = True - #executable_subfile = True - - prefixed_output = True - outfile_extension = '.output' - errfile_extension = '.error' - - def write_job_header(self,job): - if job.queue is None: - job.queue='batch' - - job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 - if job.total_hours > 96: # warn if job will take more than 96 hrs. - self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) - job.hours = max_time - job.minutes =0 - job.seconds =0 - #end if - - #end if - c='#!/bin/bash\n' - c+='#SBATCH --job-name '+str(job.name)+'\n' - c+='#SBATCH --account='+str(job.account)+'\n' - c+='#SBATCH -N '+str(job.nodes)+'\n' - c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) - c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) - c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) - #c+='#SBATCH --export=ALL\n' # equiv to PBS -V - c+='#SBATCH -o {0}\n'.format(job.outfile) - c+='#SBATCH -e {0}\n'.format(job.errfile) - #c+='#SBATCH -p quad\n' - c+='\n' - c+='module purge\n' - c+='module add intel/intel-16.0.1.150\n' - c+='module add libraries/intel-mkl-16.0.1.150\n' - c+='module add mvapich2-intel-psm/1.7\n' - return c - #end def write_job_header -#end class Skybridge -##### SANDIA test - - - - -## Added 09/23/2016 by JP Townsend -class Redsky(Supercomputer): - name = 'redsky' - - requires_account = True - batch_capable = True - #executable_subfile = True - - prefixed_output = True - outfile_extension = '.output' - errfile_extension = '.error' - - def write_job_header(self,job): - if job.queue is None: - job.queue='batch' - - job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 - if job.total_hours > 96: # warn if job will take more than 96 hrs. - self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) - job.hours = max_time - job.minutes =0 - job.seconds =0 - #end if - - #end if - c='#!/bin/bash\n' - c+='#SBATCH --job-name '+str(job.name)+'\n' - c+='#SBATCH --account='+str(job.account)+'\n' - c+='#SBATCH -N '+str(job.nodes)+'\n' - c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) - c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) - c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) - #c+='#SBATCH --export=ALL\n' # equiv to PBS -V - c+='#SBATCH -o {0}\n'.format(job.outfile) - c+='#SBATCH -e {0}\n'.format(job.errfile) - #c+='#SBATCH -p quad\n' - c+='\n' - c+='module purge\n' - c+='module add intel/intel-16.0.1.150\n' - c+='module add libraries/intel-mkl-16.0.1.150\n' - c+='module add mvapich2-intel-psm/1.7\n' - return c - #end def write_job_header -#end class Redsky -##### SANDIA test - - - - -## Added 09/23/2016 by JP Townsend -class Solo(Supercomputer): - name = 'solo' - - requires_account = True - batch_capable = True - #executable_subfile = True - - prefixed_output = True - outfile_extension = '.output' - errfile_extension = '.error' - - def write_job_header(self,job): - if job.queue is None: - job.queue='batch' - - job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 - if job.total_hours > 96: # warn if job will take more than 96 hrs. - self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) - job.hours = max_time - job.minutes =0 - job.seconds =0 - #end if - - #end if - c='#!/bin/bash\n' - c+='#SBATCH --job-name '+str(job.name)+'\n' - c+='#SBATCH --account='+str(job.account)+'\n' - c+='#SBATCH -N '+str(job.nodes)+'\n' - c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) - c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) - c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) - #c+='#SBATCH --export=ALL\n' # equiv to PBS -V - c+='#SBATCH -o {0}\n'.format(job.outfile) - c+='#SBATCH -e {0}\n'.format(job.errfile) - #c+='#SBATCH -p quad\n' - c+='\n' - c+='module purge\n' - c+='module add intel/16.0.3\n' - c+='module add mkl/16.0\n' - c+='module add mvapich2-intel-psm2/2.2rc1\n' - return c - #end def write_job_header -#end class Solo -##### SANDIA test - - - - - -class Jaguar(Supercomputer): - name = 'jaguar' - - requires_account = True - - def write_job_header(self,job): - if job.queue is None: - job.queue='batch' - #end if - c='#!/bin/bash\n' - c+='#PBS -A '+str(job.account)+'\n' - c+='#PBS -q '+job.queue+'\n' - c+='#PBS -N '+str(job.name)+'\n' - c+='#PBS -l walltime='+job.pbs_walltime()+'\n' - c+='#PBS -l size='+str(job.tot_cores)+'\n' - c+='#PBS -l gres=widow2%widow3\n' - c+='#PBS -o '+job.outfile+'\n' - c+='#PBS -e '+job.errfile+'\n' - if job.user_env: - c+='#PBS -V\n' - #end if - c+=''' -echo $PBS_O_WORKDIR -cd $PBS_O_WORKDIR -export MPICH_PTL_SEND_CREDITS=-1 -export MPICH_MAX_SHORT_MSG_SIZE=1024 -export MPICH_PTL_UNEX_EVENTS=800000 -export MPICH_UNEX_BUFFER_SIZE=16M -export MPI_MSGS_PER_PROC=32768 -''' - return c - #end def write_job_header - - - def read_process_id(self,output): - self.not_implemented() - #end def read_process_id -#end class Jaguar - - - - -class Taub(Supercomputer): - - name = 'taub' - - def write_job_header(self,job): - if job.queue is None: - job.queue='cse' - #end if - c='' - c+='#PBS -q '+job.queue+'\n' - c+='#PBS -N '+job.name+'\n' - c+='#PBS -l nodes={0}:ppn={1}\n'.format(job.nodes,job.ppn) - c+='#PBS -l walltime='+job.pbs_walltime()+'\n' - c+='#PBS -e '+job.errfile+'\n' - c+='#PBS -o '+job.outfile+'\n' - if job.user_env: - c+='#PBS -V\n' - #end if - c+=''' -cd ${PBS_O_WORKDIR} - -''' - return c - #end def write_job_header - -#end class Taub - - - - -class OIC5(Supercomputer): - - name = 'oic5' - batch_capable = True - - def write_job_header(self,job): - if job.queue is None: - job.queue = 'mstqmc13q' - #end if - - ppn = job.processes_per_node - #ppn = 32/job.threads - #if ppn*job.threads!=32: - # self.error('ppn is not being set properly for OIC5\n perhaps the number of threads requested does not evenly divide the 32 cores\n you requested {0} threads'.format(job.threads)) - ##end if - - c='#!/bin/bash\n' - c+='#PBS -q '+job.queue+'\n' - c+='#PBS -N '+str(job.name)+'\n' - c+='#PBS -l walltime='+job.pbs_walltime()+'\n' - c+='#PBS -l nodes={0}:ppn={1}\n'.format(job.nodes,ppn) - c+='#PBS -W x=\"NACCESSPOLICY:SINGLEJOB\"\n' - c+='#PBS -o '+job.outfile+'\n' - c+='#PBS -e '+job.errfile+'\n' - if job.user_env: - c+='#PBS -V\n' - #end if - c+=''' -echo $PBS_O_WORKDIR -cd $PBS_O_WORKDIR -''' + c+='#PBS -q '+job.queue+'\n' + c+='#PBS -N '+str(job.name)+'\n' + c+='#PBS -l walltime='+job.pbs_walltime()+'\n' + c+='#PBS -l nodes={0}:ppn={1}\n'.format(job.nodes,ppn) + c+='#PBS -W x=\"NACCESSPOLICY:SINGLEJOB\"\n' + c+='#PBS -o '+job.outfile+'\n' + c+='#PBS -e '+job.errfile+'\n' + if job.user_env: + c+='#PBS -V\n' + #end if + c+=''' +echo $PBS_O_WORKDIR +cd $PBS_O_WORKDIR +''' return c #end def write_job_header @@ -2558,6 +2194,9 @@ def process_job_extra(self,job): env = '--env BG_SHAREDMEMSIZE=32', mode = '--mode script' ) + #if job.processes 96: # warn if job will take more than 96 hrs. + self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) + job.hours = max_time + job.minutes =0 + job.seconds =0 + #end if + + #end if + c='#!/bin/bash\n' + c+='#SBATCH --job-name '+str(job.name)+'\n' + c+='#SBATCH --account='+str(job.account)+'\n' + c+='#SBATCH -N '+str(job.nodes)+'\n' + c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) + c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) + c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) + #c+='#SBATCH --export=ALL\n' # equiv to PBS -V + c+='#SBATCH -o {0}\n'.format(job.outfile) + c+='#SBATCH -e {0}\n'.format(job.errfile) + c+='\n' + c+='module purge\n' + c+='module add intel/intel-16.0.1.150\n' + c+='module add libraries/intel-mkl-16.0.1.150\n' + c+='module add mvapich2-intel-psm/1.7\n' + return c + #end def write_job_header +#end class Chama +##### SANDIA test + + + +class Uno(Supercomputer): + name = 'uno' + + requires_account = True + batch_capable = True + #executable_subfile = True + + prefixed_output = True + outfile_extension = '.output' + errfile_extension = '.error' + + def write_job_header(self,job): + if job.queue is None: + job.queue='batch' + #end if + + job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 + if job.total_hours > 96: # warn if job will take more than 96 hrs. + self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) + job.hours = max_time + job.minutes =0 + job.seconds =0 + #end if + + #end if + c='#!/bin/bash\n' + c+='#SBATCH --job-name '+str(job.name)+'\n' + c+='#SBATCH --account='+str(job.account)+'\n' + c+='#SBATCH -N '+str(job.nodes)+'\n' + c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) + c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) + c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) + #c+='#SBATCH --export=ALL\n' # equiv to PBS -V + c+='#SBATCH -o {0}\n'.format(job.outfile) + c+='#SBATCH -e {0}\n'.format(job.errfile) + c+='#SBATCH -p quad\n' + c+='\n' + c+='module purge\n' + c+='module add intel/intel-16.0.1.150\n' + c+='module add libraries/intel-mkl-16.0.1.150\n' + c+='module add mvapich2-intel-psm/1.7\n' + return c + #end def write_job_header +#end class Uno +##### SANDIA test + + + + +## Added 09/23/2016 by JP Townsend +class Serrano(Supercomputer): + name = 'serrano' + + requires_account = True + batch_capable = True + #executable_subfile = True + + prefixed_output = True + outfile_extension = '.output' + errfile_extension = '.error' + + def write_job_header(self,job): + if job.queue is None: + job.queue='batch' + #end if + + job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 + if job.total_hours > 96: # warn if job will take more than 96 hrs. + self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) + job.hours = max_time + job.minutes =0 + job.seconds =0 + #end if + + #end if + c='#!/bin/bash\n' + c+='#SBATCH --job-name '+str(job.name)+'\n' + c+='#SBATCH --account='+str(job.account)+'\n' + c+='#SBATCH -N '+str(job.nodes)+'\n' + c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) + c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) + c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) + #c+='#SBATCH --export=ALL\n' # equiv to PBS -V + c+='#SBATCH -o {0}\n'.format(job.outfile) + c+='#SBATCH -e {0}\n'.format(job.errfile) + #c+='#SBATCH -p quad\n' + c+='\n' + c+='module purge\n' + c+='module add intel/16.0.3\n' + c+='module add mkl/16.0\n' + c+='module add mvapich2-intel-psm2/2.2rc1\n' + return c + #end def write_job_header +#end class Serrano +##### SANDIA test + + + + +## Added 09/23/2016 by JP Townsend +class Skybridge(Supercomputer): + name = 'skybridge' + + requires_account = True + batch_capable = True + #executable_subfile = True + + prefixed_output = True + outfile_extension = '.output' + errfile_extension = '.error' + + def write_job_header(self,job): + if job.queue is None: + job.queue='batch' + #end if + + job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 + if job.total_hours > 96: # warn if job will take more than 96 hrs. + self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) + job.hours = max_time + job.minutes =0 + job.seconds =0 + #end if + + #end if + c='#!/bin/bash\n' + c+='#SBATCH --job-name '+str(job.name)+'\n' + c+='#SBATCH --account='+str(job.account)+'\n' + c+='#SBATCH -N '+str(job.nodes)+'\n' + c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) + c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) + c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) + #c+='#SBATCH --export=ALL\n' # equiv to PBS -V + c+='#SBATCH -o {0}\n'.format(job.outfile) + c+='#SBATCH -e {0}\n'.format(job.errfile) + #c+='#SBATCH -p quad\n' + c+='\n' + c+='module purge\n' + c+='module add intel/intel-16.0.1.150\n' + c+='module add libraries/intel-mkl-16.0.1.150\n' + c+='module add mvapich2-intel-psm/1.7\n' + return c + #end def write_job_header +#end class Skybridge +##### SANDIA test + + + + +## Added 09/23/2016 by JP Townsend +class Redsky(Supercomputer): + name = 'redsky' + + requires_account = True + batch_capable = True + #executable_subfile = True + + prefixed_output = True + outfile_extension = '.output' + errfile_extension = '.error' + + def write_job_header(self,job): + if job.queue is None: + job.queue='batch' + #end if + + job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 + if job.total_hours > 96: # warn if job will take more than 96 hrs. + self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) + job.hours = max_time + job.minutes =0 + job.seconds =0 + #end if + + #end if + c='#!/bin/bash\n' + c+='#SBATCH --job-name '+str(job.name)+'\n' + c+='#SBATCH --account='+str(job.account)+'\n' + c+='#SBATCH -N '+str(job.nodes)+'\n' + c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) + c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) + c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) + #c+='#SBATCH --export=ALL\n' # equiv to PBS -V + c+='#SBATCH -o {0}\n'.format(job.outfile) + c+='#SBATCH -e {0}\n'.format(job.errfile) + #c+='#SBATCH -p quad\n' + c+='\n' + c+='module purge\n' + c+='module add intel/intel-16.0.1.150\n' + c+='module add libraries/intel-mkl-16.0.1.150\n' + c+='module add mvapich2-intel-psm/1.7\n' + return c + #end def write_job_header +#end class Redsky +##### SANDIA test + + + + +## Added 09/23/2016 by JP Townsend +class Solo(Supercomputer): + name = 'solo' + + requires_account = True + batch_capable = True + #executable_subfile = True + + prefixed_output = True + outfile_extension = '.output' + errfile_extension = '.error' + + def write_job_header(self,job): + if job.queue is None: + job.queue='batch' + #end if + + job.total_hours = job.days*24 + job.hours + job.minutes/60.0 + job.seconds/3600.0 + if job.total_hours > 96: # warn if job will take more than 96 hrs. + self.warn('!!! ATTENTION !!!\n the maximum runtime on {0} should not be more than {1}\n you requested: {2}'.format(job.queue,max_time,job.total_hours)) + job.hours = max_time + job.minutes =0 + job.seconds =0 + #end if + + #end if + c='#!/bin/bash\n' + c+='#SBATCH --job-name '+str(job.name)+'\n' + c+='#SBATCH --account='+str(job.account)+'\n' + c+='#SBATCH -N '+str(job.nodes)+'\n' + c+='#SBATCH --ntasks-per-node={0}\n'.format(job.processes_per_node) + c+='#SBATCH --cpus-per-task={0}\n'.format(job.threads) + c+='#SBATCH -t {0}:{1}:{2}\n'.format(str(job.hours+24*job.days).zfill(2),str(job.minutes).zfill(2),str(job.seconds).zfill(2)) + #c+='#SBATCH --export=ALL\n' # equiv to PBS -V + c+='#SBATCH -o {0}\n'.format(job.outfile) + c+='#SBATCH -e {0}\n'.format(job.errfile) + #c+='#SBATCH -p quad\n' + c+='\n' + c+='module purge\n' + c+='module add intel/16.0.3\n' + c+='module add mkl/16.0\n' + c+='module add mvapich2-intel-psm2/2.2rc1\n' + return c + #end def write_job_header +#end class Solo +##### SANDIA test + + + # machines at LRZ https://www.lrz.de/english/ class SuperMUC(Supercomputer): name = 'supermuc' @@ -2900,7 +2848,8 @@ def write_job_header(self,job): #end def write_job_header #end class SuperMUC -# + + class Stampede2(Supercomputer): name = 'stampede2' @@ -3029,7 +2978,4 @@ def write_job_header(self,job): -# tests -#Machine.check_process_job_idempotency() - diff --git a/nexus/library/nexus.py b/nexus/library/nexus.py index 628df28410..50cda58e12 100755 --- a/nexus/library/nexus.py +++ b/nexus/library/nexus.py @@ -44,6 +44,7 @@ from gamess import Gamess , GamessInput , GamessAnalyzer , generate_gamess_input , generate_gamess, FormattedGroup from vasp import Vasp , VaspInput , VaspAnalyzer , generate_vasp_input , generate_vasp from qmcpack import Qmcpack, QmcpackInput, QmcpackAnalyzer, generate_qmcpack_input, generate_qmcpack +from quantum_package import QuantumPackage,QuantumPackageInput,QuantumPackageAnalyzer,generate_quantum_package_input,generate_quantum_package from qmcpack_converters import Pw2qmcpack , Pw2qmcpackInput , Pw2qmcpackAnalyzer , generate_pw2qmcpack_input , generate_pw2qmcpack from qmcpack_converters import Wfconvert , WfconvertInput , WfconvertAnalyzer , generate_wfconvert_input , generate_wfconvert @@ -107,12 +108,17 @@ class Settings(NexusCore): pwscf_vars = set(''' vdw_table - '''.split()) + '''.split()) + + qm_package_vars = set(''' + qprc + '''.split()) nexus_core_vars = core_assign_vars | core_process_vars nexus_noncore_vars = noncore_assign_vars | noncore_process_vars nexus_vars = nexus_core_vars | nexus_noncore_vars - allowed_vars = nexus_vars | machine_vars | gamess_vars | pwscf_vars + allowed_vars = nexus_vars | machine_vars \ + | gamess_vars | pwscf_vars | qm_package_vars @staticmethod @@ -184,10 +190,11 @@ def __call__(self,**kwargs): #end for # extract settings based on keyword groups - kw = Settings.kw_set(Settings.nexus_vars ,kwargs) - mach_kw = Settings.kw_set(Settings.machine_vars,kwargs) - gamess_kw = Settings.kw_set(Settings.gamess_vars ,kwargs) - pwscf_kw = Settings.kw_set(Settings.pwscf_vars ,kwargs) + kw = Settings.kw_set(Settings.nexus_vars ,kwargs) + mach_kw = Settings.kw_set(Settings.machine_vars ,kwargs) + gamess_kw = Settings.kw_set(Settings.gamess_vars ,kwargs) + pwscf_kw = Settings.kw_set(Settings.pwscf_vars ,kwargs) + qm_pkg_kw = Settings.kw_set(Settings.qm_package_vars,kwargs) if len(kwargs)>0: self.error('some settings keywords have not been accounted for\nleftover keywords: {0}\nthis is a developer error'.format(sorted(kwargs.keys()))) #end if @@ -222,7 +229,11 @@ def __call__(self,**kwargs): # process pwscf settings Pwscf.restore_default_settings() - Pwscf.settings(**pwscf_kw) + Pwscf.settings(**pwscf_kw) + + # process quantum package settings + QuantumPackage.restore_default_settings() + QuantumPackage.settings(**qm_pkg_kw) return #end def __call__ @@ -293,6 +304,10 @@ def process_command_line_settings(self,script_settings): default='none', help='Path to the vdw_table file used with Quantum Espresso (required only if running Quantum Espresso with van der Waals functionals).' ) + parser.add_option('--qprc',dest='qprc', + default='none', + help='Path to the quantum_package.rc file used with Quantum Package.' + ) # parse the command line inputs options,files_in = parser.parse_args() diff --git a/nexus/library/quantum_package.py b/nexus/library/quantum_package.py new file mode 100644 index 0000000000..5ce14159b3 --- /dev/null +++ b/nexus/library/quantum_package.py @@ -0,0 +1,241 @@ +################################################################## +## (c) Copyright 2018- by Jaron T. Krogel ## +################################################################## + + +#====================================================================# +# quantum_package.py # +# Nexus interface for the Quantum Package simulation code. # +# # +# Content summary: # +# QuantumPackage # +# Simulation class for Quantum Package. # +# # +# generate_quantum_package # +# User-facing function to generate Quantum Package simulation # +# objects. # +#====================================================================# + + +import os +from generic import obj +from execute import execute +from simulation import Simulation +from quantum_package_input import QuantumPackageInput,generate_quantum_package_input +from quantum_package_analyzer import QuantumPackageAnalyzer + + + +class QuantumPackage(Simulation): + input_type = QuantumPackageInput + analyzer_type = QuantumPackageAnalyzer + generic_identifier = 'qp' + infile_extension = '.ezfio' + application = 'qp_run' + application_properties = set(['serial','mpi']) + application_results = set([]) + + allow_overlapping_files = True + + qprc = None + + slave_partners = obj( + SCF = 'qp_ao_ints', + fci_zmq = 'selection_davidson_slave', + ) + + @staticmethod + def settings(qprc=None): + # path to quantum_package.rc file + QuantumPackage.qprc = qprc + if qprc is not None: + if not isinstance(qprc,str): + QuantumPackage.class_error('settings input "qprc" must be a path\nreceived type: {0}\nwith value: {1}'.format(qprc.__class__.__name__,qprc)) + elif not os.path.exists(qprc): + QuantumPackage.class_error('quantum_package.rc file does not exist\nfile path provided via "qprc" in settings\nfile path: {0}'.format(qprc)) + #end if + #end if + #end def settings + + @staticmethod + def restore_default_settings(): + QuantumPackage.qprc = None + #end def restore_default_settings + + def pre_init(self): + prefix = self.input.run_control.prefix + self.infile = prefix + self.infile_extension + #end def pre_init + + + def post_init(self): + qprc = QuantumPackage.qprc + if qprc is None: + self.error('cannot run quantum package\nplease provide path to quantum_package.rc in settings via argument "qprc"') + #end if + self.job.presub += '\nsource {0}\n'.format(os.path.abspath(qprc)) + #end def post_init + + + def write_prep(self): + # write an ascii represenation of the input changes + infile = self.identifier+'.in' + infile = os.path.join(self.locdir,infile) + f = open(infile,'w') + s = self.input.delete_optional('structure',None) + f.write(str(self.input)) + if s is not None: + self.input.structure = s + #end if + f.close() + #end def write_prep + + + def check_result(self,result_name,sim): + return False + #end def check_result + + + def get_result(self,result_name,sim): + self.not_implemented() + #end def get_result + + + def incorporate_result(self,result_name,result,sim): + self.not_implemented() + #end def incorporate_result + + + def check_sim_status(self): + success = True + self.finished = success and self.job.finished + #end def check_sim_status + + + def get_output_files(self): + output_files = [] + return output_files + #end def get_output_files + + + def get_slave(self): + rc = self.input.run_control + sp = QuantumPackage.slave_partners + slave = None + if 'slave' in rc: + slave = rc.slave + elif rc.run_type in sp: + slave = sp[rc.run_type] + #end if + return slave + #end def get_slave + + + def app_command(self): + + # get run controls + input = self.input + rc = input.run_control + + # make the basic app command, no splitting etc + run_type = rc.run_type + app_command = self.app_name+' '+run_type+' '+self.infile + + # prepare local vars in case splitting or other tricks are needed + fc = '' + job = self.job + + # add cis-loop runs if requested + if 'cis_loop' in rc: + nloop = 0 + if isinstance(rc.cis_loop,bool) and rc.cis_loop: + nloop = 2 + else: + nloop = rc.cis_loop + #end for + if nloop>0: + jloop = job.clone() + fc+='\n' + for n in range(nloop): + jloop.app_command = self.app_name+' cis '+self.infile + fc += jloop.run_command()+' >{0}_{1}.out 2>{0}_{1}.err\n'.format(self.identifier,n) + jloop.app_command = self.app_name+' save_natorb '+self.infile + fc += jloop.run_command()+'\n' + #end for + fc+='\n' + integrals = [ + 'integrals_monoelec/disk_access_ao_one_integrals', + 'integrals_monoelec/disk_access_mo_one_integrals', + 'integrals_bielec/disk_access_ao_integrals', + 'integrals_bielec/disk_access_mo_integrals', + ] + cl = '' + for integral in integrals: + isec,ivar = integral.split('/') + if input.present(ivar): + val = input.delete(ivar) + cl += 'echo "{0}" > {1}/{2}\n'.format(val,self.infile,integral) + #end if + #end for + if len(cl)>0: + fc+=cl+'\n' + #end if + #end if + #end if + + # perform master-slave job splitting if necessary + slave = self.get_slave() + split_nodes = job.nodes>1 and job.full_command is None + split_nodes &= slave is not None + if split_nodes: + slave_command = self.app_name+' -slave {0} {1}'.format(slave,self.infile) + outfile = self.outfile + errfile = self.errfile + prefix,ext = outfile.split('.',1) + slave_outfile = prefix+'_slave.'+ext + prefix,ext = errfile.split('.',1) + slave_errfile = prefix+'_slave.'+ext + + job.divert_out_err() + job1,job2 = job.split_nodes(1) + job1.app_command = app_command + job2.app_command = slave_command + fc += job1.run_command()+' >{0} 2>{1}&\n'.format(outfile,errfile) + fc += 'sleep {0}\n'.format(self.input.run_control.sleep) + fc += job2.run_command()+' >{0} 2>{1}\n'.format(slave_outfile,slave_errfile) + + if 'davidson' in slave and not input.present('distributed_davidson'): + input.set(distributed_davidson=True) + #end if + elif len(fc)>0: + job.divert_out_err() + job.app_command = app_command + fc += job.run_command()+' >{0} 2>{1}\n'.format(self.outfile,self.errfile) + #end if + + if len(fc)>0: + job.full_command = fc + #end if + + return app_command + #end def app_command + +#end class QuantumPackage + + + +def generate_quantum_package(**kwargs): + sim_args,inp_args = QuantumPackage.separate_inputs(kwargs) + + if not 'input' in sim_args: + if 'input_type' in inp_args: + input_type = inp_args.input_type + del inp_args.input_type + #end if + sim_args.input = generate_quantum_package_input(**inp_args) + #end if + qp = QuantumPackage(**sim_args) + + return qp +#end def generate_quantum_package + diff --git a/nexus/library/quantum_package_analyzer.py b/nexus/library/quantum_package_analyzer.py new file mode 100644 index 0000000000..1ba8d3d412 --- /dev/null +++ b/nexus/library/quantum_package_analyzer.py @@ -0,0 +1,24 @@ +################################################################## +## (c) Copyright 2018- by Jaron T. Krogel ## +################################################################## + + +#====================================================================# +# quantum_package_analyzer.py # +# Supports data analysis for Quantum Package output. Currently # +# just a placeholder for further development. # +# # +# Content summary: # +# QuantumPackageAnalyzer # +# SimulationAnalyzer class for Quantum Package. # +# # +#====================================================================# + + +from simulation import NullSimulationAnalyzer + + +class QuantumPackageAnalyzer(NullSimulationAnalyzer): + None +#end class QuantumPackageAnalyzer + diff --git a/nexus/library/quantum_package_input.py b/nexus/library/quantum_package_input.py new file mode 100644 index 0000000000..0b5b422543 --- /dev/null +++ b/nexus/library/quantum_package_input.py @@ -0,0 +1,789 @@ +################################################################## +## (c) Copyright 2018- by Jaron T. Krogel ## +################################################################## + + +#====================================================================# +# quantum_package_input.py # +# Supports I/O for Quantum Package input data. # +# # +# Content summary: # +# QuantumPackageInput # +# SimulationInput class for Quantum Package. # +# # +# generate_quantum_package_input # +# User-facing function to create arbitrary input. # +#====================================================================# + +import os +from generic import obj +from developer import DevBase,log,error +from structure import Structure +from physical_system import PhysicalSystem +from simulation import SimulationInput +from execute import execute + + +bool_values = dict(T=True,F=False) +bool_values_inv = {True:'T',False:'F'} + +def read_qp_value_type(value_filepath): + f = open(value_filepath,'r') + svalue = f.read().strip() + f.close() + if svalue in bool_values: + return 'bool' + else: + try: + v = int(svalue) + return 'int' + except: + try: + v = float(svalue) + return 'float' + except: + return 'str' + #end try + #end try + #end try +#end def read_qp_value_type + + +def read_qp_value(value_filepath): + f = open(value_filepath,'r') + svalue = f.read().strip() + f.close() + if svalue in bool_values: + v = bool_values[svalue] + else: + try: + v = int(svalue) + except: + try: + v = float(svalue) + except: + v = svalue + #end try + #end try + #end try + return v +#end def read_qp_value + + +def write_qp_value(value_filepath,value): + if isinstance(value,bool): + svalue = bool_values_inv[value] + elif isinstance(value,int): + svalue = str(value) + elif isinstance(value,float): + '{0: 24.15e}'.format(value) + elif isinstance(value,str): + svalue = value + else: + QuantumPackageInput.class_error('invalid type encountered on write\nattempted to write variable: {0}\nwith type: {1}\nvalid type options: bool,int,float,str'.format(value_filepath,value.__class__.__name__)) + #end if + f = open(value_filepath,'w') + f.write(svalue+'\n') + f.close() +#end def write_qp_value + + + +# quantum package path input specification +# The spec below was obtained by using the extract_input_specification +# function. Run this function again with ezfio directory paths as +# arguments to further augment the specification over time. +input_specification = obj({ + 'ao_basis/ao_basis' : str, + 'ao_basis/ao_cartesian' : bool, + 'ao_basis/ao_md5' : str, + 'ao_basis/ao_num' : int, + 'bitmasks/bit_kind' : int, + 'bitmasks/n_int' : int, + 'bitmasks/n_mask_cas' : int, + 'bitmasks/n_mask_gen' : int, + 'davidson/davidson_sze_max' : int, + 'davidson/disk_based_davidson' : bool, + 'davidson/distributed_davidson' : bool, + 'davidson/n_states_diag' : int, + 'davidson/state_following' : bool, + 'davidson/threshold_davidson' : float, + 'determinants/bit_kind' : int, + 'determinants/expected_s2' : float, + 'determinants/mo_label' : str, + 'determinants/n_det' : int, + 'determinants/n_det_max' : int, + 'determinants/n_det_max_jacobi' : int, + 'determinants/n_det_max_property' : int, + 'determinants/n_det_max_stored' : int, + 'determinants/n_int' : int, + 'determinants/n_states' : int, + 'determinants/only_single_double_dm' : bool, + 'determinants/read_wf' : bool, + 'determinants/s2_eig' : bool, + 'determinants/store_full_h_mat' : bool, + 'determinants/target_energy' : float, + 'determinants/threshold_generators' : float, + 'determinants/threshold_selectors' : float, + 'electrons/elec_alpha_num' : int, + 'electrons/elec_beta_num' : int, + 'ezfio/creation' : str, + 'ezfio/library' : str, + 'ezfio/user' : str, + 'full_ci_zmq/energy' : float, + 'full_ci_zmq/energy_pt2' : float, + 'full_ci_zmq/iterative_save' : int, + 'full_ci_zmq/n_iter' : int, + 'hartree_fock/energy' : float, + 'hartree_fock/level_shift' : float, + 'hartree_fock/max_dim_diis' : int, + 'hartree_fock/mo_guess_type' : str, + 'hartree_fock/n_it_scf_max' : int, + 'hartree_fock/no_oa_or_av_opt' : bool, + 'hartree_fock/scf_algorithm' : str, + 'hartree_fock/thresh_scf' : float, + 'hartree_fock/threshold_diis' : float, + 'integrals_bielec/direct' : bool, + 'integrals_bielec/disk_access_ao_integrals' : str, + 'integrals_bielec/disk_access_mo_integrals' : str, + 'integrals_bielec/no_ivvv_integrals' : bool, + 'integrals_bielec/no_vvv_integrals' : bool, + 'integrals_bielec/no_vvvv_integrals' : bool, + 'integrals_bielec/threshold_ao' : float, + 'integrals_bielec/threshold_mo' : float, + 'integrals_monoelec/disk_access_ao_one_integrals' : str, + 'integrals_monoelec/disk_access_mo_one_integrals' : str, + 'mo_basis/ao_md5' : str, + 'mo_basis/mo_label' : str, + 'mo_basis/mo_tot_num' : int, + 'mrpt_utils/do_third_order_1h1p' : bool, + 'nuclei/disk_access_nuclear_repulsion' : str, + 'nuclei/nucl_num' : int, + 'perturbation/correlation_energy_ratio_max' : float, + 'perturbation/do_pt2' : bool, + 'perturbation/pt2_absolute_error' : float, + 'perturbation/pt2_max' : float, + 'perturbation/pt2_relative_error' : float, + 'perturbation/threshold_generators_pt2' : float, + 'perturbation/threshold_selectors_pt2' : float, + 'properties/threshld_two_bod_dm' : float, + 'properties/z_one_point' : float, + 'pseudo/do_pseudo' : bool, + 'pseudo/pseudo_grid_rmax' : float, + 'pseudo/pseudo_grid_size' : int, + 'pseudo/pseudo_klocmax' : int, + 'pseudo/pseudo_kmax' : int, + 'pseudo/pseudo_lmax' : int, + 'qmc/ci_threshold' : float, + 'work/empty' : bool, + 'work/qp_run_address' : str, + }) + + +# create mapping from variable name to section (directory) name +known_sections = set() +known_variables = set() +variable_section = obj() +section_variables = obj() +for vpath in input_specification.keys(): + secname,varname = vpath.split('/') + known_sections.add(secname) + known_variables.add(varname) + if varname not in variable_section: + variable_section[varname] = secname + else: + vsec = variable_section[varname] + if isinstance(vsec,str): + vsec = [vsec,secname] + else: + vsec.append(secname) + #end if + #end if + if secname not in section_variables: + section_variables[secname] = set() + #end if + section_variables[secname].add(varname) +#end for +for varname in variable_section.keys(): + vsec = variable_section[varname] + if isinstance(vsec,list): + variable_section[varname] = tuple(vsec) + #end if +#end for + + +# function to extract and print an updated input_specification based on a list of ezfio directories +def extract_input_specification(*ezfio_paths): + if len(ezfio_paths)==1 and isinstance(ezfio_paths[0],(list,tuple)): + ezfio_paths = ezfio_paths[0] + #end if + log('\nextracting Quantum Package input specification from ezfio directories') + typedict = {bool:'bool',int:'int',float:'float',str:'str'} + new_input_spec = obj() + for vpath,vtype in input_specification.iteritems(): + new_input_spec[vpath] = typedict[vtype] + #end for + for epath in ezfio_paths: + epath = epath.rstrip('/') + if not epath.endswith('.ezfio'): + error('cannot extract input spec from path\ninput path provided is not an ezfio directory\ninput path provided: {0}'.format(epath),'Quantum Package') + elif not os.path.exists(epath): + error('cannot extract input spec from path\ninput path provided does not exist\ninput path provided: {0}'.format(epath),'Quantum Package') + #end if + log(' extracting from: {0}'.format(epath)) + for path,dirs,files in os.walk(epath): + for file in files: + if 'save' not in path: + if not file.startswith('.') and not file.endswith('.gz'): + filepath = os.path.join(path,file) + vtype = read_qp_value_type(filepath) + vpath = filepath.replace(epath,'').strip('/') + if vpath not in new_input_spec: + new_input_spec[vpath] = vtype + #end if + #end if + #end if + #end for + #end for + #end for + log(' extraction complete') + + old_vpaths = set(input_specification.keys()) + new_vpaths = set(new_input_spec.keys()) + if new_vpaths==old_vpaths: + log('\ninput specification in quantum_package_input.py needs no changes\n') + else: + log('\nplease replace input_specification in quantum_package_input.py with the following:\n') + log('input_specification = obj({') + s = '' + for vpath in sorted(new_input_spec.keys()): + vtype = new_input_spec[vpath] + s += " '{0}' : {1},\n".format(vpath,vtype) + #end for + s += ' })\n' + log(s) + #end if +#end def extract_input_specification + + + +class Section(DevBase): + None +#end class Section + + + +class QuantumPackageInput(SimulationInput): + + added_keys = ''' + structure + run_control + '''.split() + + run_types = set(''' + Gen_Ezfio_from_integral.sh + H_CORE_guess + Huckel_guess + SCF + SCF_old + analyze_wf + check_orthonormality + cis + create_ezfio.py + davidson_slave + densify_coefmatrix + diagonalize_restart_and_save_all_states + diagonalize_restart_and_save_lowest_state + diagonalize_restart_and_save_one_state + diagonalize_restart_and_save_two_states + dump_nto + dump_one_body_mos + fci_zmq + fci_zmq_nos + four_idx_transform + guess_doublet + guess_lowest_state + guess_singlet + guess_triplet + localize_mos + mo_permutation + overwrite_with_cas + print_H_matrix_restart + print_aos + print_bitmask + print_energy + print_hcc + print_holes_particles + print_integrals_ao + print_integrals_mo + print_mo_in_space + print_mulliken + print_spin_density + print_wf + provide_deltarho + pt2_slave + pt2_stoch + pyscf.main + qmc_create_wf + qmc_e_curve + qp_ao_ints + qp_convert_qmcpack_to_ezfio.py + read_ao_eri_chunk + read_integrals_achocol + read_integrals_achocol2 + read_integrals_ao + read_integrals_mo + read_integrals_mo_chocol + save_HF_determinant + save_for_qmcchem + save_for_qmcpack + save_natorb + save_only_singles + save_ortho_mos + selection_davidson_slave + selection_slave + super_ci + swap_mos + target_pt2_qmc + target_pt2_ratio_zmq + target_pt2_zmq + test_integrals + test_two_body_dm + truncate_wf + truncate_wf_spin + '''.split()) + + slave_allowed = set(''' + SCF + cis + fci_zmq + '''.split()) + + integral_write_allowed = set(''' + SCF + cis + fci_zmq + '''.split()) + + + def __init__(self,filepath=None): + self.structure = None + self.run_control = obj() + if filepath!=None: + self.read(filepath) + #end if + #end def __init__ + + + def present(self,name): + if name not in known_variables: + self.error('attempted to check presence of unknown variable "{0}"valid options are: {1}'.format(name,sorted(known_variables))) + #end if + secname = variable_section[name] + return secname in self and name in self[secname] + #end def present + + + def set(self,**kwargs): + for name,value in kwargs.iteritems(): + if name not in known_variables: + self.error('cannot set variable\nattempted to set unknown variable "{0}"\nwith value: {1}\nvalid options are: {2}'.format(name,value,sorted(known_variables))) + #end if + secname = variable_section[name] + if secname not in self: + self[secname] = Section() + #end if + self[secname][name] = value + #end for + #end def set + + + def get(self,name): + if name not in known_variables: + self.error('cannot get variable\nattempted to get unknown variable "{0}"\nvalid options are: {1}'.format(name,sorted(known_variables))) + #end if + value = None + secname = variable_section[name] + if secname in self and name in self[secname]: + value = self[secname][name] + #end if + return value + #end def get + + + def delete(self,name): + if name not in known_variables: + self.error('cannot get variable\nattempted to get unknown variable "{0}"\nvalid options are: {1}'.format(name,sorted(known_variables))) + #end if + value = None + secname = variable_section[name] + if secname in self and name in self[secname]: + value = self[secname].delete(name) + #end if + return value + #end def delete + + + def extract_added_keys(self): + extra = obj() + extra.move_from(self,QuantumPackageInput.added_keys) + return extra + #end def extract_added_keys + + + def restore_added_keys(self,extra): + extra.move_to(self,QuantumPackageInput.added_keys) + #end def restore_added_keys + + + def read(self,filepath): + epath = filepath.rstrip('/') + if not os.path.exists(epath): + self.error('cannot read input\nprovided ezfio directory does not exist\ndirectory provided: {0}'.format(epath)) + elif not os.path.isdir(epath): + self.error('cannot read input\nprovided ezfio path is not a directory\npath provided: {0}'.format(epath)) + elif not epath.endswith('.ezfio'): + self.error('cannot read input\nprovided path does not end in an ezfio directory\ndirectory must end with .ezfio\npath provided: {0}'.format(epath)) + #end if + for path,dirs,files in os.walk(epath): + for file in files: + if 'save' not in path: + if not file.startswith('.') and not file.endswith('.gz'): + filepath = os.path.join(path,file) + v = read_qp_value(filepath) + vpath = filepath.replace(epath,'').strip('/') + secname,varname = vpath.split('/') + if secname not in self: + self[secname] = Section() + #end if + self[secname][varname] = v + #end if + #end if + #end for + #end for + #end def read + + + def write(self,filepath=None): + if filepath is None: + return str(self) + #end if + + # get the ezfio path + epath = filepath.rstrip('/') + + # check that write can occur + if not epath.endswith('.ezfio'): + self.error('cannot write input\nprovided path does not end in an ezfio directory\ndirectory must end with .ezfio\npath provided: {0}'.format(epath)) + #end if + path,edir = os.path.split(epath) + if path=='': + path = './' + #end if + if not os.path.exists(path): + self.error('cannot write input\nattempted to write ezfio directory "{0}" at non-existent destination path\ndestination path: {1}'.format(edir,path)) + #end if + + # if there is no ezfio directory, initialize one + if not os.path.exists(epath): + if self.structure is None: + self.error('cannot write input\nstructure is missing\ninput path provided: {0}'.format(epath)) + elif not isinstance(self.structure,Structure): + self.error('cannot write input\nstructure must be of type: Structure\ntype provided: {0}\ninput path provided: {1}'.format(self.structure.__class__.__name__,epath)) + #end if + cwd = os.getcwd() + os.chdir(path) + prefix = edir.rsplit('.',1)[0] + struct_file = prefix+'.xyz' + self.structure.write_xyz(struct_file) + command = 'qp_create_ezfio_from_xyz' + if self.path_exists('ao_basis/ao_basis'): + command += ' -b '+self.ao_basis.ao_basis + #end if + command += ' '+struct_file + execute(command) + if not os.path.exists(edir): + self.error('cannot write input\nezfio creation command failed: {0}\nexecuted at path: {1}\ndirectory {2} not created\nplease source your quantum_package.rc file before running the current script'.format(command,path,edir)) + #end if + execute('qp_edit -c '+edir) + os.chdir(cwd) + #end if + + # write inputs into the ezfio directory/file tree + extra = self.extract_added_keys() + for secname,sec in self.iteritems(): + secpath = os.path.join(epath,secname) + if not os.path.exists(secpath): + self.error('cannot write input\ninput section path does not exist\nsection path: {0}\nplease ensure that all variables were created previously for this ezfio directory\n(to create all variables, run "qp_edit -c {1}")'.format(secpath,edir)) + #end if + for varname,val in sec.iteritems(): + vpath = os.path.join(secpath,varname) + write_qp_value(vpath,val) + #end for + #end for + self.restore_added_keys(extra) + + # take other steps that modify the input, as requested + if 'run_control' in self: + rc = self.run_control + if 'frozen_core' in rc and rc.frozen_core: + cwd = os.getcwd() + os.chdir(path) + execute('qp_set_frozen_core.py '+edir) + os.chdir(cwd) + #end if + #end if + + return '' + #end def write + + + def read_text(self,text,filepath=None): + self.not_implemented() + #end def read_text + + + def write_text(self,filepath=None): + self.not_implemented() + #end def write_text + + + def incorporate_system(self,system): + self.not_implemented() + #end def incorporate_system + + + def check_valid(self,sections=True,variables=True,types=True,exit=True): + msg = '' + + extra = self.extract_added_keys() + + valid_types = {float:(int,float)} + if sections: + for secname,sec in self.iteritems(): + if secname not in known_sections: + msg = 'input is invalid\nunknown section encountered\nunknown section provided: {0}\nvalid options are: {1}'.format(secname,sorted(known_sections)) + elif not isinstance(sec,Section): + msg = 'input is invalid\ninvalid section type encountered\nsection must be of type: Section\nsection name: {0}\nsection type: {1}\nsection contents: {2}'.format(secname,sec.__class__.__name__,sec) + #end if + if len(msg)>0: + break + #end if + if variables: + for varname,var in sec.iteritems(): + if varname not in known_variables: + msg = 'input is invalid\nunknown variable encountered in section "{0}"\nunknown variable: {1}\nvalid options are: {2}'.format(secname,varname,sorted(section_variables[secname])) + elif types: + vpath = secname+'/'+varname + if vpath not in input_specification: + msg = 'variable is known but variable path not found in input_specification\nvariable name: {0}\nvariable path: {1}\nthis is a developer error\nplease contact the developers'.format(varname,vpath) + else: + vtype = input_specification[vpath] + if vtype in valid_types: + vtype = valid_types[vtype] + #end if + if not isinstance(var,vtype): + type_map = {bool:'bool',int:'int',float:'float',str:'str'} + msg = 'input is invalid\nvariable "{0}" in section "{1}" must be of type {2}\ntype provided: {3}\nvalue provided: {4}'.format(varname,secname,type_map[vtype],var.__class__.__name__,var) + #end if + #end if + #end if + if len(msg)>0: + break + #end if + #end for + if len(msg)>0: + break + #end if + #end if + #end for + #end if + is_valid = len(msg)==0 + if not is_valid and exit: + self.error(msg) + #end if + + self.restore_added_keys(extra) + + return is_valid + #end check_valid + + + def is_valid(self): + return self.check_valid(exit=False) + #end def is_valid + +#end class QuantumPackageInput + + + +run_inputs = set(''' + prefix + run_type + frozen_core + cis_loop + sleep + slave + postprocess + '''.split()) +gen_inputs = set(''' + system + defaults + save_integrals + validate + '''.split()) +added_inputs = run_inputs | gen_inputs +added_types = obj( + # run inputs + prefix = str, + run_type = str, + frozen_core = bool, + cis_loop = (bool,int), + sleep = (int,float), + slave = str, + postprocess = (tuple,list), + # gen inputs + system = PhysicalSystem, + defaults = str, + save_integrals = bool, + validate = bool, + ) +added_required = set(''' + system + prefix + sleep + '''.split()) +qp_defaults_version = 'v1' +shared_defaults = obj( + # run inputs + postprocess = [], + # gen inputs + validate = True, + ) +qp_defaults = obj( + none = obj( + # gen inputs + save_integrals = False, + **shared_defaults + ), + v1 = obj( + # run inputs + sleep = 30, + # gen inputs + save_integrals = True, + # qp inputs + n_det_max = 5000, + **shared_defaults + ), + ) +save_ints_defaults = obj( + disk_access_ao_one_integrals = 'Write', + disk_access_mo_one_integrals = 'Write', + disk_access_ao_integrals = 'Write', + disk_access_mo_integrals = 'Write', + ) + +def generate_quantum_package_input(**kwargs): + + # make empty input + qpi = QuantumPackageInput() + + # rewrap keywords and apply defaults + kw = obj(**kwargs) + kw.set_optional(defaults=qp_defaults_version) + if kw.defaults not in qp_defaults: + QuantumPackageInput.class_error('cannot generate input\nrequested invalid default set\ndefault set requested: {0}\nvalid options are: {1}'.format(kw.defaults,sorted(qp_defaults.keys()))) + #end if + kw.set_optional(**qp_defaults[kw.defaults]) + + # check for required variables + req_missing = kw.check_required(added_required,exit=False) + if len(req_missing)>0: + QuantumPackageInput.class_error('cannot generate input\nrequired variables are missing\nmissing variables: {0}\nplease supply values for these variables via generate_quantum_package'.format(sorted(req_missing))) + #end if + + # check types of added variables + name,vtype = kw.check_types_optional(added_types,exit=False) + if name is not None: + QuantumPackageInput.class_error('cannot generate input\nvariable "{0}" has the wrong type\ntype required: {1}\ntype provided: {2}'.format(name,vtype.__name__,kw[name].__class__.__name__)) + #end if + + # separate run inputs from input file variables + run_kw = kw.extract_optional(run_inputs) + if run_kw.run_type not in QuantumPackageInput.run_types: + valid = '' + for rt in sorted(QuantumPackageInput.run_types): + valid += ' '+rt+'\n' + #end for + QuantumPackageInput.class_error('cannot generate input\ninvalid run_type requested\nrun_type provided: {0}\nvalid options are:\n{1}'.format(run_kw.run_type,valid)) + #end if + qpi.run_control.set(**run_kw) + + # separate generation inputs from input file variables + gen_kw = kw.extract_optional(gen_inputs) + + if gen_kw.save_integrals and run_kw.run_type in QuantumPackageInput.integral_write_allowed: + kw.set_optional(**save_ints_defaults) + #end if + + # partition inputs into sections and variables + sections = obj() + variables = obj() + for name,value in kw.iteritems(): + is_sec = name in known_sections + is_var = name in known_variables + if is_sec and is_var: + if isinstance(value,(obj,dict)): + sections[name] = value + else: + variables[name] = value + #end if + elif is_sec: + sections[name] = value + elif is_var: + variables[name] = value + else: + QuantumPackageInput.class_error('cannot generate input\nencountered name that is not known as a section or variable\nunrecognized name provided: {0}\nvalid sections: {1}\nvalid variables: {2}'.format(name,sorted(known_sections),sorted(known_variables))) + #end if + #end for + + # assign sections + for secname,sec in sections.iteritems(): + if isinstance(sec,(obj,dict)): + sec = Section(sec) # defer checking to check_valid + #end if + qpi[secname] = sec + #end for + + # assign variables to sections + for varname,var in variables.iteritems(): + if varname not in variable_section: + QuantumPackageInput.class_error('cannot generate input\nsection cannot be fond for variable provided\nunrecognized variable: {0}'.format(varname)) + #end if + secname = variable_section[varname] + if isinstance(secname,tuple): + QuantumPackageInput.class_error('cannot generate input\nsection cannot be uniquely determined from variable name\nvariable name provided: {0}\npossible sections: {1}\nplease provide this variable directly within on of the input sections listed and try again'.format(varname,secname)) + #end if + if secname not in qpi: + qpi[secname] = Section() + #end if + sec = qpi[secname][varname] = var + #end for + + # incorporate atomic and electronic structure + system = gen_kw.system + qpi.structure = system.structure.copy() + if 'electrons' not in qpi: + qpi.electrons = Section() + #end if + nup,ndn = system.particles.electron_counts() + qpi.electrons.elec_alpha_num = nup + qpi.electrons.elec_beta_num = ndn + + # validate the input + if gen_kw.validate: + qpi.check_valid() + #end if + + return qpi +#end def generate_quantum_package_input diff --git a/nexus/library/simulation.py b/nexus/library/simulation.py index 9ecc02d131..ca234529a6 100644 --- a/nexus/library/simulation.py +++ b/nexus/library/simulation.py @@ -402,6 +402,7 @@ def __init__(self,**kwargs): self.infile = None self.outfile = None self.errfile = None + self.bundleable = True self.bundled = False self.bundler = None self.fake_sim = Simulation.creating_fake_sims @@ -411,6 +412,7 @@ def __init__(self,**kwargs): # accessed by dependents when calling get_dependencies self.set(**kwargs) + self.pre_init() self.set_directories() self.set_files() self.propagate_identifier() @@ -609,6 +611,10 @@ def propagate_identifier(self): None #end def propagate_identifier + def pre_init(self): + None + #end def pre_init + def post_init(self): None #end def post_init @@ -946,7 +952,7 @@ def write_inputs(self,save_image=True): self.enter(self.locdir,False,self.simid) self.log('writing input files'+self.idstr(),n=3) self.write_prep() - if self.infile!=None: + if self.infile is not None: infile = os.path.join(self.locdir,self.infile) self.input.write(infile) #end if diff --git a/nexus/library/structure.py b/nexus/library/structure.py index 6428dd8a40..2626408764 100755 --- a/nexus/library/structure.py +++ b/nexus/library/structure.py @@ -3970,27 +3970,46 @@ def read_xyz(self,filepath): elem = [] pos = [] if os.path.exists(filepath): - lines = open(filepath,'r').read().splitlines() + lines = open(filepath,'r').read().strip().splitlines() else: - lines = filepath.splitlines() # "filepath" is file contents - #end if - ntot = 1000000 - natoms = 0 - for l in lines: - ls = l.strip() - if ls.isdigit(): - ntot = int(ls) - #end if - tokens = ls.split() - if len(tokens)==4: - elem.append(tokens[0]) - pos.append(array(tokens[1:],float)) - natoms+=1 - if natoms==ntot: - break + lines = filepath.strip().splitlines() # "filepath" is file contents + #end if + if len(lines)>1: + ntot = int(lines[0].strip()) + natoms = 0 + e = None + p = None + try: + tokens = lines[1].split() + if len(tokens)==4: + e = tokens[0] + p = array(tokens[1:],float) #end if + except: + None + #end try + if p is not None: + elem.append(e) + pos.append(p) + natoms+=1 #end if - #end for + if len(lines)>2: + for l in lines[2:]: + tokens = l.split() + if len(tokens)==4: + elem.append(tokens[0]) + pos.append(array(tokens[1:],float)) + natoms+=1 + if natoms==ntot: + break + #end if + #end if + #end for + #end if + if natoms!=ntot: + self.error('xyz file read failed\nattempted to read file: {0}\nnumber of atoms expected: {1}\nnumber of atoms found: {2}'.format(filepath,ntot,natoms)) + #end if + #end if self.dim = 3 self.set_elem(elem) self.pos = array(pos)