From 833db2e5a9f8ec572fd6ac507157bb808a58dd0f Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Fri, 29 Nov 2019 14:33:27 +0800 Subject: [PATCH 001/201] update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 60febdeb4..59c551707 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ dpgen.egg-info .eggs .coverage dbconfig.json +.vscode/* \ No newline at end of file From ce9ae15021a19a4351a7b251ba7a43d3e8c66d42 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Fri, 6 Dec 2019 10:57:39 +0800 Subject: [PATCH 002/201] add paralell option of one task for local and lsf --- dpgen/remote/RemoteJob.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/dpgen/remote/RemoteJob.py b/dpgen/remote/RemoteJob.py index 6997c583a..ec536de3c 100644 --- a/dpgen/remote/RemoteJob.py +++ b/dpgen/remote/RemoteJob.py @@ -340,17 +340,23 @@ def _make_script(self, fp.write('module load %s\n' % ii) fp.write('\n') for ii,jj in zip(job_dirs, args) : + if resources['with_pl'] in resources and resources['allow_failure'] is True: + fp.write('{') + fp.write('cd %s\n' % self.remote_root) + fp.write('test $? -ne 0 && exit\n') fp.write('cd %s\n' % ii) fp.write('test $? -ne 0 && exit\n') if resources['with_mpi'] == True : fp.write('mpirun -n %d %s %s\n' % (task_per_node, cmd, jj)) - else : + else: fp.write('%s %s\n' % (cmd, jj)) if 'allow_failure' not in resources or resources['allow_failure'] is False: fp.write('test $? -ne 0 && exit\n') - fp.write('cd %s\n' % self.remote_root) - fp.write('test $? -ne 0 && exit\n') + if resources['with_pl'] in resources and resources['allow_failure'] is True: + fp.write('} &') + fp.write('cd %s\n' % self.remote_root) + fp.write('test $? -ne 0 && exit\n') fp.write('\ntouch tag_finished\n') sftp.close() return script_name @@ -881,6 +887,10 @@ def _make_script(self, for ii in job_dirs: args.append('') for ii,jj in zip(job_dirs, args) : + if resources['with_pl'] in resources and resources['allow_failure'] is True: + ret '{' + ret += 'cd %s\n' % self.remote_root + ret += 'test $? -ne 0 && exit\n' ret += 'cd %s\n' % ii ret += 'test $? -ne 0 && exit\n' if res['with_mpi']: @@ -889,9 +899,11 @@ def _make_script(self, else : ret += '%s %s\n' % (cmd, jj) if 'allow_failure' not in res or res['allow_failure'] is False: - ret += 'test $? -ne 0 && exit\n' - ret += 'cd %s\n' % self.remote_root - ret += 'test $? -ne 0 && exit\n' + ret += 'test $? -ne 0 && exit\n'\ + if resources['with_pl'] in resources and resources['allow_failure'] is True: + ret '} &' + ret += 'cd %s\n' % self.remote_root + ret += 'test $? -ne 0 && exit\n' ret += '\ntouch tag_finished\n' script_name = 'run.sub' From fc9e4c0084427743d8ae4f16b6b7e0af97e6d840 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Mon, 9 Dec 2019 16:21:49 +0800 Subject: [PATCH 003/201] add PSUSP and USUSP states for lsf; some bugfix --- dpgen/remote/RemoteJob.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dpgen/remote/RemoteJob.py b/dpgen/remote/RemoteJob.py index ec536de3c..f76ea1f8e 100644 --- a/dpgen/remote/RemoteJob.py +++ b/dpgen/remote/RemoteJob.py @@ -813,9 +813,9 @@ def check_status(self) : status_word = status_line.split()[2] # ref: https://www.ibm.com/support/knowledgecenter/en/SSETD4_9.1.2/lsf_command_ref/bjobs.1.html - if status_word in ["PEND", "WAIT"] : + if status_word in ["PEND", "WAIT", "PSUSP"] : return JobStatus.waiting - elif status_word in ["RUN"] : + elif status_word in ["RUN", "USUSP"] : return JobStatus.running elif status_word in ["DONE","EXIT"] : if self._check_finish_tag() : @@ -887,8 +887,8 @@ def _make_script(self, for ii in job_dirs: args.append('') for ii,jj in zip(job_dirs, args) : - if resources['with_pl'] in resources and resources['allow_failure'] is True: - ret '{' + if res['with_pl'] in res and res['with_pl'] is True: + ret += '{' ret += 'cd %s\n' % self.remote_root ret += 'test $? -ne 0 && exit\n' ret += 'cd %s\n' % ii @@ -899,9 +899,9 @@ def _make_script(self, else : ret += '%s %s\n' % (cmd, jj) if 'allow_failure' not in res or res['allow_failure'] is False: - ret += 'test $? -ne 0 && exit\n'\ - if resources['with_pl'] in resources and resources['allow_failure'] is True: - ret '} &' + ret += 'test $? -ne 0 && exit\n' + if res['with_pl'] in res and res['with_pl'] is True: + ret += '} &' ret += 'cd %s\n' % self.remote_root ret += 'test $? -ne 0 && exit\n' ret += '\ntouch tag_finished\n' From d5f56c6f607c77137e275527f823cb6b1e1def11 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 15 Dec 2019 22:48:28 +0800 Subject: [PATCH 004/201] support spot --- dpgen/dispatcher/ALI.py | 69 ++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index e68d091ed..215494c35 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -10,12 +10,28 @@ determine_machine = { "gpu": { - 1: "ecs.gn5-c8g1.2xlarge", + "on_demand": { + 1: "gpu_on_demand", + 2: "gpu_2_on_demand", + 4: "gpu_4_on_demand", + }, + "spot": { + 1: "gpu_spot", + 2: "gpu_2_spot", + 4: "gpu_4_spot" + } }, "cpu": { - 1: "ecs.c6.large", - 4: "ecs.c6.2xlarge", - 8: "ecs.c6.4xlarge" + "on_demand": { + 4: "cpu_4_on_demand", + 8: "cpu_8_on_demand", + 16: "cpu_16_on_demand" + }, + "spot": { + 4: "cpu_4_spot", + 8: "cpu_8_spot", + 16: "cpu_16_spot" + } } } @@ -120,30 +136,27 @@ def create_machine(self): strategy = self.adata["pay_strategy"] pwd = self.adata["password"] regionID = self.mdata_machine['regionID'] - instance_type = determine_machine[self.mdata_resources['partition']][self.mdata_resources['numb_gpu']] - if True: - client = AcsClient(AccessKey_ID,AccessKey_Secret, regionID) - request = RunInstancesRequest() - request.set_accept_format('json') - request.set_UniqueSuffix(True) - request.set_Password(pwd) - request.set_Amount(self.nchunks) - request.set_LaunchTemplateName(instance_type + '_cn-hangzhou_i') - response = client.do_action_with_exception(request) - response = json.loads(response) - self.instance_list = response["InstanceIdSets"]["InstanceIdSet"] - time.sleep(50) - request = DescribeInstancesRequest() - request.set_accept_format('json') - request.set_InstanceIds(self.instance_list) - response = client.do_action_with_exception(request) - response = json.loads(response) - ip = [] - for i in range(len(response["Instances"]["Instance"])): - ip.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) - self.ip_list = ip - else: - return "create failed" + template_name = determine_machine[self.mdata_resources['partition']][strategy][self.mdata_resources['numb_gpu']] + client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) + request = RunInstancesRequest() + request.set_accept_format('json') + request.set_UniqueSuffix(True) + request.set_Password(pwd) + request.set_Amount(self.nchunks) + request.set_LaunchTemplateName(template_name) + response = client.do_action_with_exception(request) + response = json.loads(response) + self.instance_list = response["InstanceIdSets"]["InstanceIdSet"] + time.sleep(50) + request = DescribeInstancesRequest() + request.set_accept_format('json') + request.set_InstanceIds(self.instance_list) + response = client.do_action_with_exception(request) + response = json.loads(response) + ip = [] + for i in range(len(response["Instances"]["Instance"])): + ip.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) + self.ip_list = ip def delete_machine(self): AccessKey_ID = self.adata["AccessKey_ID"] From 895628545d22a7b13bdef42d609aceb8babb58fd Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 15 Dec 2019 23:04:57 +0800 Subject: [PATCH 005/201] optimize code --- dpgen/dispatcher/ALI.py | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 215494c35..c2125e976 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -8,33 +8,6 @@ from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks from os.path import join -determine_machine = { - "gpu": { - "on_demand": { - 1: "gpu_on_demand", - 2: "gpu_2_on_demand", - 4: "gpu_4_on_demand", - }, - "spot": { - 1: "gpu_spot", - 2: "gpu_2_spot", - 4: "gpu_4_spot" - } - }, - "cpu": { - "on_demand": { - 4: "cpu_4_on_demand", - 8: "cpu_8_on_demand", - 16: "cpu_16_on_demand" - }, - "spot": { - 4: "cpu_4_spot", - 8: "cpu_8_spot", - 16: "cpu_16_spot" - } - } -} - class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks, work_path): self.ip_list = None @@ -136,7 +109,7 @@ def create_machine(self): strategy = self.adata["pay_strategy"] pwd = self.adata["password"] regionID = self.mdata_machine['regionID'] - template_name = determine_machine[self.mdata_resources['partition']][strategy][self.mdata_resources['numb_gpu']] + template_name = '%s_%s_%s' %(self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) request = RunInstancesRequest() request.set_accept_format('json') From d463e06614c85d6e5625fda202cc8cfe21439342 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Dec 2019 15:38:37 +0800 Subject: [PATCH 006/201] use machine_record.json to record ip and instance_id --- dpgen/dispatcher/ALI.py | 85 ++++++++++++++++++++++++++++------ dpgen/dispatcher/Dispatcher.py | 16 ++----- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index c2125e976..6c0694744 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -29,12 +29,11 @@ def init(self): def check_restart(self): dispatchers = [] instance_list = [] - if len(glob.glob(os.path.join(self.work_path, 'jr.*.json'))) == self.nchunks: - for ii in range(self.nchunks): - with open(os.path.join(self.work_path, 'jr.%.06d.json' % ii)) as fp: - job_record = json.load(fp) - key = list(job_record.keys())[0] - ip, instance_id = job_record[key]['context'][-2], job_record[key]['context'][-1] + if os.path.exists('machine_record.json'): + with open('machine_record.json', 'r') as fp: + machine_record = json.load(fp) + for ii in range(self.nchunks): + ip, instance_id = machine_record['ip'][ii], machine_record['instance_id'][ii] instance_list.append(instance_id) profile = self.mdata_machine.copy() profile['hostname'] = ip @@ -47,13 +46,15 @@ def check_restart(self): if cnt == max_check: break if cnt != max_check: - dispatchers.append(disp) - restart = False - if len(dispatchers) == self.nchunks: - restart = True - self.dispatchers = dispatchers - self.instance_list = instance_list - return restart + dispatchers.append(disp) + restart = False + if len(dispatchers) == self.nchunks: + restart = True + self.dispatchers = dispatchers + self.instance_list = instance_list + return restart + else: + return False def run_jobs(self, resources, @@ -109,12 +110,14 @@ def create_machine(self): strategy = self.adata["pay_strategy"] pwd = self.adata["password"] regionID = self.mdata_machine['regionID'] - template_name = '%s_%s_%s' %(self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) + template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) + instance_name = self.adata["instance_name"] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) request = RunInstancesRequest() request.set_accept_format('json') request.set_UniqueSuffix(True) request.set_Password(pwd) + request.set_InstanceName(instance_name) request.set_Amount(self.nchunks) request.set_LaunchTemplateName(template_name) response = client.do_action_with_exception(request) @@ -130,6 +133,8 @@ def create_machine(self): for i in range(len(response["Instances"]["Instance"])): ip.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) self.ip_list = ip + with open('machine_record.json', 'w') as fp: + json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) def delete_machine(self): AccessKey_ID = self.adata["AccessKey_ID"] @@ -141,4 +146,56 @@ def delete_machine(self): request.set_InstanceIds(self.instance_list) request.set_Force(True) response = client.do_action_with_exception(request) + os.remove('machine_record.json') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 915d7e201..7cde609c2 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -153,18 +153,10 @@ def submit_jobs(self, dlog.info('restart from old submission %s for chunk %s' % (job_uuid, cur_hash)) # record job and its remote context job_list.append(rjob) - ip = None - instance_id = None - if "type" in self.remote_profile: - if self.remote_profile['type'] == 'ALI': - ip = self.remote_profile['hostname'] - instance_id = self.remote_profile['instance_id'] job_record.record_remote_context(cur_hash, context.local_root, context.remote_root, - job_uuid, - ip, - instance_id) + job_uuid) else : # finished job, append a None to list job_list.append(None) @@ -240,11 +232,9 @@ def record_remote_context(self, chunk_hash, local_root, remote_root, - job_uuid, - ip=None, - instance_id=None): + job_uuid): self.valid_hash(chunk_hash) - self.record[chunk_hash]['context'] = [local_root, remote_root, job_uuid, ip, instance_id] + self.record[chunk_hash]['context'] = [local_root, remote_root, job_uuid] def get_uuid(self, chunk_hash): self.valid_hash(chunk_hash) From 1182248fad4f29ec1318178bb17a9e5f7543cf95 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Dec 2019 15:42:19 +0800 Subject: [PATCH 007/201] optimize code --- dpgen/dispatcher/ALI.py | 52 ----------------------------------------- 1 file changed, 52 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 6c0694744..3bd1ca76c 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -147,55 +147,3 @@ def delete_machine(self): request.set_Force(True) response = client.do_action_with_exception(request) os.remove('machine_record.json') - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 0acc8ebf331e63895ca3bfd4f1d0eafcd2c3a858 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Tue, 14 Jan 2020 13:01:12 +0800 Subject: [PATCH 008/201] drawback the modification --- dpgen/remote/RemoteJob.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/dpgen/remote/RemoteJob.py b/dpgen/remote/RemoteJob.py index f76ea1f8e..6997c583a 100644 --- a/dpgen/remote/RemoteJob.py +++ b/dpgen/remote/RemoteJob.py @@ -340,23 +340,17 @@ def _make_script(self, fp.write('module load %s\n' % ii) fp.write('\n') for ii,jj in zip(job_dirs, args) : - if resources['with_pl'] in resources and resources['allow_failure'] is True: - fp.write('{') - fp.write('cd %s\n' % self.remote_root) - fp.write('test $? -ne 0 && exit\n') fp.write('cd %s\n' % ii) fp.write('test $? -ne 0 && exit\n') if resources['with_mpi'] == True : fp.write('mpirun -n %d %s %s\n' % (task_per_node, cmd, jj)) - else: + else : fp.write('%s %s\n' % (cmd, jj)) if 'allow_failure' not in resources or resources['allow_failure'] is False: fp.write('test $? -ne 0 && exit\n') - if resources['with_pl'] in resources and resources['allow_failure'] is True: - fp.write('} &') - fp.write('cd %s\n' % self.remote_root) - fp.write('test $? -ne 0 && exit\n') + fp.write('cd %s\n' % self.remote_root) + fp.write('test $? -ne 0 && exit\n') fp.write('\ntouch tag_finished\n') sftp.close() return script_name @@ -813,9 +807,9 @@ def check_status(self) : status_word = status_line.split()[2] # ref: https://www.ibm.com/support/knowledgecenter/en/SSETD4_9.1.2/lsf_command_ref/bjobs.1.html - if status_word in ["PEND", "WAIT", "PSUSP"] : + if status_word in ["PEND", "WAIT"] : return JobStatus.waiting - elif status_word in ["RUN", "USUSP"] : + elif status_word in ["RUN"] : return JobStatus.running elif status_word in ["DONE","EXIT"] : if self._check_finish_tag() : @@ -887,10 +881,6 @@ def _make_script(self, for ii in job_dirs: args.append('') for ii,jj in zip(job_dirs, args) : - if res['with_pl'] in res and res['with_pl'] is True: - ret += '{' - ret += 'cd %s\n' % self.remote_root - ret += 'test $? -ne 0 && exit\n' ret += 'cd %s\n' % ii ret += 'test $? -ne 0 && exit\n' if res['with_mpi']: @@ -900,10 +890,8 @@ def _make_script(self, ret += '%s %s\n' % (cmd, jj) if 'allow_failure' not in res or res['allow_failure'] is False: ret += 'test $? -ne 0 && exit\n' - if res['with_pl'] in res and res['with_pl'] is True: - ret += '} &' - ret += 'cd %s\n' % self.remote_root - ret += 'test $? -ne 0 && exit\n' + ret += 'cd %s\n' % self.remote_root + ret += 'test $? -ne 0 && exit\n' ret += '\ntouch tag_finished\n' script_name = 'run.sub' From 930a79139087141308e49c4728980611cd884f84 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Tue, 14 Jan 2020 13:30:22 +0800 Subject: [PATCH 009/201] add PSUSP and USUSP states for lsf; some bugfix --- dpgen/remote/RemoteJob.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/dpgen/remote/RemoteJob.py b/dpgen/remote/RemoteJob.py index 6997c583a..90d296f30 100644 --- a/dpgen/remote/RemoteJob.py +++ b/dpgen/remote/RemoteJob.py @@ -755,7 +755,6 @@ def submit(self, while self.check_limit(task_max=resources['task_max']): time.sleep(60) self._submit(job_dirs, cmd, args, resources) - time.sleep(20) # For preventing the crash of the tasks while submitting. def _submit(self, job_dirs, @@ -770,6 +769,7 @@ def _submit(self, with sftp.open(os.path.join(self.remote_root, 'job_id'), 'w') as fp: fp.write(job_id) sftp.close() + time.sleep(20) # For preventing the crash of the tasks while submitting. def check_limit(self, task_max): stdin_run, stdout_run, stderr_run = self.block_checkcall("bjobs | grep RUN | wc -l") @@ -807,11 +807,11 @@ def check_status(self) : status_word = status_line.split()[2] # ref: https://www.ibm.com/support/knowledgecenter/en/SSETD4_9.1.2/lsf_command_ref/bjobs.1.html - if status_word in ["PEND", "WAIT"] : + if status_word in ["PEND", "WAIT", "PSUSP"] : return JobStatus.waiting - elif status_word in ["RUN"] : + elif status_word in ["RUN", "USUSP"] : return JobStatus.running - elif status_word in ["DONE","EXIT"] : + elif status_word in ["DONE","EXIT"] : if self._check_finish_tag() : return JobStatus.finished else : @@ -845,14 +845,19 @@ def _make_script(self, ret = '' ret += "#!/bin/bash -l\n#BSUB -e %J.err\n#BSUB -o %J.out\n" if res['numb_gpu'] == 0: - ret += '#BSUB -R span[ptile=%d]\n#BSUB -n %d\n' % ( - res['node_cpu'], res['numb_node'] * res['task_per_node']) + ret += '#BSUB -n %d\n#BSUB -R span[ptile=%d]\n' % ( + res['numb_node'] * res['task_per_node'], res['node_cpu']) else: if res['node_cpu']: ret += '#BSUB -R span[ptile=%d]\n' % res['node_cpu'] - # It is selected only for the situation that GPU is related to CPU node. - ret += '#BSUB -R "select[ngpus >0] rusage[ngpus_excl_p=1]"\n#BSUB -n %d\n' % ( - res['numb_gpu']) + if 'new_lsf_gpu' in res and res['new_lsf_gpu'] == True: + # supportted in LSF >= 10.1.0 SP6 + # ref: https://www.ibm.com/support/knowledgecenter/en/SSWRJV_10.1.0/lsf_resource_sharing/use_gpu_res_reqs.html + ret += '#BSUB -n %d\n#BSUB -gpu "num=%d:mode=shared:j_exclusive=yes"\n' % ( + res['numb_gpu'], res['task_per_node']) + else: + ret += '#BSUB -n %d\n#BSUB -R "select[ngpus >0] rusage[ngpus_excl_p=%d]"\n' % ( + res['numb_gpu'], res['task_per_node']) if res['time_limit']: ret += '#BSUB -W %s\n' % (res['time_limit'].split(':')[ 0] + ':' + res['time_limit'].split(':')[1]) From a3878bb362e4079af6cc67514d1542f74eaee1e7 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Tue, 14 Jan 2020 14:12:08 +0800 Subject: [PATCH 010/201] modify dpgen.dispatch.LSF; add 'wait' key option --- dpgen/dispatcher/LSF.py | 130 ++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/dpgen/dispatcher/LSF.py b/dpgen/dispatcher/LSF.py index 62b46e99b..5c454afcd 100644 --- a/dpgen/dispatcher/LSF.py +++ b/dpgen/dispatcher/LSF.py @@ -1,31 +1,36 @@ -import os,getpass,time +import os +import getpass +import time from dpgen.dispatcher.Batch import Batch from dpgen.dispatcher.JobStatus import JobStatus -def _default_item(resources, key, value) : - if key not in resources : + +def _default_item(resources, key, value): + if key not in resources: resources[key] = value -class LSF(Batch) : - + +class LSF(Batch): + def check_status(self): try: job_id = self._get_job_id() except: return JobStatus.terminated - if job_id == "" : - raise RuntimeError("job %s is has not been submitted" % self.remote_root) + if job_id == "": + raise RuntimeError( + "job %s is has not been submitted" % self.context.remote_root) ret, stdin, stdout, stderr\ - = self.context.block_call ("bjobs " + job_id) + = self.context.block_call("bjobs " + job_id) err_str = stderr.read().decode('utf-8') - if ("Job <%s> is not found" % job_id) in err_str : - if self.check_finish_tag() : + if ("Job <%s> is not found" % job_id) in err_str: + if self.check_finish_tag(): return JobStatus.finished - else : + else: return JobStatus.terminated - elif ret != 0 : - raise RuntimeError ("status command bjobs fails to execute. erro info: %s return code %d" - % (err_str, ret)) + elif ret != 0: + raise RuntimeError("status command bjobs fails to execute. erro info: %s return code %d" + % (err_str, ret)) status_out = stdout.read().decode('utf-8').split('\n') if len(status_out) < 2: return JobStatus.unknown @@ -34,44 +39,46 @@ def check_status(self): status_word = status_line.split()[2] # ref: https://www.ibm.com/support/knowledgecenter/en/SSETD4_9.1.2/lsf_command_ref/bjobs.1.html - if status_word in ["PEND", "WAIT"] : + if status_word in ["PEND", "WAIT", "PSUSP"]: return JobStatus.waiting - elif status_word in ["RUN"] : + elif status_word in ["RUN", "USUSP"]: return JobStatus.running - elif status_word in ["DONE","EXIT"] : - if self.check_finish_tag() : + elif status_word in ["DONE", "EXIT"]: + if self.check_finish_tag(): return JobStatus.finished - else : + else: return JobStatus.terminated - else : + else: return JobStatus.unknown - - def do_submit(self, + def do_submit(self, job_dirs, cmd, - args = None, - res = None, - outlog = 'log', - errlog = 'err'): + args=None, + res=None, + outlog='log', + errlog='err'): if res == None: res = self.default_resources(res) if 'task_max' in res and res['task_max'] > 0: while self._check_sub_limit(task_max=res['task_max']): time.sleep(60) - script_str = self.sub_script(job_dirs, cmd, args=args, res=res, outlog=outlog, errlog=errlog) + script_str = self.sub_script( + job_dirs, cmd, args=args, res=res, outlog=outlog, errlog=errlog) self.context.write_file(self.sub_script_name, script_str) - stdin, stdout, stderr = self.context.block_checkcall('cd %s && %s < %s' % (self.context.remote_root, 'bsub', self.sub_script_name)) + stdin, stdout, stderr = self.context.block_checkcall( + 'cd %s && %s < %s' % (self.context.remote_root, 'bsub', self.sub_script_name)) subret = (stdout.readlines()) job_id = subret[0].split()[1][1:-1] - self.context.write_file(self.job_id_name, job_id) + self.context.write_file(self.job_id_name, job_id) + if 'wait' in res and res['wait'] > 0: + time.sleep(res['wait']) # For preventing the crash of the tasks while submitting. - - def default_resources(self, res_) : + def default_resources(self, res_): """ set default value if a key in res_ is not fhound """ - if res_ == None : + if res_ == None: res = {} else: res = res_ @@ -102,63 +109,68 @@ def sub_script_head(self, res): ret = '' ret += "#!/bin/bash -l\n#BSUB -e %J.err\n#BSUB -o %J.out\n" if res['numb_gpu'] == 0: - ret += '#BSUB -R span[ptile=%d]\n#BSUB -n %d\n' % ( - res['node_cpu'], res['numb_node'] * res['task_per_node']) + ret += '#BSUB -n %d\n#BSUB -R span[ptile=%d]\n' % ( + res['numb_node'] * res['task_per_node'], res['node_cpu']) else: if res['node_cpu']: ret += '#BSUB -R span[ptile=%d]\n' % res['node_cpu'] - # It is selected only for the situation that GPU is related to CPU node. - ret += '#BSUB -R "select[ngpus >0] rusage[ngpus_excl_p=1]"\n#BSUB -n %d\n' % ( - res['numb_gpu']) + if 'new_lsf_gpu' in res and res['new_lsf_gpu'] == True: + # supportted in LSF >= 10.1.0 SP6 + # ref: https://www.ibm.com/support/knowledgecenter/en/SSWRJV_10.1.0/lsf_resource_sharing/use_gpu_res_reqs.html + ret += '#BSUB -n %d\n#BSUB -gpu "num=%d:mode=shared:j_exclusive=yes"\n' % ( + res['numb_gpu'], res['task_per_node']) + else: + ret += '#BSUB -n %d\n#BSUB -R "select[ngpus >0] rusage[ngpus_excl_p=%d]"\n' % ( + res['numb_gpu'], res['task_per_node']) if res['time_limit']: ret += '#BSUB -W %s\n' % (res['time_limit'].split(':')[ 0] + ':' + res['time_limit'].split(':')[1]) - if res['mem_limit'] > 0 : + if res['mem_limit'] > 0: ret += "#BSUB -M %d \n" % (res['mem_limit']) - ret += '#BSUB -J %s\n' % (res['job_name'] if 'job_name' in res else 'dpgen') - if len(res['partition']) > 0 : + ret += '#BSUB -J %s\n' % (res['job_name'] + if 'job_name' in res else 'dpgen') + if len(res['partition']) > 0: ret += '#BSUB -q %s\n' % res['partition'] ret += "\n" - for ii in res['module_unload_list'] : + for ii in res['module_unload_list']: ret += "module unload %s\n" % ii - for ii in res['module_list'] : + for ii in res['module_list']: ret += "module load %s\n" % ii ret += "\n" - for ii in res['source_list'] : - ret += "source %s\n" %ii + for ii in res['source_list']: + ret += "source %s\n" % ii ret += "\n" envs = res['envs'] - if envs != None : - for key in envs.keys() : + if envs != None: + for key in envs.keys(): ret += 'export %s=%s\n' % (key, envs[key]) ret += '\n' return ret - def sub_script_cmd(self, cmd, arg, - res) : + res): if res['with_mpi']: ret = 'mpirun -machinefile $LSB_DJOB_HOSTFILE -n %d %s %s' % ( - res['numb_node'] * res['task_per_node'], cmd, arg) - else : + res['numb_node'] * res['task_per_node'], cmd, arg) + else: ret = '%s %s' % (cmd, arg) return ret - - def _get_job_id(self) : - if self.context.check_file_exists(self.job_id_name) : + def _get_job_id(self): + if self.context.check_file_exists(self.job_id_name): return self.context.read_file(self.job_id_name) else: return "" - - def _check_sub_limit(self, task_max, **kwarg) : - stdin_run, stdout_run, stderr_run = self.context.block_checkcall("bjobs | grep RUN | wc -l") - njobs_run = int(stdout_run.read().decode('utf-8').split ('\n')[0]) - stdin_pend, stdout_pend, stderr_pend = self.context.block_checkcall("bjobs | grep PEND | wc -l") - njobs_pend = int(stdout_pend.read().decode('utf-8').split ('\n')[0]) + def _check_sub_limit(self, task_max, **kwarg): + stdin_run, stdout_run, stderr_run = self.context.block_checkcall( + "bjobs | grep RUN | wc -l") + njobs_run = int(stdout_run.read().decode('utf-8').split('\n')[0]) + stdin_pend, stdout_pend, stderr_pend = self.context.block_checkcall( + "bjobs | grep PEND | wc -l") + njobs_pend = int(stdout_pend.read().decode('utf-8').split('\n')[0]) if (njobs_pend + njobs_run) < task_max: return False else: From 7b4f63f4886c6cb8618c9ba0e96b18274bab3af9 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Tue, 14 Jan 2020 17:55:28 +0800 Subject: [PATCH 011/201] add 'sleep' to resource dict while remove 'wait' --- dpgen/dispatcher/Batch.py | 4 +++- dpgen/dispatcher/LSF.py | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index 0b6d16b71..6261eff3e 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -119,7 +119,9 @@ def submit(self, else: dlog.debug('new task') self.do_submit(job_dirs, cmd, args, res, outlog=outlog, errlog=errlog) - time.sleep(sleep) # For preventing the crash of the tasks while submitting + if 'sleep' in res and res['sleep'] > 0: + sleep = res['sleep'] + time.sleep(sleep) # For preventing the crash of the tasks while submitting def check_finish_tag(self) : return self.context.check_file_exists(self.finish_tag_name) diff --git a/dpgen/dispatcher/LSF.py b/dpgen/dispatcher/LSF.py index 5c454afcd..b1e3271ff 100644 --- a/dpgen/dispatcher/LSF.py +++ b/dpgen/dispatcher/LSF.py @@ -71,8 +71,6 @@ def do_submit(self, subret = (stdout.readlines()) job_id = subret[0].split()[1][1:-1] self.context.write_file(self.job_id_name, job_id) - if 'wait' in res and res['wait'] > 0: - time.sleep(res['wait']) # For preventing the crash of the tasks while submitting. def default_resources(self, res_): """ From 18dda6852e35ecc45ead01e6480242d2fc28ede6 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Tue, 14 Jan 2020 20:37:09 +0800 Subject: [PATCH 012/201] restore the original style of the code --- dpgen/dispatcher/LSF.py | 113 +++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/dpgen/dispatcher/LSF.py b/dpgen/dispatcher/LSF.py index b1e3271ff..e5a542cf2 100644 --- a/dpgen/dispatcher/LSF.py +++ b/dpgen/dispatcher/LSF.py @@ -1,36 +1,31 @@ -import os -import getpass -import time +import os,getpass,time from dpgen.dispatcher.Batch import Batch from dpgen.dispatcher.JobStatus import JobStatus - -def _default_item(resources, key, value): - if key not in resources: +def _default_item(resources, key, value) : + if key not in resources : resources[key] = value - -class LSF(Batch): - +class LSF(Batch) : + def check_status(self): try: job_id = self._get_job_id() except: return JobStatus.terminated - if job_id == "": - raise RuntimeError( - "job %s is has not been submitted" % self.context.remote_root) + if job_id == "" : + raise RuntimeError("job %s is has not been submitted" % self.remote_root) ret, stdin, stdout, stderr\ - = self.context.block_call("bjobs " + job_id) + = self.context.block_call ("bjobs " + job_id) err_str = stderr.read().decode('utf-8') - if ("Job <%s> is not found" % job_id) in err_str: - if self.check_finish_tag(): + if ("Job <%s> is not found" % job_id) in err_str : + if self.check_finish_tag() : return JobStatus.finished - else: + else : return JobStatus.terminated - elif ret != 0: - raise RuntimeError("status command bjobs fails to execute. erro info: %s return code %d" - % (err_str, ret)) + elif ret != 0 : + raise RuntimeError ("status command bjobs fails to execute. erro info: %s return code %d" + % (err_str, ret)) status_out = stdout.read().decode('utf-8').split('\n') if len(status_out) < 2: return JobStatus.unknown @@ -39,44 +34,44 @@ def check_status(self): status_word = status_line.split()[2] # ref: https://www.ibm.com/support/knowledgecenter/en/SSETD4_9.1.2/lsf_command_ref/bjobs.1.html - if status_word in ["PEND", "WAIT", "PSUSP"]: + if status_word in ["PEND", "WAIT", "PSUSP"] : return JobStatus.waiting - elif status_word in ["RUN", "USUSP"]: + elif status_word in ["RUN", "USUSP"] : return JobStatus.running - elif status_word in ["DONE", "EXIT"]: - if self.check_finish_tag(): + elif status_word in ["DONE","EXIT"] : + if self.check_finish_tag() : return JobStatus.finished - else: + else : return JobStatus.terminated - else: + else : return JobStatus.unknown - def do_submit(self, + + def do_submit(self, job_dirs, cmd, - args=None, - res=None, - outlog='log', - errlog='err'): + args = None, + res = None, + outlog = 'log', + errlog = 'err'): if res == None: res = self.default_resources(res) if 'task_max' in res and res['task_max'] > 0: while self._check_sub_limit(task_max=res['task_max']): time.sleep(60) - script_str = self.sub_script( - job_dirs, cmd, args=args, res=res, outlog=outlog, errlog=errlog) + script_str = self.sub_script(job_dirs, cmd, args=args, res=res, outlog=outlog, errlog=errlog) self.context.write_file(self.sub_script_name, script_str) - stdin, stdout, stderr = self.context.block_checkcall( - 'cd %s && %s < %s' % (self.context.remote_root, 'bsub', self.sub_script_name)) + stdin, stdout, stderr = self.context.block_checkcall('cd %s && %s < %s' % (self.context.remote_root, 'bsub', self.sub_script_name)) subret = (stdout.readlines()) job_id = subret[0].split()[1][1:-1] - self.context.write_file(self.job_id_name, job_id) + self.context.write_file(self.job_id_name, job_id) + - def default_resources(self, res_): + def default_resources(self, res_) : """ set default value if a key in res_ is not fhound """ - if res_ == None: + if res_ == None : res = {} else: res = res_ @@ -123,52 +118,52 @@ def sub_script_head(self, res): if res['time_limit']: ret += '#BSUB -W %s\n' % (res['time_limit'].split(':')[ 0] + ':' + res['time_limit'].split(':')[1]) - if res['mem_limit'] > 0: + if res['mem_limit'] > 0 : ret += "#BSUB -M %d \n" % (res['mem_limit']) - ret += '#BSUB -J %s\n' % (res['job_name'] - if 'job_name' in res else 'dpgen') - if len(res['partition']) > 0: + ret += '#BSUB -J %s\n' % (res['job_name'] if 'job_name' in res else 'dpgen') + if len(res['partition']) > 0 : ret += '#BSUB -q %s\n' % res['partition'] ret += "\n" - for ii in res['module_unload_list']: + for ii in res['module_unload_list'] : ret += "module unload %s\n" % ii - for ii in res['module_list']: + for ii in res['module_list'] : ret += "module load %s\n" % ii ret += "\n" - for ii in res['source_list']: - ret += "source %s\n" % ii + for ii in res['source_list'] : + ret += "source %s\n" %ii ret += "\n" envs = res['envs'] - if envs != None: - for key in envs.keys(): + if envs != None : + for key in envs.keys() : ret += 'export %s=%s\n' % (key, envs[key]) ret += '\n' return ret + def sub_script_cmd(self, cmd, arg, - res): + res) : if res['with_mpi']: ret = 'mpirun -machinefile $LSB_DJOB_HOSTFILE -n %d %s %s' % ( - res['numb_node'] * res['task_per_node'], cmd, arg) - else: + res['numb_node'] * res['task_per_node'], cmd, arg) + else : ret = '%s %s' % (cmd, arg) return ret - def _get_job_id(self): - if self.context.check_file_exists(self.job_id_name): + + def _get_job_id(self) : + if self.context.check_file_exists(self.job_id_name) : return self.context.read_file(self.job_id_name) else: return "" - def _check_sub_limit(self, task_max, **kwarg): - stdin_run, stdout_run, stderr_run = self.context.block_checkcall( - "bjobs | grep RUN | wc -l") - njobs_run = int(stdout_run.read().decode('utf-8').split('\n')[0]) - stdin_pend, stdout_pend, stderr_pend = self.context.block_checkcall( - "bjobs | grep PEND | wc -l") - njobs_pend = int(stdout_pend.read().decode('utf-8').split('\n')[0]) + + def _check_sub_limit(self, task_max, **kwarg) : + stdin_run, stdout_run, stderr_run = self.context.block_checkcall("bjobs | grep RUN | wc -l") + njobs_run = int(stdout_run.read().decode('utf-8').split ('\n')[0]) + stdin_pend, stdout_pend, stderr_pend = self.context.block_checkcall("bjobs | grep PEND | wc -l") + njobs_pend = int(stdout_pend.read().decode('utf-8').split ('\n')[0]) if (njobs_pend + njobs_run) < task_max: return False else: From b4d9d483d4e43ad776547fc53b36bdea8be52797 Mon Sep 17 00:00:00 2001 From: Cloudac7 <812556867@qq.com> Date: Wed, 15 Jan 2020 18:00:05 +0800 Subject: [PATCH 013/201] change 'sleep' to 'wait_time'; some simple modify --- dpgen/dispatcher/Batch.py | 4 ++-- dpgen/dispatcher/LSF.py | 2 +- dpgen/remote/RemoteJob.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index 6261eff3e..55485c7d2 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -119,8 +119,8 @@ def submit(self, else: dlog.debug('new task') self.do_submit(job_dirs, cmd, args, res, outlog=outlog, errlog=errlog) - if 'sleep' in res and res['sleep'] > 0: - sleep = res['sleep'] + if 'wait_time' in res and res['wait_time'] > 0: + sleep = res['wait_time'] time.sleep(sleep) # For preventing the crash of the tasks while submitting def check_finish_tag(self) : diff --git a/dpgen/dispatcher/LSF.py b/dpgen/dispatcher/LSF.py index e5a542cf2..7c5d05854 100644 --- a/dpgen/dispatcher/LSF.py +++ b/dpgen/dispatcher/LSF.py @@ -107,7 +107,7 @@ def sub_script_head(self, res): else: if res['node_cpu']: ret += '#BSUB -R span[ptile=%d]\n' % res['node_cpu'] - if 'new_lsf_gpu' in res and res['new_lsf_gpu'] == True: + if res.get('new_lsf_gpu', False): # supportted in LSF >= 10.1.0 SP6 # ref: https://www.ibm.com/support/knowledgecenter/en/SSWRJV_10.1.0/lsf_resource_sharing/use_gpu_res_reqs.html ret += '#BSUB -n %d\n#BSUB -gpu "num=%d:mode=shared:j_exclusive=yes"\n' % ( diff --git a/dpgen/remote/RemoteJob.py b/dpgen/remote/RemoteJob.py index 90d296f30..2450b59f5 100644 --- a/dpgen/remote/RemoteJob.py +++ b/dpgen/remote/RemoteJob.py @@ -755,6 +755,8 @@ def submit(self, while self.check_limit(task_max=resources['task_max']): time.sleep(60) self._submit(job_dirs, cmd, args, resources) + if resources.get('wait_time', False): + time.sleep(resources['wait_time']) # For preventing the crash of the tasks while submitting. def _submit(self, job_dirs, @@ -769,7 +771,6 @@ def _submit(self, with sftp.open(os.path.join(self.remote_root, 'job_id'), 'w') as fp: fp.write(job_id) sftp.close() - time.sleep(20) # For preventing the crash of the tasks while submitting. def check_limit(self, task_max): stdin_run, stdout_run, stderr_run = self.block_checkcall("bjobs | grep RUN | wc -l") From 30fa43687cb8b22066f4eed77eebfacf6c26a247 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 15 Jan 2020 18:08:54 +0800 Subject: [PATCH 014/201] Update Batch.py --- dpgen/dispatcher/Batch.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index 55485c7d2..e097997ad 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -99,7 +99,6 @@ def submit(self, args = None, res = None, restart = False, - sleep = 0, outlog = 'log', errlog = 'err'): if restart: @@ -119,9 +118,8 @@ def submit(self, else: dlog.debug('new task') self.do_submit(job_dirs, cmd, args, res, outlog=outlog, errlog=errlog) - if 'wait_time' in res and res['wait_time'] > 0: - sleep = res['wait_time'] - time.sleep(sleep) # For preventing the crash of the tasks while submitting + sleep = res.get('submit_wait_time', 0) + time.sleep(sleep) # For preventing the crash of the tasks while submitting def check_finish_tag(self) : return self.context.check_file_exists(self.finish_tag_name) From 3ddbcc5cf743e4abe8299dc75801de804d26e6f4 Mon Sep 17 00:00:00 2001 From: Yixiao Chen Date: Wed, 15 Jan 2020 16:01:59 -0600 Subject: [PATCH 015/201] fix bug in checking job status --- dpgen/dispatcher/Slurm.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/Slurm.py b/dpgen/dispatcher/Slurm.py index 5ae5493cd..accf03069 100644 --- a/dpgen/dispatcher/Slurm.py +++ b/dpgen/dispatcher/Slurm.py @@ -151,7 +151,7 @@ def _get_job_id(self) : def _check_status_inner(self, job_id): ret, stdin, stdout, stderr\ - = self.context.block_call ("squeue --job " + job_id) + = self.context.block_call ('squeue -o "%.18i %.2t" -j ' + job_id) if (ret != 0) : err_str = stderr.read().decode('utf-8') if str("Invalid job id specified") in err_str : @@ -163,7 +163,11 @@ def _check_status_inner(self, job_id): raise RuntimeError\ ("status command squeue fails to execute\nerror message:%s\nreturn code %d\n" % (err_str, ret)) status_line = stdout.read().decode('utf-8').split ('\n')[-2] - status_word = status_line.split ()[-4] + status_word = status_line.split ()[-1] + if not (len(status_line.split()) == 2 and status_word.isupper()): + raise RuntimeError("Error in getting job status, " + + "status_line = {status_line}, " + + "parsed status_word = {status_word}") if status_word in ["PD","CF","S"] : return JobStatus.waiting elif status_word in ["R"] : From 88a7a9844091e5e1345e6be592542d99b0a4652d Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 16 Jan 2020 12:53:16 +0800 Subject: [PATCH 016/201] rewrite make_dispatcher to support auto-test --- dpgen/auto_test/run.py | 16 ++++++++-------- dpgen/dispatcher/ALI.py | 7 +------ dpgen/dispatcher/Dispatcher.py | 11 +++-------- dpgen/generator/run.py | 6 +++--- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 4b99ba298..0285063ee 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -105,7 +105,7 @@ def run_equi(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -193,7 +193,7 @@ def run_eos(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -269,7 +269,7 @@ def run_elastic(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -344,7 +344,7 @@ def run_vacancy(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -447,7 +447,7 @@ def run_interstitial(task_type,jdata,mdata): for jj in run_tasks_: if ii in jj: run_tasks.append(os.path.basename(jj)) - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, ii, @@ -461,7 +461,7 @@ def run_interstitial(task_type,jdata,mdata): else: run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -549,7 +549,7 @@ def run_surf(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -608,7 +608,7 @@ def run_phonon(task_type,jdata,mdata): backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR','vasprun.xml'] common_files=['POSCAR'] - disp = make_dispatcher(machine, resources, run_tasks) + disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, work_path, diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index ae6d5d776..43af51f5b 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -9,7 +9,7 @@ from os.path import join class ALI(): - def __init__(self, adata, mdata_resources, mdata_machine, nchunks, work_path): + def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = None self.instance_list = None self.dispatchers = None @@ -17,7 +17,6 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks, work_path): self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine self.nchunks = nchunks - self.work_path = work_path def init(self): if self.check_restart(): @@ -135,11 +134,7 @@ def create_machine(self): self.ip_list = ip with open('machine_record.json', 'w') as fp: json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) -<<<<<<< HEAD -======= - ->>>>>>> 919c46a94f950c32eff6e7c308bafb3f3d14233f def delete_machine(self): AccessKey_ID = self.adata["AccessKey_ID"] AccessKey_Secret = self.adata["AccessKey_Secret"] diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index c664e0a9d..681e26a29 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -1,5 +1,4 @@ import os,sys,time,random,json,glob - from dpgen.dispatcher.LocalContext import LocalSession from dpgen.dispatcher.LocalContext import LocalContext from dpgen.dispatcher.LazyLocalContext import LazyLocalContext @@ -13,11 +12,6 @@ from dpgen.dispatcher.JobStatus import JobStatus from dpgen import dlog from hashlib import sha1 -try: - from dpgen.dispatcher.ALI import ALI -except ImportError as e: - dlog.info(e) - pass def _split_tasks(tasks, group_size): @@ -290,9 +284,10 @@ def _new_record(self): } -def make_dispatcher(mdata_machine, mdata_resource, run_tasks): +def make_dispatcher(mdata_machine, mdata_resource, run_tasks, group_size): if 'ali_auth' in mdata_machine: - nchunks = len(_split_tasks(run_tasks)) + from dpgen.dispatcher.ALI import ALI + nchunks = len(_split_tasks(run_tasks, group_size)) dispatcher = ALI(mdata_machine['ali_auth'], mdata_resource, mdata_machine, nchunks) dispatcher.init() return dispatcher diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 2b247150e..b09fb5ce4 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -447,7 +447,7 @@ def run_train (iter_index, except: train_group_size = 1 - dispatcher = make_dispatcher(mdata['train_machine'], mdata['train_resources'], run_tasks) + dispatcher = make_dispatcher(mdata['train_machine'], mdata['train_resources'], run_tasks, train_group_size) dispatcher.run_jobs(mdata['train_resources'], commands, work_path, @@ -910,7 +910,7 @@ def run_model_devi (iter_index, backward_files += ['output.plumed'] cwd = os.getcwd() - dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], run_tasks) + dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], run_tasks, model_devi_group_size) dispatcher.run_jobs(mdata['model_devi_resources'], commands, work_path, @@ -1491,7 +1491,7 @@ def run_fp_inner (iter_index, # if not check_fin(ii) : # fp_run_tasks.append(ii) run_tasks = [os.path.basename(ii) for ii in fp_run_tasks] - dispatcher = make_dispatcher(mdata['fp_machine'], mdata['fp_resources'], run_tasks) + dispatcher = make_dispatcher(mdata['fp_machine'], mdata['fp_resources'], run_tasks, fp_group_size) dispatcher.run_jobs(mdata['fp_resources'], [fp_command], work_path, From 5c6b28b9b3c9a6b99488d32547ce56be2d789b12 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 16 Jan 2020 13:40:37 +0800 Subject: [PATCH 017/201] fix bug in auto_test/lib/util/get_machine_info --- dpgen/auto_test/lib/util.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dpgen/auto_test/lib/util.py b/dpgen/auto_test/lib/util.py index bbb8d438b..fee55ff48 100644 --- a/dpgen/auto_test/lib/util.py +++ b/dpgen/auto_test/lib/util.py @@ -84,7 +84,10 @@ def get_machine_info(mdata,task_type): machine_type = mdata['model_devi_machine']['machine_type'] command = lmp_exec + " -i lammps.in" command = cmd_append_log(command, "model_devi.log") - ssh_sess = SSHSession(machine) + if machine_type == 'ALI': + ssh_sess = None + else: + ssh_sess = SSHSession(machine) return machine, machine_type,ssh_sess,resources, command, group_size def collect_task(all_task,task_type): From d61e008054e8130402eb8300328c23462a5442da Mon Sep 17 00:00:00 2001 From: Yixiao Chen Date: Thu, 16 Jan 2020 01:47:28 -0600 Subject: [PATCH 018/201] add missing f --- dpgen/dispatcher/Slurm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/Slurm.py b/dpgen/dispatcher/Slurm.py index accf03069..2852a2cce 100644 --- a/dpgen/dispatcher/Slurm.py +++ b/dpgen/dispatcher/Slurm.py @@ -166,8 +166,8 @@ def _check_status_inner(self, job_id): status_word = status_line.split ()[-1] if not (len(status_line.split()) == 2 and status_word.isupper()): raise RuntimeError("Error in getting job status, " + - "status_line = {status_line}, " + - "parsed status_word = {status_word}") + f"status_line = {status_line}, " + + f"parsed status_word = {status_word}") if status_word in ["PD","CF","S"] : return JobStatus.waiting elif status_word in ["R"] : From 0f601bc2722b8d1e3467f4f764c91f43d37c433f Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 16 Jan 2020 16:09:11 +0800 Subject: [PATCH 019/201] fix bug --- dpgen/dispatcher/Dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 681e26a29..16406260d 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -284,7 +284,7 @@ def _new_record(self): } -def make_dispatcher(mdata_machine, mdata_resource, run_tasks, group_size): +def make_dispatcher(mdata_machine, mdata_resource=None, run_tasks=None, group_size=None): if 'ali_auth' in mdata_machine: from dpgen.dispatcher.ALI import ALI nchunks = len(_split_tasks(run_tasks, group_size)) From 90a467b9c19d34aa237bae9c673669d363eb5b0f Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 16 Jan 2020 16:15:40 +0800 Subject: [PATCH 020/201] fix bug --- dpgen/dispatcher/Dispatcher.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 16406260d..c9eaf17b5 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -284,11 +284,11 @@ def _new_record(self): } -def make_dispatcher(mdata_machine, mdata_resource=None, run_tasks=None, group_size=None): - if 'ali_auth' in mdata_machine: +def make_dispatcher(mdata, mdata_resource=None, run_tasks=None, group_size=None): + if 'ali_auth' in mdata: from dpgen.dispatcher.ALI import ALI nchunks = len(_split_tasks(run_tasks, group_size)) - dispatcher = ALI(mdata_machine['ali_auth'], mdata_resource, mdata_machine, nchunks) + dispatcher = ALI(mdata['ali_auth'], mdata_resource, mdata, nchunks) dispatcher.init() return dispatcher else: From 1dacd694712aecff1b6a6438f802ed449e95ec3a Mon Sep 17 00:00:00 2001 From: Yixiao Chen Date: Thu, 16 Jan 2020 22:54:08 -0600 Subject: [PATCH 021/201] fix bug in ensure alive --- dpgen/dispatcher/SSHContext.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index 185ade392..95f5ff363 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -86,14 +86,17 @@ def __init__ (self, self.job_uuid = str(uuid.uuid4()) self.remote_root = os.path.join(ssh_session.get_session_root(), self.job_uuid) self.ssh_session = ssh_session - self.ssh = self.ssh_session.get_ssh_client() self.ssh_session.ensure_alive() try: - sftp = self.ssh.open_sftp() + sftp = self.ssh_session.ssh.open_sftp() sftp.mkdir(self.remote_root) sftp.close() except: pass + + @property + def ssh(self): + return self.ssh_session.get_ssh_client() def close(self): self.ssh_session.close() From 4ef38b4c26d163333c981832076634782dcd66c2 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 18 Jan 2020 18:41:18 +0800 Subject: [PATCH 022/201] add nopbc support in model_devi MD simulations --- README.md | 1 + dpgen/generator/lib/lammps.py | 11 ++++++++- dpgen/generator/run.py | 4 ++++ setup.py | 2 +- tests/generator/data/mg.fcc.02x02x02 | 1 + tests/generator/param-mg-vasp.json | 4 ++-- tests/generator/test_make_md.py | 34 ++++++++++++++++++++++++++-- 7 files changed, 51 insertions(+), 6 deletions(-) create mode 120000 tests/generator/data/mg.fcc.02x02x02 diff --git a/README.md b/README.md index d9fed8fe8..0eeaf2677 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,7 @@ The bold notation of key (such aas **type_map**) means that it's a necessary key | **model_devi_e_trust_lo** | Float | 1e10 | Lower bound of energies for the selection. Recommend to set them a high number, since forces provide more precise information. Special cases such as energy minimization may need this. | | **model_devi_e_trust_hi** | Float | 1e10 | Upper bound of energies for the selection. | | **model_devi_clean_traj** | Boolean | true | Deciding whether to clean traj folders in MD since they are too large. | +| **model_devi_nopbc** | Boolean | False | Assume open boundary condition in MD simulations. | | **model_devi_jobs** | [
{
"sys_idx": [0],
"temps":
[100],
"press":
[1],
"trj_freq":
10,
"nsteps":
1000,
"ensembles":
"nvt"
},
...
] | List of dict | Settings for exploration in `01.model_devi`. Each dict in the list corresponds to one iteration. The index of `model_devi_jobs` exactly accord with index of iterations | | **model_devi_jobs["sys_idx"]** | List of integer | [0] | Systems to be selected as the initial structure of MD and be explored. The index corresponds exactly to the `sys_configs`. | | **model_devi_jobs["temps"]** | List of integer | [50, 300] | Temperature (**K**) in MD diff --git a/dpgen/generator/lib/lammps.py b/dpgen/generator/lib/lammps.py index 367817a89..95dcb8568 100644 --- a/dpgen/generator/lib/lammps.py +++ b/dpgen/generator/lib/lammps.py @@ -31,6 +31,7 @@ def make_lammps_input(ensemble, ele_temp_f = None, ele_temp_a = None, max_seed = 1000000, + nopbc = False, deepmd_version = '0.1') : if (ele_temp_f is not None or ele_temp_a is not None) and LooseVersion(deepmd_version) < LooseVersion('1'): raise RuntimeError('the electron temperature is only supported by deepmd-kit >= 1.0.0, please upgrade your deepmd-kit') @@ -49,7 +50,10 @@ def make_lammps_input(ensemble, ret+= "variable TAU_P equal %f\n" % tau_p ret+= "\n" ret+= "units metal\n" - ret+= "boundary p p p\n" + if nopbc: + ret+= "boundary f f f\n" + else: + ret+= "boundary p p p\n" ret+= "atom_style atomic\n" ret+= "\n" ret+= "neighbor 1.0 bin\n" @@ -104,6 +108,8 @@ def make_lammps_input(ensemble, ret+= "\n" if ensemble.split('-')[0] == 'npt' : assert (pres is not None) + if nopbc: + raise RuntimeError('ensemble %s is conflicting with nopbc' % ensemble) if ensemble == "npt" or ensemble == "npt-i" or ensemble == "npt-iso" : ret+= "fix 1 all npt temp ${TEMP} ${TEMP} ${TAU_T} iso ${PRES} ${PRES} ${TAU_P}\n" elif ensemble == 'npt-a' or ensemble == 'npt-aniso' : @@ -116,6 +122,9 @@ def make_lammps_input(ensemble, ret+= "fix 1 all nve\n" else : raise RuntimeError("unknown emsemble " + ensemble) + if nopbc: + ret+= "velocity all zero linear\n" + ret+= "fix fm all momentum 1 linear 1 1 1\n" ret+= "\n" ret+= "timestep %f\n" % dt ret+= "run ${NSTEPS}\n" diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index b09fb5ce4..d685003c1 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -652,6 +652,8 @@ def make_model_devi (iter_index, else: fmt = 'vasp/poscar' system = dpdata.System(os.path.join(conf_path, poscar_name), fmt = fmt, type_map = jdata['type_map']) + if jdata.get('model_devi_nopbc', False): + system.remove_pbc() system.to_lammps_lmp(os.path.join(conf_path, lmp_name)) conf_counter += 1 sys_counter += 1 @@ -776,6 +778,7 @@ def _make_model_devi_native(iter_index, jdata, mdata, conf_systems): if 'model_devi_taup' in jdata : model_devi_taup = jdata['model_devi_taup'] mass_map = jdata['mass_map'] + nopbc = jdata.get('model_devi_nopbc', False) iter_name = make_iter_name(iter_index) train_path = os.path.join(iter_name, train_name) @@ -847,6 +850,7 @@ def _make_model_devi_native(iter_index, jdata, mdata, conf_systems): pka_e = pka_e, ele_temp_f = te_f, ele_temp_a = te_a, + nopbc = nopbc, deepmd_version = deepmd_version) job = {} job["ensemble"] = ensemble diff --git a/setup.py b/setup.py index 71286b666..3af033482 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ with open(path.join('dpgen', '_date.py'), 'w') as fp : fp.write('date = \'%s\'' % today) -install_requires=['numpy>=1.14.3', 'dpdata>=0.1.12', 'pymatgen>=2019.1.13', 'ase', 'monty>2.0.0', 'paramiko', 'custodian'] +install_requires=['numpy>=1.14.3', 'dpdata>=0.1.14', 'pymatgen>=2019.1.13', 'ase', 'monty>2.0.0', 'paramiko', 'custodian'] setuptools.setup( name=NAME, diff --git a/tests/generator/data/mg.fcc.02x02x02 b/tests/generator/data/mg.fcc.02x02x02 new file mode 120000 index 000000000..32b1ad2c2 --- /dev/null +++ b/tests/generator/data/mg.fcc.02x02x02 @@ -0,0 +1 @@ +al.fcc.02x02x02 \ No newline at end of file diff --git a/tests/generator/param-mg-vasp.json b/tests/generator/param-mg-vasp.json index 908be9afb..f2ecdcf8f 100644 --- a/tests/generator/param-mg-vasp.json +++ b/tests/generator/param-mg-vasp.json @@ -7,8 +7,8 @@ ], "init_batch_size": [16], "sys_configs": [ - ["data/mg.fcc.02x02x02/01.scale_pert/sys-0032/scale*/000010/POSCAR"], - ["data/mg.fcc.02x02x02/01.scale_pert/sys-0032/scale*/00000[8-9]/POSCAR"] + ["data/mg.fcc.02x02x02/01.scale_pert/sys-0032/scale*/000000/POSCAR"], + ["data/mg.fcc.02x02x02/01.scale_pert/sys-0032/scale*/000001/POSCAR"] ], "_comment": "0 1 2 3", "_comment": "4 5 6 7", diff --git a/tests/generator/test_make_md.py b/tests/generator/test_make_md.py index 3ee930344..4f67b1fbe 100644 --- a/tests/generator/test_make_md.py +++ b/tests/generator/test_make_md.py @@ -146,8 +146,38 @@ def test_make_model_devi (self) : _check_confs(self, 0, jdata) _check_traj_dir(self, 0) _check_pt(self, 0, jdata) - shutil.rmtree('iter.000000') - + #shutil.rmtree('iter.000000') + + def test_make_model_devi_nopbc_npt (self) : + if os.path.isdir('iter.000000') : + shutil.rmtree('iter.000000') + with open (param_file, 'r') as fp : + jdata = json.load (fp) + jdata['model_devi_nopbc'] = True + with open (machine_file, 'r') as fp: + mdata = json.load (fp) + _make_fake_models(0, jdata['numb_models']) + cwd = os.getcwd() + with self.assertRaises(RuntimeError) : + make_model_devi(0, jdata, mdata) + os.chdir(cwd) + + def test_make_model_devi_nopbc_nvt (self) : + if os.path.isdir('iter.000000') : + shutil.rmtree('iter.000000') + with open (param_file, 'r') as fp : + jdata = json.load (fp) + jdata['model_devi_nopbc'] = True + jdata['model_devi_jobs'][0]['ensemble'] = 'nvt' + with open (machine_file, 'r') as fp: + mdata = json.load (fp) + _make_fake_models(0, jdata['numb_models']) + make_model_devi(0, jdata, mdata) + _check_pb(self, 0) + # _check_confs(self, 0, jdata) + _check_traj_dir(self, 0) + _check_pt(self, 0, jdata) + # shutil.rmtree('iter.000000') class TestMakeModelDeviRevMat(unittest.TestCase): From 1aa72ad28fda8909734ac33f36f313c5baf40f83 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 20 Jan 2020 22:59:13 -0500 Subject: [PATCH 023/201] set default multiplicity to auto; add parameter fragment_guesses --- dpgen/generator/lib/gaussian.py | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/dpgen/generator/lib/gaussian.py b/dpgen/generator/lib/gaussian.py index 79a420e27..0678c8c0f 100644 --- a/dpgen/generator/lib/gaussian.py +++ b/dpgen/generator/lib/gaussian.py @@ -3,6 +3,7 @@ import uuid import itertools +import warnings import numpy as np import dpdata from scipy.sparse import csr_matrix @@ -113,30 +114,33 @@ def make_gaussian_input(sys_data, fp_params): keywords = [keywords] # assume default charge is zero and default spin multiplicity is 1 charge = fp_params.get('charge', 0) - mult_auto = False - frag = False - if 'multiplicity' in fp_params: - if type(fp_params['multiplicity']) == int: - multiplicity = fp_params['multiplicity'] - elif fp_params['multiplicity'] == 'auto': - mult_auto = True - elif fp_params['multiplicity'] == 'frag': - mult_auto = True - frag = True - else: - raise RuntimeError('The keyword "multiplicity" is illegal.') + use_fragment_guesses = False + multiplicity = fp_params.get('multiplicity', 'auto') + if type(multiplicity) == int: + multiplicity = fp_params['multiplicity'] + mult_auto = False + elif multiplicity == 'auto': + mult_auto = True else: - multiplicity = 1 + raise RuntimeError('The keyword "multiplicity" is illegal.') + + if fp_params.get("fragment_guesses", False): + # Initial guess generated from fragment guesses + # New feature of Gaussian 16 + use_fragment_guesses = True + if not mult_auto: + warnings.warn("Automatically set multiplicity to auto!") + mult_auto = True if mult_auto: frag_numb, frag_index = _crd2frag(symbols, coordinates) if frag_numb == 1: - frag = False + use_fragment_guesses = False mult_frags = [] for i in range(frag_numb): idx = frag_index == i mult_frags.append(detect_multiplicity(np.array(symbols)[idx])) - if frag: + if use_fragment_guesses: multiplicity = sum(mult_frags) - frag_numb + 1 chargekeywords_frag = "%d %d" % (charge, multiplicity) + \ ''.join([' %d %d' % (charge, mult_frag) @@ -164,7 +168,7 @@ def make_gaussian_input(sys_data, fp_params): keywords[0]), '', titlekeywords, '', (chargekeywords_frag if frag else chargekeywords)] for ii, (symbol, coordinate) in enumerate(zip(symbols, coordinates)): - if frag: + if use_fragment_guesses: buff.append("%s(Fragment=%d) %f %f %f" % (symbol, frag_index[ii] + 1, *coordinate)) else: From fff6aae777a1ed87411ccad1a12d3c398d3d619f Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 21 Jan 2020 13:39:23 -0500 Subject: [PATCH 024/201] skip openbabel tests --- tests/generator/test_make_fp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/generator/test_make_fp.py b/tests/generator/test_make_fp.py index e77e94d36..67df978f7 100644 --- a/tests/generator/test_make_fp.py +++ b/tests/generator/test_make_fp.py @@ -2,6 +2,7 @@ import dpdata import numpy as np import unittest +import importlib sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) __package__ = 'generator' @@ -710,6 +711,7 @@ def test_make_fp_vasp_ele_temp(self): class TestMakeFPGaussian(unittest.TestCase): + @unittest.skipIf(importlib.util.find_spec("openbabel") is None, "requires openbabel") def test_make_fp_gaussian(self): if os.path.isdir('iter.000000') : shutil.rmtree('iter.000000') From 5e467ead5692a628c2e654da77acb9eb7de2f09a Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 21 Jan 2020 21:03:56 -0500 Subject: [PATCH 025/201] test the situation where multiplicity=1 --- tests/generator/test_make_fp.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/generator/test_make_fp.py b/tests/generator/test_make_fp.py index 67df978f7..41ee29db1 100644 --- a/tests/generator/test_make_fp.py +++ b/tests/generator/test_make_fp.py @@ -712,11 +712,12 @@ def test_make_fp_vasp_ele_temp(self): class TestMakeFPGaussian(unittest.TestCase): @unittest.skipIf(importlib.util.find_spec("openbabel") is None, "requires openbabel") - def test_make_fp_gaussian(self): + def test_make_fp_gaussian(self, multiplicity="auto"): if os.path.isdir('iter.000000') : shutil.rmtree('iter.000000') with open (param_gaussian_file, 'r') as fp : jdata = json.load (fp) + jdata['user_fp_params']['multiplicity'] = multiplicity with open (machine_file, 'r') as fp: mdata = json.load (fp) md_descript = [] @@ -738,6 +739,9 @@ def test_make_fp_gaussian(self): _check_potcar(self, 0, jdata['fp_pp_path'], jdata['fp_pp_files']) shutil.rmtree('iter.000000') + def test_make_fp_gaussian_multiplicity_one(self): + self.test_make_fp_gaussian(multiplicity=1) + def test_detect_multiplicity(self): # oxygen O2 3 self._check_multiplicity(['O', 'O'], 3) From f240887caee8442bb7ec6d1d26b9a82692201d80 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 21 Jan 2020 23:26:30 -0500 Subject: [PATCH 026/201] unittest --- tests/generator/test_make_fp.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/generator/test_make_fp.py b/tests/generator/test_make_fp.py index 41ee29db1..24a12ee36 100644 --- a/tests/generator/test_make_fp.py +++ b/tests/generator/test_make_fp.py @@ -711,8 +711,7 @@ def test_make_fp_vasp_ele_temp(self): class TestMakeFPGaussian(unittest.TestCase): - @unittest.skipIf(importlib.util.find_spec("openbabel") is None, "requires openbabel") - def test_make_fp_gaussian(self, multiplicity="auto"): + def make_fp_gaussian(self, multiplicity="auto") if os.path.isdir('iter.000000') : shutil.rmtree('iter.000000') with open (param_gaussian_file, 'r') as fp : @@ -739,8 +738,12 @@ def test_make_fp_gaussian(self, multiplicity="auto"): _check_potcar(self, 0, jdata['fp_pp_path'], jdata['fp_pp_files']) shutil.rmtree('iter.000000') + @unittest.skipIf(importlib.util.find_spec("openbabel") is None, "requires openbabel") + def test_make_fp_gaussian(self): + self.make_fp_gaussian() + def test_make_fp_gaussian_multiplicity_one(self): - self.test_make_fp_gaussian(multiplicity=1) + self.make_fp_gaussian(multiplicity=1) def test_detect_multiplicity(self): # oxygen O2 3 From d3708e4b70159ee097b17069ab1c5d9043c09590 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 22 Jan 2020 01:02:12 -0500 Subject: [PATCH 027/201] Update test_make_fp.py --- tests/generator/test_make_fp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generator/test_make_fp.py b/tests/generator/test_make_fp.py index 24a12ee36..bf02dea0f 100644 --- a/tests/generator/test_make_fp.py +++ b/tests/generator/test_make_fp.py @@ -711,7 +711,7 @@ def test_make_fp_vasp_ele_temp(self): class TestMakeFPGaussian(unittest.TestCase): - def make_fp_gaussian(self, multiplicity="auto") + def make_fp_gaussian(self, multiplicity="auto"): if os.path.isdir('iter.000000') : shutil.rmtree('iter.000000') with open (param_gaussian_file, 'r') as fp : From bb2e4f13d3d239d29d6b8dcff4b63293e31d4ff0 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 22 Jan 2020 10:13:51 -0500 Subject: [PATCH 028/201] fix bug --- dpgen/generator/lib/gaussian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/generator/lib/gaussian.py b/dpgen/generator/lib/gaussian.py index 0678c8c0f..41466b18d 100644 --- a/dpgen/generator/lib/gaussian.py +++ b/dpgen/generator/lib/gaussian.py @@ -152,7 +152,7 @@ def make_gaussian_input(sys_data, fp_params): np.count_nonzero(multi_frags == 3) * 2 buff = [] # keywords, e.g., force b3lyp/6-31g** - if frag: + if use_fragment_guesses: keywords[0] = '{} guess=fragment={}'.format( keywords[0], frag_numb) From 7fcda5739b59ad912c60055003a544a0a3cc5a0a Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 22 Jan 2020 10:27:44 -0500 Subject: [PATCH 029/201] fix another bug --- dpgen/generator/lib/gaussian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/generator/lib/gaussian.py b/dpgen/generator/lib/gaussian.py index 41466b18d..304622dfb 100644 --- a/dpgen/generator/lib/gaussian.py +++ b/dpgen/generator/lib/gaussian.py @@ -165,7 +165,7 @@ def make_gaussian_input(sys_data, fp_params): chargekeywords = '{} {}'.format(charge, multiplicity) buff = [*chkkeywords, nprockeywords, '#{}'.format( - keywords[0]), '', titlekeywords, '', (chargekeywords_frag if frag else chargekeywords)] + keywords[0]), '', titlekeywords, '', (chargekeywords_frag if use_fragment_guesses else chargekeywords)] for ii, (symbol, coordinate) in enumerate(zip(symbols, coordinates)): if use_fragment_guesses: From c6ff84b39d71f1276abc2d89c9a6dd6ccac6a41c Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 23 Jan 2020 11:43:04 +0800 Subject: [PATCH 030/201] add manual_delete() to delete machine if dpgen do not finish --- dpgen/dispatcher/ALI.py | 46 +++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 43af51f5b..569a7f69c 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -8,6 +8,23 @@ from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks from os.path import join +def manual_delete(): + with open('machine-ali.json') as fp1: + mdata = json.load(fp1) + AccessKey_ID = mdata['train'][0]['machine']['ali_auth']['AccessKey_ID'] + AccessKey_Secret = mdata['train'][0]['machine']['ali_auth']['AccessKey_Secret'] + regionID = mdata['train'][0]['machine']['regionID'] + with open('machine_record.json', 'r') as fp2: + machine_record = json.load(fp2) + instance_list = machine_record['instance_id'] + client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) + request = DeleteInstancesRequest() + request.set_accept_format('json') + request.set_InstanceIds(instance_list) + request.set_Force(True) + response = client.do_action_with_exception(request) + os.remove('machine_record.json') + class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = None @@ -112,23 +129,40 @@ def create_machine(self): template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) instance_name = self.adata["instance_name"] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) + instance_list = [] + ip = [] request = RunInstancesRequest() request.set_accept_format('json') request.set_UniqueSuffix(True) request.set_Password(pwd) request.set_InstanceName(instance_name) - request.set_Amount(self.nchunks) request.set_LaunchTemplateName(template_name) - response = client.do_action_with_exception(request) - response = json.loads(response) - self.instance_list = response["InstanceIdSets"]["InstanceIdSet"] - time.sleep(50) + if self.nchunks <= 100: + request.set_Amount(self.nchunks) + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + instance_list.append(instanceID) + else: + iteration = self.nchunks // 100 + for i in range(iteration): + request.set_Amount(100) + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + instance_list.append(instanceID) + request.set_Amount(self.nchunks - iteration * 100) + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + instance_list.append(instanceID) + self.instance_list = instance_list + time.sleep(90) request = DescribeInstancesRequest() request.set_accept_format('json') request.set_InstanceIds(self.instance_list) response = client.do_action_with_exception(request) response = json.loads(response) - ip = [] for i in range(len(response["Instances"]["Instance"])): ip.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) self.ip_list = ip From 0cd1e461d737b340ac60b302b00eee86a695d39d Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 25 Jan 2020 16:36:52 +0800 Subject: [PATCH 031/201] fix bugs --- dpgen/dispatcher/ALI.py | 80 ++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 569a7f69c..364a68b40 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -7,6 +7,7 @@ import time, json, os, glob from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks from os.path import join +from dpgen import dlog def manual_delete(): with open('machine-ali.json') as fp1: @@ -15,7 +16,7 @@ def manual_delete(): AccessKey_Secret = mdata['train'][0]['machine']['ali_auth']['AccessKey_Secret'] regionID = mdata['train'][0]['machine']['regionID'] with open('machine_record.json', 'r') as fp2: - machine_record = json.load(fp2) + machine_record = json.load(fp2 ) instance_list = machine_record['instance_id'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) request = DeleteInstancesRequest() @@ -129,8 +130,8 @@ def create_machine(self): template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) instance_name = self.adata["instance_name"] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) - instance_list = [] - ip = [] + self.instance_list = [] + self.ip_list = [] request = RunInstancesRequest() request.set_accept_format('json') request.set_UniqueSuffix(True) @@ -139,33 +140,62 @@ def create_machine(self): request.set_LaunchTemplateName(template_name) if self.nchunks <= 100: request.set_Amount(self.nchunks) - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - instance_list.append(instanceID) - else: - iteration = self.nchunks // 100 - for i in range(iteration): - request.set_Amount(100) + try: response = client.do_action_with_exception(request) response = json.loads(response) for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - instance_list.append(instanceID) - request.set_Amount(self.nchunks - iteration * 100) - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - instance_list.append(instanceID) - self.instance_list = instance_list + self.instance_list.append(instanceID) + except: + dlog.debug("Create failed, please check the console.") + if len(self.instance_list) > 0: + self.delete_machine() + else: + iteration = self.nchunks // 100 + try: + for i in range(iteration): + request.set_Amount(100) + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + except: + dlog.debug("Create failed, please check the console.") + if len(self.instance_list) > 0: + self.delete_machine() + if self.nchunks - iteration * 100 != 0: + try: + request.set_Amount(self.nchunks - iteration * 100) + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + except: + dlog.debug("Create failed, please check the console.") + if len(self.instance_list) > 0: + self.delete_machine() time.sleep(90) request = DescribeInstancesRequest() request.set_accept_format('json') - request.set_InstanceIds(self.instance_list) - response = client.do_action_with_exception(request) - response = json.loads(response) - for i in range(len(response["Instances"]["Instance"])): - ip.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) - self.ip_list = ip + if len(self.instance_list) <= 100: + request.set_InstanceIds(self.instance_list) + response = client.do_action_with_exception(request) + response = json.loads(response) + for i in range(len(response["Instances"]["Instance"])): + self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) + else: + iteration = len(self.instance_list) // 100 + for i in range(iteration): + request.set_InstanceIds(self.instance_list[i*100:(i+1)*100]) + response = client.do_action_with_exception(request) + response = json.loads(response) + for j in range(len(response["Instances"]["Instance"])): + self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) + if len(self.instance_list) - iteration * 100 != 0: + request.set_InstanceIds(self.instance_list[iteration*100:]) + response = client.do_action_with_exception(request) + response = json.loads(response) + for j in range(len(response["Instances"]["Instance"])): + self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) with open('machine_record.json', 'w') as fp: json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) @@ -179,4 +209,6 @@ def delete_machine(self): request.set_InstanceIds(self.instance_list) request.set_Force(True) response = client.do_action_with_exception(request) + self.instance_list = [] os.remove('machine_record.json') + dlog.debug("Successfully free the machine!") From 4383fd8acfcb6c87b7e615b1ce0a9fdb5708ffdb Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 25 Jan 2020 17:29:31 +0800 Subject: [PATCH 032/201] fix bugs --- dpgen/dispatcher/ALI.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 364a68b40..ea8075df7 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -176,26 +176,26 @@ def create_machine(self): time.sleep(90) request = DescribeInstancesRequest() request.set_accept_format('json') - if len(self.instance_list) <= 100: + if len(self.instance_list) <= 10: request.set_InstanceIds(self.instance_list) response = client.do_action_with_exception(request) response = json.loads(response) for i in range(len(response["Instances"]["Instance"])): self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) else: - iteration = len(self.instance_list) // 100 + iteration = len(self.instance_list) // 10 for i in range(iteration): - request.set_InstanceIds(self.instance_list[i*100:(i+1)*100]) + request.set_InstanceIds(self.instance_list[i*10:(i+1)*10]) response = client.do_action_with_exception(request) response = json.loads(response) for j in range(len(response["Instances"]["Instance"])): - self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) - if len(self.instance_list) - iteration * 100 != 0: - request.set_InstanceIds(self.instance_list[iteration*100:]) + self.ip_list.append(response["Instances"]["Instance"][j]["PublicIpAddress"]['IpAddress'][0]) + if len(self.instance_list) - iteration * 10 != 0: + request.set_InstanceIds(self.instance_list[iteration*10:]) response = client.do_action_with_exception(request) response = json.loads(response) for j in range(len(response["Instances"]["Instance"])): - self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) + self.ip_list.append(response["Instances"]["Instance"][j]["PublicIpAddress"]['IpAddress'][0]) with open('machine_record.json', 'w') as fp: json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) From 1d28c93ddcd074b35df279d150f026eb0d31e6cd Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 25 Jan 2020 23:27:16 +0800 Subject: [PATCH 033/201] fix bugs in delete_machine --- dpgen/dispatcher/ALI.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index ea8075df7..8f37c1327 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -16,14 +16,25 @@ def manual_delete(): AccessKey_Secret = mdata['train'][0]['machine']['ali_auth']['AccessKey_Secret'] regionID = mdata['train'][0]['machine']['regionID'] with open('machine_record.json', 'r') as fp2: - machine_record = json.load(fp2 ) + machine_record = json.load(fp2) instance_list = machine_record['instance_id'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) request = DeleteInstancesRequest() request.set_accept_format('json') - request.set_InstanceIds(instance_list) - request.set_Force(True) - response = client.do_action_with_exception(request) + if len(instance_list) <= 100: + request.set_InstanceIds(instance_list) + request.set_Force(True) + response = client.do_action_with_exception(request) + else: + iteration = len(instance_list) // 100 + for i in range(iteration): + request.set_InstanceIds(instance_list[i*100:(i+1)*100]) + request.set_Force(True) + response = client.do_action_with_exception(request) + if len(instance_list) - iteration * 100 != 0: + request.set_InstanceIds(instance_list[iteration*100:]) + request.set_Force(True) + response = client.do_action_with_exception(request) os.remove('machine_record.json') class ALI(): @@ -173,7 +184,7 @@ def create_machine(self): dlog.debug("Create failed, please check the console.") if len(self.instance_list) > 0: self.delete_machine() - time.sleep(90) + time.sleep(60) request = DescribeInstancesRequest() request.set_accept_format('json') if len(self.instance_list) <= 10: @@ -206,9 +217,20 @@ def delete_machine(self): client = AcsClient(AccessKey_ID,AccessKey_Secret, regionID) request = DeleteInstancesRequest() request.set_accept_format('json') - request.set_InstanceIds(self.instance_list) - request.set_Force(True) - response = client.do_action_with_exception(request) + if len(self.instance_list) <= 100: + request.set_InstanceIds(self.instance_list) + request.set_Force(True) + response = client.do_action_with_exception(request) + else: + iteration = len(self.instance_list) // 100 + for i in range(iteration): + request.set_InstanceIds(self.instance_list[i*100:(i+1)*100]) + request.set_Force(True) + response = client.do_action_with_exception(request) + if len(self.instance_list) - iteration * 100 != 0: + request.set_InstanceIds(self.instance_list[iteration*100:]) + request.set_Force(True) + response = client.do_action_with_exception(request) self.instance_list = [] os.remove('machine_record.json') dlog.debug("Successfully free the machine!") From 7835399b500fa87971601380598bc3884f99dda4 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 3 Feb 2020 15:04:06 +0800 Subject: [PATCH 034/201] delete machine when one job finished --- dpgen/dispatcher/ALI.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 8f37c1327..58de323e9 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -9,12 +9,11 @@ from os.path import join from dpgen import dlog -def manual_delete(): +def manual_delete(regionID): with open('machine-ali.json') as fp1: mdata = json.load(fp1) AccessKey_ID = mdata['train'][0]['machine']['ali_auth']['AccessKey_ID'] AccessKey_Secret = mdata['train'][0]['machine']['ali_auth']['AccessKey_Secret'] - regionID = mdata['train'][0]['machine']['regionID'] with open('machine_record.json', 'r') as fp2: machine_record = json.load(fp2) instance_list = machine_record['instance_id'] @@ -41,11 +40,13 @@ class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = None self.instance_list = None + self.regionID = mdata_machine["regionID"] self.dispatchers = None self.adata = adata self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine self.nchunks = nchunks + def init(self): if self.check_restart(): @@ -111,16 +112,29 @@ def run_jobs(self, outlog, errlog) job_handlers.append(job_handler) + tmp = [1 for ii in range(self.nchunks)] while True: - cnt = 0 for ii in range(self.nchunks): - if self.dispatchers[ii].all_finished(job_handlers[ii]): - cnt += 1 - if cnt == self.nchunks: + if tmp[ii] and self.dispatchers[ii].all_finished(job_handlers[ii]): + self.delete(self.instance_list[ii]) + tmp[ii] = 0 + if sum(tmp) == 0: + os.remove('machine_record.json') break else: time.sleep(10) - self.delete_machine() + #self.delete_machine() + + def delete(self, instance_list): + AccessKey_ID = self.adata["AccessKey_ID"] + AccessKey_Secret = self.adata["AccessKey_Secret"] + regionID = self.regionID + client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) + request = DeleteInstancesRequest() + request.set_accept_format('json') + request.set_InstanceIds([instance_list]) + request.set_Force(True) + response = client.do_action_with_exception(request) def make_dispatchers(self): dispatchers = [] @@ -137,7 +151,7 @@ def create_machine(self): AccessKey_Secret = self.adata["AccessKey_Secret"] strategy = self.adata["pay_strategy"] pwd = self.adata["password"] - regionID = self.mdata_machine['regionID'] + regionID = self.regionID template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) instance_name = self.adata["instance_name"] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) From c621c256b70eb74b16de884752c5a0f78dd6ceef Mon Sep 17 00:00:00 2001 From: Wanrun Jiang <58099845+Vibsteamer@users.noreply.github.com> Date: Mon, 3 Feb 2020 21:52:41 +0800 Subject: [PATCH 035/201] =?UTF-8?q?bug=5Ffix:=20test=20|=20deepmd=20|=20ta?= =?UTF-8?q?sk=2004.interstitial=20|=20"reprod=5Fopt":=20=E3=80=90true?= =?UTF-8?q?=E3=80=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit error:File "/data1/wanrunj/.conda/envs/python368/lib/python3.6/site-packages/dpgen/auto_test/lib/util.py", line 63, in make_work_path task_type=task_type+'-reprod-k%.2f' % (kspacing) UnboundLocalError: local variable 'kspacing' referenced before assignment --- dpgen/auto_test/lib/util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dpgen/auto_test/lib/util.py b/dpgen/auto_test/lib/util.py index fee55ff48..1d3a2dc58 100644 --- a/dpgen/auto_test/lib/util.py +++ b/dpgen/auto_test/lib/util.py @@ -60,6 +60,7 @@ def make_work_path(jdata,task,reprod_opt,static,user): if 'relax_incar' in jdata.keys(): task_type=task_type+'-reprod-relax_incar' else: + kspacing = jdata['vasp_params']['kspacing'] task_type=task_type+'-reprod-k%.2f'% (kspacing) work_path=os.path.join(task_path, task_type) From 1f79dc045c75d793cd76f1d8d90208b279de4736 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Wed, 5 Feb 2020 11:33:27 +0800 Subject: [PATCH 036/201] fix bugs in delete machine when job finish --- dpgen/dispatcher/ALI.py | 63 +++++++++++++++++++++------------- dpgen/dispatcher/Dispatcher.py | 2 ++ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 58de323e9..8bbca0e54 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -42,6 +42,7 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.instance_list = None self.regionID = mdata_machine["regionID"] self.dispatchers = None + self.job_handlers = None self.adata = adata self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine @@ -98,7 +99,7 @@ def run_jobs(self, outlog = 'log', errlog = 'err'): task_chunks = _split_tasks(tasks, group_size) - job_handlers = [] + self.job_handlers = [] for ii in range(self.nchunks): job_handler = self.dispatchers[ii].submit_jobs(resources, command, @@ -111,30 +112,44 @@ def run_jobs(self, forward_task_deference, outlog, errlog) - job_handlers.append(job_handler) - tmp = [1 for ii in range(self.nchunks)] + self.job_handlers.append(job_handler) while True: for ii in range(self.nchunks): - if tmp[ii] and self.dispatchers[ii].all_finished(job_handlers[ii]): - self.delete(self.instance_list[ii]) - tmp[ii] = 0 - if sum(tmp) == 0: + if self.dispatchers[ii].all_finished(self.job_handlers[ii]): + print("before delete:", self.ip_list) + print(self.instance_list) + print(self.nchunks) + print(self.job_handlers[ii]['job_record'].record) + #print(self.job_handlers[ii]['task_chunks']) + self.delete(ii) + print("after delete:", self.ip_list) + break + if self.nchunks == 0: os.remove('machine_record.json') break else: time.sleep(10) #self.delete_machine() - def delete(self, instance_list): + def delete(self, ii): AccessKey_ID = self.adata["AccessKey_ID"] AccessKey_Secret = self.adata["AccessKey_Secret"] regionID = self.regionID client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) request = DeleteInstancesRequest() request.set_accept_format('json') - request.set_InstanceIds([instance_list]) + request.set_InstanceIds([self.instance_list[ii]]) request.set_Force(True) response = client.do_action_with_exception(request) + self.nchunks -= 1 + self.instance_list.pop(ii) + self.ip_list.pop(ii) + self.dispatchers.pop(ii) + self.job_handlers.pop(ii) + with open('machine_record.json', 'w') as fp: + json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) + + def make_dispatchers(self): dispatchers = [] @@ -202,25 +217,25 @@ def create_machine(self): request = DescribeInstancesRequest() request.set_accept_format('json') if len(self.instance_list) <= 10: - request.set_InstanceIds(self.instance_list) - response = client.do_action_with_exception(request) - response = json.loads(response) - for i in range(len(response["Instances"]["Instance"])): - self.ip_list.append(response["Instances"]["Instance"][i]["PublicIpAddress"]['IpAddress'][0]) + for i in range(len(self.instance_list)): + request.set_InstanceIds([self.instance_list[i]]) + response = client.do_action_with_exception(request) + response = json.loads(response) + self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) else: iteration = len(self.instance_list) // 10 for i in range(iteration): - request.set_InstanceIds(self.instance_list[i*10:(i+1)*10]) - response = client.do_action_with_exception(request) - response = json.loads(response) - for j in range(len(response["Instances"]["Instance"])): - self.ip_list.append(response["Instances"]["Instance"][j]["PublicIpAddress"]['IpAddress'][0]) + for j in range(10): + request.set_InstanceIds([self.instance_list[i*10+j]]) + response = client.do_action_with_exception(request) + response = json.loads(response) + self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) if len(self.instance_list) - iteration * 10 != 0: - request.set_InstanceIds(self.instance_list[iteration*10:]) - response = client.do_action_with_exception(request) - response = json.loads(response) - for j in range(len(response["Instances"]["Instance"])): - self.ip_list.append(response["Instances"]["Instance"][j]["PublicIpAddress"]['IpAddress'][0]) + for j in range(len(self.instance_list) - iteration * 10): + request.set_InstanceIds([self.instance_list[iteration*10+j]]) + response = client.do_action_with_exception(request) + response = json.loads(response) + self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) with open('machine_record.json', 'w') as fp: json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index c9eaf17b5..0887c26b5 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -205,6 +205,8 @@ def all_finished(self, rjob['batch'].submit(task_chunks[idx], command, res = resources, outlog=outlog, errlog=errlog,restart=True) elif status == JobStatus.finished : dlog.info('job %s finished' % job_uuid) + + print('download:', rjob['context'].ssh_session.remote_host) rjob['context'].download(task_chunks[idx], backward_task_files) rjob['context'].clean() job_record.record_finish(cur_hash) From acc6949af0597a9a5d3edb5e0c22bd5445d19993 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Wed, 5 Feb 2020 11:36:03 +0800 Subject: [PATCH 037/201] fix bugs in delete_machine when job finish --- dpgen/dispatcher/ALI.py | 6 ------ dpgen/dispatcher/Dispatcher.py | 2 -- 2 files changed, 8 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 8bbca0e54..3aacd1791 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -116,13 +116,7 @@ def run_jobs(self, while True: for ii in range(self.nchunks): if self.dispatchers[ii].all_finished(self.job_handlers[ii]): - print("before delete:", self.ip_list) - print(self.instance_list) - print(self.nchunks) - print(self.job_handlers[ii]['job_record'].record) - #print(self.job_handlers[ii]['task_chunks']) self.delete(ii) - print("after delete:", self.ip_list) break if self.nchunks == 0: os.remove('machine_record.json') diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 0887c26b5..c9eaf17b5 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -205,8 +205,6 @@ def all_finished(self, rjob['batch'].submit(task_chunks[idx], command, res = resources, outlog=outlog, errlog=errlog,restart=True) elif status == JobStatus.finished : dlog.info('job %s finished' % job_uuid) - - print('download:', rjob['context'].ssh_session.remote_host) rjob['context'].download(task_chunks[idx], backward_task_files) rjob['context'].clean() job_record.record_finish(cur_hash) From 7248381cfe379084d67ed698602d5eaa65b4d47b Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Wed, 5 Feb 2020 11:49:00 +0800 Subject: [PATCH 038/201] fix bugs in create_machine --- dpgen/dispatcher/ALI.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 3aacd1791..7aeb4151d 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -48,7 +48,6 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.mdata_machine = mdata_machine self.nchunks = nchunks - def init(self): if self.check_restart(): pass @@ -123,7 +122,6 @@ def run_jobs(self, break else: time.sleep(10) - #self.delete_machine() def delete(self, ii): AccessKey_ID = self.adata["AccessKey_ID"] @@ -143,8 +141,6 @@ def delete(self, ii): with open('machine_record.json', 'w') as fp: json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) - - def make_dispatchers(self): dispatchers = [] for ii in range(self.nchunks): @@ -180,9 +176,10 @@ def create_machine(self): for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: self.instance_list.append(instanceID) except: - dlog.debug("Create failed, please check the console.") + dlog.info("Create failed, please check the console.") if len(self.instance_list) > 0: self.delete_machine() + exit() else: iteration = self.nchunks // 100 try: @@ -193,9 +190,10 @@ def create_machine(self): for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: self.instance_list.append(instanceID) except: - dlog.debug("Create failed, please check the console.") + dlog.info("Create failed, please check the console.") if len(self.instance_list) > 0: self.delete_machine() + exit() if self.nchunks - iteration * 100 != 0: try: request.set_Amount(self.nchunks - iteration * 100) @@ -204,9 +202,10 @@ def create_machine(self): for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: self.instance_list.append(instanceID) except: - dlog.debug("Create failed, please check the console.") + dlog.info("Create failed, please check the console.") if len(self.instance_list) > 0: self.delete_machine() + exit() time.sleep(60) request = DescribeInstancesRequest() request.set_accept_format('json') From c93a8177164cca700b6b58a65707209d03701910 Mon Sep 17 00:00:00 2001 From: tianhongzhen Date: Thu, 6 Feb 2020 10:58:21 +0800 Subject: [PATCH 039/201] add pwmat to dpgen --- dpgen/generator/lib/pwmat.py | 177 ++++++++++++++++++ dpgen/generator/run.py | 165 ++++++++++++++++ .../machine-slurm-pwmat-single.json | 80 ++++++++ examples/run/dp-lammps-pwmat/param_CH4.json | 99 ++++++++++ tests/generator/C.SG15.PBE.UPF | 0 tests/generator/H.SG15.PBE.UPF | 0 tests/generator/N.SG15.PBE.UPF | 0 tests/generator/context.py | 1 + .../02.fp/task.000.000000/OUT.MLMD | 20 ++ .../02.fp/task.000.000000/etot.input | 17 ++ .../02.fp/task.000.000000/info | 1 + .../02.fp/task.000.000000/poscar2config.x | Bin 0 -> 937024 bytes .../02.fp/task.000.000001/OUT.MLMD | 20 ++ .../02.fp/task.000.000001/etot.input | 17 ++ .../02.fp/task.000.000001/info | 1 + .../02.fp/task.000.000001/poscar2config.x | Bin 0 -> 937024 bytes .../out_data_post_fp_pwmat/orig/box.raw | 2 + .../out_data_post_fp_pwmat/orig/coord.raw | 2 + .../out_data_post_fp_pwmat/orig/energy.raw | 2 + .../out_data_post_fp_pwmat/orig/force.raw | 2 + .../out_data_post_fp_pwmat/orig/type.raw | 6 + .../out_data_post_fp_pwmat/orig/type_map.raw | 3 + tests/generator/param-pyridine-pwmat.json | 123 ++++++++++++ tests/generator/test_make_fp.py | 58 +++++- tests/generator/test_post_fp.py | 17 ++ 25 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 dpgen/generator/lib/pwmat.py create mode 100644 examples/run/dp-lammps-pwmat/machine-slurm-pwmat-single.json create mode 100644 examples/run/dp-lammps-pwmat/param_CH4.json create mode 100644 tests/generator/C.SG15.PBE.UPF create mode 100644 tests/generator/H.SG15.PBE.UPF create mode 100644 tests/generator/N.SG15.PBE.UPF create mode 100644 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/OUT.MLMD create mode 100644 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/etot.input create mode 100644 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/info create mode 100755 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/poscar2config.x create mode 100644 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000001/OUT.MLMD create mode 100644 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000001/etot.input create mode 100644 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000001/info create mode 100755 tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000001/poscar2config.x create mode 100644 tests/generator/out_data_post_fp_pwmat/orig/box.raw create mode 100644 tests/generator/out_data_post_fp_pwmat/orig/coord.raw create mode 100644 tests/generator/out_data_post_fp_pwmat/orig/energy.raw create mode 100644 tests/generator/out_data_post_fp_pwmat/orig/force.raw create mode 100644 tests/generator/out_data_post_fp_pwmat/orig/type.raw create mode 100644 tests/generator/out_data_post_fp_pwmat/orig/type_map.raw create mode 100644 tests/generator/param-pyridine-pwmat.json diff --git a/dpgen/generator/lib/pwmat.py b/dpgen/generator/lib/pwmat.py new file mode 100644 index 000000000..4011b82c7 --- /dev/null +++ b/dpgen/generator/lib/pwmat.py @@ -0,0 +1,177 @@ +#!/usr/bin/python3 + +import os +import numpy as np + +def _reciprocal_box(box) : + rbox = np.linalg.inv(box) + rbox = rbox.T + return rbox + +def _make_pwmat_kp_mp(kpoints) : + ret = "" + ret += "%d %d %d 0 0 0 " % (kpoints[0], kpoints[1], kpoints[2]) + return ret + +def _make_kspacing_kpoints(config, kspacing) : + with open(config, 'r') as fp: + lines = fp.read().split('\n') + box = [] + for idx, ii in enumerate(lines): + if 'lattice' in ii or 'Lattice' in ii or 'LATTICE' in ii: + for kk in range(idx+1,idx+1+3): + vector=[float(jj) for jj in lines[kk].split()[0:3]] + box.append(vector) + box = np.array(box) + rbox = _reciprocal_box(box) + kpoints = [(np.ceil(2 * np.pi * np.linalg.norm(ii) / kspacing).astype(int)) for ii in rbox] + ret = _make_pwmat_kp_mp(kpoints) + return ret + + +def make_pwmat_input_dict (node1, node2, atom_config, ecut, e_error, + rho_error, icmix = None, smearing = None, + sigma = None,kspacing = 0.5, flag_symm = None) : + input_dict = {} + input_dict['node1'] = node1 + input_dict['node2'] = node2 + input_dict['in.atom'] = atom_config + input_dict['ecut'] = ecut + input_dict['e_error'] = e_error + input_dict['rho_error'] = rho_error + if icmix is not None: + if sigma is not None: + if smearing is not None: + SCF_ITER0_1 = "6 4 3 0.0000 " + str(sigma) + " " + str(smearing) + SCF_ITER0_2 = "94 4 3 " + str(icmix) + " " + str(sigma) + " " + str(smearing) + else: + SCF_ITER0_1 = "6 4 3 0.0000 " + str(simga) + " 2" + SCF_ITER0_2 = "94 4 3 " + str(icmix) + " " + str(simga) + " 2" + + else: + if smearing is not None: + SCF_ITER0_1 = "6 4 3 0.0000 0.025 " + str(smearing) + SCF_ITER0_2 = "94 4 3 " + str(icmix) + " 0.025 " + str(smearing) + else: + SCF_ITER0_1 = "6 4 3 0.0000 0.025 2" + SCF_ITER0_2 = "94 4 3 " + str(icmix) + " 0.025 2" + else: + if sigma is not None: + if smearing is not None: + SCF_ITER0_1 = "6 4 3 0.0000 " + str(sigma) + " " + str(smearing) + SCF_ITER0_2 = "94 4 3 1.0000 " + str(sigma) + " " + str(smearing) + else: + SCF_ITER0_1 = "6 4 3 0.0000 " + str(sigma) + " 2" + SCF_ITER0_2 = "94 4 3 1.0000 " + str(sigma) + " 2" + else: + if smearing is not None: + SCF_ITER0_1 = "6 4 3 0.0000 0.025 " + str(smearing) + SCF_ITER0_2 = "94 4 3 1.0000 0.025 " + str(smearing) + else: + SCF_ITER0_1 = "6 4 3 0.0000 0.025 2" + SCF_ITER0_2 = "94 4 3 1.0000 0.025 2" + input_dict['scf_iter0_1'] = SCF_ITER0_1 + input_dict['scf_iter0_2'] = SCF_ITER0_2 + if flag_symm is not None : + MP_N123 = _make_kspacing_kpoints(atom_config, kspacing) + MP_N123 += str(flag_symm) + else: + MP_N123 = _make_kspacing_kpoints(atom_config, kspacing) + input_dict['mp_n123'] = MP_N123 + input_dict['out.wg'] = 'F' + input_dict['out.rho'] = 'F' + input_dict['out.mlmd'] = 'T\n' + return input_dict + +def _update_input_dict(input_dict_, user_dict) : + if user_dict is None: + return input_dict_ + input_dict = input_dict_ + for ii in user_dict : + input_dict[ci] = user_dict[ii] + return input_dict + +def write_input_dict(input_dict) : + lines = [] + for key in input_dict: + if (type(input_dict[key]) == bool): + if input_dict[key]: + rs = 'T' + else : + rs = 'F' + else : + rs = str(input_dict[key]) + lines.append('%s=%s' % (key, rs)) + return '\n'.join(lines) + + +def _make_smearing(fp_params) : + icmix = None + smearing = None + sigma = None + if 'icmix' in fp_params : + icmix = fp_params['icmix'] + if 'smearing' in fp_params : + smearing = fp_params['smearing'] + if 'sigma' in fp_params : + sigma = fp_params['sigma'] + if icmix == None: + if smearing == None: + if sigma == None: + return None, None, None + else: + return None, None, sigma + else: + if sigma == None: + return None, smearing, None + else: + return None, smearing, sigma + else: + if smearing == None: + if sigma == None: + return icmix, None, None + else: + return icmix, None, sigma + else: + if sigma == None: + return icmix, smearing, None + else: + return icmix, smearing, sigma +def _make_flag_symm(fp_params) : + flag_symm = None + if 'flag_symm' in fp_params : + flag_symm = fp_params['flag_symm'] + if flag_symm == 'NONE' : + flag_symm = None + elif str(flag_symm) not in [None, '0', '1', '2', '3'] : + raise RuntimeError ("unknow flag_symm type " + str(flag_symm)) + return flag_symm + +def make_pwmat_input_user_dict(fp_params) : + node1 = fp_params['node1'] + node2 = fp_params['node2'] + atom_config = fp_params['in.atom'] + ecut = fp_params['ecut'] + e_error = fp_params['e_error'] + rho_error = fp_params['rho_error'] + kspacing = fp_params['kspacing'] + if 'user_pwmat_params' in fp_params : + user_dict = fp_params['user_pwmat_params'] + else : + user_dict = None + icmix, smearing, sigma = _make_smearing(fp_params) + flag_symm = _make_flag_symm(fp_params) + input_dict = make_pwmat_input_dict(node1, node2, atom_config, ecut, e_error, + rho_error, icmix = icmix, smearing = smearing, + sigma = sigma, kspacing = kspacing, + flag_symm = flag_symm + ) + input_dict = _update_input_dict(input_dict, user_dict) + input = write_input_dict(input_dict) + return input + +def input_upper(dinput): + standard_input={} + for key,val in dinput.items(): + standard_input[key.upper()]=val + return Input(standard_input) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index d685003c1..a0bd286da 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -43,6 +43,10 @@ from dpgen.generator.lib.vasp import incar_upper from dpgen.generator.lib.pwscf import make_pwscf_input #from dpgen.generator.lib.pwscf import cvt_1frame +from dpgen.generator.lib.pwmat import make_pwmat_input_dict +from dpgen.generator.lib.pwmat import write_input_dict +from dpgen.generator.lib.pwmat import make_pwmat_input_user_dict +from dpgen.generator.lib.pwmat import input_upper from dpgen.generator.lib.siesta import make_siesta_input from dpgen.generator.lib.gaussian import make_gaussian_input, take_cluster from dpgen.generator.lib.cp2k import make_cp2k_input, make_cp2k_xyz @@ -1088,6 +1092,65 @@ def make_vasp_incar(jdata, filename): fp.write(incar) return incar +def make_pwmat_input(jdata, filename): + if 'fp_incar' in jdata.keys() : + fp_incar_path = jdata['fp_incar'] + assert(os.path.exists(fp_incar_path)) + fp_incar_path = os.path.abspath(fp_incar_path) + fr = open(fp_incar_path) + input = fr.read() + fr.close() + elif 'user_fp_params' in jdata.keys() : + fp_params = jdata['user_fp_params'] + node1 = fp_params['node1'] + node2 = fp_params['node2'] + atom_config = fp_params['in.atom'] + ecut = fp_params['ecut'] + e_error = fp_params['e_error'] + rho_error = fp_params['rho_error'] + kspacing = fp_params['kspacing'] + flag_symm = fp_params['flag_symm'] + os.system("command -v poscar2config.x | wc -l > 1.txt") + fc = open('1.txt') + flag_command = fc.read() + fc.close() + if int(flag_command) == 1 : + os.system('poscar2config.x < POSCAR > tmp.config') + else: + os.system('cp ../../../out_data_post_fp_pwmat/02.fp/task.000.000000/poscar2config.x ./') + os.system('./poscar2config.x < POSCAR > tmp.config') + os.system('rm -rf tmp.config') + input_dict = make_pwmat_input_dict(node1, node2, atom_config, ecut, e_error, + rho_error, icmix = None, smearing = None, + sigma = None, kspacing = kspacing, + flag_symm = flag_symm + ) + + input = write_input_dict(input_dict) + else: + input = make_pwmat_input_user_dict(jdata['fp_params']) + if 'IN.PSP' in input or 'in.psp' in input: + with open(filename, 'w') as fp: + fp.write(input) + fp.write('job=scf\n') + if 'OUT.MLMD' in input or 'out.mlmd' in input: + return input + else: + fp.write('OUT.MLMD = T') + return input + else: + with open(filename, 'w') as fp: + fp.write(input) + fp.write('job=scf\n') + fp_pp_files = jdata['fp_pp_files'] + for idx, ii in enumerate(fp_pp_files) : + fp.write('IN.PSP%d = %s\n' %(idx+1, ii)) + if 'OUT.MLMD' in input or 'out.mlmd' in input: + return input + else: + fp.write('OUT.MLMD = T') + return input + def make_vasp_incar_ele_temp(jdata, filename, ele_temp, nbands_esti = None): with open(filename) as fp: incar = fp.read() @@ -1124,6 +1187,21 @@ def _make_fp_vasp_incar (iter_index, nbands_esti = nbands_esti) os.chdir(cwd) +def _make_fp_pwmat_input (iter_index, + jdata) : + iter_name = make_iter_name(iter_index) + work_path = os.path.join(iter_name, fp_name) + fp_tasks = glob.glob(os.path.join(work_path, 'task.*')) + fp_tasks.sort() + if len(fp_tasks) == 0 : + return + cwd = os.getcwd() + for ii in fp_tasks: + os.chdir(ii) + make_pwmat_input(jdata, 'etot.input') + os.system("sed -i '1,2c 4 1' etot.input") + os.chdir(cwd) + def _make_fp_vasp_kp (iter_index,jdata): iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, fp_name) @@ -1396,6 +1474,21 @@ def make_fp_cp2k (iter_index, # link pp files _link_fp_vasp_pp(iter_index, jdata) +def make_fp_pwmat (iter_index, + jdata) : + # make config + fp_tasks = _make_fp_vasp_configs(iter_index, jdata) + if len(fp_tasks) == 0 : + return + # abs path for fp_incar if it exists + if 'fp_incar' in jdata: + jdata['fp_incar'] = os.path.abspath(jdata['fp_incar']) + # order is critical! + # 1, link pp files + _link_fp_vasp_pp(iter_index, jdata) + # 2, create pwmat input + _make_fp_pwmat_input(iter_index, jdata) + def make_fp (iter_index, jdata, mdata) : @@ -1411,6 +1504,8 @@ def make_fp (iter_index, make_fp_gaussian(iter_index, jdata) elif fp_style == "cp2k" : make_fp_cp2k(iter_index, jdata) + elif fp_style == "pwmat" : + make_fp_pwmat(iter_index, jdata) else : raise RuntimeError ("unsupported fp style") @@ -1470,6 +1565,17 @@ def _cp2k_check_fin(ii): return False return True +def _pwmat_check_fin (ii) : + if os.path.isfile(os.path.join(ii, 'REPORT')) : + with open(os.path.join(ii, 'REPORT'), 'r') as fp : + content = fp.read() + count = content.count('time') + if count != 1 : + return False + else : + return False + return True + def run_fp_inner (iter_index, jdata, mdata, @@ -1545,6 +1651,10 @@ def run_fp (iter_index, forward_files = ['input.inp', 'coord.xyz'] backward_files = ['output'] run_fp_inner(iter_index, jdata, mdata, forward_files, backward_files, _cp2k_check_fin, log_file = 'output') + elif fp_style == "pwmat" : + forward_files = ['atom.config', 'etot.input'] + fp_pp_files + backward_files = ['REPORT', 'OUT.MLMD', 'output'] + run_fp_inner(iter_index, jdata, mdata, forward_files, backward_files, _pwmat_check_fin, log_file = 'output') else : raise RuntimeError ("unsupported fp style") @@ -1809,6 +1919,59 @@ def post_fp_cp2k (iter_index, all_sys.to_deepmd_npy(sys_data_path, set_size = len(sys_output)) +def post_fp_pwmat (iter_index, + jdata, + rfailed=None): + + ratio_failed = rfailed if rfailed else jdata.get('ratio_failed',0.05) + model_devi_jobs = jdata['model_devi_jobs'] + assert (iter_index < len(model_devi_jobs)) + + iter_name = make_iter_name(iter_index) + work_path = os.path.join(iter_name, fp_name) + fp_tasks = glob.glob(os.path.join(work_path, 'task.*')) + fp_tasks.sort() + if len(fp_tasks) == 0 : + return + + system_index = [] + for ii in fp_tasks : + system_index.append(os.path.basename(ii).split('.')[1]) + system_index.sort() + set_tmp = set(system_index) + system_index = list(set_tmp) + system_index.sort() + + cwd = os.getcwd() + + tcount=0 + icount=0 + for ss in system_index : + sys_output = glob.glob(os.path.join(work_path, "task.%s.*/OUT.MLMD"%ss)) + sys_output.sort() + tcount += len(sys_output) + all_sys = None + for oo in sys_output : + _sys = dpdata.LabeledSystem(oo, type_map = jdata['type_map']) + if len(_sys) == 1: + if all_sys is None: + all_sys = _sys + else: + all_sys.append(_sys) + else: + icount+=1 + if all_sys is not None: + sys_data_path = os.path.join(work_path, 'data.%s'%ss) + all_sys.to_deepmd_raw(sys_data_path) + all_sys.to_deepmd_npy(sys_data_path, set_size = len(sys_output)) + dlog.info("failed frame number: %s "%icount) + dlog.info("total frame number: %s "%tcount) + reff=icount/tcount + dlog.info('ratio of failed frame: {:.2%}'.format(reff)) + + if reff>ratio_failed: + raise RuntimeError("find too many unsuccessfully terminated jobs") + def post_fp (iter_index, jdata) : fp_style = jdata['fp_style'] @@ -1822,6 +1985,8 @@ def post_fp (iter_index, post_fp_gaussian(iter_index, jdata) elif fp_style == 'cp2k' : post_fp_cp2k(iter_index, jdata) + elif fp_style == 'pwmat' : + post_fp_pwmat(iter_index, jdata) else : raise RuntimeError ("unsupported fp style") # clean traj diff --git a/examples/run/dp-lammps-pwmat/machine-slurm-pwmat-single.json b/examples/run/dp-lammps-pwmat/machine-slurm-pwmat-single.json new file mode 100644 index 000000000..9138f0cbf --- /dev/null +++ b/examples/run/dp-lammps-pwmat/machine-slurm-pwmat-single.json @@ -0,0 +1,80 @@ +{ + "train": [ + { + "machine": { + "machine_type": "slurm", + "hostname": "mstation", + "port": 22, + "username": "test", + "password": "PWmat2019", + "work_path": "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/work_train" + }, + "resources": { + "numb_node": 1, + "numb_gpu": 4, + "task_per_node": 4, + "partition": "control", + "exclude_list": [], + "source_list": [ + "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/train.env" + ], + "module_list": [], + "time_limit": "23:0:0" + }, + "deepmd_path" : "/home/test/anaconda2/envs/python3/" + } + ], + "model_devi": [ + { + "machine": { + "machine_type": "slurm", + "hostname": "mstation", + "port": 22, + "username": "test", + "password": "PWmat2019", + "work_path": "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/work_model" + }, + "resources": { + "numb_node": 1, + "numb_gpu": 4, + "task_per_node": 4, + "partition": "control", + "exclude_list": [], + "source_list": [ + "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/train.env" + ], + "module_list": [], + "time_limit": "23:0:0" + }, + "command": "srun --mpi=pmi2 lmp_mpi", + "group_size": 10 + } + ], + "fp": [ + { + "machine": { + "machine_type": "slurm", + "hostname": "mstation", + "port": 22, + "username": "test", + "password": "PWmat2019", + "work_path": "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/work_fp" + }, + "resources": { + "task_per_node": 4, + "numb_gpu": 4, + "exclude_list": [], + "with_mpi": false, + "source_list": [], + "module_list": [ + "cuda/8.0" + ], + "time_limit": "120:0:0", + "partition": "control", + "_comment": "that's All" + }, + "command": "mpirun -np 4 PWmat", + "group_size": 5 + } + ] +} diff --git a/examples/run/dp-lammps-pwmat/param_CH4.json b/examples/run/dp-lammps-pwmat/param_CH4.json new file mode 100644 index 000000000..cda28a5ec --- /dev/null +++ b/examples/run/dp-lammps-pwmat/param_CH4.json @@ -0,0 +1,99 @@ +{ + "type_map": ["H","C"], + "mass_map": [1, 12], + + "init_data_prefix": "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/", + + "init_data_sys": [ + "ch4/00.data" + ], + "init_batch_size": [ + 8 + ], + "sys_configs_prefix": + "/home/test/software/dpgen/examples/run/dp-lammps-pwmat/", + "sys_configs": [ + ["/home/test/software/dpgen/examples/run/dp-lammps-pwmat/scale-1.000/00000*/POSCAR"], + ["/home/test/software/dpgen/examples/run/dp-lammps-pwmat/scale-1.000/00001*/POSCAR"] + ], + + "sys_batch_size": [ + 8, 8, 8, 8 + ], + + + "_comment": " 00.train ", + "numb_models": 4, + "default_training_param" : { + "_comment": " model parameters", + "use_smooth": true, + "sel_a": [16,4], + "rcut_smth": 0.50, + "rcut": 5, + "filter_neuron": [10, 20, 40], + "filter_resnet_dt": false, + "n_axis_neuron": 12, + "n_neuron": [120,120,120], + "resnet_dt": true, + "coord_norm": true, + "type_fitting_net": false, + + "_comment": " traing controls", + "systems": ["./00.data/"], + "set_prefix": "set", + "stop_batch": 2000, + "batch_size": 1, + "start_lr": 0.001, + "decay_steps": 100, + "decay_rate": 0.95, + "seed": 0, + + "start_pref_e": 0.02, + "limit_pref_e": 2, + "start_pref_f": 1000, + "limit_pref_f": 1, + "start_pref_v": 0.0, + "limit_pref_v": 0.0, + + "_comment": " display and restart", + "_comment": " frequencies counted in batch", + "disp_file": "lcurve.out", + "disp_freq": 1000, + "numb_test": 4, + "save_freq": 1000, + "save_ckpt": "model.ckpt", + "load_ckpt": "model.ckpt", + "disp_training": true, + "time_training": true, + "profiling": false, + "profiling_file": "timeline.json", + + "_comment": "that's all" + }, + + "_comment": " 01.model_devi ", + "_comment": "model_devi_skip: the first x of the recorded frames", + "model_devi_dt": 0.002, + "model_devi_skip": 0, + "model_devi_f_trust_lo": 0.05, + "model_devi_f_trust_hi": 0.15, + "model_devi_e_trust_lo": 1e10, + "model_devi_e_trust_hi": 1e10, + "model_devi_clean_traj": true, + "model_devi_jobs": [ + {"sys_idx": [0], + "temps": [ 300], "press": [0.0], "trj_freq": 10, "nsteps": 300, "ensemble": "nvt", "_idx": "00"}, + {"sys_idx": [1], + "temps": [ 300], "press": [0.0], "trj_freq": 10, "nsteps": 3000, "ensemble": "nvt", "_idx": "01"} + ], + + "_comment": " 02.fp ", + "fp_style": "pwmat", + "shuffle_poscar": false, + "fp_task_max": 20, + "fp_task_min": 8, + "fp_pp_path": ".", + "fp_pp_files": ["C.SG15.PBE.UPF", "H.SG15.PBE.UPF"], + "fp_incar" : "etot.input", + "_comment": " that's all " +} diff --git a/tests/generator/C.SG15.PBE.UPF b/tests/generator/C.SG15.PBE.UPF new file mode 100644 index 000000000..e69de29bb diff --git a/tests/generator/H.SG15.PBE.UPF b/tests/generator/H.SG15.PBE.UPF new file mode 100644 index 000000000..e69de29bb diff --git a/tests/generator/N.SG15.PBE.UPF b/tests/generator/N.SG15.PBE.UPF new file mode 100644 index 000000000..e69de29bb diff --git a/tests/generator/context.py b/tests/generator/context.py index 0668e7fe2..d79bbfeb4 100644 --- a/tests/generator/context.py +++ b/tests/generator/context.py @@ -18,6 +18,7 @@ machine_file = 'machine-local.json' machine_file_v1 = 'machine-local-v1.json' param_diy_file = 'param-mg-vasp-diy.json' +param_pwmat_file = 'param-pyridine-pwmat.json' def my_file_cmp(test, f0, f1): with open(f0) as fp0 : diff --git a/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/OUT.MLMD b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/OUT.MLMD new file mode 100644 index 000000000..7477360a4 --- /dev/null +++ b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/OUT.MLMD @@ -0,0 +1,20 @@ + 6 atoms,Iteration (fs) = -0.1000000000E+01, Etot,Ep,Ek (eV) = -0.8463607047E+03 -0.8463607047E+03 0.0000000000E+00, SCF = 36 + Lattice vector (Angstrom) + 0.1080440044E+02 0.0000000000E+00 0.0000000000E+00 + 0.0000000000E+00 0.1049750233E+02 0.0000000000E+00 + 0.0000000000E+00 0.0000000000E+00 0.1095603371E+02 + Position (normalized), move_x, move_y, move_z + 6 0.095086720000000 0.162577930000000 0.146264240000000 1 1 1 + 6 0.366858130000000 0.378822980000000 0.293448980000000 1 1 1 + 1 0.404086650000000 0.560402750000000 0.498746690000000 1 1 1 + 1 0.141461450000000 0.215485070000000 0.346088290000000 1 1 1 + 7 0.626253130000000 0.697621110000000 0.674972590000000 1 1 1 + 7 0.745915950000000 0.874152660000000 0.525583090000000 1 1 1 + Force (eV/Angstrom) + 6 0.000000000000000 0.000000000000000 0.000000000000000 + 6 0.000000000000000 0.000000000000000 0.000000000000000 + 1 0.000000000000000 0.000000000000000 0.000000000000000 + 1 0.000000000000000 0.000000000000000 0.000000000000000 + 7 0.000000000000000 0.000000000000000 0.000000000000000 + 7 0.000000000000000 0.000000000000000 0.000000000000000 + ----------------------------------------------END diff --git a/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/etot.input b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/etot.input new file mode 100644 index 000000000..0e919853e --- /dev/null +++ b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/etot.input @@ -0,0 +1,17 @@ +4 1 +JOB = SCF +IN.ATOM = atom.config +IN.PSP1 = C.SG15.PBE.UPF +IN.PSP2 = H.SG15.PBE.UPF +IN.PSP3 = N.SG15.PBE.UPF +ECUT = 50 +E_ERROR = 1.0E-4 +RHO_ERROR = 1.0E-4 +SCF_ITER0_1 = 6 4 3 0.0000 0.025 2 +SCF_ITER0_2 = 94 4 3 1.0000 0.025 2 +XCFUNCTIONAL = PBE +CONVERGENCE = EASY +MP_N123 = 2 2 2 0 0 0 2 +OUT.RHO = F +OUT.WG = F +OUT.MLMD = T diff --git a/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/info b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/info new file mode 100644 index 000000000..fe7207fc2 --- /dev/null +++ b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/info @@ -0,0 +1 @@ +/home/linfengz/TIG/deepgen/pyridine/00/iter.000022/02.fp/task.006.000076/input diff --git a/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/poscar2config.x b/tests/generator/out_data_post_fp_pwmat/02.fp/task.000.000000/poscar2config.x new file mode 100755 index 0000000000000000000000000000000000000000..868a02263c5ddcacb7a8ab956fc23182975c5570 GIT binary patch literal 937024 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 z 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

ACZ`sg02Qx3WsOkism1l7ssyY!f#j?!<0+?tW4; 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

`+S{3=d@OTk+TC_`s1 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%0Df1p6JN`#Ve!&eO+i{nvi_CeIscAd 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


L31q0@~ zNi!A*H3f}r76>T8$85xzybK|xxG=s~79N%XVrAm&`38u3WoQ_EX^HxhP1xx%-!-$t zy@{d2KqTn%5n9Vkp0>^pKgb{!M(<5b9437}0)1X|&ukIwOwopEXr0*`F2&xE?qbFWtuRO#M~ujT;TY)x;u1gQc>ybN2JC-!j!R*h>#Q>zOo@;G1E=AU9+t2nz$Ae z`se#s=%8~dVGtVqdOe`TFwf^EpwJKC+v8)<#;zF<2~%0ejsjpEERR|EDz&Og9KpZSOby*_Sdp<8Y^w_=or7U=(m56_nW*CC(JsR6ws=N_ zKd;p!&kZoIpVQv#7%>5*YsYNxW25Qy075p z?@e{9qp5?@jLbU_Z^RsWEti`PUm5ed#?$}W$mi0BEfib(Lg(2}OBU2}Zk*G&^hBv~ z9J`zvw@H`Wp~tdm`K5h9TCBZkgh+d1pu{cODzzW&Eq#q5GS!`R2;=>=w_%7_duy1W zy{Dqp(B4y|%>NJ5-W2CUXm9E4`=lMw-YO?5#Fs`l?TuF~3mZ6x{yVhy6lrgKdNog% zMaRz3-p_E(gxj>aOqJ?a+QR)KeYCkyr{?S{Q;DT6L`rjZI$xU@zy5Qi(4V$IPbTb! z{Y{^z6GK1i^!dnMI(ytT@={Ao{H*l($Uge~8s_Kd^Y$a@Y1a8v8<<=;`W z4;s=}vs=|Si+iz;W>48#0EBtSC$|=G0nKhH#1r)o(ClB2HM?F)h2LE-t!`EJ@6_r_ zr&z0>Mym@)0Tm~!3h^Y!-7RV`v+L_px_4(CYwo4fdO<qF%)lzWc5!@4w<@ zcN%P#x~hbjNh`-ggIW--S4bK~V%MBY+)b~gStvN$;`+U&QoBeThbT6FfN1o65{x0@#wKB@B1+Q7C@Z#+~JV>r~n8MT;5!o-@88C-Fj zhUMS~ZUcrbf{yIvX`S-X(oH{idq$Po1LEr7f?Vm%evylLS@N(-EK%nH&0?4U_sJv5 zG8N~S1|EPJzC!7cQQJV+Vr#s|<|+XSfL|yDcuWseu45GRlwcLb*>FHkU>7VvFu3AV ztP=-=ANUk8dF{mQzTQ)S+Q^}+PXVT1uQpT()4AX?-NE2i(}uR5%|)s$3S(!)>#33& zuV#bnoKI3zdI<9%T2GR>Y^(Epi8MdB+zf2q5B=4Hk}R8rvVyqCpS6-JmnscD=8YK0fX{m)*_)Mik`s;I z%Uh&t?xM#nEqpXBt>rGQv&=LmkY1J2+u+X<=ozH+%9Y*#(mUIeo_jZclxHN+EFny+ zmvJ8Fffr1&**%`oa44vX>7`@Zw0a2tk}gN}q?&o;4q5o7=&E=j4tTx>75~oN#R}hO zI6dAO&AO=PC(bfY{FFcVRm5Xwwfcj-G^<^bEL?YD`>f?nyw==Z_5U&V_VHC!SN{J^ zZX(eHPef2eRFtcY1~m#;^n&E#y>Kty3s^z1MQcmT*wI=C60jYVgqv87m!r;n?Tj-;lS5!<_Qn_#uy_ob25hH^K-$sEd~}=B@`2 z7cw3Y!F6!}#C?TU_4{~>MOrMR8VA|<O64$fSIuIL$(a;N_% zR*C6DMVcp1l2ERE^=kvraZ;Y(2~r=(9gx>UzEP)+=lnJQjmsBF*W~mizkFm9eq$Pr z@sX5KDuu{Z;ign;0b%BPpFe%hboQI?20JzPm!ByRW~Ne z-LNQ!kgIV1G(3@hy*%PXLwJC?#rk1JRXT%CpB4;QzaLo0ghDj~AA<*|7z&DSb$k~u zb+xy5%t_S0UdNk`Q;>H|>9{pzA5Elp3`>@NRC^;O4(qrMA9l5)v5l??M_w>P!uSrs z0^ml616X9KhRH{IxN53%&&BIHN|n-4Opx)KQqOzN*O5#K3I-qS1L}|flC;@Ed6Ga) zDJ9V%Xexm5RjHm_v>U>?|BDMse;jc5n807w;GPjl)PE*tPd=H)HI7r95eO=(tNh8_ zfgSPtac6AE-kDwWHDCfLF(gUKV~>laZ2u~=BC0~w@1IQ>le{uUHuxs)h%5SGg6qI@R?a z=8vw0T>m4H>y!smZ=P^4#2?7~N`FjxhH|aEDxm{kSYtL2+y)nZ$8K>Z=XLadJ)-E@ z9Qto~RW|4>La!Qvif%G}xM1$bcynzysy9Y4?$X*G#kiGz(Re9cx=C3W%1!RanDL<+ z&ppXYNaLG2`2LImF2<=`bE}%=M)Zx`du?hOM%xVyt8saH_Zt7PR*&=eao=9PfydGO zxR2-SaVS49FS((wk<94Hf+hDQx!Lp|UY4&;|KUyied2&UH{ui8v^#gkKKJnkyxQW`A4q&1-*1Bv0(57giZaeBd3pUCnDRu;AJHJ z*)Xf&Z-31oJ%Qy2{jAJ`jWzU)`CsMeoclJP*D`Q#;FotQN z7|ho)ySSfyqOtB?R!0NX@p9g1l%6qH*nm>X_;E;qMYyyW}GSza6!rUHYO#XBjL+}~ z8EtCbc3K2C){*A+l1S-W8`~qL>k{^6e$wr10+)l%ftrD46Bv0(yFeJyq!rsopkcgJ zl|)`L+rTrIX&VSZ@*0}7qWj{;$eSWNDYxC~l1gSPhSIgRzYmBNmj_0Mson#>hOV z@trLJE;Z+JWwl^deq)l5Y|!~{6$Wa{;AuPh@w6|-ltK8;AuMNZ!NUU@Hq@NH$Z|DtrtQ9UHCztYGO21ge>w&AZP_RK8)KW#^t(JsY>BTaM=BcvsC~X)`ULHTm2z zwb6anXyx|sV;V1^vDdzt4EdG*FrS0z!S$}kU|5fG`|!qWy6KP|EUVg5IFPq#ri3`;L0~5%TD79gND`iS!ypP zHnDZN3f5Zn^K^}9d89Rf*dgCh=09m&HXpIic-q|8@e8`VAOLh!bgncj_b^RLH$u~$ zc&Kt`!-K^s`@N20ro)5Nk~X{Hz1-GZkVqfB(|Y8urJn@!mhx=%J8a0DItQJL@Ng)+OcvC6r-S^6PZM8pPSpuC31y5Z+fsi z6-08_%&lDAk(jJJ%(v<0a&e-aZ{u`1d8*M8G-Hz1k3}MC7zxr*OVi7YP>^JKV>8W{7QPi0- zF}XDI5~lT7Z+o+B5zjXn>uG`le#O>{zIsrtp`* z;{O}CCGkLy3#Y9}UD(~eLik=zju#<=GrPXH6@A+f`F~Fe6LBO2?N2#Rs;I>Jqp0$( zpXxkSWFDK37AAvFxpO1DHEE@`(qKG+1kJX-VI?qUEgD11uxCE1fy6UMB6GHJw>~{I z*e(mO})t!#oxWdmv?Uod?r#m;t(Pnb9=$7=X0h3Y&_-1K)U8{yn3l0^+> zn6)NWs5TD)4VZ*O?XbP9j>aoEEQTTjXn$40H)O5iF<}#u`)JhcFQ)5|_F=*zpnFBy zhxQc*gG2+0TN7GwV^A~fMyKeaBs)ljB90a#OI+EN2=AX_H+(V;B0G1797nFMnKYGb zi438(K8<^^<#7rj*7}33??gAChP#p z(nS3V(Jo8~t;OE}^WzX)fCUoBh)=ysX8yVa+VA>3ccCMf=ccDD;08LpSC7SO2ue&v z_1vG5Mrdd(NR2q(Y{fKUR;*RBIhYY7VGttLMs69&KY}KPKE+9^5*4!^*<;>d?58Gq zqv1i=2e>~tbfH3f@G4MI8wZr7NFi&7#KT!D-Ibt*{L085R4q-}^%zY_5y%Et56!#B zDu@eiGSk}&;M-1n;1nVeRdt*mb85fL-K~uqkj3qZF1b=x!i}pJ{fqX~TCb%Ps*>f6 zY2Kd(M?5_?6sWy09;R;#ieXne21%kK8MA0N5~TOR+E>5lWVh zz?u-b<~?T^xn?rFMRHBOwcknFWavB8ac7|M2I>#F<{c6g+}efLsrHh2aMAQvB1zfU zmoD98y2w1`ZAH>Pj5mRxLO6)1x{_(9$c@$tfqIL%I3D(1Uze`&mtm~ZUNDPZ%Ao_&rRTm z1tD%FU$^(7{6o{+8}z#_MViLCXWJsC?rs~^HZ^(pWa%?{hg$vZfMR|rMC@CZ;gQ2NEI#K%+wo~;C53@kEZbUB-GQN(;e4q zu-jB2TSIOK>y{bgQ7O&6VI*l!A(AA{s@y!gBq?eiW35tWp!9}G@t3X#jI=|mn6_kQ zH6v5!J2X?f8ZO%7h;!1rloL9;n^U_+5WiowRJyzyNWt>hxy8N;5}?n5Pn$v-4_UEK zwTU9B<4l8Pw@y@{?&tPZbjoV{lzLNkGYWtgr5wVGgLsDinp1)_9BIclgbvp!pR zXJN+0TS()qnPyx(5}vzx94Bk)=&p5XL)m?BOuF9bU8i~1YOe8gj_qTjKL78mR|`+& z<~CM_2m8IKk2?muSk1*01gCrkzISuL(d|n8XUc-Blc*Fa|3eKI4hjHbfwW8pYS?Ok zKY{_cA{e;*k$NAfnW&d$%v{(@w=1R&Uf9-PsW=sbPh&=l0t6(+4}WnRJ4nH0a_RDp zN@TqrV$1g^V`$1@EutJ&*ec0Qf^JD8-|ZR5w}G}|;t2XKvxHE<5FvLo%~^B3EYZFO z|9aZ-D@cl*7JiNoL)u{mJpEa<)wl@u*R9T(b|nhVI~K%*a!&E~YHi5Qd+!D=ncuB* z&u1S(5;W`PJ5-5`;H<`jH&Crini`BpF1o{^_(M2$5p-2cz*^Epo`j8OJWfmR48~`_ zLu`eW+O`rmhFxFMx%|sKS$;*c1% zDYjLFx7#`l?|iU+oT}S&7kg0SeT;J$xa&b^K)^jV1vmk-iB!s4&i4=jc2m1HtNh(R zHDONv{bqtV+BZkOORO*S5)xXzr#$9H-@%?^ru+Wf4-?@7nCTKz{>PsvgRbU1ak;_= zbe4~3_qSmF1+!vKtKpx9liv1L0N9B^n*8b=1#b9iM9_>$A}$dXthaqw zlI@M`#Dj`CoD8#-0Y1(^%&XU#EgNSqI9$)c`l+xv> zoj+tf^aqC4haWIMs*#e`cpvc8v#QXulo5K?IQZ}tUG|%&_aZU+&XX^uf&y-O~?QcTI+!mj2u+$xp6c(`92XLfiCNl2C>`jE$~=IF_zlwt$BvJl_C=F}`crAK&c4tgA4uB!@S*<9g+fMbrLdHB$eMXmtGsL6%!bBa6@Slm+yYH3*Je+(_h?~!amY9 zAs8sK?ge@G(l8yMcj)PwFapSxt1eH#5~y?65RcHJq{{8TTut%nLg3lhu7u;4idRf7 zPO8J1bTbojL?vdT-Mh*`mY$o06o#1~3YVHJ#kdw3*P(7+v%^bq-ReGy!7X$T`T;|2 zNa1-OMS}svTd?O|PQ7|yoc34}foqnQlcFW#KEo{`oipiush)OGYIE*PD*f+%EO)u4 zQs?p?OOd^OMDGF_ZC1N zT&H#Le_}3h)CI+7jitM-$=M$!3Fl-^^l;+vjU<6Q4C$|7^T-iMLQX1HJ4?wJx2{#x z<+O{vEu-Zq$+4%!Z zRCdz})vnKA3Y~jdrav6H+bk>lN%IGp6~W|5Wp2Wc?hEF-R3;)zON-Smx?>LkoU@p<_aZo&m!QmuRj(4(U{b6k+-v>4?DI-nAe5hW-TAd>Bi8xsql6Vv!sI1G6EY! zbLBmQYi!X0wT3of!yv8VYf9e2VQd%#CW7!N89YqdF8+VS{`a5`li8bH=X9&$95AD~ z>b%QfAWs>HlelfNc29=0DQDpz&IA`7=bY|{T8BHtR#&75(!;{Rq!}MK)Uc`BIbA9< zFy}j`!$rgNJwzCIO{c>JXWw`3$8fEk$;#K0m8^B{6xCMIfF8EPfSy*a6XUwb>nk)XmgFm~3uG;ktrglB6d_9-@&(w1Qf^b%l1}lsBCw=Okw@C$y z3MHw|j2)6<$iiLjpr-7xR|CwZR4`g73*8}{M(X``;nm_lnlK%N|M*D!hYKAX&V}GV z`hqA%gp*i!&HPXbC&HC`rvw{zEuqc2iew;U-BlvkWM%|9iu5Z7WtWUZyUl4fh6y)6 zV=t5IDfU>-^e>`6M4 zVMvfMJKsdL><%M4`q@~)K{B^v0D1$5bKb=V2aL%IS$!CPmp9>t7gJ3WT-s$!Oe2g5 z?`|D9>5bS&t*)K;STq74$;%!t(httUc~O^qbZrEi_}BL zMbXp6m$0rk?}07ZgX_pnbnW+a!j?cecT=G`m4lj8-ck5!ihWNIwgfVbTN>95uaDj? zJmTpG1TZ48L_5V#ijPDF1)I`oEq>o%BpCJhQ3HF!raXPrfFrmw9^NT7#d~(1KH4SN z!=~uH{^HI8EGQnpC+`rvEPI2Dm*ugS(pG&Ci_tB&>r6%MtiN*T@@lAMO%|^ zFw1Gj&E0ijOfyk^bgS-DvO%Bs3G&hrN?I>}?Ua|)76hc8V_v^xaqXI?bj|P&5*ix* z0T7s_p8b#U$x8=TdOmrN7@xeY^2zIcL?J%Yp^tJKJ0;5T>50ps9M7cZeuf5@7>i&m z+t^c{6JNvr{hct*aj$Oj`5GO?rSv-~RdkkFFGhcg^VBW@S4xwaQUsI53>du-n_<7k zDHl$)hgVvU2J{~PI|y~fudEW&5{aU$=}5gSv%<$Uppj{GF-KeCv-8?)cykg&*L z1-YqBr=*YaW_x0G&KMq>6VwW|^U<(W5O3AEb3pmPsVO#AsxC}3m+l%&`-~J;{-(l5 zOq(bW7m-&Q^(;I~s|@z>eDf(B7Dpi9O~MM&wX2*@dzM6Z8Il8+?Zgnk_ih6BNkMRA z>8{6=(YWAUx@!sd4BIZVZa|?809bMaX>;sS5P#T(ILR8F`)BxWC6}(fn!(-6z@bRT)HIi4%}bI&(N2#?BW9ydcI{ zgHLWlI5+f($(s3|A_07&&xK?6A$(#5G9&Ls19&<#6KEwfUx$l*Ar#j=12~Oq&*-|i z<)e{@azb#SYWz43$4FeMCMAv1?@WoF2VS9#7a%|gf;1iLqO}BDk8yBb!U^lwE&Nx2 zfHmL(Ob+e?-#3TnK!KBa;7vM3S0%%%%(ff8WHZMbu(8zUzZ?C;4OE-j@W87G0hEsS z+l_#g6;PCj zae*GMzjDCqMhHlSKj`6c#BXVyCE0QYv#H>x>}!)c$bn~*;2tCbVyy`f@CwotPRhYd zQfH3`UXc)uU83?RqBFr&PpQgX;t{?828ueG!8(p7O4j(7O6#rl&=4~C)C-UE2GNyi862CQ4Hom<TtHf_4crfr}?mrax4#$(f_7nT_M zj6YpOq5hcvneoOkbBJjG>5G19!h=9qt;GrgE2I_kpvax5;9c%*?|3#lE(3wT)bI!M z7O*y$XO8miI+1&;h4~quuN~euM8-H=vnfP|5WrO7)LbDh4huPtQ_C6kpV|5|jq5y? z&O;ZXEZpT+E2Dw0!N`H2GeJ-v?-Yn*oQrqwi}lev`T16D9hKN`K6+&^;4*gB%Qv5r z$lP=7m{exgwN*ayQNH2sBFCD<#jQWg{^gj?8Aj^5@L2rDA(@)`r0wKv8mCCk#Uy#i zg0I1x^}D*q&i3ZZkpQ7xxxsiQ#s|`g9*HTLNusIz7+0(D>`I=Fm#8g7<|VF3uEn0K z15Rjy%I?$8o%)%kpK2pT`v`4~we*4!6ykXwMv$1pZg(}#n|D;fu{on(zDl~4{@3ty zgZ1pJ$%QHBwgMau$HSW+45IVE3Gs-p#_U~@^bRyMyNeT*D-lMGor9nDA}rpuh#QVA zb59vo!$Zp?pem7;#&{oQ>#eG-hQFXE2^(>A3*Lu~D`MxoZ(bSLA?SX^SVhO*unLQnNP^YEQu}B{U#NpY`ik zqb%7r=jo*lfyE{#Yq{3?mAw5>N}^N{OE;9WDkO|G-AeZ$x`^`<(xzbCQAaTq%i9q2!LV)9hpY-WYu_^*N-vLV%s@Y-Am60s%lE3E= zHr_?Cn%F8niKqxNBB*GF8W3b=ZGq?#qheIFNx2pNBfw}nL5s8v7nCVG^iJdj4_+%=6%v9dB!VmAtSL5O0E-&l1%R=G#3sF5Y}iQ9i) ztl@zZcxacEn*AXd;23f#4u zD&TnB`V*BKQHW(ieR}L`TCcu%J_fLnirb$nQ+PNY{eW%-DkanPo>bfmv=dk?b=`c@gOavG< z!Gx!C$_&y;Z)zL{5q?hMnC)!BJkNn@3|PNB7@)y6a@C;MCL9ug}V~rU54@m*}thD1qs+lHqzW%i?5NjAz{KK zO91-~Kq^_bT^O7&9ALkJ1=wz2fiPFFNZ6GM_?v&-M9iIpX^6fQO8GSFb7q=9twcjy zL2BRWG&!^MJq;f;A^U>61+P1 z_acX?B*SL0+_;)OPR%5>q;LN`w zE?T`b+#8`7fS=(P==CEBk$YLLAlY=289kSizQU{oE({4QhGa8~s`X~GM^gL9Cr8B` zC;bpnP7KK=+IG)Tan=nm_TX@hCtqZJmRap^9TmHhO<3kHqH;yTB$X@55SA2maya45RVoDesN(7q{;ih?WCNq}m8aC0d z91_cs?^GM!ZFViOMah z%1@1j@6&E=ut1OTBAzMb%!DG3sNMJ-9x1LtZPSXTwrH$vBX}RI)sz-inClCnVdjdr zWaR?0f1$6qK54EvvNh>09|`xcvzvQ(1;iBhu-$MEuk$4C;T6L@tZ})AqR-6O8Vvo+ zptNjEdT|eGjK@7FE$9;O~ABkhru%^c}6~BvwAidhF3g;wyaPhyM%M>*m_wF zyMbiP-eTiV2G6mWTe4JU%_bi`G#gsd!dYA0Xi4M@?lQs;hAE zjJ%*fdf#(i6o15*p>)HZYd}2M#R(HL1u^G3O`Drw^fFm<4Ip-DuXhVi^ZgNvpMTL` zu5NIqUxO~y=Q1XvLw$SxaGjE_JTf`haCEa8#&xEyc7w6uN@Yqqg8aya>nwu9`cO%v z#~2%~WQf)9&+ru+u;WES*O#wIhK$NI<;3k^x%5D3PYt6La15CMSUEV_kp z$8#2Yz9?sbFzyR-7CT*G+#b0LnkvRZI#bn)GQltwm@guW?$(*glxz2iI#W*=hX=>U zSD3Hud#*c9XL>weF^0}&#Zqh)7l!vLYq85xG$RmNVR#yJrLYvHK*hmC#weD;JcDH` zPGl+KjM$$s=@LsJzb=1{rEqKgb1VhQpHEn@0hUqhr0g90Hs=KmYhZsYQf0R7wt{A} zpT`ZMGyQbLD<~q+zkHN*hMo>B+s9(dH`@Djrox@Bydyb#1GEJ)_BvZ8F%w!2?vyk; z2>se;CQHh$#Z%$@ye5o-Y-0a2`w-@VBbzYs#U3-o*$t~9W~jW+TRgt0UBW=7YnS0Q zTuUo$a@*AvD0Y((cn~^?XMb(-*O%iL6D_?eXG&%$u7?%wZ5Znd{V}SYsJ%tK#K@Qw zc4!vM=`1 z{wT8FcNct!^nHAa*rH7%f4{j$M5Y=bU3ENTNyHIsZ)Nd_x=+@yx`TZ7VkI`hO1L}> zP*ll%SP2y`M0<>D4=bTI(;ySVK>g4PsET`7iJ4kkX~JFRibRg9VIh=@@}AUJuo6$0 zD`#{}zNbPW(}CDfSz1}mX9e?eBlbkk!c)Ro?>1j%2L4fx;N9HJrp@&lTrG3(4an6C#Ke!e>MfW_CdD3>CI6kTL{B z42G^_M%zjd925m)-g?enR@iZ0^r+?W< zAT}5YgqsXpTnU76i|FwLDd$FyQMfrTo2nJAdK<~Z3|I270h=nKv+i^y4Y&op)i~7@xzqSWDeI#8Diylf5ouF_J>b4Qt7?&rk z`HU4(dwUPJuyuP_@!r&tIS!5RAFETACrwO5pjalr(#D;dGYZ_DGiCrfEP`C(oz!gn z?Bf-kt&QOkMETdT*@IU|I^V}$>OML7;E(*SNQ2z7_fna-cX0yPN8{$<29FVFG8@Im zcTA)GqMyHTpLc0sMfOWXUyv(2Lwc0eV;R>k!U(7w4F( z%LpJ>fDw>f0rkdKI={W^#5UYx+jWO(Bgdtg^@yJwFPK(Mf;7b76; zhWbEA%u=5baCLkJ$mC7o%&uZkeTl-wy0_P#M53SqJlO)J_mU{A>6R!ci;*lSgCq)T#0@lY z;%u{O=a?u%CL1*mlR(~39eIx*y(GM&%F^hz(V%2624J0ec8`f^mECNfmu?b<`J*oP z_3mM4kB=w%1z80ZP|T;`TAPH=Nf8X#f={i6pMY{n`z_XY!+<3!k#!yABq<72=>;pF z3aR=OajrYxi)Z?AXjeWJ)I_8muZvme;vQogw>fZ&(oJxr+C$ML=KAAM9ao`pz+5>a zY|=S*WSWCpAzcBII@gCK@O2X(_t8zDf!jjJ6j!?7p^U*(PrA@oH=)Kb^`x6nJOi9r z`ur=FYzYqS*ykJ5D6(j0fK8gHhnj3S0*)>rTzJO_7w|{EHIbRg8unfy{2qnM9e>F5 zvS6~&OsHqp;xXNt32eqaa8%07)P(>XfPnzdf}ivZ1`<^3Lc$%WgnfZ=mAoc}z2o8- znhEg~27%xi%=ABDmiQYvcl7-^LCv2CVF*M{*O}#o$U8aWCK~{eB>HW?=Yvcj23ju+-<4ZTd-5$P)sVEl_cOzX1`koV?VvZ9P7%OV_is7Jg*fT>XKPbm$%RZJfOkZ{^&lPjeB&pJgcCVla2pX z01LBNz3==$_Ltsx*ccFO0A)B~j4QQ-Jfye-W#Sw^{)t+L??$WPL$ECVM9lf-c+_1m zQjf0?=-3TwHoqG|t&^}hdr4VZ7zL!pKfebnHfcVU#lR zF0iqk#wSbnCADeKAl2KrCmH_Z+*euDgOfQLSdysxfIfQcY0@5v?RKKR$#s45p0^1A zA}-c(*ma^@vF_8HAB^Dexi6{n=_6l~Wnw_WRBOr_0S)pnjK@_$jC=kV#{B{h)+0d{ zj!N&L;gImx>jH)5n04!Lh_Ft^Hxdl)xP zP0W53{zJM)dwEQOYB{ZO)Z52C^cp!OPbh&xAqzKOr!tl3*5b~rU$Xdbc~OPM**GNTLHCYF%?HCjq$^(9ipF6`+!=c=NE0S{E|RE)zYIWcvDY<)z)}z% z4yy|6D0W|;#L$kY^4R+)OFQDu(D5o}U)(9WANzkps&BNTLl8AGC0q6-gB>V4ti`iE z1)17=gEMP<0V7vrfW(4E1Hw^si8{UUO^6!0q+ha0JIHZebZ6adnCc8Xmo?yephAQY zGnSmx;&3QOz-B??rr1xLMCh1~=MXrAb@Fvia2e?M69V7E-qb*;pyO0xW(;k@j4j_1 zaqh0FHV$CGpYMDn7|_v$k%qc^Y&O|7!W||4hh)s>MZRZ@Po%dsCCmZ_@aqVe4G&wM zti;}?J!XFx#nmu-`wEaq3y(p@jUVAZgl5*9pD=8wzB80Q72fYMrg$|t(19eJGYqbc z6LUXeC5Sz}y#PX5MQKi1%Ot1h+=3CYY7OpmTg;sV$yx`q?5zLoLc zEl*UwDrYL#m{nVg-xCUV*FMFWb$tO_t^(_air|R7(kV3D9IZu&Qn|_RLd@P2-}n)y zn$gd2t_#1ZI7=z89=V#*j3HIbSr`I;6(D$I6GwK&<|70M#--aszci@n!jVO5lL5bn z&419$NoMeU1Mj`Viv5uog{V^qQ|PxIW+Au>djMZc_V;+a*pb9*Nrn(TnC$|v?NXRY z1N}kIN4Q)8zk5F7SW4-uh>`Y12p{|iy-@(+;08C8xJEb{G@PtZHj-b9A}SJo(@OtE z5x|fhzVDhXP>1r^;Ffoc+={L4D8w!S21Q8VkNPBMU|Te} zA>x!h-0d=l;2AZ@?+pdITQ{!H3@hD_hZpPUL$35i7L)YR@`d4)-98d$0^cissH>2* zvwhz|4jC0)ft4^0-1p7hp9-(A(sO>3&)=}Br~WXT(^Ak#43w%H2mYm{YzG=S_|Dx`~FCH)4cxqmR0?#I84C9+=Du8l3$stp#(gye(+!Q)Rg-=?q*?p$I!lLCL~uy zCDAa0o(v6+SV>Nhzv?+IgZGi|GI3`KQrC83W-{NfxymczM z@up`y9mV^pQ??S+(WLa;7+DP~EE0pr?rUv7uo@8b0~ji6#= zK6kV6q}yPOWOLvBSh1yDmw(+5>FX5j1}BMEId||*bdctHAvz9-xoNmBrk~Y7KMID# z&DHaR#3n(khh*v%Ck|3>5TAMuZlD5Kk6Nkqxwr6)DF?H+d`ee5mOA$>@ZG#v$+dDbYDN(?sos#_wsq)1}Jw7@4is7T;90%bv1Um&>L7;ImLGs zd1l9$AirN6FqJ}lYj4D{BF%Mp!bk2(fHFV<|_!{%z zzh@vZ4<34R%K4_OgA?Jm7h)Z}i4hz%JErC1Lq>2)=t}V+ZM42eX=?YUO-r zf2@NMZMaVUWFDN`jvHOtH|w)PD(XMuNMRs%72b)l4n=Mdx4Sq_fO9wux_$@_N3561 z#p?bw_r@&T?66ixt*LKD@~h#3YrYkAzJkHzks?=7n?5qkYEWo}^pT31g}h_YJ#y;& z3hrL!?wtAOr;mKS<{TdX**se5BbU|=JJeiQb1o#RW3cYdNUtt18%3gOsruT7b&7k? z5s?PoNfJTXlTpAP!UddLPe!TA-gCvy!AHR(BHP)7c6`E zDP=HCC=2lum1nr()4YZZq#;T!S8{sA;M|4q6ujSA<~HYq_cyVHGmW~-Z`5z*Jm!^O z6gp@*@p6O-*yGE7s2o{otZG)|(Xm+*OPdY;4Gcu!oXoC$Y$o->aA=M)buqMQZ=J`*x77^;^3rg-LRQ z5+_*YAskLN4_}u)!aBE$SM^6!sMYWUz;E7J0^3?L$C^&lr>~r2-SQ6q+vZpmmp{dn zSa!=G>9s0G{~HgT+miO0MEzDi4m4*^$K|NZt}5Hyf0%@q6#qLDuC!&2buWSrR?B&v zqxg8x5BQjC@nWtAtXZ$naZb$?|J-fgZDR`#ShwM`FGvDC87w?4B%TJC^ z50RhT=jqxPCZzVi-+ens*KQciQ(_N{{;T-334X2{pWfo(3&8B;`1J35EPVP8uD$SS zquah7d}=8*4gR0Tr@K%8^Y}DLP3wWxpTnnRV174j{=mZ*fXx@grZ6K_5|iZ{7rX>+pj?NsSYg>KzN zksGkuW!Eddh>kwsha54Z5J1q>wg6hjLV+^gc(Ue!W+$Kc=dvRQX_tR znyK{i$KC72OWkXK?|ONIm%fl|?sh1>3W>M2jM5aSTyH({8-X8Cu;Ok62@f)6jE>nw zlcf&SJ)!@6;UbVs(^MV<(iFT{=!IYDMc<@dg9du-DC0_&cf4_HakDjNi!RR#YnIAY z2=A9arv_EBgxJ8L6_Ust1(=QUt{(3hD83(dhl-EM7{o3UUTjLQ$O2aGABdB zm}Joqd`vm_+3$0#&~RVn`sAS51YPSzFAK zdL*#$Zt$tuYxrV%$hWWucQki=)ex)Z_A;!5*^Wwe0QuqLg=nh8ka;7}o{z6@$K~;Y z-vf#!x1^4+3-CMuA1A2S{0BJfzdYZ~^xXF~!s7Ov8u6R#K(n{PSsIX(vnE#@&+k?A zNVpjC2Uo{mHuRH)Uyn^b=peU)36HgZyzdlb-}w0OeevNKQm(NYm#WtA;RRML?A%_l zM?{XHF5k$$>IS6s^I~#S9(;8Y%qj|*s^g`&Mifh7 z_zJ^6_bzkaB9(c8f__jRk_6LKG1EQunmpDIe`K21w&43Z@X_#7^VC%{opAIu*VF8| zS6B@a^QrnRU=IJw-|qUS58CHk94M;&bj#aYW`#;jQ{B>g0n@$hRkNDog5VB@yt~mR zA1HZTJN?f|=4E$oZxk1X%2kxx!#mo@FttnLN)Rsb@r#&BrU189ZZ=iWGt7}&Z;sdR zY7@mowW=UBQfQW&>(Y=Yscf8vnd{O}1=pzc`^}S`qMrZ``1td#(4}&-PL%wbw~_pI z>(cx8TbJIq)w=Yq_12|#Hd~j@{Ec<#wEt~g8vh&X(&~D-vLs5VQ#|Htp+I`vU-8Sy zo1t9@N;2aX9t+8VfYq>sa?MsViUF(rcJ}63oov#V<7eO*s)4r$i5o)g+>`V>E>$MU z4UMhs^)k|ox|r)rvMq~aWooX7N*tW*lb-EAlTtIq6ECG!|8=G+d(CYm*RK#*h>5nG zqsyPL6CZgf``TC43VaNW+xQuuqzOgScni~k9B1VV0@*FM=>00qujf@)HhF7j{w2Xk zN!dG89jdNztCTQq`>}$65n0EbQK6Ad*H&dBAAKz-Z{QgWgu zUe7e02hDP&1qprLlpWx9?d}S7jrVlx>q=@Z9>~Y~Yb`!-n|qi07m705yoBA=h&9nT zh-v!BDb^!WZsBud{($&w0$w-AE73?6jfe0&cmX0!>!Fpr=M_FQ5|vGHr%bP`rN35c zI$vTfmEnWFOK-Ih$(A2#F0dN6lGgb)GRpNiJkvPwl0^8hwe%HI5j{jJS7W`{59>v# zrV*17zjlBe)lQi+$sQOt_6Tir_n_2r%BCbbo7t)cYR)=neXnRD+`^)wY6R2xf^V6- zt79M%(GybR;kV{}6KU8jb0T&TvEeZBeQWOCj;Z!~kl`so_~omRz90% zb8;+7h@}VT#O$*BxtsoAA-B`-FP!_nvk*1!qm&hH@{##eMc_>(Yi$9gkqa05%Lvk}@uM@|`vlj{(lO-SDIM z;=P0?6sO2jMW0|3$NL~A)i1qud+WY9kMN1|*Ar8V1I)z+_;Xy1m~zO8I^0|HSN%DeV8?Whe{l zU%#~-qQY_X!Ogj14#@Uv-Wx>IhN0Ruk@O##OXtQj_y^gy+X|5O#am}wz zA1<|ic#oOdgGSOguw*hIV>Jty(0Y)k7tR;l`p>wT3xY(IiIy%@{n9{lH|Q5;vfP*{ zTc64ly_?-Ky;BmrNBBzF%8~V|YAg>!)$z<<-DO9b5JnB$Y~M62ZYSEZuMt*=8o#dm z$Sa%3%XZ?-yps9G--ZDyi9c#%Zrf-Qo@TbUcBYw;Mo@Ic&l zQ#rJ7ifdWv6{h&Sxen9v;%};1vj=WQ)LeE;{nkJoa+sVIEydmVXSr7LNIa~*=j4M@ z52`3P_mZ9-_12|Zk@w9L!C-$@qH6HRq9if}Hl5L#IVtUf*7N6Nwt6P$#7_?9A-SEe} z@`2(cKq$P^U!5_$!m1(F-R&!?lN1arwK7&=%Z!l@2wH{tNs^NBV&)YdZ=F=4GF&#j zyZqWydtSV_76^NFT~SSOs9mv+^FR%r17O_o2*!%$W$$yvcs|c}j*pji z)}Vg1+m<7r{%_Vl>5JBXzUn_m^$(y&wLOh!%o8mt4d`uuKvAkCVzkO({@6mO}kr^}6|K7pNC!_wAoS8*qhr zzWl^a&h695x!s-onh~p>&`F=3{rCF!KQewbe|r2DbdTRWw`&XB@tfD(wK`as{~wQE z?H3roo=z_4)5!(ho%|;lu9I}qA3r8pGGobP2b4%=ZY^hVt7-0+@}i zMkoYMznFjrFo0U0EZty5S1nJ5*DolaYheSu;MC5|T&x>bEnnWbnT5P~9eUoAbfwRH zLz5a|HT2fDKzGQ?0yCeCr~O64$5&QpFaAc?yryLS!hyizf*5wqC)_N{@65wi;Gz5|_4`JZ9Oq zVt9~_AeyV~$JYxNb_^v5b>eLJP%{i*ydr%W#K&KE0(HHJkcu1%>+>rz!M*+3&0B~N43VoLQyk8#!HeDzw=+LF7Hbi~=$3p|g0hX;1D&QPwM z?jSqH^ z*D*xYHXLN3;&!5vpK)-YY`xtLe1*`>z}2=7K02ng>n+RE}iAasf+x~V50gkb{`%Sz8MvCM`Mj8sROV(JOTu;KsuJL`3!c>Q zmV|LPFSn4X(sdYPfI7gRNX`*^Im7#b@EJ8qeRCya_TZ)I!_^NANpZGi<4z4zI{=H= zO=N6EvC?r`&toKgxS(bj)1u}a7f}xfYb+t^;li4J&r2kLeb)x$4zNzzequ~mRCl-p zgx8lKRP*qz&bQdHCx^GP2en&-*fh=@oMDu%0KZSQTbg%X0P}ER!)gQh{n*g}`2$=) z^y47^s+($#V)n*-*YHq$L3%t`M7%}FkjETg+X{yY`KjuGZ~S7-d>9630#s<6aOi;w znuMX!1~YblX__(UZm+^VVp=S{ukago;yGvB=K)%pSY~gnAp0UM=GBLU-oBLtBoAJ?px64R?*Wogh zkKM55qv*?MZi%uWCOz^TI((uEV3(6`)KE9Uv9FI=FK&)>zKh6W$OU#AJ=wSooBY{5 z1bi$Sxhh{9kzbSwt{W%CzfVFfDkJyxXYPD=OtKw_h6`>O$~3xNm*k8kK_v@CXLF1ex|>TDsgu)^7ti zi)wDS-)!Dvu8A{2421q}B{z6;?BXmB8Fw zXy^g1DT$|9Q>s)bR!wP#xh*oWv#~uzwc?2>>+{mx&K+ny`UGIJ8m>3(vzBh)2})4i z(1eXrrg&pijpQ>edWh|tOxrpxE zy%tgx8Q$Ef%Aw-0zv2|%4Qsy ztgG0mbo3Wp?n$wmbqr;zWm=qD!vgEC0s*{R4lU>8iS^JpFgo`u-o^1gUU~pG5H205 z1BDv**Pb8Gj9z?$hF{7Gg9BDMq{-}RzyQUqZ{Ep#UgK|1yWj+go{1k@FoNLL-ok@= zhh)Xv?A86_=6eNHu{V-WfJSpba1U7C#`=y#T*8m77Sj+sXLzn+Y%JnCv%-^5J0V_r zxaJbSMnZr$uE)Dnrnq^6H~!ttrg`A*cm$n;)y{G#iaQFBW2<`r>X;oJG^!0|jbjg* zyP4wY6vot;;g`v5aB)hk)n8}|ln5D{v%!mn^)5%Zpkr|7ssJ#pIdxnBREL@CH_o8{ z?U>V@MX~+;^12aNxV<+5=re=%adGj4*uK35I1fyq%B9_P`7`NnyCGWVE}{tP4W>gHrjR?C88np(i+NXLlR-bx z2`%nT+{`TWR(O|W41Ya0UKM$0LAZOv0sBSvDn#FVpT>v3ZaHs(r1@2Tt8+1)j>Dh9 zJ*CDoQ(+T1!6%-6z0W3k8xnxQnO%|f%$FfUF461{(TsS^>>-&0n-;>qZ!m;e&Pglb zwoU&?Wi`tUa(4u^r4p#$33U<~Std5D`q@D31pxC))n@!ExOj7ialB0|UQwuC+klct zee)triuG2wQ%3aovgSR(`i~6BysZA1xn2-iCSJ-Vou`o~%6VExDR&~A9Ia>)(%CKP zZ5BQHjJ$IPS&u%-)oM83ZJD*SjYkmt`s#*!%@A7m5958b@`hSv5NkYBynr|HO#l16 z3@&vwTZE!e`bdcfn&^hcLxx%~R0kr#rr~cwF*`Rznzx?KhN+Z!6$v(rTpsi(9%g2l z;>#$N{f$ehSY`=&9&HhN*Wfq%Zrs{kf9x}hMY=SdT z#)^SJ`Ml!lXA3%l)mP`!htGXrfZZbYbz?hqF<>yRr?_`^3v8g;1+;P^-YUPTU2nzX>MQC ziC*pRjjuD}_J{B|)8h8+@Intf6Sr%Z#qAkQcB(CE$JWI&v1j6$8O!3CR8!{mwoL81 zml5#?><8M>dfK&Ft?%|4`|V7OxDid6SUYaZvKV7Ssbib@nE$u$TfN-WQorj-^Wyfd zxc#EMy~geTyw6;IbUzn6RY#3WqIT*rxT^O)l~r%-32qO&w@*^s6V#eLH7uQK>=!&g zsprGjnP=Lmv>DH!>{~#HUm9-5+I5POWbU}q=onpTbZd<@`M0p3r^Q=iS(+E<7(~Wp z66gjS)hv9q@l}>YIvZlEL zSMP!h6ob_P#;3vY64=KM*_GY-X#zEWo1&Y2hF8_fD1y9uVlB6F~BVWu;t4{ z^HJ)=X?ZWD4ocY^Wn}L+Ssy^qvO;YKfo7n>-h__a8QrD~fAM9shy~v+m(%mnzgJ3( z6kyt?bU~|k>GlWNUl|s9ImbL}&o-+DjW{t|-1%MT!ePX6%bR|3h>4Q2KjT|%GdYXF zBjZ4vo(7W-iA|d^0dR;+psfFfhSVK{S4{~onDg7&XRhsZ50reI{gG*}y13FCo*CsD zqRCG5vKSuyyI*O<4_3CRRS#nO;E* z+!^J$3k&!lc!XpAp#V8im478(d4Q36_5jDHh}2CHDpZ9*)w9(n0pbZmG}A zqB@O5QRI%u?aL#X7jZPTwXmQF`}n0T9Xl>}o#6#ryGnbHSZG)9YF7CF$^4H5a-~Io)f@L;ALFQ)tCe z_V#{p+1Yem#dAV^`AgNnN^b;Nm$}7)>u)H8^{wVox9tCT*6rqHrd=Oo51gb9`vWf$ zX|_gK3Wa=HgKMv-Gu|!i+g@Q?u5z>e%Sj4jsD(?yhwMu7<*tRyQ&0g3vD#<3 zE95R&`%CVE+)b?gx%F&AXaKkAuIp?6j^rZl3u~8ZOb07(IBPBbYpoRoROJ4L-_yM) z$+u~yT4o!xoTS>m%{$H;y~Kq)<8Ig-3S@spGyBS0Z;3No;|@z2%MzW}a; zN0D*NW9GIK)0idP$$d0ta8GcnKlx_Tk@>g!)76Is5@+<6_qZrCrpio*vcVq8TuC}8 zGf?n#Mqm1(JEY2$eT+`9#_3RI!x%FeKfe0i>~$at)3EV1D$?=&x@vc<#|R)cUP1Is zd+fb01%@eUTlVkG5bw>7H>1Oh9^JfKt$EzEcvZ9_uO`$}u<)a-cUO8BGtDcet4f2s z7l$wA(`=h*XDbJXk<3snwFa^yRhM|RM7Y&j{EzI3aS@n(fFeXg?$97`=unL}u1s|0 z{dXz4;oX`PO+23y%EDm-ibl;27rU?O#_lnE)Q#Q0z0$P4_B7B$FBV}v3ce0?+e&$- z(B*83T-+borS89I2+ws&s$VHs@uppG?7i6#sfO%o%zKeyO23wrSnbcayNGD z)jMA`;haQnCCoRo7nTo8&i+>UU{k;5k1WF~y!v&e{vY#ocl|e?tbX%>>L1j*ex+t- ztNyQ20!QGnmV4avoei4b9J%&*AE1cfEa#$+!tt-nT>4jHTd*4*^&7u>&Iknd}cj}vIk^HI{o@*b9+6Q10qM3mq z!sQOY?n%r*hSr`i6_5uEjt~hYie=;130EP{F`{}JoJeM2pvmy;&gg>UYfsvUIWg25 zCKxXQ>M~8!Lt_&$wnA8fc%`H=$v``;3#>Dw>Te{y7cP09?ij(4djQKmTj@TSf%|tJ z{6YK2BA0qci9(>*eYfy8D^WY}V3@e=`1 zMR;4E$HV$$_{hA|AxygaT+c`uJWJS0-LeLSUfB&?Sv z6vTsVd~&J#NsB%qGMV>H27A~3)(FpX8<}S^}Mm%A>p}vBc_WRIjm6E+3L}XNSk4pyOD;XQLdaH1&w+Aw{cU!uH3?I`qGr6xT}B{6>lKXk{yj9|&hN6;VnTV*Ng@f_~- zKDdMFechML^!}K8e|mpON!`=?HN9bapF!$Lfri6ge0g+<3>vEOV->so5>xDBy^39@ zr0!zpd&NFa>dA_A%bKRL{@yJs+N-RFK4o3zm38l*EUQRm-RPFpwY{giWBQc!zTVK? zi$1TcJ|KLC+}X~HO<$ktRpt&Qb@%mQugtZip0uydxW71zTCxi(U=uDr zVdSn=?mD;H%azq*=Dypf+A~cdd$YHGUbTI?^5Hhh9O0JvHurv+=kzJ_WxWAB!Ottx zH2)t|R(r^R#pzYnYf9<{%RH~FXGuL7SbFh{R^uIALSMpsGG)b#X*zW;*B8?oe^G{n ztl6sOn?or-FZDJW+ekx_<1g?(2e9N^_Kdh}c+BBEcb6cZ>wTuM&t4(0N!WYha;Wyo zOClax!pgZZt5DKDoE=3M*gVwE7bl=Mr`Enn^jS$oAVS)#ArJLA{WxA?*h7i_B^8v| z<2mjVaL8@wVk1gnH{EKOO?>EhB~mIruo`CQ2|$R+h}-447|&i+61jHxwe&7}5Jmgt zamo@pobULhfAf?)lY2W2Kw|$3AhBCUHvJY3t?w2k`(0>2?&gE}eD=4)4gBtG7o^=? z3d_*w(>!QTBOTH`PWi7Sf46;;Q=C{GlJRf@U}XKc<>na*t8RY~w_nR@e&DRmaAeKA zB?|bkK+Pq*%??+6nHj^Niq_(v@-Bf-C->Z3YitI=}S^=7O4^w4ZdF zpR(sv#4;28=BMltn`+K8%}*eH@Y|1;{Z-n3ENOqwZGXg`NC#r}#41ULBC}kvny0U+ z_VhLACTfxW&)96I;DHe9W@fBISF<^Dd%GK0^CViDEPLO89Yb5w)JHp0cbOXfKo_n7 zaS!<2Y|{RAJJoDPTk{N#Y~uERE;E-OHF2?>Hh!aa<2t2f8r>X?n|W$&tm9V$+1l99 zeYeEBTUNn~=k?a;Ba9+5JMr5>anvqO@gG)&0?&Vo*V0!=6JxK+o;^&sR{Z4t;Vjrq zwKp%j^dw~zx@CYO#S8kD(WEjqUeK90mCV8gOE(4UTH>FjChtOy7I zL%;I}P||LEf+8~J*KS;D?)3{9ZQQ5UIE{lT)$Qz<-krT&kailUb-&fDH3h5A@n#&% zAZr|?Giy`tI?h~Hr&5CMD=gm8l*NUPcHep_3jNcCG6h^vr7QeHi z54}B+c~qD38kWD;@VCaE1V943 zKnr+&;bBDPM{y-!y+0(kog62_j7=F+W@FQ$vew3@C|yOVCf%!78FOclAowFyZ<}AV zc}+Y(51x$Qy?Sswn4;F7xRdyG>4WSU>#*5HwU=LKH~xaG8S`s5K5p*yo9;SRORa*R z4CFc{WXwxRcE0SjHTER^d6HtD1ic4ajn@hnjV`uM1kiNW>woS%_Z^B*L4f}N@Je%I zOVO;vo{x+Qw>7)^Tn2nYuQBWe*FMu~(#e3=Gp*eDih0|E;?L80Q6Sg=B)g&M2Y=+~^>CA)M( z#45o{f$z5(d$FLHfNjMjCJ{Dklv-PBYg=k$P)c=9xKj<}>rm?U^|PAnqI7<0g#1;l^=jeUQ%{(dT9} zY;&KuARM#$4joSLU}m-T1Rv;|Ss~iSo{JVf(0`hJ(H=HdG&7*6mRX=^fGl5t&1A}Y zg8h+vTX&Z=CI;UfU6taMau0A^vTSimYi&Xa4)#i#Eo)0s@xTX<8nGq7XeY#9wm3~T z)0Wkz8}B69e#<4V{<5Xn#v=ufx&V8fO5cHBLhB-v2D`el=O#)-HWT2}x+sBCb>fyT zl}#Y{C1i^q(W`xZB!GCOMulRR2`!@dr5U+oh(T1wPvU1}C2FmYB+|tX1`-bOOtRT_ zI5v#o_i_&&vM>iE_3q$0dv63nB(Md`-CBnwU9xUVf+&(gLbkw@UCx1@4$(b9h72{O zAE%NvpKOLnO(;DqjsUxY1^W`~36%_S_tXe)lsng$u;~dTNG1p@37R|sgh+uvcq*KP zNHd}tMEC|EAR(vmOOb%pM#@yHd@4zjxx{4@SEB5s=`QE4D<+6UHb4}D37i%qNr?g^ z(nSR+5kb;PCsvRYiHyjMeks|qrh#Q`1B=d*#O!TLk{iEknMjsxV%DD5h}V^3SQV== z+rv_%rVg5y2mupJu2M~|=+IxoN2)@Cy(2Ll8)-5oTdfIz3Lbi9E}O^E5=_Ov<~)2# z{4RF>N};CU)edK(@#Om{nee;z+Cj2$ErR!z5|Q$YmQzBu36P%PH!_v*ToT9?-nAm3 zR9#PSaZPh?_Kc>S*Mb+P&Mh_$mF+#NuvgjMuM~DG+k1XdH;#ie50aO&&=m-Fn>vpT zXE)!3Slzyd6>^LEaI8KTtAHW_O79u6E$7xrMMX{6KK;R@Tm3~JU~8FFcX=Y#lSPMooSm0LYIjj?3vDJ+++3S_0cUvz;vq`rL04g#Q_o+gy>?>mL3 zWLI8%dG_TO_bR%_1XxA@cJI$kS#^2J`-OeY%*gB^uyShg_N+?0-1B~7QIlAcuqG9) z7i%)sb-H}w%67g7hyQ**^(w}lF(=*~SrhO6+4wm3SYK7Q<~N3* z6_N${8V(vf`aAYeiCNT>Pv)uNrSv!~PoCGtWk^?wq5glu-(A`${inNhuMmCkQ|Y46 z{|D2`)RK+GCHso?$u;(qpYTNV|JnRA#8XuNCG8i>{IsxKA=#eaGdtvWU2S><<=V(M&~~}l#B0m-!+o|~kv|K};h%je z9{+WZ$Ke@XR~Y9Od1k^ml-dcl@7f7}pXVK*`m_CHbeu>fj!3Iy=>VuAPt3CCU#J`z zL1Q9!yi|2Nok;HBp+G3zEa&gx{R{*f{e$_W+Jg37oPJxAs#WodalAM0o7Ak_!5@oi z=RI=m*!Jd6$H|Ohq3JvW!L18Z*h$EO`%qGmMTB%{uB==>ZgiT+BD3f?2`(^7ldE#C z@-=KOJ2cd9n~FnKIM>#td^ugo39d2nQ4~~Nf7sN6NsgD9RB3DeJuNmKRaj= zll-N7#cq{Vk_bwYWs$^RxP9%O@Y5iiOKSuyaaF?!qXom9Wjf zbwbn+M@ADw<8^aB-`kAq&C4Ax4_>iBE_Q-6 z|BwnE`{WGg$(u6MVQY6)Z_%xijCbS>NBWMU$1+$$Rf?4OTj!?}zi2$G)KXVvmlD|UAXFdb$s&3t1k^Rl)eW+-m zuZWF$IZOLw+TW)+dk%uH)U~SPgre_xnO$H(ljh z&UM8Q*E0X|W)41C<(E*GJK5!Kf6N=04T$J2JDfdzh$px!*H)IW*Wa*_YY^lzhg}?w!{jtZz_l!Lja6PJm9zA43BpXj zSf0y~jzF-1C@+hkCU68Z;9APpP|oZJ_gkJi)L(F=1t$&qa8<%95)hrfd~Hi!8C*-r zIHDzSpMfbFvbKyvnJoWJ$uNB7d@j{1xDsaWHY&_U^vAGYealq=*Af&X`j*3fC84Zx zxtjIJ!s}^kJiaP&ffiqIP2^lDKFf%sg|{@)N1dkVmPU*2$EN76Hbr;g%2OyhK1F`x zJuDQR6q`BAmNKO9e7^Jfszz~dg=e`IU2rw!#{CDD=Yzp@ru+hfmvOvCO< zUDQugM7~hBU-zT`zmCHsTT#8p{`gJuc1b4K?rv;sk1rf4S?Prj#ZvgqNGi72%sjsI zgfdfgoWW@yXfu4Sm7I;CbucHfcBM(*JYWSx#EqVGkVdAOsw_pxU8Sa#sm^uH&keE< zm(Bb!Z>H}JyN%>U%vxzx{GCEX=N`rSM=4fn@MCtd-b)%MTC5M(pAZsd#~Iuy)*)JL z{mBgh*QPG2ty8jZT3@_mYnPl*eqK1v;Iwo!OlTcn7rvWCKvT*be@Zs*ceg$W&Q%f= zbEI#FVh-0a5H~%`KWN09vpBqud;r?H|?07cg!N=}qh4DelL zouWmORYsETMv?(dE+;E`sH-DduJ;6pPK2Z8oxx${Nt2d6?Sf%%xJJGcIqW>Ef&Wx2^=BkUvcQrYhr zGe>{L0;{4Fuh&FT*2~;3f$9g^h#ibTKC#!v$R*Rl&uBE{UTOEs>9|EnK-OU^XNT9y z5uH_^u^Q#hSXfp)$emTkO*eI@tD1f~Lo6BFZ81$!LxgSLTqf19Dbg>9@~V|QAQXet zt!%~0Sgn0SE_X?}R{Gham@7xaI@A`~=*X6z1li#;N1CE&Mrg`7xHn0(j0}>Y-XUo> z7yBBv7)=_YAz(SmG#I-^Mp@;z5w&!G2^pDwQE+)6_;%|WGr49XdIhsyJ~O^+d3Q@D z5nj}iV7QuL;&bhY^upha&E(n%Q^vHa?Sil|s@|Wm*T?0JL_wtrMa79N${?$nvtKNr z^~P4z!^9NT54qd~lw5DFcq3QK5Xvb^DW2tw*YFahL%kS;&XW?7Rk&tE=wmF(}e)-kpT^a8r>LI8fG>Jv}3MfFU?H zvY^LeBl`~;wg&eJVDun6P#A^YGjVT0-ACk7t9&pCl(pSC{hZ)F`UeSTG`FB*d@;d- z&Fo-mXpHpbmOQiSD%u?Q%$>0Te7J~{l(laku%d@)r5YfzuTut|H4z$TJwI8x+02nx zTikxnxslPO2gE6(xD|}z4w$i9ugIm?7|#_AXGem+YK+Ww>P;V3vCb@{S{~H%ZWru$ z+rooCXl;<26bIKu>lsSX8~khm?W~?Lb*A>PTx*Zrtn#zvgI=iH*OA!N6nghQ*qL9( zjpM?fF&;qqiUA{>NLf)6sZCUw@Vku5q)JLhD5}k2x2gM(gtmJ6ry4BENPE%Ygf{Q% z4l>YQyQovGYYPjtn$tx~HK%6U)tpg*RCD@u?m)20pCYPO^Gl_gZ*N^=CYN-<^vLb! z;Nxn!-_@KOY=#p{RS_44sq2gcQ1W(UeP3>#+kVMpbiyzrF!+G4bvc(j4)+b_Cguo(jeV}AGT7+T- z?`@T?;9r9XdpmAhth`>+D;^C_Z z&!|aMgUg)4k$Mr&_?i54Xh)-6+g}MFnsz)|#Y%voqQ(qDa)}R-co7|w?Z z-5+GuzyCbMTm@t=_^;hVgHzS*E;+keTqc=HwDAUB|ZXOc@rjJ^OIYjx>)f`B6XIbP?2e)EI`wrG$oBFV)n9)4Uv7MS%>9W!}*B zDc(>n%Vi;#`AQ)RXywJ&-N8Mcj9n9T05nu z^wU_1?C(fst9|maB<~A0yG!4dFN?udGzjjR#zD)QYnldm zT-D1XH5&)H4lLj5UcS>+-?~6LYpeV(DVWdRJS08j8IsTZR4X zVaEzxZrU2Lq8?_!X=~|Wd#UhIx)<~w{~LoY9(8%@V4ZmHs4iUaNc z^Dm28pD-(jt65EzwTRz9`I?-V(Dsg5FdRd!J;B!24Y1&N$l>(qqk}u$!4E=#A!!Xw z?gnnp+~o;gKoTn(wu`6ZMRUM0m4-;WpIKUT5!AKP#TcTYIsBaa{it@7t> z&J(A*gR4|#SHzTn4me;ClN9V12*1o~lP$xMR2c0>a;@?#VWvx=Uk@&Ftu;9#+ahb` z3fBi_m?3lZwM@>TBw6?oV zRlHehJDo>owH>TvpOaPb3kHbj+OR4r#X{RxQHsUQW+ba(8a5dMPlnRPd~|T|5i4`@ zGTtC4^t4x8bDb4e57!+IqNi+~D@$3MQ{17HGu@%<&cqTpGu0is{mkAdhv$X4PTxs4 zU1!I4Qh79d5gs=3#TkI(Nj~g#Z8&yQU>Y`h<=qDc%OY)h*Ne20{hK8F&MNKb=qjxP zz@C47aR63)5gwl4;ox$&tA(9Bu1{P`-L74(w_Ur?a~JA!1{q|4dSJiSKI;ZsH{C$z z)s%`9?Otd!Q;zy_N@a`hXrhxvIvP*%ey{6*mlpJhVsYe}(C8S|03<1+CvE8`De{NiOX@{8~|qB0))O?4_`!M=Y(W&FVlUmO7M7vW)l zfL=nMMGvtrv)Z*^`hxH-HdF+620sWk$fo@8CY*L#w>`KaSZ8^|grwKw=FmkFa=B}< zYpLsP?mD0$4TLVdcJE+Wx)$YBUU7dFH;x<3mqltt_!}(9$*}1l{kR$E(9N`dSam1O z{ZtsnckS16RlQfc*qFZE%NF*Au<7o!zo&Ee1`oL#wzhk^8=C0{J+9tEysiZvm%I=S zJHw}thE+aF5|0XHH~6;1is^UlE}lA$#>%(du6^EMjm>u>97)9Ms`m!>+g_WD zmn7wNEkj*D$#U^{_;0fASU!(lpL8Zp=M3Q@GU9bDw^iVoxaO5aFVk{waG#U9dB7l} z!A`2#?bp%Z$?*09@?}wgHRRRVy64t{S`FvuEg}vDisK#D%|P zY9BnokuRa)1Dxu>W$FY8$c3k@H*CSz6h2TGp!Q1#DzOYEB1tVlOd*kpTC&f>@U!@7 z?IOdZL;W(5cq**JRVLD5T0+`ET0yHk(R9P#=)Xs5&qC_WC!d*yuzOtHX<(L*0`iJS zC_b-iudCMU+V86Ky7p1P%$*loj;B|?T_!b zax2-#67xJUYML$XsI`cm=5{RS1@rpBE-pHi!=zI#Eb8saYMAnsqHZ2mcn9%h+8afuN~1rwNah_=o-69c zlw`_Nbc#iPlGD#aL&?HF^c|ah=-W2;(6@T`5s~e@b{L9x0a7kQhQJZMhdp(&w*?tA zUCUezQV6b9QeeatHbbEM+@a|pa*yB*f|53R zI2MW^Mc0!~aWsKw9Vi6@dEQ|H!z$4b$+~N$5%9lk0S86nI|6oy=Ey6P7iYxSne<(J z&$x`qaAg-*4 zOQt1RTZ_K$&3e1&d)}=1MdRIB2a0Zp&QVRX7yD7;%4hx&|4}1!mKD0)N(mKOr0uaN zi*ww;Nz~e@wDKc6)(8)u)&>;a>7}msE&8E1tC7zg-(!XPuJPRKyu_VR=gnwDE5@TW z9KufC=nZa0*RD5f^)xdH#}YFM_hy7WeB@vX-n!b$b#oe|rl^N$&zQrvc`{bIllQl7 zh}^}ncBZzu3)_AklJ1o}6q{96n-sHEv!Ns188gJGD zC&+^MR9ZKXjsV(&Hchrf%N*eVoKZ&LVhWVGj;WIs`3mR83VYE*z1ELsk<;Fg_59by zVDR3vMTIVGspb8^ZM_lB_@JzAP}W*2cdeUy&|BA*9Y~*kNpOvaSus<_H_cU)QUF+g>WLeOMmw1RLB9+e|PCWG2-6JTAhzA|BT* zGQ8LtJ`?s<`LD>MTRx85;0c1~I-56dE`GU0x^F5qO>6kavk4*O4>KjMBVQ9yV;bWRc+wdAWjX ztT#}O=|SJaAXKrxaVLK6td$-su+hLeg>&AVtbf`Po8n?0^N z-pwqxy;xO#v(4eo>1PXvMePjG`)FQfv(nU?l_n|W%yqoP?u?b-Yc%+#GwbcWO}g$2 z`#?mcb-FCPh+1pb7o7=%+tzSh)QjVN4@k|A40Ci{r=tyDG_fY|CfKTjBYngvE1BgX zf=Y9-iVL9VXR(SbhbwUl&dc1cC2rROw@lpUyWZusmBHqh zm`gA&m=GE4e9`NZg+0hMuV!PGecQvp@D4`ntRD4c-(XQsmP=Vek{Q=_XJ~DC!pYH< zq`}RtD+hlNb`!-&##!QZ;RW)QN){uf9D=LydBONFr$AB)6SNxVeXc8Ldh0zdhQ$rr!@b~)Xr@Zi@_t7yIxtf- zyY<`Zsx{*d`T*~K*CM7Yh8z7JEjhC+7T*nj^Mc|ZaLN}-v9%ry}gXA zGtHCFnAIMakgqtsBhTq8yFgDR46^1gTYc|x>uN82izwh}4(CLvrO z=&spmfr4w1nJ-#L#P($ED?FFU#SPKfTz4~>8=GS0a-&n3%0=>>d17YKsSLrVTu{`T zLHLyOWHxwU$~iJmtd=L?DX++r#wpLs406MizcaJ?t1wQJY2uNLb&hz%@SA+z`Z3LT z)rFI0+jnQkBsp|_LMS&S^xagZQuZpkJNPAca5RJX@tqCYR6%;VbaNCBWkbmn2Hf=1 zU}NTsZCmX5qG*MtaGgn*C-hV1i|O3fQAU|cwfdBVLN$AuK$S15c+-nG-r%l={* z4wwPa7Ml-SvfyvOAS{{B|3gwj&5l97nvYXLDVd?`GE+i<%+%2BnM_H#Q!U8NXmT^D zV^VeJZ*L$g<2%aFOf*<4%|${<#U4=Q@r}NR$Z8M3^ZF}7M3)xgbs~kuU*6+-0qj%k=$m&Jos*3=30NvcFt!k^bcO!ew&QkS>Uaj&ZRah1A`lL zLj!3;<=bU$=R!&k89!@=+#)Ed>9R#9tk09Ng98{_M__Clk(>3NyXu)k zS4jhZF_YKatWC@yznYu1`>yi>^siOV9OfA(H2Sh5^di2X^yMrWv1ne(27sm)r}SS@rF?&rP0x_j|dn8oLno zaNm>V{X{Oi5(K%%lZ`50m+f6F5OQ}7m*uc^Wrrn_UsifZ>aRi0DwD0x<~Yep5U`$o zd`JnD*FABgdEJvB98(iddF#2&fHAY(d=0hlH7M#q^n1oc262CZY<}aWGx<1XE;A^+ zoR4Gn1qUMnWX@Ex%WO4`9DUi!Ms2wfG&mtu^x%?4%-4BS8h$?dTP7yQP2^n|7wKl4 zrsGCq1rLYSraB8Z_NM0uh zTICNBjnBDNXEJ7&ig6hq*6vB?uAQ@-%|YyiqXLc-)fuU%A}KrEc2~;tazo9E)(?X0 z=$AR*MN-Rw7-lrIpYU0FF_+-waz~&kQcjMrzmiRsJAz?OKgkYE)?fZf;L<~a-}Ljj zBkfHr2{sFpAlK>1^*nN!9xa}RfGwH&!e`J~C|fNv9?&z#o@5f(&~egEB+2c7TG@DAddhsHD9>P%5g zUX8e%N|VD^0uWgU|2J)l7Q)hBA8#SR4dvP-H77CriKO_dv`q3PUiR#dvnph|B=;y? z#wX9au$Na`70U@2F2G@8zhcLJ5t5trp;b}Ge9&F>OX)32_uJcO21-G%xcg$c`!~AL zm?nVBblLE=HhM2tXD?dW{&(-a$X&YNYuTzMgIP8u>T|6=Mzn|S&FCarSFe@nj|A!w z&I`s1@u&4PFOLi<0q!F{x$`>MZ%5&&_S?S1J`Lrg{zdw=d-1x6m$`$iv;1|M@I^;S zm}F?kG;N>VoY^O`vZCMmxvj{&jlcQT@m2(l@blx2U{BjKsROcx#~$rN=>1_5Dz9PW zmO!xo5a$p97XfDN4}|&;xyuBg)k%na%h~hB!PKmM_5+%dgveK8V{*w`)+)ETCd@rg z-XO1aHQT|M2@5+urWqkPm269}7gehE3iVB~T-wv;u}S@k_|)+>X*14gm)cHy9>vF9 zJ({-31%ruHx9i;_q0d$}zz{7Vxez?jc0G0B*oEwVm3j=@WPgSikiJe=Yh9;|0> zz$di<{b6M{Av$axnzg1djTw^HFKU{4h!7y2)jZ|m_Q4;Q3ub*dWw0-6!IU*cOw$-! z`LY;!O0;YR+Xtfit@9bl@r#)T-D7A1ci^aU{4o}nEw3mo>wiOQ` zpt)}SP5TtdM}goLzvbCsU+^d5`+{37PmA0v<$g6CHwyxRtOZuiO0P9?IR_Qix|2U~ zXY4LpIH-)T`{!^BnDKYRN5Nfk5oEKR3U2f_d~8yVoGC}$KIJJvWfYzbKUAf7MA1U) zhiwaGM8i;B#*;oTy>*4?0SwT!jxaL1->`+`;>`~jU2jFSb~))oo87OZ6G3Fj!Q$q7Fsxp^+|p4mRLl^*&`;b>S>F!BYldanUx~MKC@|hAKAh)SI&jar4JEe;M+h`Q5*3ez$)47oXqx9*B0e zC(r!ux64j!e)l87Grubo^8aFf7pXknLio@8E~>*v1~DDxcXjXlAIT5_ivit-6Xt111^9# zb0Zy;+shaY?5fj6#ed8EE>dsOdmYkgdaoUKdQ32W4;M4P`!SaP%uCbY>4`UcyM!zxEV zt@tM-uu*Rr#2q|^n@Cc8tu-7#O(^{GF?cVFJW*inTvA|7PAsqr7Z+Homh;W`+}&=g zI0+fj;FWe{fWi&d>nqx7_taX2Blpy%7WNHAV0Y1+4k>zAamNS<7%l4*N z_g&87hUTuHJ9&k>b%iH$g@aseJ8~Bi&Oz?Ww%s82RkaIpr(*Y)RUZtd`O2zOZ2oIW z%->qw!UA`Z17lYS)kS`(h!?Fq60V}J!MjD^+-()}k**q8_oi9*T@33ZLl>?v;Fj%A zE6i1Jr8=YrjBpOPriOTIc^z=YFR8%xG~o8A>^Ud;0o^-Gog zxcF#hn&@(9Tg-=wZlWe=Yn|g$=fF{Yu&%(^x#gz%aC~`?fojPTRVY=f&=Nj(Y_ncp z;>)P#CM<%NNWm$w!|tpmJaGzZ`7fv1)w2TWf-WZRdeqc$smUSHT)HUKOn(dukF3zhcehYWKNmS*)7&#*duw#m(QwCkqcwxiI7kXmIgU zNrl(=%l0K%_kBXCTQ4PAbC+?SQ<^)AD~VU(C3~>?5tN&K3^&o<@N@!YWtH#8Dj)*` z|C+;Ac{4unOewq>p47_U<9?OM!k7!Cg!4eJ?4zmYfm|6^K36QTc8jE|XwHCt0)S~!yTb8-Sj){o*^#=nNLsr`m*K(H#h!vbfuTqa<@ zNHwOi15%n4(SwJ9!%dc64a}%1{RBse6xewbn2mdFQ9r(_Umq>8!cmb_cSdbyt^0<$ zJvEk9E?=v>m1$(zeL0}bgase|hJZ+7;W?VuqPI;x3;Q~Gl=3N(O_ev)We6vBBpRBz z0u{);Mp|?GCs71N!%6xphEuwy>RPEnDZO%>ho|tcCpUT-UW*Mcn~Cgi%&3E_HMg25 zsTnnywUJ+1uU8j%tX1qUtd&i$eZAJ}>)7Z%wVR2Vn^iw`kHbGD$(>Pca_B4+I`@xo zp+}7mRnz`pT_9r(-GjVg1>n!?VYko?Td7ACU!|5wx%tcL(&TZn7RxfqD?$?@-_J6Z zp{$TS2>#Yub|@zlp6#wYg|E-!UXMVs9r(|2$T+}*mes?zjInPGS9 z0%=T(hP#8Ol2!`AP1llp)Zm57C54Ac!D)McZQD)TTcY-awxXwD%U!pUgCxn0lS#Uv z5!SHlhBV^2!tRD9*Go5%>|~hWPLEaip*57CPOoH@OPv&LM<@WQIg})%3Pl&}t!Va_ z9qL|u3hEw{eKq@%#H%GNr^2|_?$m>UsvDF2H3yQoaqg6ZvPY!RpY^sZTDD9*E0;Cv z;L4QBMl5;1iCi7@}SUl)V5Jo7oVOz^WL`X^0q=g(JtZ&^rA*zh(NNDNXl#>!%MgII*O@Qgm2|-FrFqv zU3)?^d&M{kg1yY3wEZzY&alSKExyYGRRdPfXF^xiZ4CtdV?OrOQ-IU@1*-b|l;$!w zqxTYb+eWti;qe1@%=v={{AHU5(e+TkWp(NP3@$q7;`q&oiKfoHjNWVP@c!fj{^Z@t z&w4;qL3%@sw3qs8!b!Pw5LrvDvPUT>?LQVAp4^xJ*mR8X*%$>AF`g|U1a$6fo4#2s z^xnz$HwryeZ1K-e$C!|faoa?U=Su8HFU_{;ylSD-i)S=KeWi;ef4)*)Jt1M6m`qz` zcT>^Bf8qi8_43AMExBu;)6MOEGW?S-NzbMF|E+-!qV^&Il5G=dcyLui5cov%qM|G~ z)I^q=Buh<_r6$QzlV+(&v(%(nYO=FbHP2+CYOa$Px=gF;W*PWByEptKyK$s_^i=ls z%(TL{8mEVhctU*{b6er>7@w%yQ#m@xGczwKoTU-9dunIe{Wj%1$+DT2-leh2@BK>y}D(7&E4!5 zY6<_!NFlO zY?^kzY(E#bP1Ek5sBW94jV$3|4o2F@*lt7vYq-K#ecBzKN5dee0atGvs0CygMXm!K~FGX9SpBCPon%4{{>EX2me4{ z6cz@oz2U#dhj;J}|HAmk;_vC(Ldkfnmhd$5%;XOQx-U4`uLsoA7btk%7|!qX`N(u0 z=U~KyywHTC9y;$+48Qj9BeNwYMoi4zr%0rPV}u_$QDS1m#C(OAvGu``zq9n(WzlGoZvz5P1WBB*{f5QL%YE93kG3hHaejW=G`)p&!#9nRem@!hEiWHRZ-*~CyH%IAgrL&aYp>$#w zG+X^gd`_CC@e=-zs3W(Nk93v8k*kGPUVe>T&}#Lc^Ev4(jaTx2ME^(gd$Q8oHUB3o zovQgiS!s#p|74}JH2)_novrylS?L_j|H(>6u>W0t*-F!urYUW4+0Prhpw;R>=X26o z8n5L4i1zPk=Wn8>f3neZ&40G$|74>VX#UeQ|0f&0O!MD}-X-~+Y;=_7f41iTWTQ80 z{;$#epKNrJ=D+16r6rp0==#{+Y3`d@ym!_Q{lUHdU<21ma~^fzGc4Y}oT$HmHTRu> z_1t1^(`=>joL{qVQqBBL)5lf&OAnOLl1%F#2(DszepJTAAGUq$@Fz4ETCt9~obP6X z3J3c*ol;iA@o27$SjA$iFXQ5OB(#%0pHtu`OU~8MCf63hBEQgH5=ph#3r~(Wn{|4H^NojOIzimtnWK`z-!gtEkmwbb6Y5TQ+@4_G!SE2{ zm(7Jsy}|=yp5qP#^Y(^Q^tmiaCz$GyOpw6@8O%og00YxAqD%!J$^XEj*qb@tlS9 z;59YoIc}E!avH<`9epl=u1*hMgNF%kyG7GW$G4vwGuxOG!~F>%c=Q1s`K2jM zP})MRlV79KWnIu}^`G-O>1>TR>;H&4`E~N&Rlc2kcBD?eoqU`qb@E-J`TyUhU(@nF zS?R@^|C5zouK7P%X}0G7WTm4u|0gTW*ZiNXbfV_}WTgo@Z%k9_%%d80Ua?GRbr*Dw z`p^EHbe6_*<}v@<^bSpb;z>%c(R^qBhnn&J$%aF^mb*h2?48C%i*?cYCz~pLau4Sx z>@-F7$>O3Op2}XHnLXe@s$k|ld)p3aW6F^Qg_ zpUu+EJy?I>U)v+{I6+P!gd7s-$5ZyTH+SbL|Jv^1pD|^I4bKzdz~e+7cjGbt+HPUl z2_=V|&+X#5!( zzfj}nYy7J<{&0=IUgN){@qeT7i#7i38b43tU#{`b*ZA*g{O2_O!y12@#=k}5=V<(k zH2xrsFLx8j@2?uaQsYn7_&00(t2F*sHU4QDf4;{5gT}v4Q47HU2b>f1}2~O53()h1v`~r=irSZ?v_{kctMQMY^e^cZCUE`0__!nyYG>!k6(oGtFfyQ^fGyhKG zkJ9*qG=7rCYf}22#(zoUKcVSAr17uN`295gr%Kmo{Dm6-X^sDg#xK?QU(@)>8t-GJ zOEvz>8vnN%|9*{sm&Q-m_@612ODg5}hQ|M!#;?-&cWeAxHGUl(q%3_Ep5m!I#WPdR zI7+{iNEedGBbl8P_ILUh>09hRrZ$O(vh~3#|I=RSxK6RVAJb_OEG7H^-YmlhLY3Fh zRRn@TLqfk9XIzgN5~`GSWS$ukdRP|51Hqpg5~{qzer8DM;k)f;hJ;3y1cLsOK_#NCX$8Fvfr7Tjz~9<#TOOy$|a84{|L1$?q$NT_mx?J^`(d7JGrBvd)sb{P_? zoMyWW2~`%`E<-}2W{~6zl70yK5R@yV{f|Qb0R2NCbmLzG!5jXHNY5eCbNIi6|4aDK z!+#$Bi|}8BKetEw-^HKr5;v^Ee*^v-@NdGu3ICn=@5H|q|5i`ss5Z|`4t$Ws(Y82- zgeu3{E<-|<<87BAp~?c=Wk{$}h7N#XNT_nE?J^`(d9UpuF)3H>GXchKKK{|x;z^cm*0hEJs{p;6@|UXBnCKp%iU0(}JfIP`Jolh7xj|A77j`Xcm2 z=ILog1!fR54s+@9*X`w|3}bm&~4C9p`Vgh4j1?jN_l?U7RQiK<#%kC zA)(5fY?mRS%J11OLqe55vR#IRD(|vghJ-3*LN`Lg&@gltbQhW3Cz<74y-^AmM{o@9>kVx(1?GEg z*_DSb7?O}=Rcxk%r_YtM!B)jodUsE#PhHjEgxqf~wjNq2_n+7YhpqBqgyh8BMTF$j zJICOx-RAIbv2^NbfnjUupE)wwEII{x<%N2$&8zC)&)v4kq}Ku``N<^N{Peq+%$?=u zd|zu(x3g|POV=?Qm5%7JAAgy;(`C@W?+&HaO4Br51P>#BZm@;}cq!;3PYEP02_)9% z4qGf9d7-oF5%*NYwecYjH%dmXPWM$^o1R;h-B@7VwFYe)lN+!cunMfm8rBHXajX?z zba33YSA1&=tm4!8yVHDC{`BcDg>OQVf?41~*eK7kT8X)agSR%nzQRvTgMyKRek}<<_ByxKk+gA#HUk!p`-Al5N&=G ztIbb*3_tPdlwa0S_)*DhepEY~pZFMl;?pU=2(3U@`Q;_B`SDuV{KUub6Q54`{q`vQ zcu8%3Ne(~pG5o}*Gk%fLrakPuJ)i-x`O&1<@`;b(CqA9=qhVXNjh-oSt+VRbyH*=s zrzMm2p2jGfmMoi=tlAoQpDc0GdS6|QF=`ntOLl?PFT?BFMvNOF2U#8bc`EE;57zY@eSA}U!5&MuVD@K zDFK_t6OUs5H4=QH1i!;q^>B&(Xbc|3H{cNzkLiy}{v(oqd!;s-dEMvZ>~Er1+kU_4 z)rt!vNd?U5dNDUi6*vOHo=j0vnX;tOeGX*Wl13LgP(UF_I{rg>o{s-eo{zxqGC`6s zBk;%W&n}*W7=FYURnnX`YHDRlcR3&w*&Ogv3%Og8& z9@z==$fPc@j0lpnh&la_a@1MI8`1S?(TyR$2W^s2uJor$?@(Hx^k${kD)lLqN>U;v zQX(~FmK4h@sUWkljhQXAWR8@_90^w~;p`eE;p~be;hfr3d;{t=2AsQ9imR3qi$O_1 z3HD?Jk;-5q6|_>rceTdkhdWYOrr%qXl0Ea%I{z!qWhrEhBZN0e?+nx@bHOX>HO{!(e3Qll)1l%4U}MvdQFpI@%@7NtK|I#20b4Uei> z^ZP()sy@F&=|rVfN@cVnzvnePx-QLcjnWi-ev#6fl?Ii{m7nr^R>PMoU8yudpMOp1 zIHeUzUr_ot4PT;kxzdA5zpCM`Q(CI@Ii;wLe2M=&{)_m(%YPOB4g8z<@8sV)Grz5t zmOGgLGXHA+XwS;$vsFGyG4dPvH}h}dzt;{n7wen+7xI6H|NHzu~q;#gzI;GW0o%Cl4grAb#y%FEc{8nSR3PdB9&oqU$$M{@0 zGyjkv7a1}${{V#(IcsMAE<8Zgj@x^t0c*cJ6Wu1$LCN`qS+z#mP}6wUjIQe46NM%P z((uoal~$p$swz}gP@y>`Vn~Rb*76e_EK67WGWelNzeV!SEFJ7Rn&#tyAg z*k{P{oKRVc6DrGYLS=~!no}jl@5FdUj5ox1TZ|9H*xp{^9v9;uV!STKTVkviW1IaBVV&`9jVKFB;+DlDp|VgUK|^BvR*b)k@tPP*#aJgs zB>IZsnjxz=LS@B9sI1Nim6aH1PNf*X5#w)SyedY$7;DAY+WrROoFS_uLS;onsH}bn zwMQPsIYAsbkBRZL7_W%2L=2|3#W~@)7ZjfvK8-Jn_TrWWdZDryFEM6{@oO=j660kt z7z`KZtQJFBKWX`}&&bi3vQjQ?Srr#5E8r4#h8T~E@mDck5@V4VtHh8NS6f@0Ge&A; zS(6sGtUn8twPp!^zZky~<1b>&6=R_o?~5U=t<%!to*`e_h05ZuP+8~|DvP?%oC-01 zDaN10_?H+9#6TrwagH>|F^w@^Gd#*b7FfkCi>X3o;Z&lOi}8pUPm1xP81uz=PYh|Z zwY|nULyiLom8DFfvTP|-mMEb)Wn%n7j6aF-f*A4==CBM>oFmP8dy|gOjH@-itQU$~ z)&_;jI-tb3PmG7fctVWl#gJ)D4*K$nbEM^uY4!1%;Z_E+CMRxLe-kQeZ4#wajGv3~ zM=|~>MvWL~_A1Vi&LO^IU?R}?Q+pzCQ-s;G>NB!uC&kR7i*wS%NEc%SZDI*6u-LQ3 zxJHbLVlV@lCy%Btpkomwjq$@xR1+o?{wqDhiyk|dgPMu-9LCgI&A zyqn-%x=2E6>eFi(-jm8|yy8Z3+ z%p!`ji%|%6_O?%D@^VFwm=SYa|JN};KCA9D{+a@oG;s|SLl&yC#WGZk5n`BhrY|L( z@*~qRqw>amlqbI-`)&ForT1ruP%23zhcQ^h9Nu{GL~ur0I(KDfzvx>7SwL z{I}A-E8VNqt?5-MU90q~nqHyO8l{$|H&N*`N)IU=t?4h<^ap4eT&*Om6tbZ%1ml+wLQ{hIz;ntqz5ldtrTN_Qx|RMRU{x=d-hruRdoFDdP& z>5NtSC#AcUdNln-n!ZcZxmM}pO2bMo()8|Cx>V_zn%=ESpI4fo>5NhOd!=&qpZu=U z^lLT!?wU@n(npnkr1U~f?3<+n;{x~6x#(pQxB z)O5b1bhgqRO0UxN9#AU!*W`Dhrawp1-KF$0P47OX?D{9ApGp%ooo_4swbGB3 zUaIMpD}7I?D3p`mQ<`pA=?G2lr%D$oP1E#lR{FHkeM-Nj>CIC5q0-@+-aSg+QhK(g zKU>q?sC1~NH$~|@rKy_EbxQxJbeGaBO>d^s_m!Tn=@lxSuXKQ>FUl9o_UF+X`# zglXxy-|PJgU*h{dCS{36rJ^gkpeH?JLiS~c@4bmdr|z8SK3`(J zZ`eDbK8L4pD)gK6{@^Za#@HE+&BshdK#-CrJ_r+6n9-FR=-<_t(C%t&9OCqxN_q zwAd2)eWm*)Z=(7&VcKZm$e!|5Yv@<3p`+8Rp<~mnq2p1ld%iU+FU=a3pKcACFaj>w zaNxfv+eV9AC7zd*;QwC#U$7+1$2S(>Wz{RxXV0Am50)-Vtg<&qhvegUBSt*)(ybzn zHMAhx8anA3Yv|O8*3f$y_slik!*0vAhE2Z48a8dBHLTbf4&C)Ma#!ne?L8Ms8X_cg zwB+-A{&|w|f^_jB0}FXpd>gLfJX|r^xO_-lHloEZC5Tk0nq<%gkrfzt@x8(%feKA* z<40f=zJ&ckIga7xDvlxY3PrfHG-K}%y;3kzX3R+x>i z#weJBF=;l&)L9s|r0umMr$y5*&L>Lo1fmq*MwH^oL@Ay|lw!@u&%ba+V30l~9U(?i zuy*xmB)j;4F-9J)o#~X<;Bz}IuiUC2ZJacClzrQj9=N3?=WVYRg|sef;QjKx2)tAb zi;6Lb7kfCxbQOtyR$yIjZA+w}vcZ0AV{tm6enG@fid+bPQJs(!m}@Uo`ZZ}L%#ot z;+Yi1lTTsbiCgi+{dw>_KVIT4@$6h)#dC+^iSG4+caZnZD4s^j24k)_=9Z)r!83Ty z@#1;tSa|jU&$E&5Lgc$Lif3{Z&t!uqZp9P#=fQK;*>MtgiD&2XDxTXF&kvCIZRC9; zil>pX!I)2W}h6;Irs2hSO2 z9Uq>Z%d2>9Q#{ur?_0?GdK6D1WrH!-8*@v_iQpMNGfujr;CbL!c)GyzOytWzzN{#o z-J*E1*#SIpE1tMN51yCCOWbAs>|9>OGopB|L*Avx`&txFBV~gz*Bf(7w-dqhu``Yr z&;7^3vln=tfqV>iCtV)JvwIXz26y0zTk({^-sk9#nof_CxJx`cmsj!Js(7wNUOL-J zuSW4SQZ^WKy)n0RKM_3r@zNcoe(pOKo;|_ybmSX`eBX@X*&~W4qgC+4t$5=8Ja|4j z@c8iTTwcX9taz?L-X+NUN)%5cWrH!-8*@vK6Tx#IS5(BzgO0%Syy$po?-6*$jh0eD zb0E?UMY_wPXr@NdOf_iYRy1+PqS-%wv^2pY!I_q}s@70)Kca~1L~Lf)66cp51ijJe*JTY8=d zo;SoxcSQYc$8*oI@aztrUqU|S^OG)#;@K;TXD@>%Zp9P#=fU%@{f`gN&gE4+KUO^7 zN8W|VJ2#4_k+Q*<>y5dk*NNcSGhVu*;Q86H@azVj1CZ}L7k+Q*<>y5d^bs~7aa!Q^y&rgnpXA<(Af_z^_K3TK0 z>u296o_!6TxD`*_p9jy|tmDJ8b9oidO^W9VC=c9NUDI1Kr-k4kZo(P^x`o>9j z6g+nw3(rLG?1y~kBH!1ecv?|B+11SZj$84>{dw>_Enea->u2ZkDxMn^Ppn z^7%qz_s04QUN1>`Eh6U}cOA`9*RdRP9nTThk*%Sz2c^geP@Knh&OFY<=4~KZ&caUM z9BfW2o4e}b@DqOXgday}+j+s;;l;z^JkHp*^MSA76D>bZ;f{I&LcV2JeniT zV>!+|emmv+nU*gBit{)Jox|DZJkCAmbLM#h+n@73>$H5|I2JzwoCv?8iPoi5G5O=v zcBk3mME8-oIkJ2^L!~P$qEbLka_H!xE7X*Kfw2$N%`)H1^kLCFK_z2~>TY6{k z&mlx{9tYoZIP9LsLHB$PxliDnLEdidyFK#R+8OV>Cj2-W-#IUy7UywzzEeJCymVAi zjvxgzI*mmIGUNRlZz=U&3Ev9VZ;)li_ z2#ux+96$-VGroOZ%8PUUBRShYnsfbQInzIWE1b9y)-HcS6z8E0AO{Tqd7S>w=j8tc zj*jGg(pmYxD*VtF&^a%j7U!W+pi@4k{EsTiF{F_4?=tDmQ?)Cuwg+9IaFo2q;oDiR zyU44F*AeYbu z#Q22(dL=lrm#b=Oi)K(ZbX67ecXBwgMS?V9j9rE?z8a%o4#uR}7*l6qbl9)dBpZdA zWWQ9C>{n`%{Yp);U#Uscb}r6%34^oQ8oT@0%uQnMF9j%10D)n>uOoJjc9!45e? z{iF8pa+&u2SqTXVcU{b0&8er`fbf0me-i9v5kKlSWPc)F)?u5C+c4Jy+fMO1UYdeDV*7P3Qqq0!R6=`N%bc#5B0f+ zl>FaZVwKG}jbxn7hm%>ND`q3s*fe%4rLka^&Tgf27NJJ)y>JA(xY$sYC!4jd;rTT@ zpUCrxJimkIoDeaaGj%hjo<+IqX?yckX|23lQN&x0fH&I_gJfFlHLbCYvT2HBmdO?` zlBp({YSzQ%@O^R)bT)K0bQW|Lv;p6?i#3-8URDhkCwdV0jsg$%MPipDGnZ3?*j0Fq*gnHHEn95c zuuUEzw$0dNk130e*e0ZjZ8^5n6gaC)4(r9q?(D>TL0{f>*#+AK6?~^~s)v&-TaVu$ zt_EBJWWl@QS}*AV=cIY!k_~DC>eT1NwF8$xd+#5_)rxD{Y{~RP1#aAXaTm|R{a`V& znY`DS{B{K0DwA`Ral$*m&)pyYV#Vw~cD8DcJ`DbG`_~X}G(wFRQ6>rg_r~F$6obFm z6@RvhDgM|Le{70BHpL&C;*ae(@XtJ=?l}01;5!7ldN>*UIo}EXxD5X9h-+W6zP@xM2Uzsc_j;D5=!W8;5_Y{cz%1OHs`|E}Qw zW5IuV9RA5M_={cfXKR__k4^E%rubu1{IMzi*p37L7dliSXB}baXc2t7AXg73gFmNY z!5^2w|7~%tm-GaG&d!2AE`$Fw;@W}B;Qu>uwc@hzud?ySZR0;ZioeP43E)4s_1O3y zBpY%2`;Z;@fX3j z33BytGWc_l82oV={NEDSdPz_4=eRNW<1+aFLtHy>8T=m?S1T?X{|9XRaohOc6UE=; z_XP02WY4kjKR`C(_S1p?x557=!T(2s|Igy^@8%)5+Ww1O@n`Fr)_-h@KQ_f5o8pg6 z@yB)?_`lGh3bo@ef=2|odN>*UIe!iQxD5VF#kF426Z|=)4gR)?;e;9oDU^^%_8&&hW1$7S&U zo49u1GWh>ST&=il{3~tzaohO+G>X5;?+M_4$?jw0zmIIh?bic;KltAu`2Rrg|4AJF zJ!0?|yW-E*IK>~E;*U-7$ENsWQ~a?V2mUW~s6y@di{N2Ft{zSXe~$TsKQ4p+5^=4U z^aOvD0f0X)ga6aw+JVd9|CqR1aoP9>ZTxZD`2Qq|zsc_j;6HcQvGLzaHsW^pfxi#@ z#|i%57yR#z!#_0!f3Yk6Y^78Du_^x86n|`rKQ_f5+i~EZ*`W%x<1d0Y3v%^vGWeqw z0sL_p{1=OBy`(4jqdWopaT)xd64wq~2LE4+s}+}x|4bWy+&2D39fOR$YfOGm0RKx+ z=O~}RoxyWwOL~GoDks1nm%;z9;@W}B;Qy$&T5;L< z&#>{wZR2m0SQz|Geop}ZxjT-H{~odtw|@})M}q%Y!Jlc?(9u)l@b49azt|Ojw(2SV z*c5+kia$2RADiNj?Ktqy>`;Z;@fX2A7Ub&TWbjA-2KeJL_%9UKdPz_4M@t9z<1+aF zMO-^@8T@}Gu2x(&{`cGXGd&q;@W}B;Qvc;wc@hzudwmQZR2k=r5OB8eop}ZxshYw&%UG4pOTLh zX7AAriWC5_-+P@Dz%5b$g>ePYJEj1{t_8qeKrH}lS^(Iz0I+ERVABG?)?NUjlc@b`&YzjX%g&&*3k4@pn)((Hy>pvZ;P<#Cs!8Z$X^>8xyqZ0@GaT)yipbY+!p5Tw> z9Pr0w@c)yzcHlDj|3X}?xNQ8(Z2WQC_#0h327i;^6Tp9N^Re;&glxp^e+2)l!T(yp z|9=GkDRKDsjlo~+ia%Qo6@P4sKQ_f5o8pg6@yB)?_-A&gLhbmA;F|=wdN>*U(IEu> zxD5Wa;#x213I5NEOEyFr{GSll4qOKRhsD*3%f|mc8-Lt3{zi9^!QbTf1n|G4>Dc)1 zA{%l0CBfef{$m9H{}%l3jKklG!C&l(KU)(Oe{70BHpL&C;*U-7$95d}ztEuywc{^> zZxrO};bibfw41qY6(P#DwU}7;B&ylkK;>4?yI&xRv^nrRK%LEFY?vLKWv}<^(EA2`0&n4 z<;mkL6FCW(mx5zdL8s=Gys}{i_fO>D1*? zu>&PBe=EM~o~AO{gs;}S@U`Mbe679=L*&MlOAxt6T2CG39) z`yaw?ChTUy=qyz6PY8RMu!jlzJz>8m>{Wc}^t=k5YkF|1N~H&9r`*#+<3K2@iw4Q& zIlCWC{yM-`1Y*%~_VUG~@2w~3>_AFtq;Y+9IHSk>1^KTpoKym$>P6SZh zyMX2a5LP!Ql8yCbZ9QRE5q1?}-y`gMgwf&7;-3=sC}EEh_8ei)5%xEH>G=N!o@@N? z>G(((Tt zJlFW&*YUq^$4~135LTC3l8p<=+J%H&P1x0heV?%J6Gn&8i|-?B3t?Lbd!Df8340x1 zI{w$ebB+H49sdV*{Im`PVRbzx*|>TXcw-I(5Vf6k&@%@B7PT1pwy+GIt zgl)r@j(;0?uJJq0-$VQYS_g!%x^9$g#K~HmuxkjrhOi$H_5;G`JWuh@2z!FCCkT6y zuons2jxQbmcJR8#Z=Oei_yx2M3}JP5D%rT0tX)jlw+Z_;VLv47hlJ62vf`f;_AA1E zMc5w+`y*j*;7iB<26%4qJI_Nw`~vDrA*}9!B^zC2t&6a03A>iC9})H=!st9>@h=E_ zlCUQU`x9Y*BJ53k>GI)tRu|fmjZ4VdC4^l^*mZ>6PT1{)(E-`w zjfDN0uwN7Q5@9b9_7=W${BMEh7Qgd6D8w(IeLx7S+j+^x2C}w+uuZwdP?VSgp;uY|pWFCG6o;JL(quYQ3dssAo&BkeZQK8vuk2&*Kl zlCU|1%^_?eVG{}S6XqvuC}Bf^^#?LQweLl%iMN_UTM26=td6ic!YT->AnX*vP9f}g z!j32GXu^&rEC*lv6P1In92s|@{~u7&wLkik0VVMq9pCvRbw25|5Y|H2nS`B5SUF+k zgq=*-$%N$*mPgo8gdIiL{`gYy(N}VO{dIh}p^oD_k95u>oo2$C30p+iBErfDDZ&v4o8!>@dO( zBWxhPRD1)$N{a7LToFv~Jm~X!B)NigRuHz7u%(2}Cu}}p#e@|THjS`pgpDC=3}M3v z8%Edwe5v>bfMv(mH7~1c|M>hYb2ed12wOtfJi_J?Rzz44VJ8xHB4Ni7 zb{t`c5_TwI{qd#Zqn0EozD$ZQGdaEv(&-?b2w@SzY6+_)EJ#?8uoDP7fw0kpjVA06 z!VV#IthRteUU_!U_nRLf90-MiMrXu!9IYh_Ebt zsra(Mvg1R4n6ad5f4qK^u|(=;lyst`vzV~OgjEq%MOZ#z`Gid-Y%*cT5_T+M2NHH5 zVVU?+@nwQ#$Cq>8p-asA`Bl>SD(Qp?3lnxKVW$!nAS^)GB*G>Ub_`+15Ox4z2N0Hl zFBM+~Sc&-VJ@g_x4wuREK=+Z(_#nD}&bo__z#JY|lb)UV<~am0TdsQ{!&))I?TS*I z?6uFEdtXgjf0SCwP*;=QjMx6@By0P5T|=tDhxs`#W?0FmR%v|%%Mz*XBOQ`tb;vm9 zeWV|aP?nD~EGO;9?!Nm-Cq1v%7L5Bymmo&{=Jc1J)UsfSGA|y zNBaF|9BI`pwf;^cy?kH9Zo7|kkTZ2sarFDlj`X?T@0oPJtK&SkegDQTzmIhO@6z+9 zdwSWQyX`*GpFQnJT@U_rzuz;-pN{j{wm*0IeWU}NDfXmJ5$#~{dB+IGs&Nh^V7CJclmv!OMjD| zKi$*I{@iW%kv{QjN9ua;r~Cb$N&a-4hqnE>%kLu{=1j3?{d7++`*XM5N4o0Cbp7dm zzh{y^9p{^Ef9~@8NVojTkz!AOx~G@@x!dj|9p_A4xB9t<-bWgJ!r@d-wqrc<-?@+U z#mC(e=9WJ9`#qEDr{nywT|amEeWYWYDfXVB4R&{65kPA5G7n?&)QJ z?za0#w?E=YT@U_rzuz;-pT6YdzP3Mi`F*6*oGJFKpYG{pf9|&XNUwP~U4OdY@0sLJ z$N62`pS%1%($^nyq}bD+?&)QJ?za0#L(bIoP(R)8_e}Dq<2D8Ol^QU`y z*`K@ZKGKii5YPKF9pex8`#qEV={R3&`*WAyM_TMmv1k2sPcQp(x7|njg9m%?C-xWi z-AC%Ug_Z6Gb-g`Rog2h`I!QOS?z@ka>d(IWNY(AFxc+G0eWdm+soV`x{n&RODNdgE zbm`|$u_L|jK2qnsq3(CH?z@jP`SwjZ+pv-9j>pZQIF&%p-@f}u)lINypOf!=)%%q= z`@Zi!QaHiBT6N!jq&WV*?>^F`+f?;*hWqn<_mP@6WKtviV`_v`+ytuKNbT{@@U`zg z(&T$XUGHAy`Ihtc*FEVzQo0&)+()4qzl#ki+5B=Q-6;9>U*p{}6fe%C?mhREUUa8p zfxz*+C(gIg`>lP-S?S-FhL!TY{dy6kh@KdA%<(M6qK|D`UyZnvVs${u$I>ofoB$f_(m z+ULzIy!ncB_ckwhv3v)LYb##nej2ZEZ$fe1h2pvq#dR5qOTSEdO>$Y`HIMGG&^@xI zDY-)y<#dQM@!ihf9+$K3tWoq1t-4A!XYExe7^r=Ri&yRylB;GDx6E$5OM68mkhcD* zu9E#0jCT$B)w{>4pV3}BPG2Q^_uU!tsV)s%_sJgjBd6n&&X3#WzlYx^dvmwCqo3ok>LT1d{65)Re~{Y$d-#2_BaJGQ zsQ-JxeX_^hmfHV&_-xKH-hn^XIL55G@#q)~+u{@)AklRfUH)c)VY?~}dRs6q+_VoK?Z!+u9UUZ-At=~@J|2_Xc*^x#SO4R?o;6B;ou1W3xJ^Vh|n~f@z z@c&+LpX{wyr}qCIexK|}qY5SbzZcvmd)zm@{Ez(wulr;>{)pvzdy7zQFkP@2TL;)D z+;_*U;};uoU+J#8V|JraQYr73{p|}>N{(&73m)_+rCY#H=O*BNch2^7=j@AD7!Q*k zi0Jzc=%$`dI#SU|7`AWs-9LK;dEzGW#7%_VP1xOpJw(_;ggrypGlac@FMXfiE8um% z&ky(E?Yn<=2f6DtFaPYje^%YUyYK#4wDuMrr+xR&@_nOp4=uGZUG(^L5q2$M*An(4 z!hS^9F9`buVNVkFBw>Fd>`#Qfi7(wgy$N3T_R0J`c!=-2e-`yn?!zK~?7M#!wQ1k| zv#1K{K12Gl?=wXIzVH57v@@qui*q_$wD11eefQ7qyMK1y{j+=a{j+(}zXxgpwSjph zYu>8Nz)Dg4%2Dd4Wa5{MqC={s@pYqaRPd5fw*eOB-21|pH~6jxN~dG<2z=>y%@BXxrJrc@1(GfqqdJlN`Hwz zt1$k?wv&HJacq7QH$)BDKO@5mu7Cgh#aq^mng{{>7L!%7;mT2Mz`>ddFj28Kj_M=` zUrh(EB))Of9D*BW-xWqu<4=(i8Q69fk4t>kC#?ffMEB;Gaq-3D?rekf@Dcd(wc z{XTqw`x3a@e@zQadcA~dOJIIADohC?EZKb(<^4eXc~lpaNXMAK+Rrj_)=?cpw%*ub zSyMJ`tEW`2d4y7NdZ3OR`|#S?kq4wYy|!Lgr%K}Bv#t6UkdODvR=K3=G=R)?&HkjG zsuR3DWYt=!PDis6SEmsNfz~p$EVKSksyZFA{Tr~t)#=@B@2EI72iNaBzlLiO&UPcw z>$=yXD@S!w5S}nyi>{Pf^cABP9YIDn4500mO?39uy~~PSXSP)|4@WovsZxBRWC|auS-?E ze*e4>DaYfaQvEDtuB+dliMQc|zyCvWv(YWnvyX$p?Gc!{thOZnH*Vbm@s9%=@^`NN zFmr_;Hu4~|_GGL5$kGi%4h0_A{7Pm)m+$hD_*;t}p0Y`SEkRT^%c?x}x&`sgCs>x> zs=WTX1t)E8ABYf?ymjjJ3m2?CZ->>94J%Khh_;9Edu%257e`t;8e1ZGK+CpGd8Q;D z+EEe@++VUKu&Jdz(iA_{7eAmRzG!P@^{G|&Qw%vb%~*6}$FLc-w{#phqwKbhAzOlP zk^Pdc+85%Pr!72v0qnO?&Dd~AGeB4Ht*)|dYd^woBP`k)h-btH=Uj3m6t=9VXO%3L zY*_yS`7yqJ0jALs+5FNjMbnD2760gdA3)AcKR6noeMBH`1>y^Qfq2QlK)f=0?UszS zI|rWEKeTq!&^Lb{e}F94QBtPtEUj1Z1gGFJShTfl{id8tsZ%J8pLc)$#aE7?h%b72 z)QDLb9UYyoY~A!mznpuYDcSsrFTZQuh?fvplyl$45q|+P+S+l76 zclz(#l%I3oB}y!?bIY2Scjj1DQT*J&**iDq#|sA!4CLIorKoG;h^7K(hjm9u^$T($PRZJ9Zj z;B5$0vPD}HBKiH$d9t-w_1bZmh2ZMbW*u%-SIrpk${ERxEK>YW(9X_=A)y%aYt8y;Q zsd#b9&a(A?%DMCtgcrtZ?+V2qqSTkhBb!R&&5xJHJD#ROcz@jk^~g4sY#p!^l*+}l3d3a$AM{B7l|t3~chH_V=P|FKrV#Rq@T`O1Ktd*3Zt|9<m15OUF2+qDyHt42O-4DA+q3-XF&dFKd4;JGuZr*y_ z{&zOvp?yuEtC6&RPXJZ5Hx2w>;9=6(65;2>DJkktM4b5-<`v88L@W3Peyp?KA<*u!)^9rdFtj__AOdm9MSozyF8n(9ow#G~ z5Q~Hk44m||l6wCPl0vo**#Rse^#-Vf)K**Sj|r*3=BQ}5uA*nY0t1`h&R+96 z^>&-z?2mq}>z*MulI+e$cRn20`B*UCG;JUryf^XpK``ge_X3;$F*p!EcN+TZGp1z+ z<5Q=V?A%nccJt7ZlOD>sWC=N>3#sfvDk7j^=+{v&EJN{AQRGzo9zzc)hapHi zy12X0%cv~p`Y;pmiGaQK!Ji0f+sOk)_dw?RL&TvGcGmMZkHq0LX zW9kgEKj?gU0Lt%)vh`22pBY;F5qiV#Lmj`$>nq*RZvYA9+=t%qW$Fz#Z_f(EODExC z@geWrg^;e{9~9@@H*3e5SE-le@l8?J)PHV%6J6pv{ZV9-1_tBjPNH&~WcP+NRB`l% zpXOY0GI(f1E<)pf?tJPvkku}o)vm~G$weI4l7$>t+u#34dc_LpZdrd3N{|9dHf(59 zr5lo7u@hvM3U_To5DzI7qEX&H5I-HGdcaQ<=i$w79=zsN)iJ)f_7ilBuW7+wsGe~< z26Eecy5kJ2-I^2F@A0)C^)JmSd~*9n$f0vYZtj?8caB>WsyfHpfl_vR$fw;$?uYDQ zE?SH3kzN*$|119IK>V+3|K|VXF2ZE=VLP}JsnS|+;&;K1F2ta2ejvUxFfSB;KTsQrKNqMe z-7tE3`?2WfH{{L%tS%k*w@H60+5A~n$=bIDZoBF>OnVUHQBo~R<8PG4-!FuXod>^E zvi9MOk{NGy;A-wi2A+1bXYiTYbw+x?3p?JFXY7XKuFxTV2AY0_k?W?96~a!ujIktTm^S!+jAgTJY9 zdBoqotT8szUoyd85}DvHZJglG2h7XMQ-79Ku*|ZmmZEixSyov)${#QvylUbtM%WTG zbWzJHZ$)?mW>?K9L&y|>pT8UsbO3*q9gu0WtXe?1q?x(sP4hfF#2i@>kIUpf8?@Rv`2;Fm(S1h9nolxFDcE5E(7{H*SuPGO%^~)ml%l)fbJEH!Ea66hk^=WBsiL7jlwPS+rUmOje9d3;Io5JnwjrC~s@JYAd z|Nljf#wb1cOpRe6)0=V-9Y;HX@^s~+>|Ob&pRRng8LoWPdsn`f{odO7znY%j`r-eo zyjQ+@Tkhy1sUAxEF*Y!H;zTrM0#ze~FWo(x{WBJ8Fp-Z$qpeYYV+#i1Es@C>g-4!&6#of>tjnuc>ZD+Ue`f#*85^D@&oJ#YDy!zIbC5=n1(m+j3s32%nR#b;- zLKWpmhUGu`hfqFRk0f0O*gewhkVsPLxyrmJ=QLD-5Fn;Pm;N|IBv!URp$utSe; zg4(9$Wnhz@h&3qV2o*QPfoE8QSdSp-CDVg4G8IiR5q}VeQ<{_IsGjxJ zx2mmNIiYdNgeglVosc(u!jwieav1B*3y(wG$z=*T#RJp^AYkAsT5tGu>x&v z=qKSl)wg81WW1Y>QHN2$$V1{o$tK|e9TdNHm%XY_(j`>H&PQo|+jgF1; zA7|AsYi@1ue`zI&M6uq$G6+o)LYS7zRdNYC+D21bl=aAB;`7O$=P2SY44-89eTEk^ zoX7Cmm$bY21EdRK7x{8zQyOoo#fjxpc} zrmucQ%L_F1F6Hp^|E}f23`-e~VwlD7iN9&NECZT)e|=5MeP+?;GyKcIClmjC_%{&$ ze2ONVLKFWe{4RY3cy5`u@y!tSn;nucDG)#j5Y6RNo zd2VoY*JOuQ55xYVimIBbK)FBG7O8Jsg0)4}luCFbDK}&06=|>zDa~OrALD1d0#3(Ux!%ve*(+^TYa8JcV2mZfruE$dQhY zO;By1f3)%+T+`ebi;;8MTm4}*hS8~Oh_p0D8YcKVS{mCSY^&q>4Y!cHTF;I|^*~3> zNNE5@Lmf8_Y#ERh(n9)LFz|v4t?G)}s)C@uDbljEeHo&_EI1NVLU1awa&|b{7+%~I zNl0KExT>W+yi%77HS6VpvS4Ybx&}DfYxlU@TU-5T`&YqW6AH_ZI+M_oNL*B_g7ZV= zg*q;mV5^~{t*H?;Ob_wwN~5Y8yxh%kDv78usGSY)cB{V$Yfmn6`GJDD)s+FbMpZu- z3H0$T4Y{pLa>?QThK?vXpGLv1vK%9?lec!X#~K?Ve$=t0?uAuR87!w!cww+8R37vf zg-U~}o@2zU>WuAAZ8%2d5IVlXA8Us%==pF-ML}srbx-+G8KL^Iq_v~PJsb2KxA^Va z+|9jKbK^=phsqLHWb{W7x00ae1(l%@!-3YND8eRPSlUA_smD5cd7<)ySBa?9gqx@g zR{1HRE>>cZrMmTyI)IAU9B%h4Wi0DpB+{PrU}7aB*Ixe7-gk+ovBho#+9fLekhut> z?a<>zu0`sJPD9S4wjog+(O4r!UQ{FOmeg`-F)+GzLcXz7L&O>k8(P*_o3y0C|~RO)Uxy0n8T6jc!ExvDx^a%&pl zlF~*j55keEZ_sred2)KnCF73tlS2J6YA2{UNHllgyQt!sN~(9Na9X2F!!3>HsA6%` zKzpuBPW7a!U2JNtU+&)RP@O>Kic*d}>pt4-s!)x0OH&vu4fYUh)s~l56wLK}BrUXv zJ@g1_JVKAOqniQ^_q9aAQUBeJu6h==s%o(&o^*np-=BdbSMXOS|o%Y@p^M zI(QU&7dTg_S_4Wu`d*Ci+Ltv`8Y%bsF zW-*hVy_QI8M~oXADl2LrsCq;iQ0cMssGCU{Kf`~r9_d<AoVY3{;_I{6$R-yqN+6DPd%&f>NZsx^QmpG4xNVX z!Ogp_nL`@2Zf#Lfu&SVW4mh?k24x-?#JV6>5ef}T22kP?$j`TQ-#ufgidSei7_pj*QG6(bz>#2 z34J5YKU**m4>w^9fTyd$&sgHgqH`&FEF;T#v5q!O!XOLn;DTU5ZB4MQprWiSP+nM9 zirEW2dqK6|P~G0z=Fjz4Mq8I+DCWo0+uYbf(d3cLa?GAF`Cg1+?Q;EyQVWj7fJ)jw zL<@_Z*%sPyZBc{LCOqYl<_afT4)fWXo*{vVqS)2fwtr@yZO;uyGdGDXLNJ} zJabQy&<#r?TGN&CoGhl9QY42;YR}^^Dnyw(Ygat)Y?R?HrfcD}Xi znxq@iQN2it%JTF-9OxOQ(jsMp^HRZbDjOsggM!)qi7Q7pt@Mu_-PC}-SgLMN1^}qe z{2-p)*sALym6e+u9U+jhASS*`Rmb8Xn|KJ^Z6#H2*SK_Exq9mENOf?o>55<-jVhZ| z#9aOKN%e8l5m8!jz!3@cGbYgjBfI&v z)ygc5Uscl`rZKPTZaT2R(@LX($k}+ZsSfC;x{{vrj%NQ76gz5Gcm>uG+SRJ#1V7er z!?B3JqYaM)9RbC@upYmoWr99dEnt!sTBzHn#bD~jEb&u&WF>}}wBv!|lJOM}k4K}m z>n^9q4`fKC8$;O+Bwl5c$9-7KQ=@dfFxc~I9@=la`g4=5M*lP(cZTVHl4b^GnA8YS zOxDxSN$%oRxoSWz>JHn9A+ioeYZ`5Dn(0RpEWt5fZAH7)I1X&#tq4b3sMwfo^VPFq za}zdVC{>L8MV#ev4wP5L?d3wm8%kbF(rZX+Kp}OmC}~x_S?gQiVWuF~^f=s)#9}~= zuDcmaQF`@VinkFX1y#SsM#uT*HO6SUO6*3l?$S={E;=)opEhe{c%-T|M&AvqTEcW< znFcSY^HhhZCXwHiJUU(sgwU)g1!_8|W}-qwY*|bcaV!C6iFrRy7W1*JB)bn-Nk?6X z1tjZ*9BYA&HK;~Ixg0M`QKwj>_X#MEXlC>#Lc5=}s_|q}I)hk{T!r(}VVq}%TvV<1 zq7z(2n+jOC@nht$G?JTJS%6$(u+AeSG%wU<>9JO|YPL~#Fl9>&!|-_MuApfZ{M^%M ztt!&q5v9!>bdi*%B3cB8XVpRnbxt&oT-+3HS&l}AA9pke=mqQ*L#*?JYT&3-$UrC< z4AvEf=7kD_b@>bHP7795P{HW=G|W*PN+wi3FHnjZixH^8D&_^Nib^Zy8-XgUw!F|S zFpwAv*JJ96Hc7TxRxE48Fq39`RL5yKnFgu4jimZ%4~!C1{iFqp?%hT=itpVujcsU69&_n)6GH7S80; zDnR`*stpAJY(1!b1@&}SV+pXJMsbt!p)#pv+qMxj1+YOA5$YAlCrQu63M^dc<=|Ge z7=vfB`?_9g9j^aPX-0v0f|>xQm7or^!M)1?)|32^2CM+lRx7O_!T+c!s^?U`v#S#M zJvhN%G{Ij;g;dgmd8VGU#;;1gD3e*&oI7BGVdN0hfFi&l7L_3Iez=p<^iIo%T zcqETTkj<~I3Dm$9dND3ZzO14!C|t1-C@2V4yU6C32FmBMu*hO?Rc7i}qn#3?B1bDW zZgKI-mhPI7F6<4c)|2~cv{|k7SZRb0ROzWVqr@hbw<@X%6A2Z5k^wvaYAK9!uiIa# zaEX~;(%98p#YDPVa!YE9gFL|1v!R&(#Hl#UsP(9gNZ3m3{Yt9Pc-oDG-VH-`tzbow zUEXdIvR~Pg72e~JHtu5&Cx&C2Dtj``$t0a<(Bdc05uH~_dg5yoAW6$P(HV zk|B`pw5jfd%8TtJA>J4r*Pu0MOgPoUL*Mqgf!=$f_N=2t9`Z63_W&Ccxjk&t&X?yxCqZ(tc?b8 zss!L+uA+8&+n13gHO7^GTlNsB9(S+rl+NnlskK-L31MuYw(}CzjSMM&lW(*yAWuss5oZ)?>*G)df!1H3i!YGU>OwIW?_oi>US5NE3=Dsr`&N_>v!PbD*e4 zIl||~E1R$DusUvow1SO=%F5E<0#>tXDE&o=4i^&K*QpF9%3I9=6wkjT+S=?WR^2e- z{WsVOBbV*b);3H<%pO%9Q54DJF8DLo1 zg5g|Cu05Jj$IUIO3hE4%szZs^hsRvpUaN8D>lJlu+}6Lxxh?${vOaeDmJ z!^%aaS!&rUF*HTeRhg07?Wizaz?7L91H9L}U;;e`l`eT!2b@kzsOfGbKPks*9Rd{> zdyX-++SILJ#^jAj7)WFhZK|CbX@2ApNDgy6X5z~-@3QVTd``}P@|-U@6WXpS)K~;B zSgi5~sZRB8$29-`NFZnTk##lS%FR!tRMNW+xu=b(m_%h0-3AeOnNIp%`TB%Q` zeIY&CpuC27jde_*a9*IiAUK8xpM{|^v?z&lG-`Ly;momW^oO+qwNGAaPfue=s%!>e z*9W^**nmi~1V2|4gaS1|JdPOm=p|ySDx!u1wDO@wtUT8x{mKfO0aal>n=Hdzzg`kR zUa7rJ*rvQWnn#7i=M?0Ot8GUO!x9}4 zE8w)R)}5d(F2JCwGEiu-|M^6m89$~lYW>AEe$%_!M|AvErM{jH>G|7N;AkB^zn-Sh zzBZ$fW6E=A)o?x;qb@$hLYkaxJYT#oD-Vmxr|lei%Fq(|t1Ibvn_BFq)j2qlx7u(o z03EJgsgHp~@m6a@ZRxh58;a_aF<8fPml0p%X@oG!DU?XFo@XGT=Gee|?hC3*LPa%L zYi<~4)=tYIt0eI3vO>3-JV{q%S|?Uxi6AexE)JuG;L1Yv%3mK_*0`j-u09Okv8+yQ zuHRc0BOkP#XjE)lWQrJdwBub9D#Ky}SKDQDGH7gRuzWVRT`E-4&CYT!20?aq&lH}?Ji?hx}M{%t}Qd-b)-(}RIozVLXS%2(18~`#ksum;ibYr z)j~%Jh!VL$56DqNl=Eq7hk2B0tyO1d>ys==p@bYlREO9e`9F0%P6!tVYXVipGyU^a zn}p_cuKTlBR9ahIk|b8D7VcdiQ;TKMMbirl^c+xMnV1$!8^!Z7K5HP(57BJEZpS=i z0!oPUO5;wW>V)l59@zrIgP| zsVZ`#s-}3Z$^r}Upqo1Gd%kq`r)aTUb96(V1eJDy2Q0JwCBgDYhj{2824z-8_bVAjx(>?WsFVJHiX>IlbGe zqrFEjiS6z)m-xGtaR*+M#xScMt#VYqxzmF`b+FRW z@L?61nuLbd4jKm1G_g?~(WFNa^~p^Z&z}A8Qk`>?!D@u=KwQFWOVvhVl89jg@27M$ zwNkQlDo`(FJXuYRYk3eW;`DNpEkHZ3er%3L=}<{<<+3nNp3*`>Wo?ZkbvSQOSW#A2 z9mMnl+V=Pp*}xg2^ku8;_c&t4MgL@H$vD-9Lmql$>JUNsi+Qk7k88f^~C zP)iVtVb!G}SM8ANs;n0H(KpVXjnTq#)s0|JWLY@r;5$YT_K{o~W?&$TQ3f2QhQst) z@M!o@=7{nhxa>~|AA6{H<6pS+{FNfs#D0p(H9ftwbV9>jG$*y?w9|o0185qkR$LSD zD1A1E@qVu@$*dQ;cA|t=5U8pO1&XoLf%9Y)rFOXLYh?a`_LP?KCCSQ>JU^aYUg5qT zxEO~f(SFOKp~RP%R-r|1t;fo^En)>7=Ub9jVH3Wo9i&m%$YLFVU zDsbutRhW((sZ$ba8h|S=@D4`(G92bsJKm}gXuyROB^^)=(aMI7Qt6_?4yN=}PyuSr zBMF%Ocl#C~%-NDng z1I6VP)wo-u4r|_Z^sb$fZG?DM%8`@CIk z;{5}4_Ox1G(?RD0)NBhX>eCK5mf^V!I~cYwT*^=x z!I$fM!hW>>czJffs!EwhV?HxQsH$Y;+vC-|7RYxoy?;hysC*{JPW~jWRm*}Jz-~@C=DuF8bxwOoh zn^^0n;?#b}o+28lTf;VZY zQo5K*%5?%w>|-XXn|mC|d$8%p7oaChy^%_D5_$HJ=w;ZUFzU7HLXx9F{WwV#B6^La z_xKa3N58fNgU$JBat(jrRYh7hHC}i1M{wo|50rWl8daXphKyD5=_i4YqKvash{tak zo;Pwbj3&qE#B0z9j8TQeEAtdR>Y($5TQ#bp+L)Y?2^h~MUU_N`uWW3_(O-GUlUtZE z^75d%7KV=0*&m7@{ZLI^zIx9G#|G+ZN@#VnqO{O`6hVD7bs^4ouxx_{BJC%gCzE%& z)sZvg6BRFPZzVVi)IP8TRlKWkFR;z4~p(mvm@cHy^Dko~Ts8grr~@VubG#DTKqwB6l~bpsdg zxacQbqKB`^mmR2Wyxw>rfWZj$t^6*ETFO#wxJo6Tzd|LV?iA;jjz^c*)S>R#z+>c+ zUc2BJ3D7D{RdBXC%B9*>oN93IWAT7Gc1%_G9`>JJUa?5oC`{_yv2=1gIgN+#=wQ_Y zL-($vk*K6Z7UB-47MxbX;-S2DVVnY-Ku3}6KpqCtU}*I8yy@yLu_SZd&`|H+5Ao{+ zOt9k|NsB}tpIu}hul!LiRhArWu4+iRgQWwM81#03n*>a?E`|v)+C9h1Bl_(YkHmwm zUg_`$bPt~NHV7?*)2=ys1$77p9fQ2O;*7!DvV_MvZPlS)h4`01kUwJ|JcM80<?}WXC(>Y(3^Um??=oQ1Hb#bp8^k5%dq{|*(FN+|B{qFlWyUxD&hc^*+xqs5L z@8x*rqkmplUO-!6N$XWO0f~d9v`m4~3ymP@vIKjvSf8ZFqlIpP7nHptQR~+7Qz^QU zCA(o-o5r$PgJgl-?zoL_^k|8VH&{(F% z#HYbSWF_{QR0pF{(mfyU_Gy2mdlV{Td=*=|(YV`BEB#>}x_P8r=f&N=d-@Y;cA7>F zsKe_4?R7X}*U%J+(q)%}3$eIPO*rQIYVMDI6`L0`X>-61>!2M8dT}`ssMg$T%PQ-F z3krgjw69ec!d8_CY{){67QbY)wi!C z3FhQc{1gX{npRW=6R}j2J$(4IKKSNAHH zq25Rbi%NUplY;8v>Z0miDBxG)dLd00=wPuT{f854{4^8lg+@VD>ZfQ4P6jNE<(^!> zqJfe!HE(Kib7Qv=A{I+eY)SiybgV7GWmjoLig&zIxk=I&ZAe?Rd1ZY}HSk6g+bz!! zjMg=WvGRtrrV_4;wVyt55&93g@vi}7I+=7))GcPJB5_gF#oh-+-HxWIh*Kr%uEcb6 zH>s~~F%;8xi=mjlTMWhY-2%nlx@B@--C`)F?-oNbeYY5j>AMAry>-i!zPiOwOy4br zV)|||6w`ML6npEIseN^ep_sl~48`=_VkoBX7AW@CEhqHVErw$HZZQAS^HOy4brV)|}@VsG6tt*>q|6w`N$p_sl~48`=_0>xgrWo2_Sk5p)g zqsBS**8;aWrVP32NK<>FFPhpFebLmu=!>SF0QN;wPX_yHx_C<4ApEalVSyL|r z^d+^tD$pm@zSp8jeXmB7`d*JF^}QlZN?DWY>hu#8Eo2)T0 zNh8mOMjClGG}6ekp^-+Op^*PsBaJ*mBkepJlYunyY-psBXG0^6JR2Hm zL-DBz92v1F;Qce=O$C7e(&|M|vw4l36%K1Qd zmDCdf7ad*AdsQ;2hyE_{^q~f%9_+is<6Q?_wzn0_hi`hfkD%ZbK)jwl{a7XgFcW`7 z3!cR^>C-!eL=!YWI}R-}S?SS#4yGq(NZq)tZ+eEAUXzszb-V&EyTzFf;R}=qCa%zO zLzijz^iMS0dXGd_8 zqL;2;MtA6(JpVO)uEq=ghQr%BwVVk>PxwN6!7jEV(9{#UuQ1(sO*u)AKnXW#;irP8 z-pSwA{_}!UIedo)6g>$S+6(S?t&UHisV8(_VY=~}a*`f_5^mDMPX$dqI&at1vAC{z zsXB6J9Z#qBkN19R)eGtf)^Dn-zDo&H|3e5tLKFm|A%0<75GCFHEBUwQC=|Du;y(NK zcDf@N_({am^hrd|Owcp+9RT?CH~KNbPHpa87u+Df{PK;bxXPM3|-tpw^ za5=n9TJr)6C>sRTJq@_l9k(3d-Q4^#tEg~+RaCsdf_p>rU(Ht<(96{}TVTdIEdp)iCOhQXN|T&VJ-@1LJ->3dpc-Hd7^RW1icrf{9aL294HT+hWu5OQ<&TXLpYT;{VXp~;CPo@L%a-9v#=7D^xIWP zJ6qH5Mk6h#qv{q=DF`U)SB!&&WFtVwzDueq$}4KCiKe{b2kgr6Ya%st-WoLS;L!D% zaEc9Lick5iME;3S=_9_^=aQf3_O*jy9m7(FbByq*2K~#so%(k&ZSrq4nBmtLHZk-{ zM-_)pXE=`G5e&`z?8p31?{cP3?7!{7H|>c0Us(RP3^y_Sv0>+0rjOjLc<@cTQ4hJVc<@dA zy@g)f%lw?i@>LAK%|^^5BbK4@wa(asmgk{AYjH_3vGVuQSYiOY>iU zQ^V&NKE`k(LkYjzpocsp9_; zr~5k$FK75wBR^rLPh}WjIF;cjh6gkBG5q&-r~l0Kyujfb8Q#wDI)-L`E@A$;3{87Y z9(>b|=$*mxr3_~=oM6~FnCT1uq4TrE1D$M7PC%TmBn4$o!C5!xVwvD^FO zht=>Iz%=dG(B~fJ_tRvrzs1f*w&P<9wBJLYN0>iQlWKp6(5IPy0Q0?o*niDPr{wD` zgYRX>i!XNm&2}U{fgdv8%$GGl`&0M=2Qpv$EPfrze0ryoKK(UF+Yc<>4M_Tjvwcao zKtJ=PJh~Z3!T6%=kK%NSUnKn#m@oB5@-f|z7yr*O_~QQ(gP$VXcCh4s>mX-&OZqC= z?jY8X^wlt5;^B+mhFO~w`@6e-v~zl-eoOi%dZfRT`RV#+ zcl-Y$kMv*3{2}a7@&7GGzNCC^H~3PY?=kq2?~TkCyV7oKGWb&eA2Ik+?;dBq#AjE4 z6o}N1rwzMOe$N_wN#_g9m-I^hw;FuO=Qe|%B3X8@q;Ci7i#RN?e3ZeLcqTG`KRc!Fh@^iO%bU+f3G>CSq`!vwl0J9q`n&~&ox$>w zU-8c}=1V%IoZ6W$`AlX1T$UGo$=5oAFY#Vt*q3~5Qj%+KZoN_uZ+ zzC4$y(!Y`A#h;SSN0=|^kaU{uRx0}z*F&)*_J3{I_p;-~7eCKpJ7)cPmigirNx#{j zNV;BPdGW76^SSU!&kfiwP5y&iXY$|SAd>&KLO%HqJFbp@w68<^IhSYUL({U($<**z zhC)BX;r*DuEknya!0_h%HGSO>4bNpLbbgNJAI1Dl*_y6q_~K!jUNdYTq;fF*P;qGA z{Ickf^z^15q~-b_pdsyhw{_^fe@c(n2i5Dp`k{12BlH94v_r4=>v4f!d)<;|mH3eS zf@QiB6vxujI#z ze-oFdz$=&*xRz;wF{T9y-_#er$oY+rs^3(fahEdHaJ#OS;%?>K!bbdDkkn~aMNc?e z=WhhVehlC4(ER5ZPB~B0eT73v13;S=X-J%OVCov&%WK$9;R}>> z36%I!(V{Q%COn7pclsqdp8~(kw7{86k7IZs!%w=j-g2f*ebE#7i!RghReLF%nP$4j zv)pUn(D{usH1mDWrA~h=W_p3aH}yU*UA~*_58X|6z4H4-$LEy};eXNTk@$|kO4p+W zdl}qS=@vTzKfO}><97^&zLjZ_yO!x)1;ziX*{;xig^SsaKrcHl{ZD%3?AkzYc zZ|VzQb)1m4WFz{{Bycs|ntg>UK$U*x{`sE$YA6-*24WLjV|(*lKW>I+}wjO7XX z;j_9ZwG+HPQI7W^aA9|-JXE9JNqFTwovvRoyqn>-8GfDNmj7tEzQP+>KNVbR6{x1H zx9~5Y>huW|{aZiLe1Rrk=)OWPyTTVJ=@KaMrJ_Y&K`%U0^Obcvb`b>r; z3@0h^ZV%mPX0AauQB+$+F#AP$^Pu! zWY;VIUvzw4=@9-GogRsA#4uftK0WmFL9rw7^V<8O^mli9Kk)GD7ZvZ_hv{8oVhfpe0`@vHy$^;4LBdh#Pb&TF~(pARO5Tb-xoDEZEHd4ZBUH-7uw=g&Dgw_y02 zubg;3BOaM^}`GY8nlEz6V!4hoL{c_2Q$3A%t=dljQJ*fsZz_|z%XRc5`GZ#O-S`& z1=g3OKK!+b?J+DcXbB(8d=ozX4XuAQ!+e95@Bz#>;T_j%{c9M;7}hfU@Y_0EXcJE1 z@YiqCav2Oaecwq-xEK7_-CF(|4FCOOC;c0ymopq?(9hnd<*#L!Z_pAxfcYl8@IkFV z`Ij2r{D710w@K4&%r9ps^wE##@V`H-;nfUGv9=_&d_>SFg%~(R~XJ?cmQ2ag->7Mc-Bt^|L0U4PjBHLaZeR}1d9Hx z%ok|#h3+f#vMYRnk}iP~Un*MkMc#x@aru1zWS##8hJT;!q$T_&=9@4#U(3J2*E?O! zP-qFSX1)mr7HR!Q^%YlEl%dcPej@WtnEPdpKcwN+4BucV;iofg!kg)0GLbG!@-4O+r2=9}=AdaWO3cz{7m_zR1*oCz=ds^%9m+|uTxC44ROO?Y7k z%QJkp-APOM9n3f3{^w}ilNXFK8N`xRL;kj**=-H-B0uWBI(g&g=Q=~ZP}%C z=qjC`aSYcnJcOYcPH%u^;3XkS=aLx{@k7RW^_0fJC6SeW`3>aSSS0E%;NLn9xBG)D z>mzOLjjb&+2h;mR)zx*?mAGsWu&6XpTut{55-)UGP~B%(HEANlNem}5oWgJ_!xJ=| zG1ICl3~9WYVLruxJL2*eXjsUwfMI~4N(tmbS{^^3spR!fW6q>^>J_bj%M$TS$32TR z^A)eCg5xOEuzG%>;+HKbs3t81@tf_{b7$gCKg$X(sKjlC>Pkeb8b8P!Dhd?@@Eqpm zTKIE*DD4GoZ*ZWYp^@AdZt^F)cIM#GxsxX4O_~(rpL=dv0M3lO8A2}@Y}FPdd<-1d z0w6K?)a1{_g@<$_VI8ix%g6P6b+{_8U~U~UifaqiU3NW%lMS~|ps76)Z3(wW{MGn< zNF=zLeh^q!h8FxBwljTxznueIh`oTwu{-S7Wv%f4{ zzpSw(BDKzls7aenc*gY9J^}rM;c56mJ;E*}qgh2wt@u6hmZkp6R=mZbiX+;Zc4^s_ zfJ&-o!da7gNVk}oG&yh5WNmfuU?Z7QnYgz0Sfg@bFov3{92S#g7+h53=?&Dj)6dOP zLoegCQYOo(BN9Rt~g`w(#u2@Om`(*a{NB!*ag6`k){H zcrZ{CtSbtHO6w{x6{st(Ekk41*1jx?_r~jDk)|co_6c1NZhIt&4!6`tni6zF1mpc> z2|BW}u{~M8xuZR@vaYd(xMEZDnp*3Zo4k$|%}wy@TU#3HR)m#N(N{eBIr?yuZ8P%K z4p5eL{E`NBi~s1@5Y?2^yp`^^ucQ0@;Std7dAJaQ?uDqUDXYX?<`x4Lwm^L>tE8fg zUY*x+a&fqm4wV-xr&gEHrSQ%$Os)zmYB2{?@f6{b{_={t`Bj0+y0So3aj4vS|1&)g z{|Up11})+L_*Bc8a8Z^@o+a=ErUf3r^aUxPssGSX+OEJmnZA_a*BCZ3EI3-rAH-1T z5lru7xwjb};@9%~F?^Nfe#7uShSwO-I!5cgD|!s?G@!_N!L6gToj)>s)}VjF^!GgA z6(?wY|5Q%TWG8*?Bu#t4?0g;m>`Zs~VZfd5C2#Xn=-uTJew_#3w(F5j&+uN_llW<0 zKIyair~0~m>t_ahLH(M8W%GqLq3D_XbZOB)q*j;5;Rc+}zqcfOp-q@7+>|$I(f=yP z*I~fx7wPl~UuYAi3OD6VTJ&Gw_+B#Lvx{{)gfFxSQ-z!ICN25{>veqD2K4doSP5Tf z6Q&9`PM`3FHesr8Q{JRSe?G@|x&e=f>U0QSXcMLiH|0%Q^yf=_ z20Vg)XY6$53vI$w;ikMvi+(M~x6pvWbG1K&FSH3$g`4swE&6A3eCHT&@EV;C;R|iT zRNagJ|;0iU``r$hKcn=nzWofi@>(ZfXcLN_ z$xoLS{rfq-jRt(@2AvM!3vI$w;ikMvi~jT*b$qi7*m{eTFSH3o&*Y~|i~f%}zMmTK z&D(T3gfFxSQ-z!ICN26if1u+#*?<*yIQc@GQ1ncGy0qwjo8$YA0q^ABtrWh{CQKD> z%A2(4zt8b~Y`{f7)BX^?&?Zb3Zpxdq=zpK%`;h@_AJFL#zR)I26>iF#wCIodrH=1Q z27GR_lP|OhMbG4?ON;)k{5zUIG+@nRIvv6n+JvdXO?i_R{c&4#e7Oev;ge3j&?Xc; zlbf{S;LeVq% z>C&PC&SA5y$tL0Z-bd(;|$I(SMZVd%}R#J9IjPFSH3$g`4swE&3iCk=Ro zRYXhTmhgo(VXAOb-lRo;S%!|U$$$%ePQK746g`ulE-m_F`sw(-WWbjPI{8AIQ1ncG zy0qvI8>Hhq(ty9qcJhTbq3D_XbZOD|4bkxpG~g>koqVB9D0(J8U0U=HJwV5IgaL0m z$jKMlgraBi)1^iK(1STX1KxJ1lP|Ohk6=BMpDr!>hYZv49d5wO4|no~HlgU5{B&v2 zSNmi6r4v=+s)?meaz{*aTmc$4Lhq0 z+AE%hH03H-&ZCxC)(H2I5!Qb6n^ybz`i;*Vlrgw}mi^&h)W{xU?VmIBfCCS*tb;A< zkVA(ZcK8u6b)+@iI?6UFzp|hinK)COss4#T#)yogGLFtTCga$Q(HX~OjLjHN;aOIe z-$&mgeGtq7hra1A11E^_eSFqLpOrB=YYK(`cR!Ltg2{j8d3Q6F>RfqqdtH5NvwA1C z@eEzB&+&loovy>bcAADSo~q&Sg&Lk&qv7Ms-^lQoYR!L{X|W^FwD+usUDLi-JZ5|n z@41ya9VYxL^943B9X8+@On;NpCosmez%r%ZbYTwlRBl}07 zSGdU+TJ!|o!}cWHq=hf=8!Z1xl`dz2&oM3VPNoIMnHIR%L*JAaxxPZNmkRdPzNt5h z^Ci$L+~f-_dIAq;dlGKa!WZ~&uJ_Zqoe+2+(*ob(_CnwjObfh;X;a^n7rDMdv6l+= z)xN2>n)4;lE8OG@EqVeMvONhmY2gc;%JOg5>gVTChF3CdVmOQ8Aq@Y<<@Za55-xT` zU-%;b$vl2e89r~odzcn@71IKRFLp#<_#*ez0v%5(cof_Jys(qw6KLA=;)|Sw3!KdM zBwXkz+z+RMqW5{>o7~R}H0^ouMNYy6UeERP!y+qf?{04V8G_3i}XKL8M@Zma5pUt$`5$I)4_+nR}*cWKVW5y@(4qT+u zVM2)~T{xWcA@EtYEAS^w3yd)>@JgmleN$fK`U=HfD%e;1rk>?a8~Y2gdJk>#H{OFxGKFJW5Xa;635F)gsvL*JAaxxPZNmkRdPzNshq6X+Fg@`V;X zfkW6&5^mDM7x*?m7q2eXZ3jO2dI^4g^9n!kW(n#vxH+JYe3hmjzedAv zF#ONAG<`e6iy1CsSj2D?!%T*6Uaj@|F)aS3rvJe42!9LHUhpmsPX#YwIWKrpns&^1 zjxy|;;S!(ly=ap!^65e;pM6l*qrJ@!+pg5b*_oNySsB^BjO>0H+5I!J2V`Uq%*Y;;l|48!d%vvg?5yk|S=sw%WangL56#Fv zAT#^G%hJUQ_x0=V>)+oupucZmf8U^izQF^0`wjGE5A+Qg=-a=) zFQ>n6Xn)@U1AGS#@EtV3cklq;Ap?Ae4)6^d;5%%9@9+V>BL?`69ON4@*mu-m-_e78 z#|-uzJIFU`uy6Dr-*JO{V+Q-i4)To~>>EG8_oYF;34?vP1AKXdea8>*O&aW*+~3#K z-`CvV*V5nD+TYjK-}lx2zG#16EF-%qBfB{xyCoyLH6yz%Bm1iv+0l&b*nWQXDx5U! z-f#du+1bN}WdjUjv26VjrC}3>(PzT23HX4iZ?u%qbl?G&{&$RZj1|DYV-!ue^F!f@ zf6g!q(rc79SH4ghLnO1LMPd3vDJKUO6AWMtA$T^&Ykm z%>!^TPqfXv2-KG7j`XvvQS^s>Z}pcANPma&ANHY-Gyl3&}?xFS1OC%x6{iGHwp^#?l8)re(vN;F#Poz<(~ zNj3(8X|2Cw_39nTmUisi$@I>h53XMQpks`pZ13E;8XvNWFZ@T8t5>T?J9q8`lsZH# z1gnog2}6+jbTYKVwRksIoW*hiIvJ^B`M)wei}_m_ngC)d>ELr-oU|#QOpiA{e&aK7 z(&Q;qPdIVf^cjKtg2JGlJR}(~)W;j@{v=U_rV2OZQ_&S!3w!bJmP|M4%QMfYB7G~93MfgtqJIFycPEZL(9 z?-WjBl5bnWE!M;opFvqed-1P0(@lC>=9tWBnZpoPpSi5tKc~iL8T9Y9j6dL;LUvdZ z-UjSUD`?Y-@1QJDz144Mcl{in;Eq83Zco(jL_iOic-!jc-$aPU=&Txjve)C&8;N|x)RKn+1-#$sf*Y>NSb9&acvrE)ee(3j}-A43{ z(elG+NH^!4q5nD!u=BB7&fh?hY;Jw`0vfVhyl7>x9+5PB|0nlu9RdS!-^eW&BOv+H zsfiC)Gvuyk2h%&4zHt`%1nLy&cPG&K4#_Vun`pBSE7 zT`}qTjA=8D8hi6+r6*3?GWy;9W^Y|S?Yj9Z#vIr5-DzucYkz$FmglCuw(^J9e=>c) z>EGzOIRE;r!1QQd?*FVS?3kW&&WHDX@2Ve850!p$#f8^zoqopAKVPx^k%MPU`TlLs zjeI{eqjOn2@`o9nGj4q7&Hv0Txo1Z4Pj3CsnWt@^arjx4zkL6$qh}7C_3nw^eyV!r z!B5`t$ef2Rn)%{+|Bt;l0gK{T!$zC#5e5`9Dk^Sq+;I&WHK?P5Y@&h^MI{DhM^J>3 zMRAD%F$N6??!+bHu5lxA!)S!KVuDKCw&=H8Q*XCs=79N z?=9}v#$|Dxb*oSH+i`yX@Y5-8`t5K%|LeHe9-0*wJv_?mPS6~le|5$8=T~bQjehve zZtlEhRp)i{R?W5SKWFOTh@{dnct?O8YaFT32n zq;|J@1Nx>p-d#A|eSrLTN7Db=m;Lf&&VZ*8_fq#&+?W3jiMDFn)AQcJKH8afy9V8v zF;~0tYjPaaiuG11aa(8c`V*0zGek~3Ci27zfyv)FVQ>`H4-t6ol@$VqojF5T9gxks zfYI_lBD{WS)5&1x2df>|gEQYhe)uxDXZ!L=bzGUG;*X{u8|cbpU;ScC&#A6V!LSLF zf7(HgsS8DfOT#o}~JAK{Rxo_`Jxi;TDxqizJ9(KrgZ*}GO%Hr;2?vK*4`+PE> zqet5uW$NGQ0UnK`Ywg>YyToJlX^+) zJ*OAgzqtJUO;4}S&V6$7?|Os3@pmmfFv(-^$q#)WZ?c><*gt#ko(^-r8;tT14!PaP z?f#k1GF4uA-yWN5mz_HNn`gI1Ctm#KHtRsgqqhzuyZbI`m#|bj@!VDRFcgKbetRH&+R^O%mnfl+){&VY~i zzqy27?M|F%w3=D;(&wXZ62lqWlg?f2E4$0?m~cKzKI>|ckBgFgHrQ;i3AH7k2k$+2 z?__=Q+0u}FA{s|DKHcPWlLgHdG@JB3`FOP=pRR4mr(64O?T5G5wXd0kT(5Gu#a3k8 zJ02YEJ)_r8+ixE}{qZlV^K08H7meP~?}MeQ7mWV&uLHlhkGa$$aCGtE@K2WS4z-K( z9d_aBu&_F#3tV@@cJXtvqH!J{xVx=L;42gf}3c4vkXriEI)n-R=`1{&D zYMy#waa`orZQFjEI<4qfUe}!$_YDcYzC!bOZt!oFML!Phu=$6jecX4x|J}LrD%YW1 z_MA=bQDry&Wq`~20sU6?Dt~$Y=ej>LDbMzO`Nng0@uCC2#(#5Tjo;LEmi145eB#N4 zU0*JLIkI1Z+ZnqfD?2P$yo^~n`KRx?y!;`FIsNqSnb+fevRkb9vuVA>p~?q`Hw7)* z|JjiDA6t!Uv3TK=#Y+b7-PpUwx6^Vzz2klS$kJuMU)zz>_vNe>neScox9)Yb?cN4; z_YQSw(CtoPs!xn{;o&s?+qS>d=KtKCcj>Pm&%XWr#)?gQdk+lI&3|(?Xl}h}h1(Ci z%y@8VY2M}ikx{#A{rJ(mHgg9x8?@uZmM&pU?)o)8@N2)@!_!wp*H5vzvO(7$|D7om z52<>SB+iz&K;lY~omY$Ob4=v6Wdh3&clF^T!nMPsCj!4>ml1Y8;Q9k$`O)6#;oL>I zOe$0Vz7L$;eOtgiup;ktvxVRj-43t+4q8+DPO5#Y`nevwzjSsZHa+S&4 z5#1wk-j~eYF*e#p5!;w`e-)l>cJxB_Ts!BWk3X4Pu&GJr!A&un`o5yYuw*}2wB zD-!!?lONPR*!zlA#=6_XCMeFFes+3!>tuK5Sus`L?9+ALy7R=ETKBwq*KhLCiBrvr zkBz=P_l1AGsJ_ceJ%pjWZ`Eq2qI{;krGZ{Y~#Blt;tG#|?+@QM5^%z@|d^Z5CE8lTB8;g|C(`89kt zzk%Pvf6MRWzvK7v2lzw$5&kFs1Yf}a!k^>M^OyK*{7wEge~x`TrVpcGobflYJ(wYkH{-+jGGmyr z%y=e<31ucQlbC2`3X{MjGBcUanK{fC%zP$|$zYZ+%bBm4HOPT{KNN>8_MW2nuzbVO zf!i}DTzdA)p?i%jug^G;o5X*+b?3f)MURGs`n36R_tZ4YCQq)XUwizc_r7_`kz2Xi z*0me@j(gjB+m(h*?EZM&F2?PA(Y+b3rge#KXXEDibnNARi!YBXd*XWbQMWw-rI)1-5L2Jf7ST@x0j9gy?VOE z#5>E@+zyyJc=f%rNB-~~nRoL*od%)0=1czvDhD4{3S!;!jq?ORST7 z*pkmimH26AM?Ui!lh3K9 z;>YuS^4Zo({B&y9saSWuIa(~N>|M|~G zRWB|)k^0@a<%c&nKU9|bNa}wd^oT0D_eb~6azA`-t!r?%9(-T9KR)*5#ma+kk66q7 z^4@Lpr$-#=<|X&fDw`KWEk0WFp4?9_O?&>$Pjlyvl>6%+Vsb2>Z3=Uc`|XSF-P{g* zseSc})PE1Y(x>63$c&e7rGC7bck0->ZgKmLNd0+t$7L0%HWA(Be!c&!A9{DTJ^H3f z>fgPaMJlFd_UJG7^Q!NDIy!izcJn@|zxQns`{B)`bMMLh{Vxby1KJg#NWMJsRj z|9j4oCT|B9UvxM#(qh38hdzAoBdSHY^%pc<)gio+TjsoC-TmRWf`2*mbY1{=q_kvl z{Nj5aso9o>pJZFZ&m23$Pe?Pvk6UZQ&jW|*PxuFhpANmMKPCODKc`&`KgOkAef`SM z=diDTSmDVYK$w zh9qeldYxXmLcM?;5qNy^xq&b5?JeWOr2eVl#kHT5dJO&X){`wg2h92S+fjbDo(E^% zYWJj{BI$CQ#|wsTuT!-D2-mLQlh5l98SlN%EqF=4mI)L4H@3-n7~JT3Zkrq4J_$~L ze!ueMhI>P%AKkWI`_Z!4gI9waRqdz@Df{ZTvrDgcXxU)R#NADw^}Qc__fEHn^A~?y z?UHzZvs+<8=A}OypK(#>M)%zK$Nkrj(pSZm7F*IkKiL|7*4r6=4C^AKS!iqd6V}1- zgCVuSr@AIO^eX6Q_&M$JpFLmwUmbWh`N8#{efX7ci{Es76I*rkb;H-6Ri1v;;MFF} zA+}4KcIb70iMD9RUS$_^jTJvD=GN+I`NXoiejgUB_P^CTGq0chp7SO3HsuXk-zwPF zIxDaFsVVq(IIrQ;)JrxAOSFC)>u7#kxm#O*hh?vN55Lil5{^q+aS2>pb77oI^F+%M z=fB6OE0SAQB>$Z^;Xvv3?u(w}-AFz-HE`Eod9_z@^G9EOoL8Km%vsfaoHx8#$=>8) zf94$tnHczY)}y>@AF7rueEdgV@gJ9~ay5$h@=JC+zwkj`!-~6)tXe$C`!sJv_T}C8^Il&I z4y*9EpT};yf4IxtdwF(F$qk&6@8#vGIuE(m=3ZWQmt^nPr|;(V>X4l46L&W+@pY@@ z11<07wMbsS)AHP%y!!UZ_3V@HoTpT< z$9e-ycO10nL>i%Vq%u?-+&>z3sc%W4KpsbeQashJ`e9rB^BtdRopzt;y<*r?tyOvKmZPyxwHGXs=8j$dR6Fj3Q(EZ3 zr&{+q6WjcL&$NeMzt4xgd8S?eqVR*HX3w=ss}nmleV%JO zdA;xc&FJUaTVA8Gx5Pcyo}D^*$fudlwfjdu%sI53@M!R1M5T4(u_^bQRrS*z3 zZQZ0n=^dMuX}`bLb7-JznRZH8rn+`wnbvYb@qiuM%e3i}HjneURi?G_w{hWA<=UU> zy}5kOxm-JGN}x-=u3YOBu<_+@Ys{WP#_`jV3`wTm_!o>ckxrS{UAGEuE_jH9;rHa<||EofKdw-A1Jyox? zUC)&s_@&1yZQs1cn-rs7X`39X=(+pLSK7vVK1#9q`IYukgF@@ZwJWuMH3>a?e@Laa zyKeNRIrA&Ex_vf2Z%foXwANp~ z*Jf9jH`)w!!!_Fi-e_w**gyTnqBmN$O%-Cf-4^4be)7M%3y$7^r4epPs5 z0`$4N8@!HBuaZh_cnW|BYspZ~v|1yo0_Q}Ve`mWd5 zN9(qfg{{`CYp1%c%?;6l?9YKn#@ z3_Jd4rzT%9tN)zBDVh#XLyw;Bzf7~`#HF4u_bt(|2e%yD5FV$A`t8He)Pip{^%^@5 z_nMKV(JnpGZ0+{78qMj6Z}uI}*7Q=vOzoxIrdg)B)8*RQY|YgI`?;<^?bHOh4B|g4 zNY`9Qx#Az2nxXl&py=VaW4km?%QoKVw{(MMP#<;YWv#YpqAGshJ-Yl8&8tnzx_^E1 zTTNKqY5iTBY}7njF=<2eq%E55-{P2fv0q5hA8(yM3(*WaYF9q``FzbEj?9GC+GUz^ zKgF$^vU!*0x1SDc{aVb`*ykSXSn9c4v$vCa=j6@nG?Cw5el{~_i)QEX&n$XROwk0! ze0O{pr=y2D59-)`5mbbs4o&Ggxt5xz5LkG;K9^Wa3oN15Jnnwc4+HXriJ&@}Ds zJ-qLrHJWqdCzW2l`=#djzQH%kUu@Dm9XR&Awt?$3hmQv5NBM2gEFD*7yX|DQruLn( zCmP*)O|XBf_yKFTY0js{IQ+e7x29$7q66Qw`C3!wK}Ckk$?Y1SNeMPn`fSkLytL$q zU#630Mc0IxPm~KaS{weozdl&0xqaf%M?KXWHMb)NM@5X@pxN}^yz*UhvNii#@ATco-EQ@xUmO=~nadS^abbEd0Cu|4CvtqqejZGyWm9_6)Dqk6yN5!;Dr zn(b4UR*efv(zdldX zr1NUWMU6LWYJYtxQ{}Qz)B0$qXM+P)YF<65r|9OhL$k{|E2Yz~OEmUw3p^G@ZPeVT zTA=)}`C5(mJS=vVp14WU^HEygD&_z8{Qcka_kYjd|2=>I_x%0e^Y?$x-~aEQzjz*3 zh;vxsf9bhgEU`*@Y!^%Ono{XGUfR!gRCL#bxUf+5vBH)GAw4-$_sB8WUiWD*bILRy4z9TFNI9TPiQ=E&%& z2*Ym;#Uw-%^ZIjQJ0vEaKnjm1$&zbAOjvAqh|I(Yxh4X_i83Pq*FqX$i8==8PPPjR zOk{~V;<~_sZlZ2-ckz01cTrA)nefoa$ug6G@X0ZPPlU3ykZDaAKn46lP*~FcO-G5}t_Cj0z=QG9n{hlw??B$fIJ>CJ-*La4i%Yt_ec} zvX}`3LOum1c5p4&B`jVG`9^pTq68-H2`pU`Vx;6mCVE0LGEZTm!xH0z!tpm?5m$eX zc;q}jW?E1zN~!vM3ZsjeMj+UZ4VxTKxo3`$2zFxyejzpM` z#6;d2Xa)3VYqMu8wpy&RSZFcPVk~}$%+`V|k{@e^{|H-%+(0Box@1`C`e!MisdUXr zEK_sosejpfl?=-_?6o3I0-WJybGWRmfB0vSy1L`Bs zi;;!;1od#P0s}05^=(cUFwo$F2s`kS^CFgl8%VGtd_{yEc*$iEOTi5!*b%-V!VbLT zx`?IV1`_PRc99ITGy+V3o@r?mm;jA-DQanhCm}=4m!kTz;b{E8Ul^T`8b>d zjet4+hyyGKSfC-$2x$BX!>sTF1NJ)^CTj;6Xz~-oY|UpF2f)4+eqRsBQZvjdz#eEu z*a~5mV1W4w^$#Rr2VFARMF&^_wh0W=97vC6nALzS&>U#7gkjb!1_QO%K|LEx5Qg(m zFz`OU3e^($W(dQq1>Of*62{&4jRzRO%VWj@NLq+=0S2%DY!4wFApHlV3)lk90rhv- zFpvWVT7Ath>$1QEpW%EKm|zXgSAz+Xah?Pw7=?3RFi?9F{M-lzZ1L){Igq{|X#uuC zbD(t}Y|3&11NCvEF2+q{4f)8=t15AK^Dcuna)INrE zkAi_VwhXhO4w%5;ye-Z*B8+^Aq#e#T5higuBEAEdz~H82yybflY(G}BiBYk>{G z763j7I0z>AQ9>S=-~@0AI0pU&3UPwPd`omF2qP$||msHd3UcA#qhhXWPX3R@0P zop!9l0jl|s)pj4nrG<^Np8sJzg|#YB;XSXXYQg?`h&NDW-MF4&F!0q&Yp%BEC~ipO zdPL9KT7kHV7Q`0LiQR!DzP0s0mEyFcBjWV5MqI@Zhx**mK8v~caGQ4p+<`zK0zh+N z(1aK_Kn)}Tn}LnMaljc+0(Sw%0{t4`Nf?)*;JH8`kOZKLg`+P1?*eQ^7ak0Tyt3S)rpGlS=)gNyqYdrtfoIOcZpd`Z!d?QCrK?>&ObmVI}HThbfrg&1F zvUL59^yM;9$u8*gM~c1rsE#Mv{9EsX%}D&6)Jf|2*r1RwHHNU0Bct(mRYTQ5!DM<8 z6cshSn+>s3BVKSqcz9T>I%XmJ>@_t0OHga%K>q!R4o$}l5h65=DH!x%5}AC8eR39%tKv$63d z?gkUbciqoOB8qtci`?!(+i z40a6|Jn%z2RT;+5=_AI~d-!N_?(OZzc=(MRINBZcG18qthy(tR$VW;ipq>HUg*ZI} z5CiHiP+@$A`?!){&-QWk@*d^&u^YN*W}y2>#-n?0)Hu#Z`!OTjMhtVqxrg^a#>IOu z<26=LVLZLunGwF;B#w|KxL5Zcf(d5mFxO#t{16$!JeYxQ#Lh@}cNh}G2E$x0LC<59 zm)9ri!NZ1+z+6P_Gh(=_`^b^QM@aG9M@W9aj`*XgqP{C}i5clRkn#5Mlj6GIsV3VW z?PYLI+-AC{Nr;(4|NR8|eJN_0es7^>XJOh`)V%M_q+K&8spjQB_eaz(*8VHM&dAmnPu8OVz(kX9w^L7oYDCgf_!i-`}&LpJ0|ko!RH1Gx|6 z#m^89xejt4$ekfqL9T*a1$j2)K9H*+XCN&JBd!LKyV(p|65&;62!cJ_B8!hp-9# zQs_JX37gO_hQ8z9unGMq?cVabE+}UpvorDwcD! zhHh)10QyeQFMzHePy)R~KnY|)*8#|dt|xF6dJ#Yc^ilzP;0W})LH`JJhXJ>s7Y)3H z-a_DgU>9@;0B4~)9(V%1*+5-jFZA0&e=l_1fQ!%#11edxsl z3gBDlH--MU(CrKS4BfH7Bk0WlY=D)}?F9S)-66nr=uHGFp_>h;pq~w0M<5@%K0q<_ zbN~ZnLRSssK-U>4f^HyC3f;_Rh~EZbtsw*QTY-UmH5kZm2?p}t2Lt&nkPeXD9O(k{ zpx*`hJD}SfSPk9gz-s6=2UbJ3Ij|bK&4Ja>tpm)0?qlfA0$M|VJ#^~>i=kT|SPb3z zz+&ju2NpxOKCl?NED#I*yU>jV?4h3pT_unTT_unTT_unTT_unTT_unT-3sVN09TDQ-fZA4^kxHZp*I_N3%%LETj99zk~^@CdpSf$Pv60{j5o zAwVf~bwDw61A$`b1_H&<4Frmz8weCbHxMX-t}~DWU1y*ex`99ubbWv#==uOf(Dea| zpz8w^LDvV!hpr=#P4v-E2BIG1@YzbC^t--ayb-;DO?}6)q>x1oJ1Nz0J zAGJUj^aB-O=m%PXp&w`ihJK(e82W)KF!Tc(fT3^i0(ON@?yxlo{oG(+DBukYM;nE_ z2=W}rGa=VO9te3LkQYIo3Aqk(AIKdcS3$0VTm^YH zK%NbGHsp?wml7Y{;0NTHkOx8@2zemn zrOpV4JQH#qw7NdH6F?}Rzx3{e0-Fpc~j1Hlfcz*Jl9w)&#_#1^G-|1I05CA7FsY>Cgvq z5@8=Gng)HKbSmrvDqu788$o|FbbA9Qp*I@%9ePs%OJF&4+XMTd>j7MbUO4aqx~qXY z&|eMRF2G^vdI2|~7X?&7FAZo8?0~KYD1@#*@EE#_0T%j;q1zhx9=a~TdFTcM&!9I4 zs1K}%ZV%unbVmSpp_>X+Kra=r2ev}j2`GTBA5a3lL_i5-LDvDug{~)X6}q#5C(xS> z)CInQZa3fvbcX@Apcf6ih2BEoeP9=K2LNZG8xP!vUOb=xmO{5Juot>+z(weW0%g#f z3)lf0q1zKU4&8~sb?8k5DxtRkXbOA_-M+xj&>agrg5C_k23QH*PQVY)4Frmy8wiv_ zF9}cq+0b-0J@$)0dzfq0_b`I1<>^b3ZUx=_!hdIfN!DO30MhT z8(;=>ZGar;Is)0yRRh`3RRh`3RRh`3RRh`3RRfvOWdI#?86X?FY9JH3Dj*ZODj*ZO zDj*ZODj*ZODj*5ErO*omio9_?47Pm0AA?7NeZix_W5Az)$AbOA0>-@mb*L@0xzHU4i`nn)6^mU~0P^daCEF>lx%UZD$5+;Y?<*^6ZAE0(8`xW}Cv4KEXvm(DP5{cz6H5Rkf zo?$^ctcj3K4#F0O$Y@7GFDjUlEU1kLkr7sL`I4#BsNS~PZTmMgw)6y*nR9#zuw&I8D|s!^NE# zBjTf6gLLr;v0sqQpd0#FmDxQBorg5a5Kk0%oDmB(x>SQ%`}rnX_$AaubSMiHS$W@U~m|zGhtg8 zsY7%hQjpl8;%*c>7K1d@W6-#;I)ss=+n>J4nsrp~pTT|8^WSqyuli1hs$*eg z{vS9dd8=3dC;p*VjV3n%ZYtcrG3d`GOC2$~s%Ujks8qwa(FFU|_0cs>7Zws3PTu1f zYMCuMAyDt1zDdW4RYqPsbn79Mr1-AEuuiJ`5ud2AAarYZ7DtBTaT$TWGB^euAGu4g zkuVlL7wLM1EU zQlujyPY3bIAUv7KfXwiLMyegpNsN>U#o_^BkjyAFE(|^*qYbpsmqp@n8H3*p*s0`*Qa$WR#ppXg+DI(fs)OImkmJ^n2jnW0 zRznpQR`Ech!a`ZcLOijG0S`TLLflwD(pw7admWrfB7)y>!b_)S%!IJ$fH1K;7lyI{ z;u}>V)r$I!pt-j%mCABomBs@dFRp zy7e0WHBOe^$L~VOj*@hj14&(~)4fY3Y4~-LsGxYbE~ZrnzgHs15yJy2Y>GNuS1J@% zpTj6YilUa)c3cl3sS(`bB#GOZT9*_n*Z#`hQmkCDV#UhU*4Ien9xEqziQBd3F0mjb1&MU_D|d(8wPxcTZvV0^&sjsGhT1>C zkjvT~l6W5RRuBtkceq{4cI}c=HWlt#vv%#8Efo15#798~+4o)c;}3hxCt?(D*N=Js zPP}#dfBa$DjzvPz897$d;5uagohFlx2nsV~BHp452Pt@~;mCpOFZ>NsrZbM-+TVY9qORh()x2%EBsU1@t_GeVH_1Sf-%fyfqBAir z^h@4>r4HPAP9%tX;AQ5+8)F1g|F`0bhQzKw)V$S@GJ+eQPGJ z2Ps%(Yjy+9+=0D7B;c}9VWo5>z3|z!-)Ey?e2G&M;R{4>LBi#LR0Mp;{=y`Tq&JkT=@<8ZbgvNc+2}Ba@5{r$9D4gCEPRRpA2h5xgsf)#0a!MlRawH_~68!5g#0Jri?Eje7=lV*TeqT3y_0zFP8Bo;Hx*_ zPksDC*)7-u+utqYofp~9B0w;oV!P#JC{c^qp?AH+T#*jBH=eL6GJC^g#*f5p* zQ8}Lqt^ntNv-*_tSHVvGkZu#i)0Fd0V21(ad<57NoCVJEhJI7%gLUAn;m`xyk1Xds zvCp|+H2eeGk3sriC$N2UT!)wQf#Af5a=hzAyom^Jf$&MN2aeFeAHs=9|9#{a>;%r5 zUd|_hBNo7JON4`q!KoSLyjl(a7C{bHE{9$4)~|8h3i$$W1t+e8z1Hx5O*!ue-U?0v zE7zm^+Q1Ijk?>~t3y#>1cx~a|?sC2WTyhloZjbz)N4!p`&j%Ml0dECg1?PfG!3AI?2C!GbYH$VE6Kvn=1+N1~fV05`-~w=JYsBx4@&r4A zw}Sl$J3_xF+Q~qq-y7+MBVIqGn*e!#=r4G|lbx=q=`Z*aaK)k*yt6ZS1?&t&{5|l~ z9d@q6&QQd^`GQXbE6WjoIJovpKIvolY5S7T2N$Sb@=n;LR$=#&F918e|B?^Hdfrv_ zOTGx4*y<&(^hG{ezvP_>w|U7Yf*snv$?iZ~?etEYkl3 z{)|UBSQ!XCaBdL%9Si;7mwYDJFBJI#SAa{v%CMKb>QnG6$OGWd!I!))1a=QWAH4M_ z!m(1VJcVm;?tP>e3BOboyiYXht$qdG%^=-I6?_rcuSW&1ibuIcRq(lqNawQ(K6M7- z&#vGLz)qi6@XDF6GZ*1t&jl5H2{;0*nnmibf_DacepSIIf|cnN{8kdau!6rzt}~(k z8RUy9cqg!ONd+GPE?8Q@XMvrTSMY(ek?soU5#9>>;DOs<|8w}ay@K}yC+?`=Gr^ua z;TJe-59CRZA4I;u5#R`L#gE7z*s~DnCL_IT6}&nH@qVx1D=aCL{eN;MiBZAK_{67wk6+>46=RAzuo6UqByR zvHr=1~F8BrZzeacw?1LjN!9LjO+AChQ67haRIN>|5_!98d`|xuW z%B$oRA3^vr%4Idue~R?Lspaq=tgJ-7!CT)TU9hJ`CGWTf;k7FHTyTZ5k}n09)T!hh zzQMJslHW?WK_!2caHC3Ixfb#!m3#zP*}Rg^1UrCp!C79Fe8nc%9bU-?ZpO8LC0_#e zn^4L7Y=Iv$D|x4FD34i{d=^-lRLR?a3;hL^e8G0u{|b6LAWwr`a0R%CT&Gv^%uZad zf<17_8u+~n<+K)lfUmBrNX5lF#3Va=(jw?}vS`6XAQ6d=mKT3*CYj2u=iNgSWPP&0jqPJL=cG@-Vp7Yu*Q(_0em-80;DHn%Dh^@B^>;B5*{V zD&Fx3u5GJ$PjCe|5F8O$#g~F}4^;6!c?i#~;v>MR2dns0@Kta&*x_&$pASwvQpFd8 z3yxLsOg{WLQN{a#b5B+AnPBDVDt;^BGgW*A*a@uq3F&|xz*%5t@K&%NI2Wt~7l2d2 zerJ&$Sb46B&jm+-i@;mKC18glq305!OBOl54H!F68;PMISIeOMPN^G2{`LD%I7rF0Xu-Rs!$GK&o_t< zUJWh=7qHmJ`ZLl2=Yy3T_PhQ9f34o|SHVgp_Pv7b!OB8}gVkUsuoKu5>;tXq z*b@u32WNp(!8zb8Z~=HLxR}Uo-tZORMB6uf;u(ZD#$MZB5nm0y0Q;@p@b=&cup>Cp z@eRNAGVE)?SK+T0_N(2%^{0q;3-SFC4_q+r4etrA00)xr@vslJ4}zb!;YSGc!3E*i z=Ua^QW+NZqf`yQS6E`CtV82}C1MGmkz**puI@lutj&Q;r5W?7ZPz=rr!(O2~D2E8_ z69Ox-x54i&^x_c@d=>0?4|432Nd%L_P9(-+#t6nDQDxDrj+HXg!b(NLn*&9S%6JSJ z^@sWR;&+=!{9+a60JU&ZSLIRHe#m>Zrzw*dZPR|8K5W~X80iVP0TGQchXFGK>#AJq zSgPi+LsWI^IyJW?-vw%FOMWAvb>vwOc!LInL!U13Qt(j-7QRzO(v{ahEydt!vjau_ zE(j;-%4?kBTBt$t?3fgeTf-eQh#!ym^4jDEW4oa#3Kl8dye=uONgDc-34ih|WDVlK!lHUTQCv$jjPH@6;FkB55>5oGOqmcZ-6mA|X)XQGPm)Bk1W%nlH$!jZDKSn}0 z^vz-hiXrP`Ia`9=IxPC8L(W3H6kCC+<8!lZNu3D!M?87X|5%Jx9glrTq6y!h^G7^+ zE-%jY4bt~Rv7TY|>HEUhG{g@)<1AVvZ5@Z+-(e}1>D)p^MlEg;D({@jS(h`eC=<(h ztQb7jTp+vAT<9m}0$G1^p@HTC*$8u?!^{PufK6Q;@oxvRFv;zDs|>nV4bJ#`XrM z)Jo&h73}0=1J4LHQ)&^b>53rZfT6|#ql^RCfl>(WBc!zh>E>K?Cdo2tD}fC1kzz?w zSy+p8IW`cDLrfdZLk#8mS(*zTY%Y*kUJ5iv8!WovOfK;kcLb_V&pX9Rw47nd4MaN@ zs##3bN=`Jx686mtG$X4e}Q)-*G11Qu9Zu&V0^C8wkXDor_M)@xHT=zV2Q5SxwCk-H*%~$qD^e8Pdys zD5mElq-v1wk>oBApJ~m}#!97lWId4_`ttEHD4&kj28j$zF{pq1`aPZ%u#;WpOw^g2 z+5%PEVIvI;Wl}T9Wm83hLDDV?k&X}kS}-}(#^uOP(BB|iXpJ6v8o*A8ew!2R=#1}g z);~p&#<71L8%R2g6a{JPUcv=QRw!Rz=oc$Q{Xikqpxgq}6ypkqpCtayLOkbMQaZ-> zv#&w1CQh2-x@id5+NsBjHj7Rbrig-CvJRjqFF>FV(=yes|XUBhy? zMXhh{8;m5Y6EM8h*g-T2r&iEJ!q`=Tds5X=^eg*ae$~P=#C+mHd+>stoR6e(6SG%c zZi&_HCLZxhrFhvEM&-a+Sdbp53xZc8US=Q3FG@W2bFsf7@e2{(v7Z>fSTtLmK6~n< z`#jc7^gD56NRU^gpa;WL4YNpo) z5&whqNV(5KdikTp^yKHTY5qJB^Q17J*g^aoBgR)FlpLn<(Wnuh^t(3^-%*NRjB|3B z##bOTjmuCdjw6cL=A?Qd@%@3eXuGO#G2KAvJ~6d}Cdj043LWT}&|0N-idmatqJ?Q7 z;^83u?OfQ`MM&jfd>;&pWJHn5yAWZ+<7*gFv zxk74x%ub^9u-xa=%#*B<_EbpHPZZNnB7r#c>9gS^!ya<~N7_df;ulNtbA;Fi@dt_1 zN;X31@P)aN7&2K35EaN;fLlG>r_;rBi(r!+`gBJcZN)Tn;|x2a{u0keo}^*j>+?OkT{+Rl&6gHNQPZ+91?3BK>Q&7xi!FZW07dT z*w{Z|KAfT8z83~A!q`)o5NC)H4?}-0^qJ+7e^S4z&!6G_pGL2SNLn?pKhPwYf14l7 zu=1*h$#idufeL)aLF&=X4)dF@MgM(-P=orJCEjtQ9-cedMaK4Bu#D!Cp%{M-|FJ{QWhh3Ub35iD zuBv4yyw9;GPCcSF2vIi7L-8`j@r>K(?GAV*|P z%cRIh(gdPIq~AM^^qKXdpGjhH_5Ii~+K^0JYICj24B2n;rM}yKa2lpP)>z5c zIDqvw4)He*7-by5ZZQtgr;}nUJRj-^3(2c;x>LXYRT8eJTzX_gON5%Rq zlIB0v^;tbGU_A}mLXAfyn@g1h(Ing{>gXz%ro$R8%GLCM>RU9`*jk!IUd=Y?A8Qf`Kjaye?~w`TKcVKU;lYa)WFQG;1^&`i;qTrQUv| zKPS(z_!fAt@IXvI=Uwx?(bVTJ^@cHQO(eb5z}HGMMi=8*M4#?m(`0IUVSh1b$;2;`zbe>O^P*j6<95i| zSYY1Y+5#St`0+$Xjq#9$3%O*HECs5rC(nggL*XuwM>O&ZM zY7I)a9F$@;vCs_z5Et}ewbCtT2p8-mgW?q0QyJ{5T8j2n7Do05iRFOTCBoB0y5FVz zA{1FWCr?xKq2fHyt2Nq_TC}Sc%~rRMPsDj(TZG3Wp0gA$&^R9Zu@DhcIc7EDWlQm- z>Js*=>_vEvneak{=NpBS^zI|PND5aQr#GzHuR3jT|I*?O7vquprYGX5TDb_8Oy;|| z&(pZi^)JLo`k{y)D8&yHv<&(MDZd(ory*Xx6ffU6o^Og$e)GakKn*K(s8+EeNK}$e zA@q}4i+;)FrEfPws`FWfc$xnq-f}rFDZq4E2PMUebn@96;(o=*3}qX)UT-U{!nC^b#)g_3;zFe) z$cR!uT;}R<7;4tlPhV;X&1$gWfKB zGHL|MWmhBcbHSOf%o*`SNenAwm(`TXsk4V`hD&dEHku1H>kjs~m^v=sMm2mKRw%D^ zO2M)ZRDrS6qI1DQfn}OYmn|uDNvn%3cBU?wEw*8W9%*&ud3z0K*%3q}uYlALagVGD z2dCAYWG)r|Lbsa1avs?V%xCJ7`Al8bY&FGE(-RxWmo@E*MPbyW#6V#riYHDDr>SL3 z6^tcCIj9ih0>i?T^Yad;*zNDg3Z)6S70MHIqqgBwXw;Uf9q^`kR_!w8(Rj`kcE$KR zT>pVV9lXD-p`L2OS+H3&Mk_D|@hJQx)%1v)w5xzt^g9_XeBO$B^0Z&J>NB-`Q?&qw5>3F)L|59Coo6l!^15b?L@<>w zEv?ZcOTz?9Ddyy3%msSWd@}4TL(wMOdSKqVKx`-ZXgB20w-fpO_il7L=frWS^!{7u zA`M@Ni*;%kg6};DI^sT=9Y`lBT}(%ZaZ-(R*x!WZxbM;w0flJ__d?$^#fU<7o)CCN z-1>%9sWgRKAv-ipF}9H1FD%?Wh7rYwnD;Ld?J|}|cJ=!m*;Juig)B&`pS3b9+1KVm z!&9s#980lcx0k9VKe0_FQ5;Lv(GST@l>UozHF;EIk_J^Ek1GXnKQy^<&6Go+pN)$TzrO`ogvDdM-8K_+drGWv4g4csnZ&& zU<8?!(2Egxo?uD+^?7jSnA`I{t%^ZjiIZ^y_HG&R26OqHJ>#$GuBumr;o{2 z(n?0|GEF?A+M){NyU@hcKNql1Y5r2{i#~?zK^>|8b7R!Zvj=D%vnQ#N$5crPRYJG+ z7ga}I_%*2+Y61NJZ*bXKsAldxnq>CcH%A7cbyUTpZx6w9CNTX1v3f3;nO zfFk4Pi<_{&Y=_~tFwUA4E{Ou!S2P#;MYf`Tsj>bLX}vE<9+b*I_`ro*7zYhA4qz`D zhxkYVX-KyU>E^4%bRF?LBZs~{d8H}RYYkdx$?iHcsL<^!xmsxHN!E-worOV=m$1V0 zsc{I`*n;f7IqM-Ry6IP-U<|?TL`xKj=`IDOxno_%PV~o_eAgF;-XFu~ba#cp%~9iF z5TeqX_GdxsshrOS&BCJ$RSi z4!yp}^CBL0iib$`W!%rPlQsZ>6Pf~g^eJ!;R;;Me5 z1-Xy>q0fvF^^=VCy^;O|F)bn@&%rdrPmKZgy>pv?#4Tbuv4rYaK zyBPKf3&~51S$Y$`NLS&7=gwHkPmXwFP@ekz*Jg)J+z)btu9183eT_ft>c)$9GmXn1 z&ka(k>|En&W`&WTVa1CBF0s#vf^U0amkAW@=9sm+ko?+^v`i?po`nib+G+-HO)&VZ z8H9;K%>c1KZ0n77A0(!yHcpS#kp{=ECI1f=zo(8r>CO}di}908;>Qc|E2a3PJwdUxdZV^rFhadw(5A&FFVY_ZE_Lum@v^EgYT4)quQR~jrxvx z`9Qg1O?m`s$H!t7dIuUuE)q+N9C4(`YL4rZ*E6YK!Fpsg6-~=y%gu>Mg@9ql&0^EwwaR zst5l~Tig`tvqZl_V^YxUI9lDX)H{%SNG%)s@6mB5=t}Y?B~$OH>&fzxk%?f zf3S=Gdno#!hBQftUUFP9J*xw0#qD7rz-^6tx#wui+YUjfIS+Y0OV&^_tnig}1iFY9F|sUJJ{cvSkcASrbM9h1UxXs<)xch)pL9q}Uu}f| z9QytR^9Qo{z~-kZ2c=uHo3Y%&JrLWUG?x&>;sZtsNmHgN9~Vpr)bUWnogu)OJC2i@QLrFREiy0lSk{kirkmn4Nd952!kHNgh>) z{{fh^aa;DxH|A#jZ^`=&4jdeBjUvtr~rJ?i8xi9u>#hrz2t-4<`95?o% z_FeQF^bmCn5=5&AHkK-(ZsX#1Tho-d-2cGszSI)rx>0jPy}Q_BH0P)VN4>eI0_yVP zKTl&e&0Vsm-~8i)3#OJB-J1MX&E&??e_zQ&@?XrQxCVxmCzC|DJYf~ooCx28r4&HG zKQ91kUD7+2LA3PgrA%EOk-y1fl7G6b8?@xd(**Li8k0oijWt9d{2ql}5Z0GfZbAoC zWW0W--w)1xX;EDP)hn*#mkh+36!g2`|4`TqtS{O#{T+!599iJ=5LWrH?u>nuOA%k! zNQ_@>vOn8!{jM#ws_}DXl=cxs6L1Tt{S$pic$Q|3-g1Kfu?iPK%_p@_p|8?WRX87N zkIMcR$7IlH_D59z==TIwg_}vezqtX_H@Q@E)LWk_pwA!aDr;%w z`Hmmc`;@BVOq2ZcT(VSK^r~Uyy;vq=lSrClP_tosUv&p zavAmJp>{Y_fmm(Aei5(9ct8J{SfANaxV}E6-64N2;-d@pIzag7Bab&(D zeR`v%zuapJn>%3Nd4XtOO%lPO&)=uy?SQyn<09e(O7Q}PP=k1`>0AKWTtfB~K}PoR zDPnLRO7WA7<5!nEemc5(W=H0A8t6HHCHj+ZJWu2@guml4L3B&lR~Lo&O!~hce>L%I zp%~xU*gm_5;)b~Yhv;t=^#==~2JL!$iUP|hCrNw0 zhiJlTiu(K8u$(ig&vBugFTzgd9??#|aX%#e zy#O|r+|BZvM)GscVtRHUyEv4?K~Z1cXGQYW1>u2Gc&5V0zZjwZu5gC+hbW;0&@25> zwC7{;J9=XbCM1_FX(ekocBt_%72RO$P|t-($NB$Z?+oCpD6aj#clRcQ05K|PEKx5S zFlxl8AW_rT3nUOEK!l)CV@V)^L<5N-U{Iu22^cj(Y(Y^`qJqViYOJWSrS?_>jTI$r zL8;RBZd3qX`@fI-fz1BCbLPzK?Ck99?B3noI-lTR zQ9k?Ho_yQ~RnBv+{k;3h!S$GPJxXoY)4cs7w;FJtyY9nVyQ{a`-D1kzBIq3sE<V_tKOah>Pv!`nVrZ?5C4d9F!Z?>NnOO_Jjl0qYv!+AN)~ zxSkB{C*2C>I%^p@l2;9w`{Nz;)K9;s+jX;U);;4v{XLNi%88%T2!AMqt&dQ;5YqV* zbi$bDnZvHvDe8Qjz09(Ff7bc?Wd0HFl`SJXKkT~XIEWrJq%_4o?Y^QWJ@9T73vK*JI&o#D0ey)+)1)uozw;LDgSJkegbc1iE^V<>a zr}-@HC|oa*r>PHJXN9WV_NJmX_a`k~Z*;qV)WUU3T_ZkwZQB2E+mz4osSnq) zsB5{tScJZw?EVH_pXca))m-O<`};}y!at3=+q3}vp2f|Me)GZ$+vNx7yb3!XXdDRx6?^R|;+tFUVzzMFUu+{cq!w1n$nlKQ?{s44!Q0h8Ax*oC{N(9^Y( zsSTTv07a}xpdW)4svjsvevD$RGl!F{q0dzCBe|DrantVC->|PW5$uigtD^sfw^KXF z)B875j$@|c`P}>X6O#5dR17Xe`0-k%>H*zJl$Rb3asly^;%hmYOK~}`)KTKilwUjL zmpm2u$?d+H=NqO+{sPYa$(s0rl?iJH*jKzUg+5Q>$8$Fcm`$Z8lG4I|%JZYe)F_(^ z^!E084!VfHx$0ct#rqxEzKQI+3j1eYM)o_%zTj#X`@)r8deWLYX?~iThr0BT^+R;} ziI90wl22PbkyKwK@cX}2bi7@s^T~{(jA;oUSJ!e4IEitp32Xa8vJcJepFM9!1KB56 zXnS9ra^K^>Nd5l&-p{VIc3M` zw^RC>r#@f$21c_C`g_>6ZM>a2c@@O`h4j(Q_^nE|7Dl-R1d19=?^BW z`4XxJM(Q#$5>)aQM*?TCs+LHkDj^cs>iL04(#wR3sE_}xH+~al5qU%51-*Nj;h#xtKMmEh;2(n)i z+U%b0PPL15lz+)Soqwks&zbF=rsn3jT|~Vfg4@MjN}u_w&zGL(!G_*Le}1jgTXOra zL%VGAsEd5cU%wP)ZCs@U^d>5a_|s+)H~7}Z<$fxlve3fYwT;X5n@`!U0{413OzArh ze!lcvFBxkrtL4x?pn{^LS%arVRn^axxH5O{7%LK+ zdEE7p#`WorKGL}FqI|b*KZFzBv3}Diya&2 zx;OXHq# zTIBPdYu!rh`WS+GPfYPfnx|T>{Wr_;ZbUoMUz#@K=g4<@Ip?`aP3T8n~Up{Z90FR zUr5_T*Hw4xas@Q*DVIII$y>C08ejhN$HlTKzL4x|nq2IQ*zyKWGnv=a1Noc~emTE& zoS$2Ik>5eGZ|D5(L4HZvQMleB9{Y6~2Dlx_&L%Z|7~adD! z^G0;5-dQssXKljGd-B#MES<8S}}S$;mQGajdL;CFTTd(Gcmg?Zt}d(Ca~=Bx8z6{mm7 zB|ZHvLA@vYE^`c;F48XUMYc%doZsuzH3c>*cb)%X&i@&mf2VA}=J#pyq;GqAFY-C^ z_tJ4zwVCE!x9j{vGXKc&KxIT{5H&<|9U;wgOVQUi`WCw-ihSBQDu36f%`uF|^+ZJP z85zO5g8Q+$E!2-auj`??uX@qui(MT+d#v5$-Pyb1e5_-|ZUZ{slwVBDg*v@0!9n z*FFC{xAtYbUkj~vP4f@em^esCk=jP2T!x<^ZqJcFEXj_NauK~d#-+wQ+H zWnWDHw^4aIw|sQ0YgRwX6ZARR1N+J%=!+~5`yc#_WtIH>qhpz~`{BRS=iJ+;|IR4? zJe^%d{xhe4e5{k!&BDBa_#xs!11aSC-$=Z9#>dBMW|H`{_RxHs^WCX^ZEy2&k=yG$ z(r0^!Hm2;kelosK{pq70A1mPZKz~|!I!8F^gZnB^=ZMJia6L4U|B|C0AM2uZv!v3> zX5x*1|M*xYLmF6J%gsA^Vp0N0PS;NHM=8F5<8zXH-=G*ycYt{2N5uJBhqyD0 zT(15WsJ~bC`dg>Sbap<5T&*L&d2!vx)LakM^ICg+Qp!qaa#Cm@ecml8g^u@RI^HRh z(7=_>THZQU54$OyZ$S640*on{=hOd##ETNTk6B(4pSIruw)0LOO@r%u%y!F47~HM* z2WeiI=M(qE;AO$XiFd`|+ljXw z-`%Sp*-gCW1lrz+{Xdk-aFBSyP^xFayNO%FXnO@8`aG5Aq~7JpAl^m1Q*2ib@vSF! zAFE+b$C*7RDS`7XCT^uf*G~qaI^y-jyYz9H={)XH2f%XmS05_&`}$Dh@!n>#&l*nU zguQ*6GPK)NFkAg)zq`q<;8favbG$#M{Q8t%pYrRoxA*w%Ci~9QyN}JMv^@jJ1uMPF z@NyEuW9Wa{3zn7S>pu2tuBY&Yojr}7Y+6KvDSA4NUTH$tw0ebRl<8uxfozK@U2sG{ zkGrl3c5YH&;>?V<-@9TikzByNQZ%BNb^56=?#k zFZm2)p?E6VHsnhrUSdU+{MF zl|kG`+~RwR)Q{f7<^OCse|@W2c1F#vSb>NQAgc6f*zMv zU#GmG?`V7Fr^FY3N_=kL@mwEAXgi8VbszK5I=5#Im=FE|^-shVAkHVGG&+{jh_k(V zhs>v)KSH;8oFg9heL^R#+dPp?*E!q!S*$npIc}7`&-pARvGYGeLFk;^#}OyW@!5>~ zXwkiD&#w9`#rRB~Kg$N{^ZyQ-7rrq1I4h=hRYts?co#P`YCmiAc!my=)n0oKUBRdk z4IM0Wp!ldS^{%mL@;3&huh;fac1Fh}{ck7#2^Zl!FWRU5NaR%!wPmtDLiUYhU&s!^ z{rS9=-Wz)QeI7TZ{)m3_LUtKE{tMgLx6=$sJbfI7o{y&A8R*}p=Ir6#pReW-xvNubCp zZM|(NO^$aD^}D;ZOnwEMqJxx`^mLV%Z$bAJKdA3`iq-w#&>z$I@Uj@=AL7Z`(e0c2 z%^czhF?ca?D+aG4ew6&HewH((h5a-U?~1`U6K{;+X9w{>48E6meGGn>cxMdm4O&)5 z44z6nDEM+J=NRHCU#0Og&V!Nj6f`lBU@ux5Pp=VOo#0&N*)#Q(yj>Gm&;z7XU)i;1 zpa3rQL;!Bk|<=sGeZm0Z@evPg_xXp>{kF6#`)%6)?E5|gMs3r-Wb^0tdz=0xo9~?1=inl0 z`}-WxMp@dI_j{{q`fW)U3w;bfqVlikx8;m}TUy<8`pTw3amys?C;5c(smG!HPQSg= zsj&aH*Y$s&IS=*Uwz}UXu=|{Li`o@^U#3Ny=JmS({olch`t9=ycT3vY`h1m5*SUdd zz2|`niKk4D=5^F>R}s%7o_t(C+Dnb|dY_G;CaT~5U7tHP%GQ3!ebdG#MVa>5!$(>6 zxeKB!?Qi@GvbXQ=x6}WNo9}$3&r8Ut`k&YTn(A}@CCcf|{dZc}Z__@n?4q*2&fO>X z(+6Lr@!i!l561)KPwS^^?8ENf#PQ+8_5ZMQ()z!ToUw8JF2gPTwzP}-Z(7oS(>_m{ zQM;+nQ%aO&X}`1Yqd9XoZEdG3$AhoY{LOqiPxEg``riMOlY$t#fmqy??ZE-`!udZJ$R~v}t+&i~E_XVR`@k&hNje3qUhX3{LmgjK_e@^%Fm(%!{r}(fi zuOe=(p!2Zc4aDoe5j|dJ`;Ek#iTi2YEPH8XJMlK+>RD#Yhuvv=|BKvW1}*`!XGAAZ zg5H$p6MIvh=j=_hV^W^`>1`VylcvX{yottxRY;=g7c}2=8{KE+($RV99^3w_zI{Tg z*L*AfhF{u`HpDM(pWG0?w4L6b$MeM$b7gpKN_dSvqpk1rm65B1_ua4~U9-;jjF#*Vx(L}uFsqSOn=JZ&Y zZzi6wtNYkHJb{UY`3~ZpZ*?ErdA6bYx@<4;M$ae5om5A30*cW11OzqTonzVV(wq%(X!fPXS;=u2U$Gvxf% zQGQ$R`sA38=lH|<&86GTm)Pc)KKAQtlkBhWnX)!1|DNPE$Jvj(&M%0pCin=$E1QPQ zQ2c;;b!N_<{H7tSDZUd#=f}r~_f6Wqdue|#Lc3jS?n|sR_a#<))jfx#7O!^|*(LdQ zVeTDM@yUC1QuIA7UcQr~A5J0j9Qt2H`Ls|z<+RSn_gb37qWh=0Nxpl$blb8iE-xu_ zbv(^`tQ;^c$xkOddWwebtJ3{dY9D0JhxAZNx}E%VkRNWAUoKzPz?U`fWet2;1OGc~ zz@O&V1zUKz3VlYSxSJ18RckhN<|Am$42_qUYwq^Rhh2Ret4bp%rNDqb!NrTTu#uu1-Lw7)*0B!wA`#%vn6`If2oIVqJ7<3NwNzg^mYUoTX_!sRj z1@VoDKLy(NR~`Qq=&gw7&z5t3`=L{zlmD#kPlZm0J`Fkmf2TtiBi;wy3_SvR2lN@x zozQ2J=KXaTIt}^=^hoG#=uyz#E?vH}pshyT&iT@b{U!fYYaZ&e9t`_)Xq~q=gz}#Y z9e7{+%Ye><{dv&7BRYPxu}A#*&_TrWQO5q;5Pt#mVdx8?tq-)ni=a!O$3Tam$3kad z|6XkDp~pc7ptFqrQ2W0GdNblLg|_~t<1d3Qf#!=>E`Jc3R-+cKOI~F7ZyJaBCZS&t z`V4eXe7y0p;Z(m0@!B3MXNlf_+HqX20`<2#S0&gkqgY|K@?p+GsZ4L0r+_4 z#q_V{Qq@1qAH@ShwKnArs#we7{)-or|38i$t7hGKqMrln7_`~WF6e)ppyQR%|Dr#^ zKk9mtMS2ta|2>2sB8)#xZ^uapM6gz!p) z^ARpZxEkSo2%kXsJi=WF-$eKx!VeL~k3jwiMM!|I*&&@^q-Ia zFTzdQJYQaUe&vMn3BHNG>Ao4hNxtdj(@Uo<7+*E3vcNaXH?eAb?Nr~y(pmEgE3foT ztev)KQu)N{>6H_gT(f9$>E!b9i)WQjsGV9nbMCC#t9{qhPMc%h0TTob4Fn2;_LG}2$nTsaX&MX~2udt$^bhdALO~I1!3nwm| zaKps;xr-+)oK`lyI=^=2{G7S77EGwfTRg2UXYtGh6D$6&Z=Y-*X1{*Pdybj^i8`Uf zv(F`fSU#5j6-xk4zo>t1N2k(%_QQP|_e1?z0x+`sWBfU6{;&A|qWkCn_j+*K^=7-i zX#MB|Koym{O7U_dIv&i~0KFm0eqGK*R&f|Sx>?8Mw=}#l$ zrIRdm6G_tEy8AAD{mk{kV?*EFT639M`=QNt6hLzv+tfhw^)l-K=2N)t*=Gyl6KI{& zgrNC)m32y!&X2FBS?5EWdJjOGHn9#mJYNQF=F7UF&3su1QZfG-K)(vgu%hJE8{hRr+GH5ejwgKAAm$gIl zvBcYR2%7s1)(PLz{<5Icq0M|*9yHIFaeJ?THuGgo(Ah{Ig#Ie@A!wc#HNM% z>wL`RK>KN(<7=SD(>lj*gr0!-R_KY)2cdJIeVFIZCC%xJp(i0eZIjM_GW5|$w9bS5 za@bFSZiZ$n_O}&!D)bKMeCYkq(@6979EP5bc<+5WzZuXz=mOYhL0a!p!1+_h7Q2~7U)LAFNf}c4v^;d(G7h&;)6)P z6598W&Tkd8A9^)(HuN3P70`{)4bXQ&w?N+o9fZCc`XF=@X>QLc8+HEoAU*?n4YVJ6 zEp!odGjs*?z0k{{zX{z0{VnJt=ug%`w;+B!^mgd`pxdGEhwg;l0DTnt0q8Lg>+N|E zx(xau=m7La=&jHXL$^ae0(}^I6LbptzZU2$=trT8pdW*7fPNgh4SF;5LFg^e$*7+v zptGRA4P6Gk6}l1nN$6JS??8v3zYFbc(e<+pIs^K9(7rcx{8P{+h<_S7fcRGE5cD(9 z{yjSV_o1th{#oed(A%Ngpr0en?Xd;vpNF7?nO)dBmnp$|fz18qH~?azfyh0cKXL$4*R zwjch@v$a9g@4c`$`kSO}%HQa3k>>3)dL3zAj9xF&-zU=FFVb%y&E+xaUqujDpg)AR_UZck8*~ZuQRtzM>-fKurhir* z^heM^*z@m+IKBq)pFp=j{{!0hruN5`&GyEh2f7pX{M-k}?}v7vyP)}*LXJ<_to_A7 zXF$h8=RpsEE`&~iZh{^N-3pxuy&HNE^a1F>(8*hL{zFL9KPweF3EB^R9CR@>zr%~| z>!FV)!P~PAI+Zl28=%jG-VL1weE@nS zbQkm}=#+2k{Lg|O1Dy_C27Na42IzC3JD|^nJ_4NqoxWB3KM%SPdNgzc^!d=+p!wNk zuD`v|7eIGIUkIJ?r1pOibRjgqCzR9ILyv{t2z@d14(M^v2cWZ{yP+?EPWg_`?^5VY z=*yt#wxeFgMp=$X(# z=vmN5pbMc>zNhn>4V?vjC3GJ2RnW!IMbM4VS3_@vz6QD#`da89bTRZ^=Fw45V{L`5p-Is&c7NuAG!v*4Ejds2IyMotSF zhrS&;`C09MCG;5R2IxZQRnS$?tDzgA?||M4-3YxO`cCMh(04%(-LCV$8+r_M6LcB$ zJPuZ7+X-3)C#r~TgxodW$$=sf6eK{rCLgWd+c9{K?EebC|Ib6WK)(Q;@Q%3Hk`)e+)flhfW`aE`$CFbTjnJ&~4DKKp%u| zhxYzR``Zbf4!sMy0Q#rU_0S#A8=+r??u7msbizy8|7*}eY~OC^Y{b6~?R#6>zX4r? z_&v}q(4Eko&_9Px{;^K~3+O!PUqaVI?}gqBy$^aX^qbH}px=V_1-1YE(1p;yf^LL< z8#)O64s-z9cL4eb;(rb8`-%4d@6b8W{{dYF9fEFz{tfh2=-)zjK>rRp1bq;C@XI>C zccHVO--9lI{yp?^=tIyUY|no}w<7+(p#AS?|9^n)Li}Oqv{!WeAEC3M{{&qK{b%SV z=q~6M=)XYkh5jq_A?Wv^6WVotN1(?*e*j$s{UP*n=)Xa4fIbS{4*hrN!_Xg*=Ig!W zojSjd5uXL!E%YZs|3m0wLi6MWU%wcC9-(ca9q1P94==QJK$kBbIuGR=09}H3zOl{u z1)&E*`+lv{Cqg$M{UFl3{Xyu#h!4Pi2xU#}ohkSepJ_m)qpW+eXhMANvOVk4*j@Lhj@~68$`S0)+#qy{RQf zb04JP!Hc8$^@ukC+w*_7Wma8vjqf~PL2dPd+R}x-y7JnEm5WO2%F9N(rHJM;%j=fZ zF7nN*E-Uv%$MjFpZjRc|GS%VTe20+U8H%tQq4#yo2O}JcFco1M!c2r&2>l525EdXT zLRf;Z0$~lpdV~RljR@BvY(dzyN0+M`;WqGAgl!0e2s;pVBHWKKgzyl;E`&!Bb|dt@ zfqFnV6k#gDG=v!l#~{o`n1e7MVIjg|gk=b;5Y{1Fj<5k?Bf=(x%?Q^a+<58|2f}uQyAke17(#du;bDYF5OyQshNtq9u?1`+N?xEJ98ga;8GMtBsVw-fsv;ZTIB2-6T|ARL1* z3t=`wKf)Y@c?h#`oaG}dL0FG)1H$Il^He>xB5Xm({W+gkLch}GJcO_d;ZcOv+d5u3 ziSft$4l(tBw#z`6i7*RcHo_c)c?b&-79lJ_Sb?wxVLieC!X|{x2sa?yh;TE)tq8Xv z+>Wpf;SPlD2+j6%LhnZyLU<72VT9cXhyGe`zYk#s!YqW@2>l525f&kgQMC#M5Z{Qf z8R33}A%w~QuJcbp=tG!?um)j0Lf-%8obHzQ=i#2)0^#3caeB{fmM59R+f5koq(nG2 z@e$|?t=hK-JS8za5Vry`xIy;qmdzDH-=*}>8izIyZSo&Y)K?GNfuZ7*})F6IFhlA`T1oJ`eX(|!uj8i&fBL$>3P>w!bseuw?hc2a0t zKIO-;2CL8u?(mP-DBjEQ6gutaL;ICoeE7$0l;8jK!uuz3w|(0F@=*KWX279p*A)7o z{S^LpFKR#X{-O37pv*a>J)}e4f2JLCNCmeWsN3gwIv&stZ0u*9{x(t^&!OJ``Z!?C z>3RR__M^CJ57F(yWBI5cVsSWMYA0VVU)Dfm4XE}*?Ma2)&vN_X1I%Tic+>7wdsI5| z02b*e-a)_Rg!?C-Fp7|5?}feT&s0AaVbbR+rH{8V)po?wL*KkT@m5KnN&bf;sq*=N z_fz1b|6-XuapI-E^vTm_pY0nnI&1V;--VeMjLp3G!i#+AGt0|-d8KulpLfy4XItl$ zmCr9-QdL(~IrmcfUs*fPXZ<@lA;H5B0cpPpVLHGwEKHNbG~SaI zrYT_>=i$e2vS;ykwzaFzydjlYo807q5UgTy?Kl8&e`E8$vY*?DWi zz=XJ^v_238oz2_|k~s?ceFEWvZbt#nI>%2Imc>tU(Mzu0AR7~P)!BZIu{`w7D}HeA zKiJ{YqIi-mB!8h~Nj!a^;|aD>=ZU2iaf3X=sgd!MY6G1XRA2l+Po>2^?4&n}i0 z#a-xGKx=+VFwi-0uAZBTyFe9ivVwsly4}Q|A18Q|KGyG&nZ!9A_YV&+&PDXhgkB6U-RCj5XwyuaL{TOL=S72$|ig;=q z+emwOnw=WM3Zr>i*jd`KA})?f&Cl#POSi7D;`wftvvmK8gm}Ks;w(M1A_3KlD!26W zT|eq6I!|zhiylCc=PvM*Pihlf^hD3`WFwSG(S|0CkPsv+^7gXEF>uz^mA$+Blhs=D(y1L@X6 zf~Q{s2VbHmj`Ftej~{e@a>^O;{HUn)l|ga$Cuf}zpLBn+|BU$ilQYkVKO@uz_IwI4BFjbs*`ZLt6SiwBGM0nuh(IdZe7F4 zMn-W@_8yszqBt*gRNRyOk+e_g4%L(WjkITq0-Xwc!k|!r{VVvy$0u%2F#$RpR~TKb z^r01W3b}(;ITvT?wiWU5o+gs~04}w-4BfT}K2Gp>1w(-0gL1U;baX|+z)NVYgT5Zc z{ku5Nwtr9X(Jr2mrLmgj7Wdq~AKRK|_+WWscylW-x@pP{NvC)rUEH^OsqI7z4|&gaPvCmD<+ z@uz#rmMyaQ*=1+x(HjO>o^*RWStZbA@R`=-w8oE7C{|)a?T8Tt|7@CjFK%j zA4t_k9Eo*QYsLG8A{`MTf(El=moL=#o6rPe7_Uur#Xu4-l~ zi)PO^$mAl5QgW+j70Is}8C0De-_upG9iF=>mZcScD)rjBPTDKtQa$$~!dcpOBgqY6 z+01fNST^2Bea90dCsQ8OJ!GPihkJfPaGHYt3jn|3d7B{TGSCTi>%J>)q~~`8XOX3| zwB`D^G|zvMT&3ip>*LP!{Eg%bO19o$4bmf}!5t8ygVcyrU-LLeuvg04#^jc)Q2P=9V)huL-Lhy&(^U#{*|8Nb9vm$;p5m| z>hG!Fe(eKipJzC2o7pDQXM0ZA=NUr0f)2xycoHX*SaJcW3RWXnT}Y}Xz9+w!Yu2x* zJ8`TfRPi`haN6*>q6X)PYhTxOx*~z=e|$SvkXujy-pHX5vR|Oos?vt<1;6Nc+b}<;hPFt7wLY=ir2ji zeO6JO57miq!1ZL;VR?@C3{N_7kdrhr>9(awvj!&L4*EH#q~;iyypauPH(BSqv3|RN*kw~O*zp?vJ-5}{**yx?wH(kUS10s188~PP z6*zH(b;gkMhftl%+6>po=PeDgkMG&|h?0Eq?3@I(Bciv}Y+WF1&PyXXAT-+U6FWx;u*9$ zm}`>vUayjU=6y8+&9-+tj&4ek`wQx!a@r-H-PHpJ8`&HVM$>T#z2lTE`o-q>;dpI{C)-QL`*{^bHgLVxKFAPN3Azp|=)&XPryb))BBLX)~{ zTYtMR?)SFEPdPc(c`=ie*Vb0o&RJN#cyZ~1@`z-c>3lHIHnV+6iMQLSPJ+kIr|Tkn z3IAEff9|ovt2Ml)qQ6Er7YCh2sm&ZBm63l}?5MY*^e**+@nzg#2?sM zTqRfYhEJtG&pvb0P4;>ZZT;p%Ckx&=@7 zJ5TyjyxRMm|1G^%9S?0el{?S*hQ}`8=p0(}2YO8HQlh9Co#{<%=I7Q6fAOBFGsca} z8fV|md3}wR=5gc3Rd2c@TODw77x?M0rp4nKO@AFLU9HP*a$fbK#dW2NB6~@?s_&~~ zEv>6wIA?C@;>aFyU!t{CWpYl;Ocu{8T{NezvaTvJO)CvQ2ij)xDJk)<)T<8iy#J1! zKogs)BU|jzZRG`h7^@C-0=4PMl+Mg!(dO0Bm6gLc?qYOeHPw`NE%#uwq4O&j=$_>- z`&a#Y-2U;LznR@}hEdOQ5>2$)>CTP(XNc!%`kUbSgKDJuFq>gN#GU+PdlgstxEV9b z!iS&Z8E5~R%H~-+=PLUO-nkE{Ch&OA->d9K7I)i&oOqAD$hk%L5zo$|c0l%zdnt00 z?lzuNsjerWj!im}2YBpub%NQ$QET6^M^I+kp-!@zJ6rn^Wux-Z_)dkldpVL%1X=bI z$E$qqRuOL`l9)_$J1Xco_Uv^WJU~hztcjUPH$w><@9Hq z{R(HdRtd_R?x#@#)j0Ub7-R>NJaiW16XOY7(f6}~{XWlP+6UBM&L35CFC^?}0LTM(NKZDMv_F}F%sjV$mEj}$~yIWXViL=;yfBBC$<2vX{abWoJ zFe#Cm`cRKu$Mssv?i1+5W}ocTd+htTFnN>hXFOc?VzL-?Atg1JoRsZQ%8(!Jbu3SP z^56jp=CuG^5MEl-{3@iwOD*BA?2O-~v|D<<%=OcMGtT*lXY*(Yr?;=7&-6U(ywVRp zjg-y|Q65)bBK(xC^xDge7kzon_U7&&U*+-=&iC4tj{S6xeP8A0p`RI)%%*N3%8%Vl z<^QqAc8n0xdfuPOR0pPq zeX>`5Tt^jKMVm36K4)aB&kwn^t0GG4yWUS8>C#O7jo3eE51b#{nI3ij71}VoA8)4W z-*APgHjB59?afTpO1`S5C2a4k7{V>q*qK?M!^$ec&i=h>*nRC!zO!j2XLyF2mvBBs zv^!t7!{s`^qIzNZ`IU(I3m4AmkvG&-SJD+o4^Sul z9$8hrXaS#5>uRg3BCL8}Kt&O2^y0eO(Nm^RnlUGL=FAy0sZ8^$=O~kKNwdzjM%OK@ ziLe@7Mb|`Erd@DJPu?yo5r*2tIxz36koUXn6@e^|M z$=}@aDvOSvN-<-En=dR=T)@kD}ExtMRvQ$gy=7gv@pvM7D|JnYnY zOKR2LwMrM!l!G-_{iQ-v6|28D>tLA<>Zxg=YA!(C*iHZb1p5T1GcB_&;gY*W%?Yhb$wpy^DhnhtSsB#%f_*6t1u>ath$)o8~)Bp zq%+5g3x8iFn#YH~!xGI0Sh2oe!Vhkm@18{4546npQ=)mIWxlf#%?E|QHxtbVTftNP zkw~pO))1@Y%ve0h3dH2cg}+}D>5LyZEm?ATB!d6rE#J|pG587A(POdriB|LA{22B_ z!{5n?jCAO$HRk&`(flMU3*XI&<|kYJm^{Ub_5BvdI>qYzb5*1ZweIlOdGKAF$Vi92 z;uFF{n$bMf3Z9F5P7x7~dgFWqz^Y^Mv!lHi$2z??B;r^;7e2y;pV5nZh0SSCxY&o3eX>=9_J0v+Ub^T%|A1sOI9=1~g*K4+ zHt+$!$ANDbeog~F1a9hQwBo}pKb|u!fuAX`KMZ?Zms(eYcY(*jz7qVX;QV}-`T@*R zeOw#+JHZnKe*ipL@F&4jz)g8x1WyI`qC6eoK5$c?Z-EyI`}e?$z)gMf`|r5?C4zhD zf`)m8;3tCD2z~~5z2Fys2LvAv-Ux2?`xW5L!oC!I1GuS&8^Je%n|innyi?elcKch@ zqY3UM`&jMYw70$R!xxUc@OxK0)Q_3xmqUtL7Qc^!>+LA)F9YZIQto-sD%=l3PB zAM=>8@niP;n45IFGVQ_a_bkE9e$N)1oU8Kq1vl*>M{v^~@&q^SAzyIQ9ts3E?V(U` zbG#P`ZrVw);HI6F2yWU*nc!T1s@)21+E10>rv20iZrV?s;HKZG7yN3}^K!vWzY!38 z8SEPbH~mJV;19sQNpRC|Gz)I}!*zlWqlU!wvqA8Y;2Q-W3*I7lF8F4_XM=AQyc~R+ z;7h<;1z!ceUGVkbZGvwB-y!&O;6cH6g0~C454=P0cfofH{sDNW;Bj<-b3N}Bd>HtC z!AF805PU3nNbp?ngM!ZnKO}fL_+i19fOiSL3jB!R>%or-z6HEn@aMp-`sjXaC%9Me zec%a#zY9KC@DIR~1*eB9RQ(SXd>D9&;3L6P1s@CU6Fe6@P4L;^>4KMoX9&IoJX7#h z;9~?|51u9X7VvDrp9A*`z7sr0@O|KUg1-x%FZc)G1%k)L>-sMgd>D9<;3L6{1s@Au zB6u!%nc%a*D+DhGuM&I-c#Ytz!0QBG4_+_$7VzbQKL;KVd?$E=;QPQE1%DU3N$?NA zn+1=f0Uq~1>jWPLzCrMj;2Q-W3*I7lF8F4_XM=AQyc~R+;7h<;1z!ceUGVkbZGvwB z-y!&O;6cH6fwv3(7I=r?hro9W{&(^=9 z@b|!T1pga&p5O_Ix}Ng|KLxx%@N>Wm1-}fuNbr2{V!^KiFA;nZc$whe0Iv|d8N5pH zN5N|Z|2}w~;IDw!3%(b8x!~`D2L%5Ryg~2*^k^yf8;ydefHw(#Hh8n(mx8Yod@A?` z!HdB+3ce7$Mer5in+0DBzE$uR@NI%W1Kuk5%i!Au|0Q^v;0M8X2>tw2Jmf&7`G@ILVw&26S{eq7I z&k=kac%I;S;Q4}I4PGGl_27kq-vVAFcoTTB;17eB2>uj!nczPLuMoTwyh`xjfY%8A zSMWN)o#S*p*9$%re7WEw!2^O{4BjC4Wbj78i@=)%uLN%v{ATcVg5M3kLGX>>8wLL! zc#GgKfo~Rk5BOHWL*Uy4{|k7l;AWooZkqRsT=?ZovF2%wJ^unQayiAt-i()0ZqfY` znW@E$XVSq<_TllHu)hrF@oZsl#*=~F+JwCs&vXcTbDg+b*qiap0bxHD z*JUALZ^koS!roj*9})IuJmX!i+lgrp=DI!s+_WDvo=Fk*OK_c@D(ua8CPUbdg?*;5 zH{%()Zsv&Gl@u;O4qDMR0R{>J!{tho%c|t~WCUH`kR}f}86{zu@LNF;8%FJy;;Px$Y|x z++5$42yU+9Dg-yzYc+zK>#};m&GlD6aC4p2D7d+vY8KpFH*FByTpzUvZmxs23U01< zS_L=PHEn{M>zAP5<~pTAaC1GyzY4)k|7`ASmkE1w z|GGl(5d2gLKj!}Ra$#@oQwIc}g8r>R_%ZjX*9m)bKYD}Ucf!v`;m6#M-X`qLedkue zhv9s*UHBOZ-Y)EO!8-(>4Zd6WDF;6w>{o$@1YZw+Q25ybeni;s1V1YHKJaeg=Uwo@ zf#`M^hjB%+;KRU&f}8d;5~p~z1)mMxB>a?vZxr^cz*_`g558IW*#f>@ z*zW{y6MP@|4&mor@ZG{b4%Y>pf)4}VEBuTEKPc>T!4C;O8~m{FQx4uO>{o$Xw?((} z_26D`)1J404;A)1!BYg^2c9bYybGQo?Bj6Vmnrx#@G-*ANbnqCp9`KR_-yce;inwD zSlF)uFA;n_c$x6C1-wq!oBNgZf}8u5%Y`3vzp_c#oBNW@f}8u2>x3V3UvjgsH}@a6 z3U2N{ZWDgY{l^``-aIcC6x`f@Y!`ma{l~q+-rQ&0FSxnSctH3u_ZbfhdviarOK@{P z@rdwa?k9S0k8c0wzF~sk=Dy)zaC2Ok`-Z8)-rOJb32yEWrU^gh{@@s4Z|(zT32yEK zW(z;&K48AEH}hJBWYLE7H802Sl#5()UF>HO=jS9tc+Qz_`>N#{@I!)|^6;DE zB9~GZ|26PqUI^HMM5|1`cqgFY4=6s|3gSJF{DVMV%=oha^<%O(*NH{obU3TUjAKg# zH|2lI7{dM;m-6pIc|s^pI_5jgdASMx%{X=m&LeH$xD2(-cw+~+nKq5Xc{wQfa~RLG z3vR|s9fF(j)o#I8!B3~)Bf30wvqA8Yux}E4EciOXbHO(XZpNRR1uuvF zHo?t#XFIq#9?kiy4czo&X8xp0*we7Up7C1f5p%yc*k|Vg8S1AyFLX?XA6oDsbHp z@nYs%Y6QO;^-~XSw#&?uHwbR#Rhq%gdFlc9*(U6t1aA|37|xsR;67~ESnz}3)I8LZ z3w{`U6!>iLp{t|&jdJi*!Iyxi3BD6N1KiZxKJYPuzYCr%{CohOBlt*az`P&xg`ctD zg@WgT7YjeL!OH|M2d@%-mVnm@z6yM~@UtGgLGb6mn*`qpz7G5x?3aDuL1F(cc!%I0 zfOiUR#+kcJ!-xMr)A>5qxZR9@LhzFhdvm;J+@afvIUdcpC=1-Qx1(x2kZPG1jGA%F zQ0gdnoNwMSYWPKp)6syJmeS^^ngD`I^l${YD1(iK=EA-N?X&At?SHZ2sn#J;&Rf8TitSpdc(UdDsjg2`&h@ZQfxRi`EXCx@Q{oD zKN08pDY*;v1OMJ(nzuZlc>(zG;9cMW+${MfKxC-`yT8w4K#zESWjlkaz<^POh+wrDOQ~>VFfs^%ZTu753i;4}u%}UEsdq(fJ+#4}dqr&!3Hb zYIJ!#C-eSFwgQ5m3?2eE`KE*WPmT6-8F-7}Gr+CWw0#%KQ)c+-nwxUg8$aMd*f)ch z_@eXu4tP-T9pJtZ+TQs2Id}-X1pa>y?mt7@o9+Dwyan8pGbu&aTkvFUza4%qARg4)8!Qns*Y9 zRsOeK_#fb>}ggaD>h`2>WZ5J@vz>x}2HdRmivbjOcRS414QTZBLu2mi4d?2>b65k5z9! zaN)1I@cqXBJ-R$KI03u|-gU3$=1qL>6X)}|59P$^$QnYgbct1-R2P0Oao#V%@3M70OaiwCY2FE5M4Zc$CdyL_`>vtd-j9L!H;Bh7XOr@iYPDhin)Bep zF7{htzeBW#9g5R92j%hW;#-~I0l|Ok;{Olu-!97I&?OL`_d5hn0pBh7Xz)(K|BZO8 zdYD3-_ly5U)Enx#96azt&3zc(u5j^lj|<-jKf$UTz2E6Nke4T2>|b=@9WMM$7ydiq zd|YQ;e z5qOEt5Zm=7Reki{`hw_-}II54rH~!GFsW+P~?CU;Y&Pkg(t9V*h*i_kCOYH^)~u zxCL&Gi<5k@>-lUKewhoOOq};)w#fH7;@p0Mzt!!BjMQ=i>@!7qR)c2=KcALwzOXm> z`VuGW3`M?vmwfYtAGdrrqMQZ7kI6UF7oG15urCt+Uk5K1{CC7-wSPC>*b;mdYqq* z{j~;s1GqVFepB&Ot5u9+A5)yJkAAM}jlZSLOAy@umgbu^w%!Ck2)+Tl3%spU+Z*nj zsmmF{IJq9j-7v-J`HKD8KGUNejRM~QKc?RpuXxXUYfF%C33vr&JKE=LG*uMlnLBZR>I|P5t#s4p0-wFG{sGql8?0=)| z>Aja|Kc@VLz_$wiC-7FmKLBqNyc;|yxRa*qEhM(f)WadcO+9o8o(Mlj1vmS}dOLc* z98dPK+R5p}`M3+co2P5ctAh*|`)t@J|54lfP|q`nb9)$iAUfYN#pyX%9FJwNUj#ob zA8S8d;CFyGcWd4X{viAW;l~U9H1Sy5^)mce4o$Su;zNSpxY+;Mg?GE~#F4S{J=uky zMV$Mw5ZXf?`hjsS_LGS7eyJIv%b$rG(x!d-&(!^40_wrE&pg3R`z#Rr3RNC@f99|C z{_??35qO*6=6ER*eoQ+r5xf-TZ#h|)KZJr-!T%xQzYg}TK5g$ud2R*o5_~myO{TVQ z#(mp+!AnMHo=op$;AJCmZl5*C*IdV$<1PSh>eC!|je?uwu32z%T<;g$9FHNvw^4aw zwOg})D@1*o{aYir*}wIIoBf;q@6r3WP1OUvr&rjU{azsK&Hil^_NF~F3vTvrK=?8B zoD44OIYn?&&pyF_jQyA=xT)tF!OecD7krmX{qJ?*zjNV#apA{Yc+#lY`#sf#pXD4LD1mM_Q0CeM&+tDa84FVJ+0pftdRpXDEBRuL?hAd(S5x zYrk+ih{eY%KPOs^zcWvQ>3nA>-urt292?8ewPbJ6=d|Q^9IBL`-rr?d0(&3opHuVF zsCe)1N<5_e(9J0Ey@qFqpFs6fFl@X^?~hY~^&@aU-iL3FkJpJGPv;BYM%{l-(B{^E zfLq_!{3sIq5q_F~o~JBBuzz21h=^c7x-L)GI9;9=9DqLXz>B%cI)EK^33$sP&8c$L zG85eQ=NzSd8e1jcCAiO90=@*ib+2C$(@wULfYaY_|&l>ZXs{YMu+$6()_tn)Q~jyqe|vo8VT0r*J+ z?`qTjGr=zaw_erU)bk|6uho7sQO;`&7yhfkLzhofo?8d%4C;wjkCiQ)6LeUP5R;AOw!dOh;*pNSt&_oouX^H_hc>znk+$vy1&^7ycq~-mbvz=zjYx z<3C5&n-69FGk9mA-Yn%ukauGV7wy9=x2tUr?AfX5e`RVn)@IlTuZ+&O4ffV^x*lGF{cEuIKc)AVX+OVJ97YkrQ5QaFbnN;$op`MN z^J~gJ$qGEC%W2L#Q(XKM!@leHepMQNUk@)gfOkHME&*_p5%4bGooyCxO?Tl%q^T^n462>BNt>hg)0GZcY0cZ|tAa`I_HbUk4ss zqy3nE@_KNqIhx-D-gTYsPy8xZ%eou9;Je!1hjKmv-tw|OE=o|KmyDlPx;$OrZ-Qsu zr~L%s=QrT3s5k0))$%v+ooe}Uy=L+~eJ0~O zTySswRE_g{e=l!_@`J)f1oPm(<`=poA;eWG|H+o`E?qwj;LBm3EcW|4aO3-$;arj@UI7CEnKkVfPjvsNc z|32(n&d}RMU5;8_yfpT4^ds13j+m_MO6WOqUVaAN{49=NZ0~=7`(D)bU!tsOoMZf8 zd_~PtEgylG;5x_;?zu?UbMx=CpZVaYfM;U<)A;d$`{~=OycA$Rj&M1Dje~t~lin}p ze%v(0ky%7g=E9c|=l;Zp@kR~O1YGRDr8r%8BlCN?hcc=ng9WYk6!{9aOH*_(rB=C|GbiQUhc@cQ%Vco9G`65qoB#sD*iF5n0zNh>76vS02 zPUXS*LLUW|bvyib-Kz6#f&ChAIsShX-1>i|eS3H$S9NFE#>QX+;t>Ma#3G)=4h~IM z>p2SxtH- zu#VOX1onfV<*^HVgw+xfFtHO6!eg@w-rsrL>Z;Q%^2hdPtEx_&d+xdCo_k*Rc27_E z8P$08gAN{wzeWj=KMy z)Af6!!oR0>;9EMsd4+HPQd$lR3g7*T*tfh2^i_b9oOk|6SXTTR!}TF?=_&rfZ^^u5HI99=!gv3RXt-N1Z{vI|K|pWcs`w-4 z#11U0oPYn2em|}FJH~!KatQtp4}m`m1w)F@_tdXB|8gDR6fb?kd@nDf_}*KD&z&n| z0C|OP8-8BJ@WJu6m_E*Xm^|@UmCl;hA=k9&zgGCAKNNW$DEvNxBQD*)T=Z~M;U7}` zgAvgyPx<_e!bi>u{|^6uDtviH=5gqKV6E4uNkgo&9S? zKF-d+SK%W+E&Y1h@1H8XwIOnIdh%I?uYF;Tdv)>NqmKS_h5o3n(`yci$8KQwQ@xs5 zzqcs-(yN4iR@cRx!bg5i_}S5T?{g9s(bEiSR$F{?zSXJRk63 ze&@x2k7{4Z%gYYIUtu`D_i>-h*TH4ds-p1Tzfn0W`~u)4&r!`cU#FeC-q9(E{){O6 z9Sl!$BKIF6I`~HwHgq4yVLYsKvOgnqTweSc!biVf`-sfrv$VlC6@Tpk;nU^m|Df>w z1A)7I@M$k4I@n(`apO&Z50mFVBRK5LT2b=5tm^Y^jE{5I*NENN)d8PX_`!dYb-Jwj zxuJ0HcZHvx^8ZVK6F)m9F8WP|!+)uKRGq#1mCnBA>3d4&0fqPeoy?=l%N|nrj@hU9 zoWi}okoDzh2af_y{CFxKv0A9-{ByBmz3&QsPw`)%@a(r`ym^(+1jCiN#N~wI=YJ@a zSM}Xlg&(|9=Dl}?yr?UD^k#)C{ym4#e+%Hm|4HTF$?d(4&Myo9uFmi=g>M`Ad|BZ? z`jb5O_%Aii`5Mz7@xH8jc(wB5y-ehD=~|gzXV0%;xI^k+Z&3UZvkx_{@ces4p37HB zn|XyV=LFt@y`aw;;IuCCT30Cv*t?+k+ZTnOJ=M?G0Y1!*y;JdbRL(m}=Rt*M>#8SL z%8QQ!PW|@&Sk~jODgIX-z8Tkd6h3l?%;SOLU-5F0+x8!e-nw|~xqwp~@*GpQd70wx zpA>%FdR$R>_7@}`aDH<`;og^2o;r``6~6ZAyl|r9dbPrT_HKcH@pgIrCPzo(=TQNB zZ&CR6{lbr17ave~zAN^j_Y>0KlM272`^b5v^EV1_y+Gu%qwpUBPIBJYxcbs%^5WXO z$Zb#Sa-rg14>;lPzDwriYQ=xC;@|v2+~9LY*Xb-CxlU{5>rRFfX8KdhgFfP)lcxUm zuK;H{Zx%T_{ePRndk?4O^FD@q1D}s8zBev%xO9!6d%G&y*I=0i5jimdRUxR^dGZpHw>g>%xzV50@Ap-yb)2<0g$udpG7C zi+pV=oxKkTol95B>o+Pq|A6r6T_NyyD*WIN1ipQ_z~8U%HIoPZxx#yULVy1Wg8wbR z$-a4p&nH4$q_>{&N6%-tv`zlaD*m#5&(!JF4B(_cU)MSw(i#4oQ2a}KqHp^OKc(;k zV+V>#XH@g(yyCw`@mufFI>6KA#Tyl#*SKp(@$X|g*LmO6{d5(B_W{MvYF@UFq0{G6 zj{X%;Bz#(z@x|L$`0}6YdJ(Yq4Ta}7Bu?Aa4!)!Ck(Uac5uL9qkBgi~P2JX}w1E z{|Xtz^?*~qEfaU$toWl}6FvEZ7YhCi;AA)KzTqjwKlt}x03T`Pd6WiHzoV+R+d{@G zD%>-5_a|T8Lo{Jmj@KTbtKPF`3rjwIE`yp=XY&U8vUu_@923XXP-Z+ zaE}%UK6%88^!XnOzw}v=r?Y#HF@5;=`$bRkjO}^g&TZap7L&nYvaU)j$sfTPwBi)-+fN$jOckeC;z`<{Oi4r z@fY7w{Fd1_e*(e)(yQ&FXj)d~`CNsMd@avCj_P<{r0|{pBlGC;l$SgFCkvf5rSmF< zdut*eXHUA0&Nqa<Fpf~-+hDFw_TNUh2hdR`S;a|?|oPBy=TkYHy?t3zvAy1yZUj3?_VqWys3PCMdA4+ zC=5Q!nlJpV!VgwO4lb^HBBpzo+&aIu{0fMj)}8l{BF`ONFV`!4dpRxVNrms3_<2F$%bwa*rE{0UFa3(>|Mr#A zaRqQ%FAtjUCDA{JM{v*h@olA(eGCeK&xnxmb`;)o^>c;4Tj8T05&UJ<=LZ?CNQujT zJ_P;+rE~V@@{H})*Iz4qWJl!FQ+s{|+y}{X=Z9%|UZ?Qozg9VD-!E49y;_I)d6my` zg|A&J{JVZ{SNQH<%DNcQxV@tA)^~*dfzsasob-R^LEPZ8#(!}RT=ADbC-nCfPCr{g z{I?!bxhX%tNpx^7XvdrjqO^$gYR#Mvp|1&3zj^I0i=)>SfiFeTA=GCibLv zl{BF57mz+|m^kWDrVl;&wB|c1|HqY1{$-+v%Nk!jb3w*+=}4XlD|zn*g^y~!Aa6bI zJrM7(bvnuT!dvq1Nx)feHUGL=U*5%V#Gjf!ZYlg6!>{vxVB+xCEBwH~-%W7H-^OY0 z1Dw~d$#*`<@Fa(F|5e4``yK{}&#n&g+YY`y%kZB1scTLt{XY}9KSA)<1nk|V@PjuBKRtyP8LkhB%WDbqZ4_{a^S zKO@S|eG1?HT%IvU75@7Ue?|DYqEzqzgSKd`Y2b(A4E*2~8=zH)W-6AZ`sbE{W?)4Y$|CUz#P{WcZu={epJ z<^Q!r2mL;1`hB~@e@N&!JN6-k?|fP0>Ef=BGF*u!E?*}4Sf_WIyyJU^;6G_e_+ML7 zJ9AcuJzL?e2gQ!k^^DcmJdG>+-LL!gn4OJ#_ie>i{SJ zvTe?>zfIvi1OE?7XXHj%cg_xch~X*G@WZ3Gh#W4dJVzAelT07+yzx6moK;ch9-I~_vvhK(2DEtP6kE)*2$OpMCy|d>n9^ zuQkJGOYyU>6@7Mj_iGiN*Eq`Ui~R<}m1yGfe#LJY|FU-o{v(RN;;+M z(JQ5spG4eB^4Zn%z*+747KQJBHO~-d-;ODK+njTICEz57J+*Jn|27={lIUAc*W;@d zKKcQ%Z@rtO<2NgOZ5ja{KHqzxz`bI-6Lq@fa;E47!7FA@2MhBnt3lwE!gg4xMxC%7 zbm~E|)@+0kFiOp!QfsakYC);fY)3(%yX6&|^;RwHgr&^n*u=zeq@Y}FRD(jhUAP#8 zjZXWbS8f;TVNmMU>le`^@e}~FlSXZ}gL^EH<6|v94R;3^+G3T6^iH5 zg#2mqd^G)>v!s3VK4+fC%(LOtHcw-l#xz3-Axx$pr_zsRAZ8rC>DV`X`=)E(^zEC@ z{VA^!HiEDg)}bUpgEXYkt*?h|p3H%FN9GphPR%W@28%PN=2p_2`{+E?pj!`)96!CZ zJij;y|1orOhR2+dP|k=bXJj;H=6cM?Y|O}P%*bra%r#C-rsi+V@Hb}o8#DZk8UDr% zf8&O~al_xZ;cwjVH*WYFH~fto{>BY|LS_Z^H05 zVfdRc{7oAECJld+hQCR}-=yJh(#UVp@Hc7rn>74Q8vdpXe^Z9PDZ?L5dZ+XYC!5pH zhQBGp-<08R%J4U3_?tHTO&k8E4S&;yziGqYwBc{s@Q0I|sqsu3{-zCoa5E|VqlUku zhQFhRzoUk~qlUkuhQFhRzoUk~qlP~pU(rc1=w}Uwe%7$)XAO^j)-dU34VQk_u<2(F zpMKUb>SrydzU6ele+)Q~0hc}CLI!*YoJd+81K!xTj9Gu|<7+)>8aXS%oE2ftiV*RF zpZ#{}{U5amgif^5YZ@r3d-HLDhifymQTCFQJ3%2}6` zvo0xTlbW3MQ90|Qa@I%XtdGiBACOg88= zgKn#(xmK;vLH5LHm)xe)-D0y5bqWnh!wa2eJyXiiJkdsdj4e zvi}$ySv|EJ%$}S%oxll>EG^D0%;QUmNyKEH-D=_uY7)%=ER?Gi=FkFzBgYnQ%Pqa4W*>0c~Mu2t*W~+@5g(yh2 z+PN5X3hT9yImUZD2#a0^l+iC=f$P=ET+oU#2+OCy?m4h&t%B9W)!oFj%iO zi{~;vtS^me(kph`Z7Pz0k#-=3uZ=>ZR0EeoUSUo<;a0~htT!?1P1IPnsvWOf@6aGY zb&9EuF}D?=Rz(dEg04j!yw8nOuQFxSG|;|u3aDhKw={hyz=8Jy=@;gKiPE%oXiVBL zXtpAcB+J#l#0aN(faBb#3X9aK@~k%7V@bWLmFWS7O6H%pv=_t7-k{Iuh28q zL&FBNzfo_NLcI;Qieal$Z8r2dfXxWVm^Q)^6sy>|*b0Zjn1(~>QyG91o83kyrW7Nt zqx?F63+}vgAys^f%@TwOp^TFvO))g8RvKwYDJ)`{*3v*sZljb26{3rc;zk+M?nA0kW%s>luH=qaj>15&om-dL#K-+o*X~K)ohn&LelVN zyHaRW?igkl6=sKx zSOzH(8vt3BE#e#sRYc8SY!|O$s3R>kT9KKl$)jX27(l(WIw?@A4Rf7>uo#l9#P+3d zc&$&QKujo@jA1l_R@d30c1U{-g+}orc4b$wx0Nug-nnY62G*lyw_Oa=PXJ?$ zM#T+Tl!0g;b|kP$6GV+6^K_rY(TqGsaE}A86TPGm6L1<_JG=mMh2ii6q!_@6Cm(Co zhz45~`&O=oHLUw;Pziy_4`d6P^Z=_@#PWbM8KcRX6p~SRK1q@um1LA68Ic_ha@Zki zal-(sx!H#M!*&QP@jxe2V?M8ZY#Q}>VTB8ejS}%(u5JaL4Yu8|Y7H@yQK#9$JGzjY z#Re2yEc-&Q9+fFTfMtd+fg>j`oP;$PLS1aSP$Bk0WP70G!C+ESB6IqHhvY9b`oRGKU z`goMyWiu1#Uy(u-J;>I`+fRFFn)#Udy>e zhQJUO6%QdsKoq9R#A< zJ-JXvuLv0WPEss|kkST&!p0`M>&-%~O9nklGlc|`RFPtUWVlSdUuYL-z0lgEfRA*L zOszn=xSbq_<-(>d5YkVZg{^=>4SGZOWMtfZMemE)zt!97dOe6X5KShYu}kbQt6_-U z-DVddutOk7gmujj;@?I0U^}Yb6Gn&=HhHSig5Hy)^_C)4S_(v~RzP?u!F5>2p< z9IuXkZeo(jjC=D)&=K=q3{Vi79_(&{b(v-%$=*a(%**7k24c0+4RVv@ShAUML&g~` z<}pBxLY2D!Kh|l|n1;x6nuq|SQK0Br?!}SoF!8R{ya4Gmf{C#h#epC}*UEuV)CUaN zY72D~EPxJD2xtw?OTcwjq)bJc>bX0YgHtQFjk%kg+{~=phNIahf>U(U zF1B5V`?-tT2WOT&y*s{icF|8h zR67XPQ~`?S5GfBv5`p)HR*TmWXV*%G-pV4dPG@I%*QPlb`K4P`TSE@`;1U zK`3)!v+DrprGdprU1V|-6f@D57mtI}Dn5rY^fg*C2Ayb#;kuT4osjSW3}>kEb2xuCet?wfo&J86(6RCS`R@*qB6i#FZHxkmE> z5>YvogXoM~P$buFP!tQhS*sC6FDBau$%!D&HZzlxwD3`WD7Lym8Sb)+K$Exx7Xh2x zT99}}E*E}by;-{GRSRP|=v2MPzN1`-I`q&YFQyOm<|Y@Q&z#6xwo)u~cGW~{&ORKP1^Q-teWVK!7L?A+~TA~kyVBz6l4_D{7awn-F*YK1V3M?@$ zUb57vh#(7wVZBCrRW0QbfB@pG2EmcJJ~rxhTpbeSqPmn2yY(~<9|d!Z$8l;-j?lS# z)gvA@4`8y`B_$3=%>8lIvGGG7S9B8Cj+5&^7v^lII*yFY-S-?x)n*qYOp$U z3`J{>8Blbhb8f;Kh7n;=pf7$GH1FZaM?8QyuoihM-uJ6@&XbG~XXW!J7Uqx52DwZQ z4w&L44%4fRE+pcj_<&t%B9x;bgJQs;FDPi#+Lnvig|JDIwKppK@*?wKM5ydW*+rTy zHLk^G%UNP#6p9QMt97qks77JB1rLPtOLXdrZY1zd^E{3mhQrXsF%{YsSbovwgz{!i zrF^x>T%B5=I!N?+am4GVb#WM}86G*Ms~TC58Ag=PvqO%}He%DX?9JW*?Vp>Q3yv?H zAthK{3g(ZU3Ko_YPsClP*QJMIaiK=V*CFEJigC3`*)&8cCE+1xG$e^~z2J=4>toLP z1ATTj7(@OD4GUO7B(mr5)@25yp-@%$Ok#)1S%ckhyv~6?Syr#xs8t(A3t-sDlnu6p z=p>=yEDuhFO5~h1u^NyPIt)p&MDs{JHYIuIbPNOK^Z^wo)R70f5fHO1E5KjP>w8U6x*~Hosr!`3OgkRolXk%(wBj8xI0u*`(oDoD=!{Jo2Ey@B#O9YW5j7n=T$1g2 zvq_~*mkW|`NsxnUs$6VzYUwdSCIf!KE&LN<=QNIkHybOotAlpD`m^3j*qP%U2#M~~ z1bL)bgP7EAyTO*C+n|6kETIUYX)U>3wPZO4QB>0s5ek~fv=cHUkwjj+_Z*-I zfD8*uJ1k;O0|kxP?^-e^qnfI!?WxY%KA=oS#MP$-`7V*ig=4$5p5S&~{n z?3QV*Gj}XfcE?my=$BWdFI2-&e&D7_OGrf(hQ^z3UYS)cJ$XT}g~$ZeqBIyHgx*oY zAj*eu3vP>S4eA(aRO;EOEd~tJ*Nw+b?w&=BRQ#S&;T{;G^h`GEVWpr^25c7g8`Hq=8gA)Uj>b`+M3dfYRbIwUg*^dWdVlFaWECZQ2+VdpaAexD41`-Yeq~mie=osu2jRJkq z3DIvjJZzT;By12{ZhM1vyEYM@RX0(#7!<6O_WHc`-9IU0-?{Ze7M5oE=slh9_o9m^ zgw)eAJ2ivT1}skGDd|_FRXTNDDrV$Fu>L_PWt&RYUa5wx)W{CYkz>xl}N|JXx96vO3dtswoi` z^FxJdYX*>9l%YpadL&LKBWT~4CWE4CH4Lr<7bH(q-9ROuH%t2V4rPo@a#M-}+YOFF zxDPdc#Ge?{TF28fN;21Er)zMasIW;&2mcqcwdOI^xUdXQ(8i`ehoxvnc{R4oBdRRW z78AS%?Li@W!>(zt9#Y?I{CV!3D#Tt)64vzaMMzpbw5|&A8+$4qo$Tt=z=kYhDnDo} z#vHS&qa+WTd7|mDcM?_XM?=IBCt?A4*5U=@Svi(23<+}a4J;%~2#djW>?GMW9D!wk4Iq`l`N))x|*Id!g>-ADPx1SmS{(9pv%P8npXy@F$s*Hh<@oX1{2|g9s`C! zr{a6C6)%Q_f0n`?>%g!%laP<%8jY2hGOHwcONt*kKay_JD#=cQD@n2gjP=*qVCTUR zAB$X7i}@3?r1nRKP(lpJiLN}#y9)W>^3Yg{l1DowL)4Me7TRdz(ZJ7Qmnm#Du|KG3 zhO9rTdEo#|UpTrj)E<;H9zJx>jeHEY#M2rTN7qPaiM3(*mk`!NW8N(7zpWrWE!0ob zE_i1ZiK_~Zm*WNOj596gR%IqBRpII}d>oYn;+n&)O>C=`qyPctH1Q}OmX-0T1ZWvA z8qVBlV9@f;MVOED=*kF~%z#BTm{G_Zo-*A6v0NSbqAWlfI0D~+`j1N5I&B~F=fHSdVr~mntG5eX^)*1_lGwRENk31u)q6I_tfR0`;S@`iF;!~3vN4A@dW>=##oG~u9 zaa0&59wh>f)hm^}c>&IoaxPA>4_Q5Y*H}U^T8oK*YtTPr?qJY1l<0W=;Bp(y z^tg3qKUBqkJe8poK2>AoO;2tb=pnxE&3I7(HdvQTu2!Et$xad z`WX$F!*aUebbIg+HzRR+&e|APQQc@I#bT`3r^SM1i6Y>IqR}xUh|Hb}CXA%_;1~tg zf{h);FYE$kJ0Y%u(9r~}Znt2e2$!@&^@I#whq;l?ILki_byOJ|xFemv{3y_VJ}bf6joxuAC8 zs!&mc4H7zWj#LlpOJ3g1WJot5sKHVU4!RUI>7b2LX;W4f`-@U)DLj$uiDDOO=;x(; zB&L>zsF?tBR>CeGf>h(Few+%4$L1q-Brs;4WexFYtjb)_;cR#obxz3HT-}+9UX;!s zk1dg;`RP@2$byM$8BS8Nijy-Dvyq%JvHh}q2ZLN_+qwQg`c~ZamS!!wScfm_FJz;` zyzhdIpC}B?UIfd9dPlh$)&PZZs?5;RL5*h^m6_x;_vc#Ho2(qk9c5q$Tv6Jgy_qz_ z=ux&O@XH)@q^<36 zU;(=di=JQ?AhXk=&_`LpmU*d?&|~Z$40<|ZhC-2kN+8rIj-d1EL(E9ZIZB;Sd^iuR z+k&3p3@Har0giA`ycxMYkL6heAhrwpW&(cb{a%&)sIJ7_J5 zibxy_;9Y~XVp#d9Ht5XV66&;Z5(Q(haV6z@Dr$EClF#rktnYgJJ3u5?$b=)|^=c== z*N3)rJAu@<$j3iu3$W7JkX;vOBB_4bxDMHUq_uxicu`M;`Qm2@Z)D6A9CDb>ZxXIY zmjowZsVuB-ra9?y#%MDd7VV3TPA#c^Y{5rCUGb6?gN)gxGiF%3RNUb`1~4n@uw;T|cFB+uvinwF#(Q=X7yBzA>Vbh|+tJD1%)IuX^vFImyp^}oE<8prM*4S@p z1dSY~-K8H5O0y`bsSv_9BC4BNQXn~gk;W8JqmtdGdNyp!5O?fC4|TOjc(KffY`qXI zHj0vw)2S7lqh^KGZEN1sOqp<53|tCVZRlH6_jLyu2Yx4M)9EkgZ8S?VP%H_Awpevs zU}JAOET+*)Led9EAxYN@hfUTfn{p9lLJ*bj6g?qD3Kx(E!rM}BcX$l zY~@fBgS7C<6qF!wz|)gqfA&XqBd|%wd+T=YV^#0(!&8za!y%q!|DV3SW%ImbsL55K zs2_rG@W@l{wu99tQP`mt(A9>i*j2&N9NMm+Gi2m328!5xMmn9u;%sfpWq6KO2WL2E zX6KIK2d{3wT?WBPdAC2H_yE0tRLxv85QjzX2t#tB!}Hxj4Ykp#5xLtZAVok%;dTi6 z))^Ke4!01$v@S%&a?%8)-r~f(An;e_Adl5kvwFfR)+E=XoWA#J6suX6*;nm2qNMgR zp{WBMl=0zB-;E;F73okEn?4l$IMmW-m zGYe4PLIqnOZEWVEqWt6ZPR<#Ml^No)-DI$OxcQHBEpo4@4?@PeylJhA8&BW>wf)}V;PYN1$5>c z&8o*Nc(6iJH_@5$7TVQKP0Ti-36^GoehyM=^`fMV@0Vt%9yZ}G|9=s zZ*k*wI&#;SkZV@rFQvuxS5Xq2+N7!D7(jDzH;GO^=$s%+h|a{DlUihL(%aQR)TVFj z(RbJTEVyH)oRz?aF}*nci}z;wJyR&Bi57=*UIvR63l7&#Tdo`Kj#RGQ@-p~hW0;va zHvggyzGW>>l}0zS-mTV3FT$$J&_OKxY~;lTXvq~;C_^|n*+*x=QV#*%!2}ejf!?*S z*_`3>SO&M5N)wM{g1rnM!OgUrWXb@eGqheN+(7M^jyY_=Y{XAOK@I>*LwA375okn# z`UNrOu~0{$iMpaL<+YbV+UaEwi_+UH@;rJdY>Od#h`&zptu4@9O?JZO_r6Usb&Z z|GW0PSLX4A-&@uO+fU+0?KP}I1-0!@e-k$yr|0RC%n133x z<6ptvCT6RuMOPq7rX1WX}@-3o-x-* zvGEz@zx_Xse+PLRZj$}Ge@CpR?H|7V$qtpTu3rayr)hs&|6R}HU1_K4=g?!lb%1wk zdpCdX-vwRM_PhUrxa)sL`?q}eUi>8t*M9T|_y-H{8T|pR3eWo@EzZQpj{k#rmu&w7 z`Uem2`N8AT{$Bmvat7j$oxg_xOSa#>7C-!t&-S&_@c;gIevJRv{%L(Y75}?sHu^}7 zaYrAK_Ez4my}N!9*iTEe&uaUuws&|;6o1^ayM7%o;@|Z@t?j4(iS~}(Bc^>`+vl}? z&j@VPHPq|(aHX~5`rmy_#=rZRj$bF%tuM#7lgqtmo8*7*@1*_S-%0zMwI6qJ?cMcp z)80Ff_TGWC-*<|o1YEoLiq4b$yMHHp|L?W^wCh=}qv=QQsc1~J;_>V6ZSUUl6vkYp z1(NuTChqC>nc9EWz+L+p11DbTQP$W}bV_z4zR6&OPVc^W5iSz7VRtykOk8fcX>zJ{BOS)^g9qpTzag){HhAf z3LK5^4+hQ*90j;YaI9fnRmO4HBW;ewQX2>q;y50EvkDY3tDs5>=6H&t1LkNr#Igd-d}RssItL;vrBY-{~XU@>Bo2=n|HPMgM(5uAg=BxfjnoZ{3>nE)=;* zpKY(WYJrSo^jHRnJRgrg+5qdXc>MZzKJ?nj&+VQ1-E(`ocJ{q|#pH7qqiz}guEife z@7IHT&+*Ea;CKc8YCV|aeHwq~;IABiC*Y5t3HYPhK7&7Ua~l5mxn2+QJrUnaE%-XG zd@+t^d*z6CoQdP7yz&ejulC9n-mwwK^YFLOgN^4*1AG$x!ub0H{_62J7k~5c*ML9s zxfBKJYra9?xCDP6!rwLcI~RW^;{CyOEClc^JA2S6U zC*$u%{LS`obvQ1_Gfq!3C-IOet{&~8E zE3?WhUX%q_0xtD%$Kg2Lsy9A#CU74bR)D@eA3_XY^y6IPUYv){>3Q(K0e%X!m^f#ZkmmgJ(}%xY^oR1$S(OLh zl84UvJbJj&CQ85JL+#jU(7@2N<)L#z9(*(pp6g&)wlM(yzj?-eD-WGN=fN+}gYU)|x$OLNdB*)@9{o(t z(|%u`aWBo&z9Ucj|ICA*n+MNiGnZV<)woV|c<1&|^9$LJ3X!LM*p7zJ*!C#Yy&;QB8=h{5@7xLg=%`@)( zdD`#K!_PPK;CUWFJ#_v|*M$96gDxL;i99XpIv#Zvu zUbHT{cx`miqQI)f(dA3mt_>_(wPs0R+0y9JbEb0MQ4zRh?TYBqz`9$}bQS2K`|34|R<3DW99^+ybztq{73-D;R<2vR^rnkv29`Ch zj;;)>Tk+Y&jZ%x!$~CJ$`?*z3fz^vwuUWTp>Cz?+uwQZ8O>0-IjxGyeBo6!8rO~DpO9D5o0L2yS7DuDE zLYZrp1(u;=9f_|bk1IaAbX8OI)^$sx?8gIY@ISDO+7epO3p$ICixw?gk7BHo7XM=?9#xpre=&v)*#WcwM*GU=7OSQ%0(QE3t|D* zQ5%aEnZXtU9B{&q}tUN~qCgp1_71G+uMd(zU`9 zBwMxS=B1b*8iN=B=`}VkT_?(J6s4@yxnH!1X5<PT*s7*w=bzS2c$OI-~uPbi+ zY$ICH+%5<#TCn<-6|0vlx^!{lP0_WB8<+Z+E0#vDS^8Pn`L%Q)8y%_lq085-ZCtuU zX#}pQja+)!q6^Qv@VtuxS6p`4qDpv>9xMI2bm;;v4LHv(z$`;a3r5`kmO%c zVq7c3U-SD{;FS5O!0lKs_#utcQD%Jw{^~z?D^ii7z(x;ut-#|0Tm1Ipup^jFuh61b z82GVQk3-wi}`G^@b;e3Rts@Sh!khgb)^%C<_nF2xCM-O23iIi;@Z6&_pFRuEJstDuxA0~!k97kU-t1QrpS1AY zPc)w)3twiCI1XF*(=B}9Pieb7!@?I^_;L$hV&TuU@TC^s>|3$A%)(E%+E2Ie_C8pJ zg+JSBKg+_OW8teT{0s{pw(#d#_<9R}o`qj%;qCpq1`E$PX+Fy>yuAn4WZ^F~jd5IW z;klP+J}nkr79y`=qlLHkBDYw0?n#@^RtwMlLG$Ub@XYS?& z`@h`67hCu%EPRQD*E0vLEVb|vt9_Y;pJ(BxTlgz2e1(Otv+%PlJooC%r^>=}|JZ!O z7QWsfajduSS6ldn7XBIw-(cadweZU={Co@FWZ@TB`1KZ^*F4Op#lkN%NE|m>_)l8+ zEf)S$7JjRR=bo|obXfT74HCy~7M^?Q=F@56KVy(Mc3F5{6EUBJg>NuO9D6PNVhi7A z;TtV{zlC37;lr&j7Kh!U(Eb^La4ZomO8yj?N4WLT;zz7TD2`loDVmI&S%dH3v?`Pc zFK46Fzz9BPev~j%lT@F;7ZPSll1d1CHsM0TodTaum?=oAL*VxlX4;e5BJfngOhr;H z0#78&6eQIoZ~!NSG;HDk1RwgqdojItBhV zVWt?V4uS6`%+w;aMc}U!W=fH25qJ|}rV^gjrG(1}4+#8G!XF^q zC-8-YnF^#50-sIz48olPpH7%df2u>^_Y>xlpV}huRKi^9Q!N5dB>W-5O#&AX=8~Uk z5cthLz+CE6^#Z>_m`i-BO5hg=b7@ai2z-z*m-JMbz|RooQl2Uict2q-;i-VY`v`OC zP7S>!^G}#dc4|Q29fY}5r}_l`Az?1jsf57y6Xw#K>J<3fgt;WAIt0F(Fqh)g7Jc@1ipemPTv}6o0$)g&OKK`1@Y#efBit$Q z>4dq2raAw)mm0PJzEom`hu#L*Tm!b4g2W5%}wbxs;_^1l~mWI>Jo?e~~bk zv{Zw@pL={pVA+k5Q2qQ!Pok=MULf4l-52Gdr>VRuknF)k zzqTwm4YShKgAK<8;d6PO8g3mvHthWFmVz4|>B4xjP`q}-ZdQ#91gEtS*G0&*?;b8h z`>?Yo-2K8O;cQmN8zo}(|i_=U$A=??yf=mk-i&J|9~#RX@S%_J(3JWZK?hQR=D*M z=a4g}yz)@xNV4^J#erJqP&huPJb6p?xIl9McW}0|>cVkc_10HIMK9j)$g(Z$|JWm2 zm$??>&HPvj!V%!;kQ4-D!V-} z($-oAdJnyb8Q3KaoSv}Lyv6xd*x46$o({XAEfFUnf+j2fREYUW)9Q(pLPExst|rc) z6XWs~HbdN=Im8f~#2;jr-BqtrS$Av=%V-_eXdM_0gKmvD-8$%1-^w0zIw)}Q?Yd6~ zz7=2+hWzg1Ponm381{X?^~DM}K3Y?fX4u<;Bs%o(fLcci zOIqJtbMyPVo&;k%nA(V}zfwl_VAbde^}*2tZm2KpF6eis&fe2J6TJs^FfR_j{)}7K z7jAqkeA8nE4|UKx3!e@b{&Kri8Z%8643CU-kxF5x&k6N`R_k!no!{Z+f<1SIcW0@YQhP&v{KMu*)B{d6a6B z?|g1NWD5;|b(|!e0J44Vf`n7jQ}ouja|2Yx;Bg+02`7|*Rg=dJ;hshH$9GIIz2*MFmaHMgxu2u0(7n zRt}myvFZ308xc5`2!5pspeuL@N1-h?ur=J1K#!4dVQ33xq{FV;W&@q+h6uc&F3ed+ zxKP4M@JE5*k1#`vgPXrkJ-evQthp`t)ppd_^b$7EnuEB1Zi&}+sQL_dyv>v(QE^*MV_@GG(;_k=b=0LZ4U zY}WA#tYPZTJCpD$*wv66+K1&354(sq-uNtX{NPs)s=5TBJQPb^ng{mwc*xl*&4W}U2SbO!yP*SWQ#Cmy zRsm#JF)cy0Z1A&)=h=G}zDhs%*6uH0B zRr(K-*LHH`c#JJ}FzK<`FxOtgXPq?odN)4}kCN zDz(M-Vyi0kWo!>v)lxgr@ zbKpG(@%T#Pz4qC74DAo`Tv&!szSE>UIeb#7hp_cw$x;}Mm1qbs!4yO%{|LlvS$bjv z@MZdkDjOT(lv*E^b4pZxal!kM2~n0 zN-PdQB=Mse>PJb6nA?wr)Q{Lh{YcK#cJ_+R<2>oXK9nq9BB;K^CcZCC`$zcFoUwhW zU+AWNiLmcWnQF`}Gc8_!)ABm6C3{`w zmCbbI?h+>#v`oXHKWoobShEImfk@wkz2H!}=bSyQ!$rZ^g7FM$oSZ^Gb>Ptbp@~Q# z&hNgh;O5g0S0DW1=t(X{$}E#yrITF36gu}L59=hehneJ31k9vN1}Aw4C2Nui>Ljy? zKgl2c8s?{d>`5LLy6H(K>`(G{jxOX}75%OT4v?vmVsDw9d;oP^T9e^cT|!I2IXcDk zh&5f|qOP1ouJ29;y^gcIj(VIDs|Rd(7^a4|8IqqHKq_q{hLt!w7`9Fqwm#E6hM_2P zk}ynu`7%m^E*WujiZv1k&lu%NuM3zlQQI%7ncwZTpqVd2+3;hU+k@@UfwR{llOU1A z86bfXj6K`b%yOl59DPbbF6wrD>X+EX13j}O{v{aeR!rKbjl?DGs^5jkq)OUVao0Zq zeeVH0dHdm5EmAVO=p_ZPH3dU}HSF1%COk4uVd<`Vi=_9!VN#M?T+)QK-6G*VB zwUd`eH1jG($qFO{HIT50A4vYR8G&TU*nxzXVXRG>7-2tRYvE_Rp;USIxkk8vxm`n1$162x?%9<#Vbqa1Xbs3 z;_Lhl7wTF+w$7&u-E{CK?CU&PIHu0wxD08NU;3D?#n6^2TAY`~T>3{3dzlAlBJkw< zJVHjvS=2^n1ZG|t+pa)o+dU-+Sg|-)5b!zX>HT#@<3R!_bv<7QipkpF&rJ_tn%JD0 z0Wv)I2h%gGs5ltAmR98D;74vn3yl@su2$5L--=4qirB+g5%z+2@eUKGu2sBFSc)Rmc9GUJqz|= zZSLWW5vFQCXc-yyXTTKFQXVudJsO)?xEaF?V`z^(5$nxF+@}-Kl7Aw~bRyWpOa!h7 z>|8Di<3u!}WK9G?od`DZC!+gHn1~0)o{05AH$4%A{fU?{Qt&PU^+qFXmkPTjzp&F) zSoSc&HbB@GVT!_TM9C7CpbE<-zOYZmAZ+j0!fp||X<-Tb!d~)DfxS*)uJ6Lx2ZC17 zf{%>Kb77t)b@YoR*@lr}lR26*7(P8qXd8<)rp4B|f_~~79#7rc&o*bD5sA1uUT&T^ za03X~qcO~T6tdE|%vKu1Pq%s--qG85gbC+1dj{uw+iy;{ysdTk)4>?8shH6Wsj;1F ztFgDo)!sVt+gpX&8+#agTM2vHDt1A8>p;n}H-c(!Y~tJ7l2+K;ugA8xZ9+F~Z-jk& z`^8`0jdN`?!Vajgo%w~GrNXj@5q3RN4~Dn$B+`|NK_z{IZA6?}nZ>(sOq9%2aTQ z`%to`n4nHEoA^_FR|}?i{Ox0I>-G!X^b`~Jr+8%eaQ2)}-d-bbF^0!&1bzAC4XeED zVdUMS@(u`JlsAczB`-mhmrZ$ZqkY6Da+o~ET4Ma>nF(^niezlp*to1YND8IZ5RbKWm@@`Xkd0(lXZj_>A$xBe>WfNcCUOY_JRW-J} zWkNS?+k}02EB~yv{ppHawr$zj;j#>4+qs$_nH3xAjNYB+|5b@nE~7S10Y~G-C7b#+ zO5Jw}&u#1`=c9=c$#3V$!FD-MTi?XJ#g^+F6O7?b8=uVR94DXa3>#CbQB&gGm3cOt z8q}27!W2-sNM)#Zw~sCLa-o}+ny@c*K@O?mX0ch|kBj7`Ger8h2e<2ySN(D2q5X4u z$%3~Wqj5}+Q)0_0{=3=-$ke~AdLX3!FQ+-i^Nd4#u(SjVaSkn4uDn2ytLtH6n28Um zH}PzO<^Qj}TmLTwKjIH9Y8hozP3{fn^=cXHVJxFx{h>)_ke0C?CCf4hs%5Z=Zy9&o z49nOuwq>*k-Lz#8_AMj%$9LoZVI%KdDsO#$d0SLo_Av53ukvmbz9{b&lq`7(s=RFC z%NuWoy!VYQ?^dCkmY1+E@9;~Iw~P#=uUm@$pXK>Kj`{q*$w<9TrCy(3>WwNjdl;#Q zRO$}lj#6(!$ &O3fy|)Zd6g>aMY+?i9LdsR{d17vzwd{@()r#Q&$O|9fz|9*zGm zQ~%$njbnQJTm1j8^xDknGU38Fu@{0MhJX5)H(Yoz!E{Z%iD#QE|DPO;Rq?vE$DfHp z={=5o>;FQiM*RO_wTuS9dHjEiS_XR<%XmvIqf2IxmXScovJ8T18EoQP#vN;68IO-` z8NEU`Z5f1p%SgWPZv20Xk+)Cf-I`zCttu~j7~Qx@ma{`|=K_@~r=7dj5}NKL1Y`sh?M=d-F@(p;EJlk@|R*Iw{;y>LHXY zsR^poY~oA(jV4I_+SpPL3*EHTgng+Ca!5`8?*o70|K;lc9^9@+$*t z+4-q6j`S=f$CAu*hbRcxCj{V(UTNGf%N_M3`Mpdbc)Ea7e2o&Bw+Ps096L`>v+YNe zZId%1%B$}-qI5vn5>bZL*0ur86H&IQt+9u(H9m?1593|z-c@XrEL$rEsJ6x?zO9|J z8n#x9TWiL+f3QU8rfrR|Z)^Yb`*#yj28_JN@Xj75mdr12r^?G7M&21JZ>jJ_-zr1N zl9!;$%O<|OvsXdhQ^uB;_p{rZ4}^Vrzl+zE<;p1jt4t%x?{Ul*Q3AM+NM^ZAr7q@u zM!ECGE|r=+jMTGKYPp@Cx0B2B1eVmZe5u*QmpZf(Qdf*Ebyb$sgng+ypC2tXBT5PQ zV^A2HrV+)1+x6&0l*>Nj-2|nLW2JITN0e4{P6q(9;H&TXmqBE~&s?dcB_qn|AYj5q zR<|R#dh+~f>E1_rX7S$TD4#ewBcdD|jNvY9Upf;c$IelPjEBv{)WgGu^LtoAZH+yQ zt<|Wlg~cXlYxO8uwnk7rj7@x7dwK*5m6Qj-E>4D?Auz+!FLl;%3xIDTVa)V zI^aCM)vNNdhmltwj@;QGd{N%zC|U9nRC(FNmp6sC0dy@KTizz2o0gZbFYo*VkT-LI zLj;*$bp0tBQSQP~BT9mqW5$iL6-MgoRq9#!rS4Oy*~3VEz0UG_;SN$ax1>c5wWz$T z^yPg6?*Q#uIi|eL8?Epe+UQIAxq;D9gp#g3StY$6N0n4=63>vd%1GLzl7{n3+OLwb zhmn+Tb-?p)c;1L&Zneb36QhZ+Jgw?IIC=tQY#Cd|4qHY(e%giE2_(PtTgW(_6P0jc)rt< z@!P){EhATh25`gG;NbgoHSpkeJ$kFbMGJK`Sgws@dYlq#0-WszB)XpXvP^{%-$Hzr zqKV(Abc;e@B8#5pl9{v=mq|x(5M=WEL=b&MhI|0mu16IHaGZP)ug5grHTV;A6LH_Fw&VZjK`7TskA>E9TVn-rTIRdiek>bJ4g;HWhjhl{ zpABWOZLJ79lv2A_KXHeetAVG2Io^L<0wtPzk3FW-_j(`Uu(W7>1SiLX0;A2d6FCE4 z$kNrT=Gm1$%NSDgWDjGWo7Ft=SS2IYVV|%5!eW@`^JAN5(l*cZBh^Ds7a;1&BWWe_ z7=%3MQuL7%XpJ~A2CgU8H>?lp^u(S-2{#M`@BleVxJHE1uf1B#o02GzZf-lfg&X&W zZ`zONAoVHC);)Y;XCjTpZ6~pGJ?$=>QO_aL^_(ZW9&})c3w!-xrxd%wsS|r19bMrS zSL&rqQjTC;sda>Z?_nSc;D{Kme9OZ?=V5tQ$yHu5i&?QG2LmD~6YcHCrS+A@YYbaA z^|lfuC&S<${R63e+@bJw1?|r-+Jx>t3YmRREP(4`Y^{%i>$@tMr;!M^sj3#sP!T~^0^@JJeZZRcbjft|$TzM7I&jq&EkR3_X_labqlz z-r4E)Y89zcV73_B^`8Fkz-zKRi!J>#28hSXaM)4y=E{J)R$O)C^oF}r1+RX`>25tx z`09Ow^=LQDb_1C1?bG3s`P+3$yTjA#1ij^K*D_W+*#5BRieME**f$x^Z{P7B<9#zn z((R$UTJ4u!Jh%|*O51OYpr1x8#E&!BMDgf1coA2rx`3_{TTb5cviQ(ejD-h0*lTyy zU*xgc22L1Pm`4leZ0USIhaB$Rdq7vCcFD;k2^4R9WmkT3L4uQ(09qG{2o#t(5tl$x`!0 zSfq*`Jzf_rP7mP+Jeb-fe4htXB`NPQ#WIu`0w7K=ijskEW-Qi~)-%BDXSx%@_Ji!9 z#@E_&JoaUqd;?scX@CUP0NKPhz<*qbT&D}8jj@Z;B6QQc3WR-6dZ(`di+BxmqRV+b zN|Xpe8YNi42VAM;^T;E;cqF-WhfGW%68!c7NHBOdgzraz{ShEploua-Wjf~>frCUDgd~=u67&VTXjOqPM6@(Fk9U@v8R`S+?UdWM zqvXt3K_DgO=gHuxn-b%_?qbR~$+2+CFO)Rx zO5G&uhLYk#-)RlE?g1%nE6;-WG2=5Tb>qs;V@M~vy$8Dskfr)B%&^C3glfF}jsrO| zr#iEe-xZ>sM*ij9c(t0m>*M_HbxUt+R4;UTH2zGxQtz!_EUo4jZJ0urDVZEmc=G23 z#@X4{EA;GaICTbe~7jhBI;*WgB*#d@JrQnY8M zvbc~8<$fy)S!`hSXclpLs4OZuWs#&j7V~#YvnXx6ccpC1w4rCi5-zRCxg50Z=-))X z@<)LuH43noiJmhMJ-5JuH7aaH$%+aDHF~m%A3a0m*okPUK!VAPK@!}w@2~$)==N6V zZaqd}Sm=r|Hn{D*$aTWq2R|&Y`|Cb<3638QpE<&@C+$H5llq=m7n<=z2^9KJ6a(pr zB~TqZ0;liIjv={uC1RQx% z2gSBDO&FR^?!Y*ySs)DYTRa60mbgWKd>9!L))C}BI>iIjg4-^TQv$UlRV|{rcx)qD z>HEXnINQkEyD&*+NDWHY<1QAw3X750l`hZ-lisi!*V#INSxQ~fdSKiW2U-uj?B2_! zt0c$hJC z7LIlpqmX*M(hkjV9|?_OBZ{6;tVa<@8pSr!F-Fn-5RKvslhJxB#$prd@P43Ghs2`O zR(5jR1ri;0C>0qg9_v6WqeHfNNbuP@EX9v}IkD?ihwK6!E*HXa6)szcY&uGZ+Vlu? z_}Fuz!)+MIE$ZI^vx_x=U^)*$Z~_eb{L0UuCP+E`CF2hC*3s^8%TaI#N&VyPY@6i{ z>+h##)8{VmMT~6+on8tJZ^ePumriwwo4 z+%mX@cbKkzCKsv^0Hh1|>(+O>}37w`HXmuK5 z(W#!D+;)M)X%;FK&uO~Q$~X<%T;#ybIHAVm4K!CZneJmBmQY zi=O-uW59Cg$BqGnv5hm|b^@POEPJABIwNd*j~gf~a!97A72kPHku+!T!Am7GbW;YY z>}W8Z!sbswUQ`L$%cLND+&2NiRN~%Di3w^7!X|zSvgc&XC~|=@V%tBDOUE|0&5CXJ zdhzxm$A6pVR1=ehkI5-C|N#6P%Vy4e2c4ppIBUY$fzjy zYI+u~Wti2JZL=)y?0*BV85XxCOPZ~e2Kzb&?OEJ5qDNWW_%`mv-Afv3aU1q-!NjZ4 z4U2?nqa*Aa-331oqbvE75hu6N?VHFMkIIbmTW7pQnPF$lIHB#2=9uvS^FvFsggN6+ z9WOJ!QK|Hg!NL{7!g^tW-^xN~x-(ZJve27@h2kt02$O|tl!Y#(((%gSweaZC)WYq; z0>71oM7lFrAhiAQ94wS%u|SwC{Ngy#La$O;{_5dbs1_Dlgav*p3w`O%V1dx~z8oy@ zOvRoE!epUVS?EVUz#&GMoY)LF)|0-MC_zUDT=E(Z5W+PBrj30%#X=TN)BP5 z!(=8>k84tBR!lqXiIt-0o$eK*NWw7lS)`+x383i;@gkwAYfWHZ$~y=A`S<3!;fa8#0II)eK7d$sA z!?h1Xw^KiASz^3 z^MVo04=HU=^8#gPz7&0p=GiN^=BMG;p>Z;=nr9bizKfLIxT+*u^K3dw^V;+XG~f56 zXub>s8O>L`j?oUId8x-@i)NnYXQ3#$InIty^fbSabWAj9`43;S(=nFO>Q09)aek?ibD1V<4mX^)gKU{ZOgL4Vq}?X}$$TPxDPEdYbPb9bfZX6!J7L zb++cez0KGBLhxcV?*grwCl;C)GOBsOh~~E{ZBO$8WoW(ueU0YXE4SwV&`-_Zqnc+I zXug4z-MFeGTk~u>O7q(E2sFPHSLv`;F2_JP_y2dEEO&sQeX>lL8)gqrg1KfS=S}F< z3nghrkGKB|yZNr(KXi|;HahgA6dd1skN$08fEs5~R zo^bH{CA^D@=}9`{apN`U#^0HU-z&{qA3>sYa`^RUv3rd!xKj26E?Xubnq0Q*LO~)a z#-mc?QrMqicA!~|yZNAUw#`&$5*S;ZL)u~(2XS8fk_$1|N+AVvw&P#mKVHvvNJVt% zo!W?=k^@iknsov~fC|2y*yDZ~lN%cXZ$>xKQ@~eF3GRV^W$1m2uXmoWQ9AE?6tkipA|@ojX-GIjVpXa~2$2_zSWb8vI# z#b$_d43TwV_|r1LunfR&9UwIybPfM*yU4|zsX}fOzlHE1>TKO(DJ%H>iQ&Sy>e8=< z>84dU6-Ikp_?&(4m^8tCASO*@BN}^O``m!zUs;xRPK<}t*>qTIn*8h4>ODHL&NE|R zJgy^m!)XE-d0hA${F;l9(oMoX5Yv$ZfXgVP9JUs1mX7B6 zyysnRyLestf;t3~5RpSm>gj%UhJ#yLN)x1(W_hN|5zgVQue@^WYf9QW;zFs1)v9!K z7<87Vvf>OhC8S%kG^L~ScP^+H9i5uO=tLBA5I3%^@?N&L*=j_Ta3uz0-mwyFy9Oy* zhK~#Dn6`}A+7I(IKF+fGI7^<7ORuz|{ug2^WYpM-g6HF1lv`q}t3F;KuLF`F7|4vR z0aVzrwU`ZiwX=@s8j>@~m4xbP+L zhAjfojl>1Aoh3m!E>w(;3zt2|0p1d#{MG?d=UFnt;>rL2s>S;RkHF$pdE_DWL5&T6 ztHsOcc`aTh8Wu0D@>{&c*3U3^uFOof#Xk;1&a{>$v^^8pt|zfw1fy7=Hj3<9u4am5 z$ZI%FQbX@SQd2=a@Z#+zzwe!nnnCH7g7)%v^T!1#F=uS9L}Q?4APfp;!D7nWZHz$7Ar+Ex`B0z6Zk< zmi{&_Tp`44JP#jk0qzA}4q1ot$l9f{nrg^8EG_Mjn`0DdgfPfq&*|$ape%#S-urPd zjJfDU6*?ln@+Xn+dsz4KUXVA zZaPIQ>WvePBoQTw^$MQxgoACt`sq0si?RrP^;BSB5PxIj6M9_QbFB5qG>V#EIGVS% z;o**jy{`gZ1^CtArxLt(EDU*;1*glsX)Gs`WqQ9{KbgcX8g-b%{}aTgtKk#6D`EQt z61Gg%q=|S0(-5#^Ue(~=^Cs=F3x>f{`)Q}Vm66$!HCA>gIwDr$vCP4}8{p)D7a3%S ztm=jQ6@=pVU@+dw<~{Yauv-VLfSDjCe~$(%wFiViTk}k$Y!#%dF_9h8GGiif^A3^z z)T#DxlO3nWd+#X)`TI>1Y?3!H{(YzFJskACo-Wv*nJ#_F$=`jt=mCHC>3XjQeXpkr z^u1eHbKS(mrG<;pcW_@z>gz{y4SL^QT!S9ST7w944O)#hhyj1oIy34l(z=CMZ<^L0 zg89YQc*`$tAx;OC?1=q0#hmv*4q8b8{^I(8g~~DEy^}+n@jZ}(Rw;6H;p%~R$NA(- zw!CNap;jtS8x-+A2@H?FDY31ZJG=)=PKho#7zok-J+U#`!o=)#4M;w(yzsp+YEGIr zU^+7U1EMlcb>i5rM>6smGV(X2X1VhrZ;1%YY})HOXSL(Ko>e;qd+%r0%lEuB-B}yW8ILS*}Ruz3=@jdxK@qGVM96`@erv zlychZm5QAoDIKRh1&q;~Tm6kEvVp{ff?ha*K_iRDmY3S7z;LVe`vxy|V zD@R$svO7n&2iZgS0vAF9)&)+2{^p&F_u;ukr*2~I_i$G3N0_(Zw*&wU9ljICDLp07f|vqBbi zdtTnwAD36=KwoT|snG^tB$&Edi7V1UCr` z@vkG_e`;YsenZEWH*0G&OC9;PMnu*;9s=qu6vvHgtGxTwQjJH8(Kz)m0!iNeD_R3> z^A&FHX4qygEbaZ!Mpv2IW?z2WtW`a-hbpZpQ)vW!+kE*Z+UABY(>5Q)==Q!LVcI4h z)y}+hB*9p0llN(Ow%Lc)U{Gw6WwlL~yh}&YE8ea#IMX&8)HbC>*5zSop4&FpL!V-s zO(=M_$)eikKhbvLv3?@3)z3qQMz;dv*``$D4hgo&ZJYB|r|e^F^Lf&DcCLpuMa^Tr zVNII6W*z#E_Ar( z>iaOJctS4LUq!KT7VN;{xIag&&-tFGxp@|W* zT*d5EF}o}=`EIwc8|w7M3_{GoCHD1#?5Ed-Z_l>%>2+Zo4`-6`oAR2oRO5kKv`W35 z-<;**%Dt%1)IkHp;VoWW^*YhZZ4G?1E<*!tssVN}8qoJz-jPrZ^jaDi^+L4CCo$p% zzerOK;C3WC;u2=W{S9v^O3#t4E-`XQQ;_!3(iyovmsV_H$fi z%Hkc1ma?32aRRsck+6StSDMJB3SM_Lth#Y+mGibBsluxkKqqw^mt8BH&00qjnox+j zf#ZDHomh&$j%J?!fV$WX?o^^J*(oO5!eLUUxqUr_CCx< z`r-Leudx$*Oip`WMx?+bZvhJ*hN*U4uaRskx%Z|x>n!Gb8WPyUgoL{|)y^Gw-5o=M zzA_7U#Dv|@kcI@j7RrUB8Pi#2&1zJKzJ)X&z z$M?GrOke|fCV!vzOuqB0jtBjtTVe*Kc@|FqzB{O!|l$goZ`QoKlH-;0#!DLyx6Ufmd+%}~w)n15zw7{}vGx+`Z=lvj6p?{yrIb$2JXq&1skAI+JN zJRT2?%WieNDh!P`pp_)O3sFEvP6&&d^uDF{l`khk+xEoD0VqY5Y>Lc|aN+>0P;5cL zqsXFCyjCf0Awt{YNiqORk)=$E6=>B7tS06tcoesxs1)y~xN&*2gVz=!P%O+0dBK8C z0)8H<$ z>3eZ^=(Pg9j({|gjU-LvdsBw7f6hxa{!LR-jWiLh2LBndi#FjQ(u=S7l~=@hSJw

rmyPMT7U+c7~Sb(&_x%Sq2|7jv*!4bw^r4Oy+tOIE7!egu;J7QVVvP&GlfT?O@7 z#(nc$GU#8Sfz%(FsmNJap&^^436ND4#s3N=gh*CpHDpy*n_E>;Tq}-93HKml?}^QT zlxp%U!Q}DSL)tCXk@`_1X5WW`XHG1t+2d(Wn*9I~cn=T`*et`hQe_ELOAJ-)4`5D( z+Ayj-YAhv6M+|x}jEvf=7)-rFJ(k;H^Tbu`T!@i?Am00hbh{ zyw?gR#p;p?Y?|Pmw=$NvWKOyNATFu$iA&~?=gym5;hrv8s4kg{&#&TP8lO+MUHDJi zIe1ND)Y@~nqBOyYB%~VrH^?j+blXL<8MM-vytJeVo-%?4Qhe#PnO?k@fVxYQGYNOS zs5Gv%d96>Xu`@xkKad{6u5S9O(cy)XeEvnJMy1Y6&XPEFl#)}~ct5I5PE`@t%Dm)k zp;yr58Zg0z&?w2lrC2;3k>=3(L!>SAqvCS2&_SJprO__Om8PE4$d`(SQSi)~MK$Z4 z>Lk1%=e6yL&GD(S1gie}+L|Cm?&%MH2o!m##G}ZfQanj0hFZLacu$T`jwO(rVmw%4 zLa1Z#G`ygDs}c%rRVt2{acCRf(%r%4B=5G%)daUrH;lRUq+a$ZoIWFw5$fV)IYs@g zc$tbMUY3LXJ6E{EFTD&e2gbse#n@NzPzwADjJXT8+UBdl+FL>DP?gU*AfXf5I{2R; z(gKl0ah4loE}B}KkCb`4I$NbKri$V*nMt=@X4oRTUM$coa9W#}tW@KICCP4wRx?+k z47tPc1yIG<@}@pOI++V)mgpb_g~a~~k=y87Dw$c%vgC$usY2v^=lLf^WQE0w$j`#J z>{XJfif6JY$~_NuwYj0z{0c@rBt<*_zL>1Ga^HmcA-?Rk$cRD-YZ3GaH-x)Mu=P3x9EN(6-~Q7CrhuXT!P~Hf4T zf+W*|3?!c(QbC6D3Q_?J2raX=*feD=Y@hdpU5{7u zh9C<#vRcwUd=e;YZ?8c{Z$nZ@crDFuwWUdzRzk<4?K5BoRuVm-VQ>stz26hSZ(q=2 z@k;_jT~%M^-7Y=JyB5gh)?(N=+jvepIg|c;> zu&!p|*N75blqp${tM-G_VCF3Zy8^x(1mNs!s!kb?@lSU5rh9Ovca9UM>?p?j0`V-aHT?@0v@8AZioj)^nM;vTlE!Y}7 zE7Y+p_^S}?wXQ21zoVo z1`+4YXhZDT=Ias5Ti+aa=f_*$yzY*9XfP|-hPRvsTHidT`RIsS0F8DGPHBDfs^)XU zZU}PlH`OpKhRicuH{==o>O&Qfs>XR4je2XHSGG?AnEb?lP>!BZJl+BjgWY;dG1?^m z_;W%dWx=*JkTet<3AP(+Hn`em)lH0YZhBoKRcqg2*9P6cw|TIXvU;WfdwD*zn2Y(+uz zBQ?%HG1wAx`7+=8Xp0keehvfZXmL&e>n+KDg}uZQ z(OW1^THnDo?(>UoXF8V|)jF3QMza}%c)EaU!H{Uz>E>_XEFQ%TVp6!Ziz>i`?!`B3 zWEaK`B!73XkepqW&DobwDV*K?Za9ndT#g-t0A?|R;Yv*ZJ2lRm$zPm^skQaBo6I=B zah^#&kh^NHvnTn@>?(T31H19I^pA>Vy`21a0HdXe)=`pR+vAM7??TU`q;Sa1q-<^w z0&w<)`}3;1V04gAep<@?d|5Ja9F~Dc5KB%(EUCpiwZX0zWl|6FhCXrL*vl-s3>zAu z+_m)}(uCy*QEFRlysk6(dpxz8>Os3L6948`e*(d;3N{vD;KV&2ao+cF3j-ZYb^)SL zyi@xRB76+n}YaE~rMJTGTE`E7&q;)S?TF_bN{K*qG?5>@j+<6LqPwVGg zD1y^kFdMpF%&%T>ZMgNt(rV{5T`JzjGSSCnVgLh2gVil@aoB42-Z18{*7+l)O%9+x z%xY=Gy|)8JM!cKEInIGO!C!&Tj)ZkiTu||)$xm0`KowL6f7H(4TkE`>>8?MVD#63F zqXy0rlQS4~4%l3+lSc>ZCcb!KKiEktJ zi{P8?lFCDS#y1}r{CKqm?r+&wQoT{D2d^d-+(s2BY2INPwd^Z4^%%OSc~W)DW+7HG zcshA$KBZ+}nQ1W?^!W(JqOcpXjXRtIZxfHZ00Sk_lWUznBm?-ZfNJL_q1a2IVCW^b zV&%)#Ef1ITJ#Sc2{{>q!hFDefjEK9rd_6)Gl~G)|f8*5`ZoImZlumq{x~~a_l5EX& zswH|Gna2%r0SFw$t^T0|gWKSgLwibsZI!%b0w`v^@_w<($Cfz@I+r z#Hmp4F6K`(gaw_|?!)CJFv#T1sA?S^X+AOe_!W?E=P6WnV@brph%{LDqM8LWU}CQ) zUz~?}_a5msyTAE^Mg>*TlPeE_^~(Lpk6bX0J*J|EX~~{HM9!Y+0?BVH zd&M>FoquhacEC*bipVQ=G09`UGmGf zYfrvp#7MDgD>>MPuj(%H9C03lk#N8WMo+W*5fFXY=%uyiH0maKu@G!twd;F^i1sza zlSPz(#O}G=D^v>i%^ywceSC8fxjr`ntE66&^&y$^Q<5J>0p{36votn2SP(6Qv-ypm zlbMMY0XKM3<$g{riBy51W!11_&LkTrsem6@jl~Lx<;TorRrGW=h6x6ePIhBrO=F4c z!5FWHL|}ovz(?R7eR903Jb`2Nrp@Je#z1J(l}m$d{|$7VdsjJEgSzwzJz z9?PnOziKP*047wK2sv%#+fZD_!uN>V*jD~uI97L6p|7?r0Z}b6JX8nI+Q2#<>%C(G zj+X_`su?#V-vjd9C*O%kYqF~OU}%%Znd+8-KUBB$7IPdjTL;yV%M>#=FmxDh|6Z&{ z9Mx~jfr9Ey_lRm+o_l-Z(-BAYU%gS0w3Gz69+Y*4!u+(U%?(~9rGw(;o97+I+h@<1z2xw3>AOpl6)OA#k1v*9$wpj5x1H+@orOyUMCt-?|jXs&T|?2+=HBp4Cg zVAvfOj$d7jVZi4}VfVtYQyFQUP@K9$>_1rDI$W$xs-5cb+9cw9zBuAuQtO^n>l_zx zPYTCxL&A4S^Rcy!=c8Ow>)ZrlmsCC`XAYJ7@tX|S2B(?)s@9!W8^5CXD(Cu%rQ!G$ zC3Vh(@JM&9``O}fYftG_&XS2Gb>}=6aVA7Y9<6ih(Qy96GCrgi34Z^zNMrT*@NA@* zm)!niSp%V!aW%oKl68$Q)ds)+M+{w@+5;2G5DnaUqSd*iE8)@w>scoE43&HE0 zV2U@%=jlUr41PZ1emLBCR5*T>=ql`<2&IA!6!?*_Gh5o!MO>V|UW<&lBn-u{+edd7 zgRq>i?nq!pw;#Z);gt2?aZWIJs;0ypK+KE&HE?)TiFUY5jKTMA}BXtP#&QBxGlVRsoC;_fe zj4cehL_J*M6Bwngv7527wj4fjT*Mh)4yTwIQl~f+acaup7001nb?ZCuil~<073ZN8 zY1~^Kzxw&sH%o3gp~iWo`vo|~uby}luO5mx^UF&ICt<9a(ch5@+~S&%);Hl62gEI& zDs`}t=}z+8LYiPM;TI8SW_h@^usFqS0ko)YebaZ0%f_2F>oEDVYuyQT;5h6aR~x?+ zI|GHy!Mer@lws@9@=_owN7OYQgKI1dPHW@z8UKj5C3W%o;(5;e_m$Sg!|WajojVWyQTsmlM|UaSz&I3X1TV9PF@*0Q@6bPn>KYHh zKMs+B;uQ9XvV??97$IxjNpul0YH^V|>IviNBXt?NvAR(=gX_a?rROE8I`>4X9L=E7 zk3^g~qQb$eWdg%)Av`7G9IwbZ*~aF%%I!Fh8JuQ%&=lw_Avy~*9FEW2$B>06)Vikt zem;oRAN*3GKvpJ#yQWq<3$PYXC>SgQQ0sXEV5vd%xvj%6{H zxv=v@*gYxYJS8XFk7B2?uP(T&8B)D-X(+fW1gBXDv#im_dn%uWv&?B$XTb(w5vs5! z2tVQPil78PnadI`8n(Ux+s7#|mV}=*!S*A_WK+y_s@+q{2{`vYiG2Ys1I@)DcMfql zvEd?ufD?Q6&MPAB?U>+^JrQ?idBnZGyaX+vr-zFM&@X%r_E;Y%O0qbDTgvY#8e)+b z#)IGgMQvk@#3Qr&Zr?6H-U8let$&F${ucF556+7y7o~$$Ua4g8VpNie!E>{5u#L;Gm~uhf+w#c_)QFpv2}wY3t~xGw)t;=u zB(cb8AFVG=M`i&->y0=+!?`$<4aqS|5THzsu|WdrKIRzLBA$j|PfRlA)OjYNNd|sh z$TO3F)e_9))6(;|)$vPBjxj7bMo&hL@g{PNTIbX09D}Ez$T50hP<8GxFpF_@@oGc? zuC?RK>s;=aVu2iwSoF`(E!)t-su3g5uG+Z+na23?T0}x;L2(^z0I|X;jJSxZ@g^)@ zGs~NUwT&MGfcW?iSifeLr;T#t&g-oGoLcv^TCCd0H>Q`?#xDoSW6JqPXzUqb`9nax zeR@gcoWDrK9_b-($T+4WqNO2Y`k00M#htSNlpvJwn zWU$z*`mn2l=!N)(6a5IxSG%D=YvM1h!{cw66mI>8Vr12~A)+0T6InFpG@s#3bY1*1 zN~sELyb{aaBbekXVLFURM-`-Q!;Ve1P??zt+^!Q@2?FneL~{0Iy5E8XvXd^jVQ1$zM6Pj!uUxn*Wq=Kg8x+}~Bs2n3oZxkWedaY*My>{LQ_l!{Iv0{a+2q;(ag zyGNX&j{-Azlv{Kmjy*-&*b|?@$3Sw_`N4)4yAZA-&f|{U$bm~!dqch0b9$%p*^t|& zr<2~U1THr~u0*iybX*{5ZNY zd~-6nXV7z)V~+ z9oe_54~A8lXgTnD^~TNRY?kc#2sfQJmk&WPy9*d@&e>dEj6Rwwvm?}b!ATT!WLKht z*Pr(Q^Hui41{yb7s-mYN`@bZ5QXLevo?!$$PON;adSm!P{8fhHcYKoz$}Koqxu?7y z4cs^{5aWssRm*L#5Ln>3uoG`U1(wexa*%!Z8YjL|AztLGb?;@9TIbX`7h=9$*|HBi zlP?CO_w+cEFJuk2e8XYqnIoB;J<#j+Drf-rT2$>Ge+sWJ|0W!~v^U)NMDn83Fmu>v z;7;(NZur{UXb5?+*YGM!8{S?D#)ECYSAdTi2iu-j;E>c{#}Ugpwmbl#?mPIFCGHVy z7w!U8jP^`rqE;=z-AUCTElEC%L_X~74Lh$Ki45Ul`T&To(?J#BLDG! zmHeMSocyPZk{_7E$`AdRS-16YsJjYV+E4&@hQ>9I?;JN+aA6`bP}FkZw-bA-cR~{* zbS_#;>Vr_;q3-d)wn@~>hPP=Oq7<=<*q`$# zF&jjnwP5H8CP}M0tDUE6aDBeUY3_(PdoOk8JXqF)7}SVd^sjU{xP;w+8HU554ha%Z@v_pq+N%;%+0E@=$T>ntBh2CBk7X}S^sziH~x#-a0tZ?1=|=$a2kyebCs3~g0ZR6MMdS*aDy}) z8{w8buU6GK5-y@e$&W2Bkhx2WYmERi!^3c|7G0uK{q+Yz7bPd7+FSyNjodi_LH+Ct zuo5AF^nykm_|>h}&E*Ne+{Mh=YV9Dp^4UndO?FxFtY-EuE4CB2p%J!N@A0-+@6j#R zhw^Q)2G43?6%O@!;Qs2R*k3IjlJ5ce?vw9Cq;*jCSMQPi)asV!u)q3K?5`@EYW@F% zzdA=XiT&S#klCc&v5aLHTK2^xS3wS# z{0(bQ#l~&U+5~Hl$5nGZ7mGj0NKv!#&T%#F>QC0VcV1oN-W*AueWL6UM=wJX*%fg& z8jn7HZ*>Kn!H381t-c_zNpg-h+)r{9&NZvuE>XuHwql^UK@(O732RT{7 zrHVl6kBDVqM<^xVItOLVWTdlGoIDHN?bRann%pZecW63N&*;qEFEal+6z(bdH4*qE zKXg6`7A2pCg0bBlosY^MQ+YpjDR5z+^05uu8*qo*IB*bb`vxYg8o&OFZPV%v!@mV| z`#{(|;e(gW2<&?6Cy0&loy-T}&V!(xx(@F}*gbylyg>5BvpLk>Fhh;LC*z0`i|>4{ z7`bEO!pHW;%J4g{f$&^zW888A@<0qlRC4MG#ZWJcRPUpZtNNky@Qvh_$gtCmeAo*Q z`*AsC^-q@t8<2(bujw(|_PPdtQC!uJb}y?5{!c%@_E-OMu=GEN2bU42rw(!M3K17KL2_)dA121hw;{ea z0m&bn>nX)*d}@6 zc~saQc-wroJqvAf?l)|Bv6vU89evKmk!ONx9eHf1)``y|H^CqE&x?=0mv&YgKlN-L z&cQtl*?;923X1<$J5Q0=_E`xsbF zwR5QQ*}yOS=~m-Xbu!m_orH{{;Cw?=pl! z{%|H0GC%DIza#=5H0eh$NS-uic8YHFhx@Z!^e>ug8kAui4zQt% z^k0Jm1l#V$Yl1VzI4I{?yONJziDmsD9Po1xrut!?L+uV!fdJ0c+WGRPxeBpA?N?r{jNS}~7|6fi- z=A{>G_h6Y3Vz?!X`kd#^e7k|+`wh5lEpFW9z5;z{?DO)TrO1#A6U@K9^7RS0UnZ2m zCLx9lmGp!jtw6Ch9xtKsVrP%nS8AP}S_hkKA}y}aCz8*DSq#dFsdXpsnma@8N4(_- z8MvGbG!E3wKCt%qT4yh}6WqC<2a)8%LCw#BI|$qj#BBo(<*QMavo5p67(qCyDM-V=F{oHg!~B7w9fs)>6o}+>~fr;*0@El%wdL_h(`W8 z;x{Sr8qNerhuw=l0J6#X7$NKy{SHLAU2{~8+fY1M7^-|8+FK5!-1B)Q{9R*r>)X}Q z%IbJ%2X=>UJ{yG}tcbfdg+D<|{Xi&z_+^OOzR>ZL<0-#@rNV1sCO4AiBRO z6Z!X8l<&R~JmXRbIN?lxUesYSI*aAKNQ6EE?U?0xDt_ZAy$C$sHzS}+x~rt(53;Y& z@nqf`I4^msrCzt_8|XAR0cQ3U6qs2do+1^+DzI`IO}lLepj0ou_R1?v16abJL^P9u zlHDJmf^?4Rj(m=8TkOvfWj1r#pwQ8+b3eg8IJ^BgTDsz_$=ASs>rUn(f#?}vaHWish!%z2S%U?YxO2n{ zYsnYx$4l`pldy3zZ|Xb|#lmQbE7?oYN^pzNbIa+jBszNKZU3Z4f;Iap4|xLwYyO0r z00+Y5{aQ>o~kA98Mhk?gy44HL#{tPje1op`#Hor!bouZBHzkHsLX_%sN{yD zX=NKjPj7KC0sCAK$zz2}$5aB#H_eWmObJAXd?U=Hf2g)m^16Ag!R(;9DQsqV6JX zK@xWZdvjey3l>|gY1OCLYQ-uL5Xc5F2}(79RY276BHSgu1jHl|$o{_HnY)`!f-Uy> z*MB~+_s%&pXXehFbLO0RHOJnXX^RF$e!`Z4RTJfbd`_d1wmA6j*QDml@fZ~}u!Tb; zt#6uf;JX={Q6wWItnVeFb~Z*wz-#KDaCK)2mjfv%uxB$y4@pWt*-D>-DAsy!u|Bu- zvj(9?{%h8HGYoco>w>fStx1mN0|c;L*0KF``@FaDv2--&K)`N`BUSXa(tchl?Ppv7 z+u64LR9HwnE~LAK1m8byA#p;ys-fU#=q&dOqcscWy>N3q{{tRCq z*Rbb96LgD2|7R0MNz;a!Dr?HHuSZa-*6a8cfzcKH(7H;HTg{{VvFv%s9JiK&G=P6^Y8#R$3!!* z+6Edn-|%~Q}^u*q2{hR3j% z4r(k`!;!JlcIoxgbvgwvu=*A;dQ{Ntj={S-h^bC<9me318WXc_V?ICN6y)dN#FNNR zYy(!jaMMd>psk1U2yGsG{l|UrmC2v|76JV1ymJsix4-{p{IlESXTKVcum}`7#v*Eg z#qwFyb?32&N{(j=;io@)3WdMyWQFhIdP}|i+!-vPv|Bivdy00;lt~G_%>zcdTE_sH zn{2{5#w+zU)j~pVGs$#RNU(si|8F`AY7J8c|FUnDqVl)4SN{FlPJI+F6 z+?I+o-QuDtvo}JErwlcJ&3ZF>9pAu~<)Gr_%p@>+XCE}Z5*&h1ls_<#kqHgtW;|t` zU!I4kR-KPM^*T2vBd0G?~hmaS7%zak{k{7{M>qu-n!t5oW|NR>`wq7`%7_6bYS$%(?m_LI*yv= zPfZ!KVNKhhON@o0lgrD1y^ChKo{>8`xxXU5P`|4wa!{RDkh;~)P3tGSx%r4>yei0i zFEUt=Ii72g`J*sDe+th@kio7>5c#s2MC8!#ITn#|bglxU-w+9tY`H%ZfIsuF5J3BD zOiNqtDrm(g;z&PyvJUl-4aJ}Ye~l7%Ru9oVh$dOY;GOTq0UxsVf+19M|J>fY_N;o{O7R@TO$_B$?!4rO@zH^2fTw@0270pjHQ5; zqzl0v>acY>47ly);kFFAB^?Hw7`W}?mT*ZAhHHfZH@q*K5gTq#ZEek;ZU(CtZ+91d ze19(#isdoyy*^{!G)4RaHYSMEhfi z6lPVl@BWVU`Wp7gA__g2_!KIm9Qw3lKXKwj1uj4kEBV*fL8*y(85q@4hkxZQZZ+HP zg>Kht44A=L7=7EG(J1x0MxB#<*%tdPWv@*C=II zgKj0%6ik(7?9OZ|*aWbuIN#w30R4e9>(6TZoV~S>myfW?L_E zth7@$Bl;<}_Uqn+ViUbXXaABQ^L%_kywCLYi;u5}sy6b17%ebAfk!Mxd1~#I)+Cb0 zcv)1>^rT{4eCov-&{3>!GM~y@SS(_bhkY0}uS0oYf^!QehJC)ohE2@RQ&qH>4m{aD|kBXn8J z0bM2r^`2qsTKho&_$P<)?vXj3D~IuZEJL}>B;6?F;GB8oX()YZPO?>W9&}ts&;2WW z9=J_3l`jW;C7VGE?UCbvmbRP4k>2o}t5V#GtHo@45@+hafcET=M>VlOru4IaD^GvD zD@mUIAOhXxY1SAaPs8|416XnWSaInpWT{J~bx2j8JtvW>UTEBZB~{_C|1+r?FfcAv z3f`nt^%Ag7Qq_EcM4B&T{YO%D+YSF#s@^{GFQjTHh8#>CqGL(b9Vid~;!@Q+saW4a zs%qVdVttcTT_K8fQmKl9FS|(9|7J_6dI#l^QWcz@ELC@%iSH;yq_|X-fk94Ls_wa1 zNY#ty$E9iV@A}Qq?j3sosJ?o(e2x zaqA~M#TXb|I}zg->&oDnnrC1%KwTHa>lkHS@OwnO_z48umxWD|K6{|GmmeDt-eJ=+U>*5xr#8Ae)aXq}_!Z=N!UZe=2*TIX$@WPx}Jj*LE#u*=r5^HTiKC zAA&c@;+G0oCl){5LYnI{I#~Ss$m$e3j$e;U@X~nX-^C&($TV>hU3r+V(9nsFgJI0% z-iUz>^MAoNwf5k7vORfjP}xsW;(}(iWG>v10q(*hb8+5nm|r-iJbh>=eYh5@L1hfJ z`S((_oC_65WKDXHYqa8KOuLJa^Nf^yY_mw!^d{5~s~Ly;5#!aHPGNLFah+sn!^+8- z*34aP`tl{8v);-o_^2%_!GkDyPsZq1Pe;qF!PGBI zeMcIaCV7ZGmjIUWR>%{ah5oN2W4dKOhG4d(TF8$-L$NU$CRdPrjV4us75K#l7_L$| zCdR8U&!0CH(`8~s1ZT`gU&WlqFy~H%l%Ct!c4jCIoNqBGu+5p^Q#L{uJ|)O3`jULg z*KUIX1b!Eu+fhhj}T!KkmAH44I9)Zi#4UjYt1Z*Dc*K)I5$c zaSRH~1wal8Ocj>H=v!0JL`v|-2u1l*Q^xdN2L%qpszd7!Wn`}CT1ammZ1y+6qaGxa zE*u*c&DM{ZST3BCttVD=JJ1UV*hNE9&`eC{%r|iR{dBw3#7Z%TPmQ4S*>@TsCK`G$Dhlv0aD~OtAUb zm`sk{2loM1*-ETf>1|=}v#=b!s>olKW@wxFGcXj^dH z>+OTVaY;+&DhR+I_Qi-=+y}l!biRY%|L_W$h3g%HuX8j3Ysb1opJ*nc%fWM9PWj!P z-F5IBnO`L`Pa2&cXi+nNPHEEZmnX72#*VVqo)wSq`2i~W_}W#uWbSWVmX|z0PlY!F zmYrHjV?Ya++0ikbLKT)=dy*<#bslO(3;XMhwWGjm@b(v)D{rOo2p1EDtp!B{Dpe~<`x|Gj9~qTtvvRbu`7yGwI2V01tKw89FVewIT5%mxv{E&P z=L|oqpxJEyo-CgN`wEl&O7Ii35Bs#`q-%s_4K0=`VJ%Z)4V(C~*VAahS}aY(DYQou zzNP!lG_1};a`U;XV271(U&9S3L$ntw2lGE|OCSVBsF8cjDS!UT8Z~_K{i=8K6l1j~an2S(BaT{P~tfR7I&mihjy8oDd z7hhnX^A|B46aVs0akOh(eHy)P2a@Z?dQHNM9OA9SNICfRilbmpoL_}dbOU}c*A99# z6Skm7mj;>M){Z**)-z*cz!g?AH9>*Io5@-?Mv(c+r!>g;%7&>=ZWsc;i-y^JO{a$G zHZ#U@utgT365=hQwWC5#(iF1H_CrhM%-By@Z$_^@2+BHt4a422^GJ)R^JNG{`BPKI zgsgMUjKPBz3hVj;oMyWcgZdrnZLv^qQvV=-5p5UB5*;Y#R&FQZWaV=s0r)eY1V2If z+#-s?ZgdHtP(CB#5%P8?_iIx5d<>EN*UIO1Wd7}x&oft@qN*@Y$DDGU>%N5>uz6&H z>u9)i;W}Y;UA>oF$JcId0D<2H*G)nxyUqIEiRz&jtS* zpBwxdhdYDUW&t31?L4wlVDyBq1+Vo-D9WE2VUH%QU08~!LGq%8zRqQSV( zb~mY84fY!pi4FD!A{nbhyN~{=mmqVwAd|GA=M})C*M1$RW-Tc^D~Xy*P{R`Vya40& zL0pt)|6!J=r^P^x=ZeBO#p3^6&vg_fQx=62Y|=KChVm)cF1Hn&M14r7B5?F>U(dkN zdz7-baHX{;{4$Ne=sLW2TQ8J69cua^NpAG-`8qu28Y=-!2xRrmwmG~K@lNq&!49GLPnoX{oW)%(21$XhI4C%y#t z^9k`f^>t+V7+y8qy9Te1$k&ZVuX4QU-p$6^=aGqYGFc$>(lszN^|eb7FfW95FvP2H zG8Y1-1lyq0J2{yIo0nc8oy?DjO?EN`HpjwN0ULKRiTD;yW{^oOC-Yk+^?7mFjz@2B z<6LWFF_WGG3*0*VBrFg;1h){2>se9Y!%0o#!zAx=3<3DV%6d_K^U>2HI^Us2i~dM4 zV7Hix8b-HxPM7E{JJ|Rly7VqfrFZ$a>@GbdH)QSjl9?yD0B;h2KQkA(BD*aW@$59X zG05(vc!bB0Pgn2q>yvqxy|a?M%Qs=MokGQ3Fzh51XL*;)E8)@@YhPl0gcpy;NVWMx zZ^Fl10!gxb%;)hPFg+N(o0}<8*OtX&!_5>N_T$^&X5JHbGl?~?8YJDg@4>==gGOr7 z(p47t11k zKkjLAfuAtNNY@CEKTSBB z5ZY7tu$M18$%i$Ll~p8M%u}Ot=gH`t;yTDxs9a}Z>qBRumF3clwB6d^9o8}jV<%=9 zPwJ~mRbgOvbV5~pYhQIV^0us-2)I0Mj#|DdL2;Gz+;;k^BG2+L5#4-M0>2C6bQS6v zzAA{C zY4lU+tA-AO!Ij|M^i{bY4725|(oVkWN1u`RzqPOWeT)6G|9IoKj5Dk0t?Pk)IDIZ0Eg#bpp}ywFBiy^SyZZXz-}uWpQ4<*b zFe7*PJb7K6)CWl2@_C*`k?8X@C7jf?=6}>+v%m+I8mCh0(qO{O*}Ihu#@B8QM&NhR zU^bMp>!lb%4*cT`g8>|G8;wz4Cl4QySnItDQ3-D+Q z{5wg_a#DRs)G7ouI;kIH{wDf1;-WmNAk@uC<)*i*k`fooqDUu|D(#e=)K!j?C=Ek+ zl^srMKsc#CTNwS7@KB4A=SMZ143dRO-C-!Trl;~aCk8JOK}^W>(Dc!LMZlhihge`} z?OhS~i(p*}55O_(X96n^V(5uCfB?%7uue-vnodZh`Ru11(s32d%u^eXpL}6LI-udZ zN(Vvao9|LO_?o;#gkj8;4Z`??86aocinu7G*8XFZh<}*vYBhUep(Mu42UpQjg?kh+ zAFM^I^F)gH`3f9(Fb^GQ=U^!}@!&AVR&DhiKPmp7{Ni8yydb8n`^C@m@A<_8zI?56 z=+L1P?*B>go!Tnjy+8Ng`}4c=)f)98Ox9fkPs@$VmT(z>ejiq&_*>n2id%Q*s# z9y}gzpL_8p+vjcp>(oBuEuP7Zoj|>4 zgZIXGJL2&MC&fEbNqT=5@!*Ov-tu_7OOoPk7V*kOJWMY8&NK2Jm+|td26e;}YpyF0 z*U}4}^ou$HJLU$d`ft|AKY52hkio9&ou__X}Kg08_FM0RCa!%uFt#ljmI4+W!Y3{>fEc){RUBky;nF zK0=A*L-JQ=1FwbW;$8@Ullh0qxW#U66Go0I07ka?f!0C350efp+xQUE>o`+%j2CqcE=hOQA;Ys#T4eo z6?7dBn4^Bke3jsI6tHWy+|q+=pF-5AlOJ&T9s=O)wsK9F7#HPwMqDjwceJvv`FHyt z!8X`6Vc2^hw%CcPR)H=K5J>U&3$$h9sC*nB)Ww_DKdH5E#XJ*R#ypWHhE;;;XnI%{ zD*#9LhE19gkM6JYWoTg+Gnd+|;loKkbxoP=Ew;p^M6 zj#61LlOq?7H4c>dhOtXq^fkgV3Cko5y22KH zmatsHatXr-lr3rymahZ@cvfNwflW3+b4tEbake)80(qbAACIy%0B02Sd$9yhfBMoC z|HDF~;WgcF2pPp|rhn0k*q+D;x&CKfv_aDRLsoT=T?(qMqm5@(-3Th8W^a5QH9H=k zp^Bps)Y!W?EFojoL{{`Q9aZ8}SsngNwTkWDc%`V4v#ny47okePvw{jUYdoVPgM+Uj z10SHSg4mdt<3ztt(6Ht4>^BoT30N3JvoT-wh6;>m0*-zcjcBed%4Ck3fq%FzLB$H; zh%c?Ee3;vKvVl=b+JCPEi;*?nzQP$YpoO=?zB}=|-G3>rZR6c?%T}`mY7q@w`vIP% zbC4y{RvO45^35m&*Pm~f1kkWrFggbdj?jSCrK^|WDPj&RUHv-VMa(k;tA)Dr_YSPajcZ6^ ze}e~Q>z|0`dFk({pDV#BsGrG4C|wa>hE*q3k073iHJ`C^5z8z2q{UT_gI~eXMPTzc z{ny9rLzvdE=XE7 z2ln_b#Z?Fw`EVrr(V@O`@q4AOAAYaJaYteI(}AO@vvAze(I&+7Uk9J7Tk=Mc{8N$q z6Onv}NWLwe{4FcFuNv^XeQ&6x)q|jaF&X@Dob}WH>8T- zp#-O(UL?3xf*SNtW;A24e#EitEthxNa!}STpC3wfZ!x+ zp|Kz}`5G^mK;%JpIdvXGxuOBISHnVvdVeS%iw%dF)f+RAyc1#=5Jl$F?ua5F#+#3I zM~nwznmMaGVj2)L&8giHGquv15|qPKyW!Rm4)j8zFA(T;4FrY#%e2y3g2gA8J%F=V zUvAG*R!&Y;ak9Zk&JpcUOE*v{R!6ff*tUk2`n%Ngi_*PPiQxRd8xVhY42}rAf*Vr3 zhREnHSI^s1Pg2u=LC&38wh&Ei$L=L`oxL9%hwf`3cu}<0Ft*mn?lc!!7W{T#rMJhy6WJY33f@2YX} z@O=oQ=Wm=Pj%5<&ff?Qbug4RQakCX;LXxF52zJM++s~k^nU|B|6;(^)@z1m3JF0Gu zgK{j;X;rQ`=;%wFZvGtG!fxjXg)zF~3)UacjJj44_NoB@|6D=n)MZc#qJ&rv`Nj5j z%n!YfGSZ?kKiP>loTV#k>5rfry#ymEOJSB=i&V15j{dnd7E5$P^Ze%pL^ovfpAqka zdRcvt68sZ}d=vf9Jf-Zn2nR%8WK+t@ML={$O4(cy5FL^d{2v6O{LzdtnWA^N4FUAQ z(GcWp{+sUe9(%B(ztc8?KY~nl8{M*IpWM z|26Fu-G-)8_4T+)+l>oi-PL>2Z`M7z`FD-O@1P6W$&KEQAEoRsi1_X5{IPZ2s#f+G zv$`W>olotKkah0tj*xXexH}?I=SnwRr#f$1%i^89&ff#>zgOqCLoC4kkc@vX0k>fS zg%Y54mVo{s@$Fh8MSSi_`fqbhw`P*9@osm7Y>gG&5wbO&>5fRWMrk)(r`EVbpikZ! zV}bkcwZ@iaY^aL6nvFeSgL%N}aL(es+MPHx0Df8AO`F7Ez7iaXr{$$xFJ9SeMRy*C zXU;dX=%4N4{It?uHJHmv!JZJy<90%vtZUk={e{-~2RyYOyUOaJ^vuIpkz4-E575th z!pr63g-KAG=Di?`(;zfu-8S5Ir>x8w-%?HU1>5RkHEIag+Qc@h<>wOLf%Na?{vi?< zH0(GgQZ2c5s5^WUZoHszv19Ig%F4O#>Fq7=2M$E^CjD@rAvdrm^DZ60L)B)w{#EtC z)WGg^`T+;}x#@>>3WP ztfqaTToTOVwq`z;S2A`jKG?H#FXF*=%W!6|uWn3tW>=T)r(f&L-cbETW?IadT>}fdNUh%8+nHVK z%C1(kBg)^(SjVtIaGaZU7K2(o37amTVfCQu4vVTCHM6BwRPEUzT${I{{4^ZhN2Ibc zj`8oW!SJJ1tFLUugy(B$v5xyGt@iD7zJYO--h^#I>9!WG@Uj^L{)#y+&=8g5V$JW# ztlpEGeONzKy2*ymY^lMg;&6y1__ZmnLjym~{#YO2M=EpkyO@D5e_@Pd6Zv=XQNyR_C_`EX}TKNpQ2{JsaEe#3+&F{ z5>>Nnva9tSf!!&pz6&erRQ;ps-H5d>SJgj8g3Q37>zv`7#r#~PO;>S_f__jfct@>1 zm^Lol-=P+~W!2ZZD`1bQfrbd~GExiPruVW`pBOsji_bF&QBLf9hNb&Wlg z;53MXnq7C7Ja^*5Jv&|&E%P_tv^TfTsUHpOjv#*NCh~iAL$)z7R8UvFrzgHIqu?#I z8XwG}?#omQ{-G9ZZrMOz+jkS%GK4W4vaRl|lVgrSyCn&BOA_pq1W1UzL`Vd#-rWgi zW&X`&yYO4&`$524SJdc%GXfF&KT}H&+*w!k;U1^n=7i{PgXolSY(d`+`Gd?|C`4|j zzN7lfY{(xa&g@yb8*w0d8QHDX(e&(N@S`Aa*+;7zGt-*0+tk1TNZWzl+3m{TCf2Fh zF~b$fuI*y)2j(upIb!p{w$aD&BZY4{nGW)G3=9hfOP{T_M1=VD51oPi!Ey0BmW=c7 ztLc&8*P)OG-L)k8Bh=J4pND|^u#B_y?Lq{bH>B(Sh~nB#cFe5aotwQw)we^JQSO>I zq}IC7W?%=zz?mL6M%CWFXRvMHhV1S7fFpRb*wA!trX0R31#qo=ydY#~HOd4b`=^G2 zVU(&v)~K&RehBrotNe1t=|FbqYe8h;zisC_tBBbuKRE#HNzeh==$YuYtdN;1d-j%(@62XsjZv};qGN^xO$zPx$ ze{8sKSbTh7FF5)wm;NOrYzs!hg{D%gzf8-nR}0>~KAi685W|Ue1$C4{6v?~9sZU+{ ztweF55XFjwD9-C7ifn=2fw>WzFEvRjaY^jldT?)Ge}p*0Re=*%S|FFD?@&OEiAxHa zAf1MHX~E&@-5He6fWyRg`}$3k4k9;S0_uP?^LC=W2qKoE_jstlus<#WsruOThb}bi3q&YQADj8mnV8cNp>%z0&O<$M zz79hASlmM_<~GFqxXJMYObP=O{cIoea?$`RA#Yp#^DQ`Y2p4MW?Ny`DpWw6~oYo5m ze`sBMjJI)IShXGVI;K(!e>=Z{BR8sXm1^9Qt{OMvM#*uxX1a)pIfMr=Gf;Jev3J*C zBC>R2kucQfI0ku*365c2+-W+}i%U+6G<_5fjKX!cyz*2da=b^!g{K~Uj-wx=O=h%d z8iM(Z2k&@};~vJF$#}yU50s`jW_xih4nKxbix^cC_g?|RdyqKn!UUKj2s#=>U=C?N z?Vvfqv=w+V?KGynhiR7z%p%7!Jed|b&S5@JJ1PZcjU$35u4Q$U;OW7AKh%;;3B4(D ztrzg8t=!jGc|Gl*E6H>fc!Ft0Dl2E>_Q^#eM})_xdvMDVvvnd8GXrpP505cB6XA$} zFA|w(#_U|DF}|M@@nvRf1aYS^xj)j(&Sx$SRss`Gz%F184yI@lh&7H@K9R#kOfm>5 zI4I^sc{r&6&@iSL!4&d}p*4mgvnNl@l5dp0H#?f?vW`bFvTRMm}#Y$ZKasZ6w{ER#7Ys8 zDehp3=}fWEO0mRBfop13&b|jJDy$SsWr~?haX(Wmvr<%ADSS*Z3n?O2iaMEMHsA|O zM8%>>tVG*oq9qT@imnXeiKGoyQd1YDPi1B0 zA}jTiR%&`ttVW)FgfcBzLby@Mc@{wPt;AT9ZZj&E3f#iyt)#=Oq$Bw>mQ)CAC5O3? zvdBuw$y}pSFsHGQ6E!R{j^!1ZPL?SJ3mcW2Wy)z*%6mjg%81igxY0^E(@Hs8ri=(| z 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

ACZ`sg02Qx3WsOkism1l7ssyY!f#j?!<0+?tW4; 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

`+S{3=d@OTk+TC_`s1 zN|v_5Z55OXC?1L&f;VHoiGHo7T4&J~%%ud+*g5VglY`TR9DvN}49M>hV$&IP7;{p> zB#iBFdhVA@N9@W&+3ISL$f2BqGNco8xh;0no!MDw(1rABS8!abn3mLHIkJqDBlpL| z96yicC?FUn2E&6K^vYmdl`2O;VvYf+a^xiD_?|3thDSmzq_tzU>`Ij*BQeLDNjb89 zU~TD|m}3cYlwX6+gsGfpWpR!myVIvohYK4$#EDpV-Vu2;#xXSW#LDZAW=XCwOO=PO zkxcO69{fRS+)GbI*vee1_X}d?DCIeT0Z&$~WI9UTR3mz17=|!jTRA>rn~Z-Pesdtd z9vY5Wo@NXu8jot9S@NM+GJE|PuVfuIJk}pi8Ez~o?f?a;(bOwa;16W}N_llsV15Q6 zVyTjZ>#)qg(p4{;k(AvE(ZGHqR~6REY2VS}G9U#DKB;;oq-=U1H>z8UgtWF&o+l_+34hvdEdm?6g(Ve3bsfTDr|8$tj z7|8@>d3}V6GCG%l?R9hmp7$A|eiE4+il0iWc?=s+iPaCDa^J`< zPG`i4w`VR$hR$e1vTv~T*&$iBQ3mX7V5M7=;_tMI$~ypjPnIU5&{I4adSEP)va*?E z97hg%WjWkQIdUUR%1OFnIi@A$$TBk+Unl0cG$}`dwu!+Il5!+yn^=x-5h~d(R6Kbp zAr`Ri9*c>z(S45ACfMqJ{)u!~CGx^Lu`9@iG7G4TD6ISABoB4E>COz+vS*PC4X2MS zA$#7P8KUPElDU0Aof5|z-5C(S=f~g~#(2o8G@EuIVHpP_R6NgdnTmnI@%dN;Twcr* zDh8s&6lKGQt56-1Sw5U_da?lNqI@t-+;R9iW!dwLp>cT1ZraBX>JEe;-gYP%s?%;N z$PlYN1QT6RkPW*NM_)WEX>i)tW=;tR85%L*bkdUZO{I27rZT2F0*`TRRw>my zrb{>!8x#YRUX_;NJ?r^iz-|y?!8;xPp!M*H!ro(lv1TL**F#|+3m1OSOxufK3)$)< zV;f<-i()UskcY}!NzH-_1X^B?Q_l!|wJjBrq@62Li5)bR; zHQ3A0YW_X#W>MLq>`cZP2Z zwk>XnbP*O6Q?8KfN@WvPkX*UtA8g;g-5%ZAcqqE1@~|mr-f608+~eCByu&nlqJK!5 zvdjIcvL}*(EEB8EX`}P&*$M#axqfeS3;YRv0H*XnB&!_53$aJj&u0*1_r)~k{1s`w zhLV01#rbZBQd99WBsi44;f4p&4CUxJy+naG>gul5adD&K-6dQCuK{P>RCtamgBP* zAWTA+pGmkN$#Q(wan*%x%5R8#Nk+o;cm^(5rlY>&Sm0JvyhNTt_1W{TwkmZvWVQc@ zR1~5rF4n6sN^&KtsHG~Z1bHTkpc(_BF@%tSYNjJ4KNLBjR28fZiejQ;F(> zn1q#MJxV7=oA`82#1X*JVx^8c`&@LOu;?H7OP4+``YZKzu%Jb5>M1N-4IrXIk$uu! zQ}H*$-Sr(Xr6Dp2FWGpZPKsQGU^TxXU(o$Z6ZTIu;>LQ!Xx}QlQJZ%Xq4vs_gkG6q zP--Un9htbAa~s5HXXF~TEffVW7vZ-+*^3c}F1pn|GK3PlzAN)knTdWYFA;Tr7Rkbg ziPcCL4H)@b%7G`t`1)FugJIGOP)h3~-OFLhV#I~72MO+rs8Nf3NYeRcyi~N?p?t(x zCtUHrI^R%%l5-HE=kAhwQ4{6JK`COcr;$l>AE7L~(%JL!6WQYat{Ph)$fCv;*!n*E zFX*qH#1?o#B=8mBi7+;|`({(`zk!{h4APYdA_ERXmDK&0B!RM9HqAPUqieJ0Lsl|z z{%{kWV>_u+@^y*)ADCke6|0jYDLJ7X9=o2RCHax^6eJu6$lPM*AqFzTUNxu(8 zk|li_Qb^JJ4o~9swym^quV|Q;so_hoE8oXB1Br?B zd;Y?kX6l^8M4yc*_iDN!2PMNVB7-LOcAOX{vA+z~8WYx?B&@$PCCWW%WeDre*?)tu zcK;baQwX_#76+grU!%Q(kK{J;nyy2}CF+pNlVGu-1!_Z(ALZQ86MCQP5%qrJhK9(2 zxa_yl?_wdv%E@t(j^Co}A}#O0;3zEeLwU1KFU13GESrm8F(~rhUxQXNZBLhZLqTM~ zynGKC43U9MUHAroRmAt0&hNs{x?=qwC;FB9_KS1?!@Y+NicTL6kJK2PZMAtD++EN| zp;Fq3z8|Zudk}>5X|eG~Dxp+S0obPOgdHM@Dkor59ExtU(I&5ER15trc#qY>1NUL!My57PpI_PE=~FifS8q(w6rn zV(&(`(%{l$E89q;%XR*FeP}$gS-zZShK2C~*2wCt$^n%fw&2fTBkwOjaKhi$KrP_L z$1uhypYlQE_xI|Yq~oA>andmH7$V*<>mfU7n0Oz;-akG}tfgT>Cl}Z|m#DD#ZcrZT zcz`&v5{3d=QRe3Zq=Pjfx=lV6o!3uT+ZE&i&P%p{S}d_RVlUccH&x-9>Egj<8J?4) z1@BAv=sAg-XmC$9%n5v&_oX$jnn@>yC7WqeBntx9J31PjNP}&Nc;F7&WHV`V^ z&#WX)aH2FK!(!kR9LlF`)C|uo!sgO?CNq~SJMu$xbw<$^5}HMZWGYn7I*L7bv)=#} zdV$F+@dBY)iCZ(m)rK7PHV|JQxs!-)Mh&{f$MHx>^d7CMgULo?b1I@!U%v%CnzFj( z_jIbd=cDG%_OWiEapaT6@Bhm0|5N#W=k8ARZPkyR?%V(G<@c4l+LPbyKO|G(r^>Gt zHFQFLovo_-sq*{Dot>)g^Ha&K>!7+0$t}i`5zv3u;2b!*wu+b^3yaBB=1{)l3i*>6SPv*_5F2ZdYH)RjjdICo zwJToe=~DGaq&8OJkJ&XnB{cHO#Gow%G!2wHv%$9C=;}=oOM9RO0ykY{8{qlNBzAb7 z!G^PGIjW$nldQp#AMj8zLX@LGmP`varhX^MRX=c?N1zs)(xL;!L7v5{+h^3?p;NsTuy zj7`m9hzs(|7&9<>jiRYJ5->lbFmB9XC!2A6^7!|f4rLU#0=WDLGHqo9lnY~Hp!zI9 z*#fg4hB0=#O6)2s5K z=+W~Gqa$6|@16jKTb+f zN1AH^WxozG2}W-qMklE}M`zKmH z*gjz0KN(9%oNu%96F9uJ9fz4Xv)>>rny)c?8GhU0YOb<1u09t|Yp{gZv8J*U__!mLee!r}KBn!n zkcI?4{xfk)$9(*n4(x33@m=izJ2O7^LW#oiNbnye#4j6#`&E?>MCF8}6ysRVcZhsK zVKWNrY)Z8g&EuaXNQ+9q@=$-%=H7|XKFu{6f62MPeaWNnmQ8QW zu`o!V%_lCqi)?O6YcEt|z**}ITGL@KZ1RlH$Z_Z3QcufgrTruS)`c>$EYH==eV(Sv z1@98b11nPU9@E>2xe3r=XVDk-=yqYzM?fBzRU)1kf|tpM3a}M@wY)!#j%|IF9dMS5 z@>d7|R#nUs-wvp6ckyhO>0Re zgdoMoqqA|!EKHXXDhnHmSIfmAgDcx^X%gfJJ}Y&8at`2q_5g?J!8%9YcR0Cncgf)V z1{_ZgzdcQJh$a#P8M5bH;!wgAd29cXnZkFW<5>N;N*@%C*)8wS-coHIYhbGq@6S3B zc^7tpib`g6!O%S?lI|aXN-LW)31S^#KYTk9;Q$LKb424#CZ6B z_!D^8jHk}>@OP-Y9UlIeJ|*$+QlzBh;Vk0WU&r|u{0udl$io8X;iEUCArvmYF}}UL zW4`5fdm`_u%Sd~NA_p1&sD1*ZG4i*`j9f^Z8)xK^^a@74M(0P>fwIJ*7sXP>A%!vR zIkBV*ki;!#I<%^9vnPI}$=|ymhp=cL{?g>H2EXpY{16PlYuOAA%CDiU?^sWBj_Z6+ z^JsUE8Y4PJ_gCj*@8qS5k!ejtIAyD0Y<5yTZ!0g(md{u^Gl;vmrsnSia48PlJZ^lB zYxMZ(E^)leb@lj3u8YRcbd4H+iMv9ALm$WX)(REYafmUvPi~s-8X`AMa&?cqND=!i zyXRGMIN)4zuGbjRXpmNW0iFh~srg6t&ekjGT_;4s_(SyAj9kJZ(qu%Bo%nUnVDy-j zfHNn^MMRLENN!CLEoUmSY}UTDq09A*F7|{UC@0KHoiG|8 zjTm@KAdQX#4TQYUw9bkVCvkp75%K4L(}cVf;}CbWiOPr8z|gr7vq9MfAHr%;LC9rW zxg+>8U4Cs1JeY06UE_*vWqAP#S(Uy%yPU<~b!nC= zt3OCLIt$J^YjApRGg|?oYt`w3_3(@L;yisB%V1YF<2r9jMI%{xcG6r0Ij}yDi{?aA z)s7~3-hmPA3~}`kM&fGgiH zdCogUfhqluAwHf;fdNpWn@YPV2nvED3qEqPFc>UwuCe#K}>oo6xJ^O4`OW>&;L-tt_k3W*8=+nRlmzcJNFZXV1My0iT7X#dw}KK@M{ow1uR#o!MiEpT zw&ajpByD!!l93F=9K>Zm*4%7-Sq7H%Ao}}Kz2g+{yw+U+m;=^FFeB_xf76I{3fB~D zP3Z2j;cq5(_tS+JSFvRpDNwwUg8FWoj6;t>b|1XMvfqVGQeTI=-gPpnJ0lY<1f1)l zK9Yp>$F9Zlw!16atP<%*z!5qWpI{4;K6;pFX4x$)`7J%Ln8o+;Cj>42Ii9R}$G9&L zJ&N5hx6@@3oAT>zc}>*e=s8da)XIgVOPnzv)53am7S~9)o&hbyAxFT5Q2UkT#MrKM zw9jq+hcS{iW~U7i63pJ6?q9)PZQNtoQ*xddoRntSQ#@4vGL@E+4q-<=J{T3o=MgDJ z97y7d@ay)r)KO=orjD;3QWoWKO1iRTJQyo*R%xBw4c#OKZ6PlC)VHDu#wOOW&ACWN zkE5q~2eY1bE`{_J><_Rj-u2G@V?$DBg9>s}7ev?qThW3T^>uI78)K0IY6!kpz={<) zASd-WYt$ZW&9q|HfK^OHd@ac&jy#Eu8Hpz=BAb%nfFt>!TYht4VWCxl*hVT(4bR&Z z9CA}*y=bcU)mwuTO`ymP4o0ldM8xLt=cT==vyDzI8?t4ro4bH!8FwdIOk*zcW6W*Tg{+3x-nJ;{xmshU8XLVy>R zd;$uC`K=a&Sw|!-C^n^TJ<`w@O^}AEh^}s1jSO<}=BO=b?VmQU8*pOVwoJfVF7|_M zZ$NBLTNMl`_fXO!2Wg|9TwIIp7!0GI*t-vYBfVlDDXA7ofZ)R=UA88hrAAmn_whN3 zoU3tL*h-8u&b&&esm7taMVpp%g(Wm&hp=o0mq+7Wpy?RV(pcKXo#DkvxwlB^)Rz_JV)V}N z9Lx?KHj6l=EOxJ>?zzTW@0yUzL^mN~Vr#{___owc1T{7d!_oER`^IrwliO3>WpwbN zw8+(HFOzF!uatL6GO;eBTzM^wAeiBrPG|&}KT#(@&}A~}Z7LQhJH`wtue#cBab&R0 zWxc6!JrY0WwMKW}ttZKhO+FKl+{y<)_148!p;#(WNiI1iMakc^QxZ6n=-_f=C_7Ez z*4X?sh|ti9_;_5!%n0$ZmJPCr1FRUlY#7v&Z51DS+ZkzxWA{a?krwU_><7b+rtU7Q z+>9X>w=lZKCAZ{PAbe{YymU)y8c0Qj%P<7vTto$V1!%0-RsRTp(Q0SkV7^hmAqFui zSVvx+qX=Q(=-dKVs0bKqY6?dtpmJp6YGEUUC}}XD3c@wWgNC3HloR2jk&AAV^sd7A z%N5X_#sucK1@BD5EexRv!M=T zji+x;O$u}>=`&JF&{U@oY`SqTw3Ah^Z?#zvv5)b(O>lmQ6)|UCH6p4*rF2EGTe49~ zCgQ9RBNO7bIh1`?CCI3x_=po&t9OV*PnK*8-e`p3?`QN3uq@O*n{wQihb#NofJ6H_ zrrL2!GZYPk%84VWIMf31GaaX%K+I+nf-3BFVsn{klOqrDbf7}9tr_&~T0r#mn^;Ft z9RwWJl@)Gps4}~HI=EgEs|%tRMY0m!E`hT2i>AHU4upG;g(a_mB?7k1I3u6X8(i~1 zkwX=AgPuu;?iHw^ z+X2E|h%M%@CNN%vpi2CRiaf}khdYnOG^nP?l}@FZls&!@i9oU`!STK9c}=98Nl_gk z6^+fL-&_OI*!vcY52f=wxz}vVvkC_`d6f5e1QWTCrDw(s?bg= zU^*>>4evB<54b}l6X692;fie8t()<8gwlk`-fF~@ie@Z>La>`Yvs#+xP1f|Ds~7B8 z#Gj$zJ^a1}8&D=VijHZ@8y7I-?PgyxscB@{Bd4htw{=jbg_l$Q-pNEb0J^tEXlP1= z&@^4rG7-6`E1PpgE|l50VsQ2Hg^CAYLY;lt(lItM4m z+U=ZC3gn>%^2pyt1LDV7NMWe~<1Ki&xu7}ly z?CH@@X=ejMb8@>I2Do!6Hs)iH{elz{U3$Z9mEhatf@6L+d*y)~XXaM!atrjU1qeh{ z{Ugk6GiE^Loax0MnGcM+rTh07(V>G|q zQ26E6a=+Ya=9hE+L(JR)FWs8Vt;{P&LGqYa?tD;lH4c2O(-N%yt)l0s$8)e$^c?Vr z-FNtxlTQx)KxU__n@mxLuVlWsXreFf7Uqjfmrq?Y?@rwn7o5mEanF(BOznvq2%&_R ziZbA~iZ-4&cz?+kmf927lblQ92JVWh;jTE9TygUc=uSB1w86abAkgQYxISn_q9-oM zJ#jnSOc#+SZY)Ayl4pjy;trvD%)a>&CEPcYaa*``OXELH+Z2RPh<+saC&BxmZa_8^ z>ZZO$54txl2#RrUoQ$WkAQ3X|j01T?DqL|}VajZA+tp>5@#5hojR%z-^A9j5n`)ka zk~!hVNVpJ!JK-pf{)Mfba03Nz80tjBUuC=fy-}nPp;Wg0L~i4SYY9FIhlCRziex8T z7rFwNoNzBeO8m&NLO0IMU$&ka_?e64EjZz}xU693?yhUdQ{oznzgey^FpIc)#>_2H zJTN@*$^->)g#2)g;nFGEsHbj+bE19k8~I;o#U6T!yl;Lyik44>C8hMhoIGy+1e5;P zc(Ud-^S(nTk|*^Zcv7tgsDH)A&#b0HxV3u~5lnZ(olK|NnJuEb;rI%)?uOgJ+;C1$ zN;h0RbHf!`m8M4M(ETMBkTV(&#lzrYW#6u&weiI-Fi+fdx+iX8YYL6HqAi7HOx7v% zH+WTWMG_zdZ-{a=(^)4R0w^7A?Sw-Ib0^#i=Cw+8!VNf-)Y-ZdZfYAR+^66(=7jUn zyc9j216GCEOFb?r&yj^2o_sk?LlE~C(=P;|XPilMulGR?uu$9^w+UsLH_oPP7aLWv z@`vGK-pAHp?oH@5o9O_WE(Y(&MckCBhX4#baX*1j4mR$UtKfGcaYN7Z=`ox3kT;T= zcSM3S(uyrQIptPJ%smOuobHtCbr5ZV!& z$E*c7;>Iwq+zRfM8)LBLZD*F16#NyOF=z`mRJsb4FB!w!a5I)7CfPuHUcIB} z|FCv0@KsfJzE5%jM1$_AsA#dIHMU^1#e$Z4sN`sl?j6qool3=yw9cesUrvWYqPFD` zIEk`79A)Oxxpc0*!^~x-w3mBz=F%1|Y69Vn7#R+rW~TYgByd&kJg98s{+-vivZxX&>lMBx%!?T06-yA)|P*h z{C;^F#F0&!YpmP@`7sS6M=hPfLmj@cCnfE zHZtwy3e#Rb!nTlUZ&^uWo0;_jk9O9Mj`H!7F}=FXCW@5xx_qU3;hjO3xgWJx-&kvt zwia#VL2tiPIfcGWEy#R{%4#SpICJ?&JmG{+d&t`&BY+ca7md*1rtGPjA^E7{&gqvL zM9tLJN8CP3zc#q;<*H0ckNaKITEBchjA~k&me19zKIeE{&-&cT^Jc1=e$rP$*CSA0 z3AJtPBSld*#RO7NIypH&%1Z9pN^tna0Bil%MCW1J^)2E?A7i^CHEK-E9at8@f@asX zyhSV-RAFq-%YOQXG>%$f_U|u2zVfzO+Kgr?797wy9=;~sSTjcW+E<0I^)jCQ`>4h3 zl|j9q2~qIs3B0o-2Up9!b={Zy!qw*F;%e~)ae}Ak;A*GZ{u`-OKyR==%HZgw-}$}= zm23L}PiE1zIaYn6Ks9OjAM3!t`*(Fm*D#Oh)VorH2C`r84x;~7TH{w(PY>dGmLnF= z99SuU?G^*DjmYT)6YAPN`cm&6F#3)_5)HH@?I~DIO~{OjfYK4cYHKPjd#e;rG|YO7jN0$z)w*l?OW z6`%@6-80ehT{-aDc&$h$jaiJBuK+Tj$yT6EG|Y(11Ly|6R=v&%f$<+Dyo^J97)kio zH%P+sviO=@FYvW_KEB3+yU}Z_?*Lva?mGDr8qu?xsXs^0{L{zknMt2Y&)h6MlU-oN zG|?nKxUqMW{7WQg5`B6O{ZcI@^KAqam@}zmKI;LlR$`YQi=-wRTy2aLc`TWd=fD#Z z3{nDHD*{zRQc^<(TSFza%_~PN(RrT!d9cvaKUa5uhoArI{x@d6fTs#A+gC=EBeyKs z=X&X*T@}(tnH$ypb2ZBp8pRG~w7%~8zdYL)w$?N4Eu=L%D?>cl?%95(k8JN(wo+1= zUpp@_vE*NO7|ol|@FDZWaRFlaL=s{1WP@4SPqaeRFc0uCWa|&V6O2|_yxRK1OG42q zi`Q5Uzf_`){JA4lIL0$->BS&Ho$RUKYdtd(1S36yKbS`=)ye@dwHX|qaX9UitFer6 zIO9||8`}W-X>4I&9q*vA=+Xu_^XzFZb{5!^mxk-(L1s`zLhq)R^RArn@A*NeuJ0}Q zo5u$IZ8nt>gi=BA(?2(GOC6HSOP|g`&4NzY_J8`wD}7s#*DWTm$JeF*lC%*`_n1|! z3GlKgdq*v9pvu9^(gKy-9l$e-;-O7KWTr3nK$*d9_e(+N!eznyhd1F|r#FFN2!pAf zw8#m)6P~bm<~_a^%K^y-w=En7u5x#*dbbaf8FBE@JTt%AgXGZQX5bj8%(vQYKUAvA zdr9&C>G>Yr_wd)f^4zBK{O2(;rdXJyK#S&pWMAdo=~r_>vM3Fm1<6Kp0y&A9LSDL- zW;7Fe$P?_Tj&7|C;qcJDTYDsXpjkYL2FDv=rjukQmAFn6z_f^7Z~%jXSts2(GK79 zOY(cZS!1-Dud^_h>jFS*gdm(OAjW>Ib<+nM%<@tX5o7bpIv)}{WFWDe*60fpo747B z)CQ$ECw&?64=}M5n3%ueypHzp;tNdBULR=TmC4JGHy7eHH?p{NfCc`<^b*4(8^o!2 zWcp<@!R;5dF3cm&U2{9Tz*F&7^TBnfAQRe7L=P0k+(Bh9T?pCNd7sxZMt?ox`)7u@+)`08k0 zxzoAI+jz#;U)si9M@MYnJ}7WM?-`HR5@NqzG&Q3r5@ufXO{Sc&)0N@Q8JDwOqg`>b z1DNi%AFDxary3{XFdnBXUn#p&YN>&6fK-b$Yz z2v;9|phtvC{YwdX3VZyDR`8w=7=mg?Cdy3KeTJYuNkjWiqGe$SUWmmm1GQ|)6a`1e zKqFb4xvSt9O}6dCU7@<#FGM;T>t@0eybK!@-yTW4Hn8v-A7GkW39$~lvM-Hj5sIG_YZJxbsOGm9e2nBL}PE{wu^f>n|w1yU3< z{}2un2{+C$da}}z$9Z2~7b~y`5wy)^s6FK=QbP*VMe1bI8&aUOMc+_lcTymXz^I#! zB-UnGfd-Jo1#Rob3Oqu7$@rpzv6VaCZn#ZAi{905?Xc_}%5#y0w^)Cli0aRVsYDw&-IqcxtsfzNkH{eJ|BZJHoL#jI)}J!-Kc&vvq$x#RXvn(A!Dkd zC}|wOpoN=$)~$t2?V4h?0%9UPJKd({;OAY?ORvfj-L<260>US1VFay*|(w|QC zQy}Q`Q2itjP{n3d2b_GuT94{Sg?*}@{4eJJRNSD73v;P{l2zM=pPCU;s?tIAQ--4Y zEexoB${Nk>_oS*6kzmsM5UQ|Ez6wiFO z{r4e48r7Yk`ssQkhw2Aj$em<`u4WMZ{bvFJ_*6err-14=@8hU`-c8N&yIF_hBR+;qyLqjw?WTNXUXv3xehk&mtI2c(-%|!Je7SEK07>F3=TiMd={Lsx7P9e; zyO|z(R6lspW_jA9`YrURex|k_)h|ykr=W@HQVYLi5cw0~>85b1V%u9+3o6^_QT^ue zx%2>xoNd5JDWJZne$73pe#3ZNRKH8Y#>CCIBk_`YUJl{U6UZaHucts3f2nVQ$QaR% zQcZXVeGvX6o4yEtdUGFyKaG5VNlD1mz4a0GTwBJvgu(%p1;0t zNeHeoB)Uk#JAWZ@jc&m zC(;BC!?p_!TkMngTHxh$@x(kK4AGN40s)+&P|d<(cM_i(J4@ng=}F>isW>l* zZ|$0Bv)ZQZzf+O)f1^Vt44%ro?FKqP!pvUstcIbStch+k_~fHfb7nf%i^TU5fMhO- z@Bd^0%1$*#4vCM?jYby>q^$K?=E*J+U$>SSKkR%hV@Q0>=O^(!-&TF;`5NapG(+#k z8L8*93`?(ZX6nfvjq}GZ_EjeFJ+h6nCBQ6)cSN@#ubH)%G;9%nPmL4s_qGVERO9sG z@2&L*sNXuq)O|?lZ0t_@G(*au80mXKDtqsj6&{x5?~Q-H5O9pICmN?&kskO$46MCm z$`FcOkSWgrN&QZ8&+_-avL(yl3rKqWuB}X7a{)O*=UakHUFd^rLe+?jK?l)z6n2*7 z@EP2UX#@Bm9-45o@!fH=rYvqY3ET`&%=!bN!-{tbH;Yrc_)>5)9X&OPRm}L(=$xIA zx?|vH??K$#Qy0B(1F_iu;Jc7MZSN|NwkN#og#a%b4_;AOP{l7BGtM+kbe&INecy4pzkK7*G**P}UpPmEQ_h8`-G1}-}_ zb4K~Ba|SLO?p<#p{V9W$LC)KYn(zyoF5{(yHZ>>(DMt@@QT)}Kbnt5g=}q2w87 zECA1-TM&Jk|9R zDS6(^MZP6|eyhk<=g$+MKaWgaDOp)3&QuNg|7TC`6} zo`;3;)W!e{1B)`;_Bd*V`MHlojrq%NdKI5?fro>A5+!dcV(wY*=Y|CJ{)I=$Pc7n1g>U$i=_ni#VUPp_na9{zPNV_c#wMH*SkHRM_<*S39t z@EIQkTVU1y4}o8zyyjk%yu~ zX99UN(4Mj?G##GFGF-g#5c8IGBj%}xbrJK_K?Q^LAm(X!)0}2-tMWVh8*owQJ~0pV zhQs730^_a(#5^Sx4CWK_^g552N5g$GF^}&x_LH0!W^pjwt~fZ@LLUbcG0%+MfSBi> zK+Id%o0xYVZ8PapX`9}}ym@Ucl&)u!47@sMlD>#}5&(S)kZ$35h|O24dA2VMtfy}J6={v;>P^g> z-AA@B5B8;*`Sq;*2JX^C0Juxv@=9On(eNhyKA6faWh!Siye7Eo41YYfo%&$<)h@Q( zM9_uw1pXMdT|YpBPsz4ZMuFjbofgQl?TljgL)+wlTqoMb7Z|pkMvB;W@A1-J`aP+f zf;qph-`DWnMo+&hG_c^U3ObJYm^qAT^iiJq!e6hv#`cld&$D^`g~90*iW`^%k8QWsW7{0;Y?=!swIOL!B0bNZBF+u<-y0``V&x2$ir z-BW~4ys}{+DBAPzhjpFqYj&pamot|ln>iHg0oOOj)o*x*tT@GH7yCYR? z^Xb5>9DWz}f-kuRXx2I^l0KS?X8n+c&Z1e9I0?|K^Rn$u(hX$?vTVEevtX9kc8xh~ zyS0XGmu(FJl(d<)3U_Ob#N}S?f8??4)D*yrAXsXUKG}Bv)R}Mg2xr-L^MclRAEAR_ zVf%8~c0bYcSp-XLyLoLd>B$}?IW@>D$$PNv{IGaaXd7dS8eNc%Aa=nC z>9vMo*Dd~P4!>^Eul@`9bz`se+TvJmeqCfuRO6P}Hn{J98Ncq(+5G<$zwTl+Vc-0^ zW9Rb!6n@=zDM=4R`&0AlU^|Y8C9nD1vxQhS_7+WQ_h1ZS!46huj~+KHk4k2%CE$JS ztho+qy9{B{?@LW!KWk{W_RYrE?IWJw%Ky=guNQ61iRagythY!+J^pVD$a*buj48l2 zKb&7}nOnOJ8m9TRlV7!6ViY7FE8_Rr6L(p4wcoLu4vhd~Tn%GSU%alp#ri|-d@e-) zK2qGy@1zBh;$u4h2k$)ktpEEz{ojTB)-bec=L2CsYrDV82||(?sp~+M|I5E#Ip-ar zl@)dSXX)o_jsagx z5Wv+|yq)NOw{#(wQlV{&801fxr@Dgw8Gavb>v<5%?)<(=_mG1AZOYR7f}nqgy3zZH z?Z5)B38)h1dvy2PUhpo6x+e%JccPEjf7<{saqRPwwLagk;Y1~bM!CoV-EY01%e}O& zfiBot@Hi-wtu-dpNWL$^Cy}|*e2#ov_ zG?Zcn`iN-7@7Xd~@s$%oJuCP;*Y|tH3JmTT0%56;={{lwdaFOPPW2YsOBeMmwtzq= zbueRqhZz?rtaQ*Hz}6Hjkp5uc8Jd1xB(%u}4HR>ZJKhA|>i}?A;))}9A3ehB4UccM zlasf3J4k{)@wel<^$8;g_=Iowf=I=lH4y0{0UD(U+bn;HB>Ah8%7c%#m}XmIka~g) z*5At!6IVHD7;qYA4XMr_IxsagWQ}`=OaSH1frw_=gc_t?;J_AtUzjI^BjBL(sH28} zoqW6<(s{nW*USt7BNBkpAa0NvK|^{!PZYtcJ6CrF z5gbNkJalw#4mvtET)S+>*@hFHkHWr&4V;vD;ye@^DsGaIB8 zCoJQ;c=7G-gk|n_Z{PAc_AMWEYduEcMnF>5;=95{6?Cpv^gZNfMUVE39N0p8Q~`O4 zPIx=NJ?MlTf=E{rF~EwRgAZptv&6uo(PbDiu?YzP<%EFl&8o1|xi-6R`TT(cqjLt0 zFo6TtcHg(Gz=3n_ghgm{80PSK^uHfAP%cItCZOh;^G6L#-D!db&K2VOu|Wg1XPH^9 z;!^byG%$WV|2#nhE1s&|rl5fpCsu3EayDpSr3uvaEA?Y<1tFKY<3}bnp}M+94aDgC zQ3ER)4}~f&+}&rgw+_-_xv8)Ufr7Nh`bW2 z=o6VbBawX1WPd}lPbT4|MTvXLf5le>22`!(p-5Jqsz-KkLnaLI)i&A2lMP6g1=HSB4Q7jJ6*1k3QaTe=)c zP05#_fK%0VR6nr_8kobUv=wt^kbc<*m;R9o#)f#$)4U{fo6L094 z2`YFj?jDPk@6wl-UpXuRcZ(xP?usM0P0KXEsix4Y@AURL2Pa=j))GXJh`p&t+2;If z`ny>k%~eburO@LzdocuqU<&7nA=uazL(o%9iT2)2@=9UfP2y1s8-o~v&%?A0twDb(D? zaHLIa)6eycAt*{=?jyYv)!EP|DypYwl%9QYHOcydQ?o9BHsRb$!cW#_<$YxTJ!LPw z)zcY&870d)=}~*Bd?r5gEt7YU9C8SYhWRzoB3lX ziZwtx0Yyh(_HMa>1$~P) z%TYWB#u@|~oXK(bET{L8J(nSuqj-a!&w_}aDtKH^ z_R!8t^ZF`~JfvJ2oFNKku7Ap?P9iDzJh*K+Bt^r6+jf4E;zo8xGcxkpj#9GpZS=?> zH>-XdJuthAT77a&w1tUFUb=>kYJ7aYdx+x&V$oh1+O9UQ0msD_NvpHTb$gnRBn0iE zUDP=w#f?}PkEG}|4JIl8cDpsrZ#z$(r)dmHaqY*E6kkrCuXPHkZSU53TF>`ron0UF zZk;RoE>mLq{5(zUCp-%&P(lBVjEMYiRlY-L`!{umtHio(Ysz*R^+3?d{41ke8fL_zx6u=_WRH5w?5ZKd-sh8@8Q;; zu;2PVO437nd@4LRyVrUGA7fd7je^g z!zaII4gdaTYxqM=*6@k{W(~jRpRD2b-&(^fW<^{Aoz|DNkeB10wcYx%hd5-Mf(U8H zc7567oHKAocJf6oKB_%)Zdp57k31N2=azk+d-_lphjkqCg{Zu|d^l6{%jR%uyGzO@ zn(@dkdp0z>rtbqUk)elJRX3iR;wE;!Ip_ej(|Dz>GboVA<3xcT$T?x9LC zmv3HeA-d+^__gcpragtk$-K-d-l?kF+$ZBwp10~{HJ^4K@v9TUn zXzjkZdw}=Vlt!P5=SAvH-B{b-PHZU3^yj#|_Ck&ec#MZB`b4s&I`D}F?i;>$C;4k; zWxaf~(mk5kUBcT-c#IQ@%fpq?KpAM0t4?oDU9e7I6Y7 zrJL*|N&?RKgk43IoKw4$>$vD{RRw)!3qrSIeqmsC5X zJX+~GR;oN!Lpdh`*Bd_1fs;4|!abOy^j9qvWS33JRHR~023Erv&eCDs^mC|B>&kHd z3bnB6-{4BjZ6&!=+(Zs6YLdeWtKnr1D&1*iMHKrTip_VxXwVsV$J}SjWGPkS7Je(A z`h2v0Z_TyQrzX+R8%rZ~8%p^~Q}ouitcE`mwr!f3q%&WSCHjLf4?{Z-yTinXz{ z^43-YoN$RUQAB#5Uo5XUPZi85EPJZ3j2+=MF-ExBlfqDzyqb%7&oFERtf$)tWd?F~ zwSQJ(C-!t0>96O(fi#`^7+p8Ii(~aaD)|$BRK)u4a}cSUo>yu$OybZVEeF#Y&vifR ze7NR`SaP|kSlm79@Rf?t8xi-7^iACM8?p9F>F={_6P%Uu67_t#$3PxR$GYxW^{2U> zSXXLSH}UD%-(()G@nr>ylcQOX}KOdQq2F(OtOY)udsi_kcPY z_4*}$Ul_q3x@FDZc<*Upa2J9})1#n|`aNpaX{9iR-l|-0ckFb+r{k#yi}P!)f)WI7 zl$>kyfW3A1xK!baSg0}cpq)6IKlO6EbJZ&v0`qEhfuip~@$fasf!46KHlVrf?zHZ|=;6A;yxNd8tcj6a$BO!_bxMZp zbYfNROwU#Z9V_X=c{O31ch2FR{jDeO2iUb;dw_YsxUsMP4D z%s{I7I4L`J+Jr@OcRGpp@|A+u1Rcw&C&po()A433w8L)NUtl-AU!aUS-mOXv`lWjc zj3M5zjzVpBwxT`e8=XK+N*=rJB}qFvlF4G@jN*8x(INDEL*vX3X~D8KZl$%GfoA1m z(5QT8uJcvuV_G<`=BUje&`=>4TUP68r3kUpd<~~FKeS!%c3eY>Ed|tlxyf2x#(Lt1 zJZ-uf=s&I7qP;0iZDV7pSieqZ^C`-0J&`0;+&u|Z8jLeg?Op51PW4JYvTZlD7Dk95 zkepvQnLHhr${uo$IK}69VWH|mK?@%@x6aXZNn#$bx4!QbZ$~PH?Rrgk=alCFY=X{& z05$gW9IR`VyL^brr*pmA;^;f*|3|r2AB`bmnkPB`xZZ5LE%w&7O0I3)ZC_A-+$3@5 zA6NF78Fuw5dR5J%>PXhpVU}Ucci27GBw{3Crvm zk?Aly?eb0P9Mvt372RNmHuG8Qt=8Os2cP5Ui`FyxK8T&VXBSffJ5`gv%eKaEqVShJ zHcGA-{o<%&UB7nv)iJkG7o5#)43&JRxbdjX^`q9rb$06J7iJaarKMQ)h+I8&8027{ znSQnHt}r*0c6YvA+@kC2P1?lS0&Di0RFPb!k_&WJx(UH~$RJNWW#kqDPCikh(ZCmS zJ8eE%N4SzFTyDpzcabd)(w@Sj)&py+l1qk|25LB{{9m7bwc~!E2kgxU9Vw>b*Hstt zo47^Ks;}5QYfW6Qr%W2<^Kt1qSLktlmVP$O`&eqw1eMe2T+famC)_kO>{Ro_18d2! z5jiw_bXgEvyaab>v)#12IP*C)EHk#I-2>^jGm7od7JUqqWxEgU;eNtd-fsa`#b`k( zMvYacdee*ac{*463On)M0CiVe-3y;f_7K&LziM|hQMSCA%a;B2w<^V!7iGXv;VqfZ za#l0o#XK%#E`4$FV#6rvXi`VeeOjcT1`qpGVQnG1bqe@EC2ufwN!95)hK#PkGX^Xh z%wgsNb>=x`j8@xSElrVV9UHNJLCwzfXZ2!)6HZSps$XZ-ucL%gmi3RCgT|`V+>Ncu z45zn|-&pbz&&MDbZ`^$&mYO&gKck`&gR=E7sxa|Bk+|^eBF2C_Tig_#&6!<(XyiJlyv@(i%{ZGgV3LnI zfd&tpZ^wO2HbkY26U(T`?rhR;iIZ5v$DB7z`5aa7NaUwQeBmb^>NMqcnlfX98kyKVs>l=n zz=OJ7V5&7&S(YD?PZHEiw$M%6o6~l~x~{6l-FM_0%2Vm8+O*qBoX(Z;;%T=Hb`q;g zVwf7L<~=noHOAPP9#xxqO|NRnj0DwU{^fODybHYxtM?`gx34;hJw-A3>363FCY@B_ zU5r_KD{j#WRotq7R(T|e&J${hg zAiEQd?mgmPt2sdBYlj45G_TI?^gFcgMP$CD(dQOqzJUI!;p0E^&Fk#WwRT5K>{`0n zr3%{W6+-Ww<(a8E-aGSp`A!-hv#n?3wg)#KLB&kFD9{}Bb-Lv_RJdDRQ-OZh{M<|h zDhhc;s-NYZA;KLt?spzv+*_SjK~lIz>DRnGiulniqTlYHc}dMIqTe?EtRzTVq_m}e z+W)4sWQx0H#zP9?nWt83H{_7z~HkT1DP z(z+INOFglBC&TpYL+06w^@tAbrp#b>usy4J0Vk9GJo25$wWv4m?r5P?>tH*Cn`<>@y-fwOwTZS7-Hz%*jyI?X20h;v9{)xU1i zFbsS^K%QwMc?Q$wKv9WxLoorUxXG|?XB26m>cy1^s>uA#^)L$BRMW3YYAC0~A&|fI za;@evC8;|B7`|ye`IO3?y1aQR0K@MG70}VL00TQQYX36x2s=Y-|2*?oRIZL^GXtP> z)!v-}c-(;oD|L#SYaKEy&3u)eA+=ut&L;TV2%gl`?kX*Yr3x?LfeJe{hBZEM?)|kJy^g~2p5v|U=_i;5sV(Ap8oW4639-1mF&`&9YNy3$?xGkXHRbC zWHlgk`Z`_hn&$>>;&c*h)SKc)I@hGHhcb^sE|^y1A@+QCblMNx6{#vd8W))NR;k#OiZHx>w**4>*yK}c0 zEv4_Aw@hhE|ACt$w`5pX|HQd?`fu9B|2ti3)jyMAeem!21KMbQ|A;vHryqoIj%&XF zeU<(U3t~*`y5q|Xjq~HnV^IO03SZtfatp|^(Bg;45W6W*c>yIbpmM}LVLCUr?P;U7}WH66AQU()wT2Fr8q~ftWU!G+p!9i(oK!^k*>ar2RmB!#o zMR27hxN=W$r8T(nP;jL^xH37olE>SnLFZ5^Q`mVtq6;c?&7c=NfVo+yt<`YPbtsd5 zFL(g+3G3-ET#o?UJyG|41-T@L$gKDITPG~>@t6y9p_9oI=<>TBx|{)B=A&Q0C%hGj zl=84uzhgMLST9cpUH%T}@)^+O-*d?f#!a>RV(vjHD9~krj%RYv&niR%$L6adeM4D&)vEN4kqAOz!y{jpnElov3*U8KcU$_>kC!Bjk6B}z9{w|{kRY0cv{&+THrdKNEh-aF|F+T$nF%H22CeRVl^DlNy9;_VGjq6J~-8| zlcV&}YxT+<_H}y9F0L#a#@E<%1*`*03bFK^ghaRl2)V~H*}RF+^ZZzbIU;? zYA>b$l*cc_&TeI>ohm%ow36=xUPS&ACl}(uRtz&f;N9Lysi*eq&DaUd!ay3P)aZT= z*v3|WKLs@15MpTn2aD$IckhGgTR5qE960z%DlJqmv?AiJP7md>-!`@7>3crkb#@Io zPo}xg($mvl?Yd#rXSkAs0pHJq_wZo59+Yp&xy^a2&b740`9qv9)%o|4Jm(a-q0zfl zLFu!gFa4@!N)G62_{c(Ki3Qyq_a$+jRk@j(IN{ECDt1XO?pw#r9NhN-zKRR(>vXO# z)5dXb{Wc9t;l8)Uid!>ZlUNPz+Y0WxgHYtmfHK!o&e1;T`#ri7Vj>X*|CUY+=)2An zuhsCZ0e!u(roNoiFl4Hy2CifbYLv#9fO&{7Hd}`k4+CC6Nz&VRfGbYw_Iw#WCBdU;R=V0NG>J-(`{KI&UPY9WsZ4gNRK;HuO>GdmZOI1Uupl(cgl-(nI9{5#5TS0B+A#yk{Q zFAPxNBR&djl7Ip$+jPRD@Tw*N%SVGZluBTYc51HiCA!dHW79t!8vG=AcxbQ^`Dn0O zss|c;{~DpeOHFS`&u3Gd=|O94z6(!(sb0_UX^N!=<~y@ay#xo#ntSVKcxKkAo5{+0 z=0;9psj6KdPO;R3CA%DJ{7NcP`&ByDWslv26O~1Vt<+x+k^CEVu{%g<(BbqrZqv16 zDfNxf{ zT~v12e{Dj*o$$3!n0*Mfh4JK&Q_+zJQ{(d8R}DDbba1GNLhq{=#+qKPf0dv#sqcO% zKeG8FHj!(U_=y;0jJa=*JYhE-ELFr(_zOeIZ2YicfaBW6-1I>ym9?$sPV@iu2ozGV zE9<$}V%BqK>6aur33gB#4{flU4ws@n?a&*n>x4HT+Y0ON*2Ghz@?+ulWh%h9Wc?ty zvDV+Q=k$aVKH<3URJltBseoqZ6bV+_`ULQ5w#Sw7ig$SFhf%kQHy+{iRY&g#;mg14 zG`(NSt77hJ@lXevhm#htH0~Z_z{bNJ5$o?(##4p2#lmZ3v+`6JhiAeqRqlLK3@u$! zmKFNUXFRl%ly>NJ6=hVr*r5(5v7#g%K48s0LQjlg0gCQO-FIWY)3mQJmPAS*UBhV9 zZQm=*qZT))7O5Y00t`O!w;b|)O~-xPp&oH}7v(<`Vcp+7VT-~a_O(WNTgh6dAL@YI>)2TjZ zTr$2~!~Er#7kn_%lo>j5OEmNjv%ARVGaxC}6aSmSMHv{Wr5~xBF+Z;e_Ei3Step4x zzJx!TnSVOT!ke7b4>7a{wMghTQ?~+V>jAT**UF{X1&a>ML5`g(;?=vccn73vo#Z1o zoz$|5S^KNHpnk6&aS~1K_SSt>?vlNFC=%W}Wuft52#$IwB=YW#3|q_DVt&+m;$AAw z?h3upFEW;;oKJP}arG9KwW!EPYX7*q+1Z-rF^9GI3+BG1SB`V%zomDj#W4ty$EsIA z>8s}x2o&jLfi?HQ)w=1mvbU`0l=IQ*`5Tqg23fRIRYfd+SQr9y{JjNnOx@hylgeF> z9_ZMGWsih5qv2RNt1Mq%m73pNrxLN=h*4lzK6>T2{_w99$DnD-MNUT}5+DLPFsAIKN!eq z7YT_^oVw0>+I*!t#{pwV$RAVR*S{H`Q6+W8h=BlOMocg`f?JwxBvw=emxCdKRj!#F z$cQyC7Mo62Joas2QF23^j+LDQaNXiEj^+*MMR#qmh}Jq5)mZE_wT^O9&Q}~bp`&7< z6&*X{#VyX(3@(6H%;MH~wkr(rmiN-%f=s4`s`|TOZM;}V@vY(YgW8dfep~(D7XMdL zvlAaw)Kd7s+nfE5BvO&(AIZH_-!wiGUTuz)?C|=j;h>}}G@yXYQ)?n8$-uDdq)&K5 zo)#fgJKMcPkZ%jtJ#(LqOahk58cGIp*B@W%0Df1p6JN`#Ve!&eO+i{nvi_CeIscAd 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


L31q0@~ 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 Date: Sat, 8 Feb 2020 02:09:45 +0800 Subject: [PATCH 040/201] Update auto-test to support DeePMD-kit 1.1 version --- dpgen/auto_test/gen_00_equi.py | 8 ++++--- dpgen/auto_test/gen_01_eos.py | 18 ++++++++------- dpgen/auto_test/gen_02_elastic.py | 8 ++++--- dpgen/auto_test/gen_03_vacancy.py | 8 ++++--- dpgen/auto_test/gen_04_interstitial.py | 16 ++++++++----- dpgen/auto_test/gen_05_surf.py | 10 +++++---- dpgen/auto_test/gen_06_phonon.py | 8 ++++--- dpgen/auto_test/gen_07_SScurve.py | 9 ++++---- dpgen/auto_test/lib/lammps.py | 31 +++++++++++++++++--------- dpgen/auto_test/lib/util.py | 10 ++------- dpgen/auto_test/run.py | 20 ++++++++--------- 11 files changed, 83 insertions(+), 63 deletions(-) diff --git a/dpgen/auto_test/gen_00_equi.py b/dpgen/auto_test/gen_00_equi.py index 59c1ddfdb..6f02c7096 100755 --- a/dpgen/auto_test/gen_00_equi.py +++ b/dpgen/auto_test/gen_00_equi.py @@ -84,6 +84,7 @@ def make_lammps (jdata, conf_dir,task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type =='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -91,8 +92,9 @@ def make_lammps (jdata, conf_dir,task_type) : else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) conf_path = os.path.abspath(conf_dir) @@ -121,7 +123,7 @@ def make_lammps (jdata, conf_dir,task_type) : fc = lammps.make_lammps_equi(os.path.basename(conf_file), ntypes, lammps.inter_deepmd, - model_name) + model_param) elif task_type=='meam': fc = lammps.make_lammps_equi(os.path.basename(conf_file), ntypes, diff --git a/dpgen/auto_test/gen_01_eos.py b/dpgen/auto_test/gen_01_eos.py index c129a4df3..253792aee 100755 --- a/dpgen/auto_test/gen_01_eos.py +++ b/dpgen/auto_test/gen_01_eos.py @@ -106,6 +106,7 @@ def make_lammps (jdata, conf_dir,task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type =='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -113,9 +114,9 @@ def make_lammps (jdata, conf_dir,task_type) : else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} - + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) vol_start = jdata['vol_start'] @@ -194,7 +195,7 @@ def make_lammps (jdata, conf_dir,task_type) : # make lammps input scale = (vol / vpa) ** (1./3.) if task_type=='deepmd': - fc = lammps.make_lammps_press_relax('conf.lmp', ntypes, scale,lammps.inter_deepmd, model_name) + fc = lammps.make_lammps_press_relax('conf.lmp', ntypes, scale,lammps.inter_deepmd, model_param) elif task_type =='meam': fc = lammps.make_lammps_press_relax('conf.lmp', ntypes, scale,lammps.inter_meam, model_param) with open(os.path.join(vol_path, 'lammps.in'), 'w') as fp : @@ -207,15 +208,16 @@ def make_lammps_fixv (jdata, conf_dir,task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type =='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} - + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) @@ -246,7 +248,7 @@ def make_lammps_fixv (jdata, conf_dir,task_type) : fc = lammps.make_lammps_equi('conf.lmp', ntypes, lammps.inter_deepmd, - model_name, + model_param, change_box = False) elif task_type=='meam': fc = lammps.make_lammps_equi('conf.lmp', diff --git a/dpgen/auto_test/gen_02_elastic.py b/dpgen/auto_test/gen_02_elastic.py index fa9ca9053..45553e49d 100755 --- a/dpgen/auto_test/gen_02_elastic.py +++ b/dpgen/auto_test/gen_02_elastic.py @@ -115,6 +115,7 @@ def make_lammps(jdata, conf_dir,task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type =='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -122,8 +123,9 @@ def make_lammps(jdata, conf_dir,task_type) : else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) @@ -168,7 +170,7 @@ def make_lammps(jdata, conf_dir,task_type) : fc = lammps.make_lammps_elastic('conf.lmp', ntypes, lammps.inter_deepmd, - model_name) + model_param) elif task_type=='meam': fc = lammps.make_lammps_elastic('conf.lmp', ntypes, diff --git a/dpgen/auto_test/gen_03_vacancy.py b/dpgen/auto_test/gen_03_vacancy.py index 93b061ea9..f464ef148 100755 --- a/dpgen/auto_test/gen_03_vacancy.py +++ b/dpgen/auto_test/gen_03_vacancy.py @@ -100,6 +100,7 @@ def make_lammps(jdata, conf_dir, task_type, supercell) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type =='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -107,8 +108,9 @@ def make_lammps(jdata, conf_dir, task_type, supercell) : else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) @@ -154,7 +156,7 @@ def make_lammps(jdata, conf_dir, task_type, supercell) : ntypes, 1, lammps.inter_deepmd, - model_name) + model_param) elif task_type =='meam': fc = lammps.make_lammps_press_relax('conf.lmp', ntypes, diff --git a/dpgen/auto_test/gen_04_interstitial.py b/dpgen/auto_test/gen_04_interstitial.py index 376be45c6..ae56de0eb 100755 --- a/dpgen/auto_test/gen_04_interstitial.py +++ b/dpgen/auto_test/gen_04_interstitial.py @@ -114,6 +114,7 @@ def _make_reprod_traj(jdata, conf_dir, supercell, insert_ele, task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type=='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -121,8 +122,9 @@ def _make_reprod_traj(jdata, conf_dir, supercell, insert_ele, task_type) : else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) @@ -152,7 +154,7 @@ def _make_reprod_traj(jdata, conf_dir, supercell, insert_ele, task_type) : fc = lammps.make_lammps_eval('conf.lmp', ntypes, lammps.inter_deepmd, - model_name) + model_param) elif task_type =='meam': fc = lammps.make_lammps_eval('conf.lmp', ntypes, @@ -238,6 +240,7 @@ def _make_lammps(jdata, conf_dir, supercell, insert_ele, task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type=='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -245,8 +248,9 @@ def _make_lammps(jdata, conf_dir, supercell, insert_ele, task_type) : else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) @@ -291,7 +295,7 @@ def _make_lammps(jdata, conf_dir, supercell, insert_ele, task_type) : ntypes, 1, lammps.inter_deepmd, - model_name) + model_param) elif task_type =='meam': fc = lammps.make_lammps_press_relax('conf.lmp', ntypes, diff --git a/dpgen/auto_test/gen_05_surf.py b/dpgen/auto_test/gen_05_surf.py index 56a275630..eff97a622 100755 --- a/dpgen/auto_test/gen_05_surf.py +++ b/dpgen/auto_test/gen_05_surf.py @@ -131,6 +131,7 @@ def make_lammps(jdata, conf_dir, max_miller = 2, static = False, relax_box = Fal type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name and task_type=='deepmd': models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] @@ -138,8 +139,9 @@ def make_lammps(jdata, conf_dir, max_miller = 2, static = False, relax_box = Fal else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) @@ -184,12 +186,12 @@ def make_lammps(jdata, conf_dir, max_miller = 2, static = False, relax_box = Fal fc = lammps.make_lammps_eval('conf.lmp', ntypes, lammps.inter_deepmd, - model_name) + model_param) else : fc = lammps.make_lammps_equi('conf.lmp', ntypes, lammps.inter_deepmd, - model_name, + model_param, change_box = relax_box) elif task_type =='meam': if static : diff --git a/dpgen/auto_test/gen_06_phonon.py b/dpgen/auto_test/gen_06_phonon.py index 2b1e2af14..3be4427dc 100644 --- a/dpgen/auto_test/gen_06_phonon.py +++ b/dpgen/auto_test/gen_06_phonon.py @@ -169,14 +169,16 @@ def make_lammps(jdata, conf_dir,task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name : models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} supercell_matrix=jdata['supercell_matrix'] band_path=jdata['band'] @@ -214,7 +216,7 @@ def make_lammps(jdata, conf_dir,task_type) : fc = lammps.make_lammps_phonon('conf.lmp', unitcell.masses, lammps.inter_deepmd, - model_name) + model_param) if task_type=='meam': fc = lammps.make_lammps_phonon('conf.lmp', unitcell.masses, diff --git a/dpgen/auto_test/gen_07_SScurve.py b/dpgen/auto_test/gen_07_SScurve.py index c97f3c733..557aaccf3 100644 --- a/dpgen/auto_test/gen_07_SScurve.py +++ b/dpgen/auto_test/gen_07_SScurve.py @@ -104,15 +104,16 @@ def make_lammps(jdata, conf_dir,task_type) : type_map = fp_params['type_map'] model_dir = os.path.abspath(model_dir) model_name =fp_params['model_name'] + deepmd_version = fp_params.get("deepmd_version", "0.12") if not model_name : models = glob.glob(os.path.join(model_dir, '*pb')) model_name = [os.path.basename(ii) for ii in models] else: models = [os.path.join(model_dir,ii) for ii in model_name] - model_param = {'model_name' : fp_params['model_name'], - 'param_type': fp_params['model_param_type']} - + model_param = {'model_name' : model_name, + 'param_type': fp_params['model_param_type'], + 'deepmd_version' : deepmd_version} ntypes = len(type_map) strain_start=jdata['strain_start'] strain_end=jdata['strain_end'] @@ -153,7 +154,7 @@ def make_lammps(jdata, conf_dir,task_type) : fc = lammps.make_lammps_elastic('conf.lmp', ntypes, lammps.inter_deepmd, - model_name) + model_param) elif task_type =='meam': fc = lammps.make_lammps_elastic('conf.lmp', ntypes, diff --git a/dpgen/auto_test/lib/lammps.py b/dpgen/auto_test/lib/lammps.py index b6a21f44a..aafc35e21 100644 --- a/dpgen/auto_test/lib/lammps.py +++ b/dpgen/auto_test/lib/lammps.py @@ -4,6 +4,7 @@ import dpdata import subprocess as sp import dpgen.auto_test.lib.util as util +from distutils.version import LooseVersion def cvt_lammps_conf(fin, fout, ofmt = 'lammps/data'): """ @@ -89,16 +90,26 @@ def _get_conf_natom(conf) : return int(ii.split()[0]) raise RuntimeError("cannot find line indicate atom types in ", conf) -def inter_deepmd(models) : - ret = "" - line = "pair_style deepmd " - if len(models) > 1 : - for ii in models : - line += ii + ' ' - line += ' 10 model_devi.out\npair_coeff\n' - else : - line += models[0] + '\npair_coeff\n' - ret += line +def inter_deepmd(param) : + models = param["model_name"] + deepmd_version = param["deepmd_version"] + ret = "pair_style deepmd " + model_list = "" + for ii in models: + model_list += ii + " " + if LooseVersion(deepmd_version) < LooseVersion('1'): + ## DeePMD-kit version == 0.x + if len(models) > 1 : + ret += '%s 10 model_devi.out\n' % model_list + else : + ret += models[0] + '\n' + else: + ## DeePMD-kit version >= 1 + if len(models) > 1: + ret += "%s out_freq 10 out_file model_devi.out\n" % model_list + else: + ret += models[0] + '\n' + ret += "pair_coeff\n" return ret def inter_meam(param) : diff --git a/dpgen/auto_test/lib/util.py b/dpgen/auto_test/lib/util.py index 1d3a2dc58..06fd27208 100644 --- a/dpgen/auto_test/lib/util.py +++ b/dpgen/auto_test/lib/util.py @@ -1,7 +1,7 @@ import numpy as np import requests import os,re -from dpgen.remote.RemoteJob import SSHSession +from dpgen import dlog from dpgen.auto_test.lib import vasp from dpgen.auto_test.lib import lammps from dpgen.auto_test.lib.utils import cmd_append_log @@ -74,7 +74,6 @@ def get_machine_info(mdata,task_type): group_size = mdata['fp_group_size'] resources = mdata['fp_resources'] machine=mdata['fp_machine'] - machine_type = mdata['fp_machine']['machine_type'] command = vasp_exec command = cmd_append_log(command, "log") elif task_type in lammps_task_type: @@ -82,14 +81,9 @@ def get_machine_info(mdata,task_type): group_size = mdata['model_devi_group_size'] resources = mdata['model_devi_resources'] machine=mdata['model_devi_machine'] - machine_type = mdata['model_devi_machine']['machine_type'] command = lmp_exec + " -i lammps.in" command = cmd_append_log(command, "model_devi.log") - if machine_type == 'ALI': - ssh_sess = None - else: - ssh_sess = SSHSession(machine) - return machine, machine_type,ssh_sess,resources, command, group_size + return machine, resources, command, group_size def collect_task(all_task,task_type): diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 0285063ee..b7747916a 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -35,15 +35,13 @@ from dpgen.auto_test.lib.utils import log_iter from dpgen.auto_test.lib.pwscf import make_pwscf_input from dpgen.auto_test.lib.siesta import make_siesta_input -from dpgen.remote.RemoteJob import SSHSession, JobStatus, SlurmJob, PBSJob, CloudMachineJob -from dpgen.remote.decide_machine import decide_fp_machine, decide_model_devi_machine -from dpgen.remote.group_jobs import * from dpgen.auto_test import gen_00_equi,cmpt_00_equi from dpgen.auto_test import gen_01_eos,cmpt_01_eos from dpgen.auto_test import gen_02_elastic,cmpt_02_elastic from dpgen.auto_test import gen_03_vacancy,cmpt_03_vacancy from dpgen.auto_test import gen_04_interstitial,cmpt_04_interstitial from dpgen.auto_test import gen_05_surf,cmpt_05_surf +from dpgen.remote.decide_machine import decide_fp_machine, decide_model_devi_machine #from dpgen.auto_test import gen_06_phonon,cmpt_06_phonon from dpgen.auto_test import gen_confs import requests @@ -104,7 +102,7 @@ def run_equi(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, @@ -192,7 +190,7 @@ def run_eos(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, @@ -268,7 +266,7 @@ def run_elastic(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, @@ -343,7 +341,7 @@ def run_vacancy(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, @@ -440,7 +438,7 @@ def run_interstitial(task_type,jdata,mdata): else: raise RuntimeError ("unknow task %s, something wrong" % task_type) - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) if reprod_opt: for ii in work_path: run_tasks=[] @@ -548,7 +546,7 @@ def run_surf(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) disp = make_dispatcher(machine, resources, run_tasks, group_size) disp.run_jobs(resources, command, @@ -601,7 +599,7 @@ def run_phonon(task_type,jdata,mdata): #vasp if task_type == "vasp": mdata=decide_fp_machine(mdata) - machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) + machine,resources,command,group_size=util.get_machine_info(mdata,task_type) run_tasks = util.collect_task(all_task,task_type) forward_files = ['INCAR', 'POTCAR','KPOINTS'] @@ -623,7 +621,7 @@ def run_phonon(task_type,jdata,mdata): elif task_type in lammps_task_type: None else: - raise RuntimeError ("unknow task %s, something wrong" % task_type) + raise RuntimeError ("unknown task %s, something wrong" % task_type) def cmpt_phonon(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] From 9e385ff652cc75113fceeb0bde18841561f35835 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 8 Feb 2020 04:29:11 +0800 Subject: [PATCH 041/201] fix bugs in job restart. --- dpgen/dispatcher/ALI.py | 84 +++++++++++++++++++--------------- dpgen/dispatcher/Dispatcher.py | 19 ++++++-- dpgen/generator/run.py | 6 +-- 3 files changed, 64 insertions(+), 45 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 7aeb4151d..ec6591177 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -5,9 +5,10 @@ from aliyunsdkecs.request.v20140526.RunInstancesRequest import RunInstancesRequest from aliyunsdkecs.request.v20140526.DeleteInstancesRequest import DeleteInstancesRequest import time, json, os, glob -from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks +from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks, JobRecord from os.path import join from dpgen import dlog +from hashlib import sha1 def manual_delete(regionID): with open('machine-ali.json') as fp1: @@ -43,45 +44,51 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.regionID = mdata_machine["regionID"] self.dispatchers = None self.job_handlers = None + self.task_chunks = None self.adata = adata self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine self.nchunks = nchunks - def init(self): - if self.check_restart(): + + def init(self, work_path, tasks, group_size): + if self.check_restart(work_path, tasks, group_size): pass else: self.create_machine() self.dispatchers = self.make_dispatchers() - def check_restart(self): - dispatchers = [] - instance_list = [] + def check_restart(self, work_path, tasks, group_size): if os.path.exists('machine_record.json'): - with open('machine_record.json', 'r') as fp: - machine_record = json.load(fp) - for ii in range(self.nchunks): - ip, instance_id = machine_record['ip'][ii], machine_record['instance_id'][ii] - instance_list.append(instance_id) - profile = self.mdata_machine.copy() - profile['hostname'] = ip - profile['instance_id'] = instance_id - disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) - max_check = 10 - cnt = 0 - while not disp.session._check_alive(): - cnt += 1 - if cnt == max_check: - break - if cnt != max_check: - dispatchers.append(disp) - restart = False - if len(dispatchers) == self.nchunks: - restart = True - self.dispatchers = dispatchers - self.instance_list = instance_list - return restart + dispatchers = [] + instance_list = [] + ip_list = [] + self.task_chunks = [] + task_chunks = _split_tasks(tasks, group_size) + task_chunks_str = ['+'.join(ii) for ii in task_chunks] + task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] + nchunks = len(task_chunks) + for ii in range(nchunks): + job_record = JobRecord(work_path, task_chunks, fname = 'jr.%.06d.json' % ii) + cur_chunk = task_chunks[ii] + cur_hash = task_hashes[ii] + if not job_record.check_finished(cur_hash): + self.task_chunks.append(cur_chunk) + with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: + jr = json.load(fp) + ip = jr[cur_hash]['context'][3] + instance_id = jr[cur_hash]['context'][4] + ip_list.append(ip) + instance_list.append(instance_id) + profile = self.mdata_machine.copy() + profile['hostname'] = ip + profile['instance_id'] = instance_id + disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + dispatchers.append(disp) + self.dispatchers = dispatchers + self.instance_list = instance_list + self.ip_list = ip_list + return True else: return False @@ -97,13 +104,14 @@ def run_jobs(self, forward_task_deference = True, outlog = 'log', errlog = 'err'): - task_chunks = _split_tasks(tasks, group_size) + if not self.task_chunks: + self.task_chunks = _split_tasks(tasks, group_size) self.job_handlers = [] - for ii in range(self.nchunks): + for ii in range(len(self.dispatchers)): job_handler = self.dispatchers[ii].submit_jobs(resources, command, work_path, - task_chunks[ii], + self.task_chunks[ii], group_size, forward_common_files, forward_task_files, @@ -113,12 +121,11 @@ def run_jobs(self, errlog) self.job_handlers.append(job_handler) while True: - for ii in range(self.nchunks): + for ii in range(len(self.dispatchers)): if self.dispatchers[ii].all_finished(self.job_handlers[ii]): self.delete(ii) break - if self.nchunks == 0: - os.remove('machine_record.json') + if len(self.dispatchers) == 0: break else: time.sleep(10) @@ -138,8 +145,11 @@ def delete(self, ii): self.ip_list.pop(ii) self.dispatchers.pop(ii) self.job_handlers.pop(ii) - with open('machine_record.json', 'w') as fp: - json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) + if len(self.ip_list) == 0: + os.remove('machine_record.json') + else: + with open('machine_record.json', 'w') as fp: + json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) def make_dispatchers(self): dispatchers = [] diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index c9eaf17b5..c6467caad 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -152,10 +152,17 @@ def submit_jobs(self, dlog.info('restart from old submission %s for chunk %s' % (job_uuid, cur_hash)) # record job and its remote context job_list.append(rjob) + ip = None + instance_id = None + if self.remote_profile['machine_type'] == 'ALI': + ip = self.remote_profile['hostname'] + instance_id = self.remote_profile['instance_id'] job_record.record_remote_context(cur_hash, context.local_root, context.remote_root, - job_uuid) + job_uuid, + ip, + instance_id) else : # finished job, append a None to list job_list.append(None) @@ -231,9 +238,11 @@ def record_remote_context(self, chunk_hash, local_root, remote_root, - job_uuid): + job_uuid, + ip=None, + instance_id=None): self.valid_hash(chunk_hash) - self.record[chunk_hash]['context'] = [local_root, remote_root, job_uuid] + self.record[chunk_hash]['context'] = [local_root, remote_root, job_uuid, ip, instance_id] def get_uuid(self, chunk_hash): self.valid_hash(chunk_hash) @@ -284,12 +293,12 @@ def _new_record(self): } -def make_dispatcher(mdata, mdata_resource=None, run_tasks=None, group_size=None): +def make_dispatcher(mdata, mdata_resource=None, work_path=None, run_tasks=None, group_size=None): if 'ali_auth' in mdata: from dpgen.dispatcher.ALI import ALI nchunks = len(_split_tasks(run_tasks, group_size)) dispatcher = ALI(mdata['ali_auth'], mdata_resource, mdata, nchunks) - dispatcher.init() + dispatcher.init(work_path, run_tasks, group_size) return dispatcher else: try: diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index d685003c1..05ae64424 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -447,7 +447,7 @@ def run_train (iter_index, except: train_group_size = 1 - dispatcher = make_dispatcher(mdata['train_machine'], mdata['train_resources'], run_tasks, train_group_size) + dispatcher = make_dispatcher(mdata['train_machine'], mdata['train_resources'], work_path, run_tasks, train_group_size) dispatcher.run_jobs(mdata['train_resources'], commands, work_path, @@ -914,7 +914,7 @@ def run_model_devi (iter_index, backward_files += ['output.plumed'] cwd = os.getcwd() - dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], run_tasks, model_devi_group_size) + dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], work_path, run_tasks, model_devi_group_size) dispatcher.run_jobs(mdata['model_devi_resources'], commands, work_path, @@ -1495,7 +1495,7 @@ def run_fp_inner (iter_index, # if not check_fin(ii) : # fp_run_tasks.append(ii) run_tasks = [os.path.basename(ii) for ii in fp_run_tasks] - dispatcher = make_dispatcher(mdata['fp_machine'], mdata['fp_resources'], run_tasks, fp_group_size) + dispatcher = make_dispatcher(mdata['fp_machine'], mdata['fp_resources'], work_path, run_tasks, fp_group_size) dispatcher.run_jobs(mdata['fp_resources'], [fp_command], work_path, From 160398127fce33f172eb8f02bb532643490ede68 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 8 Feb 2020 04:53:44 +0800 Subject: [PATCH 042/201] fix bugs in restart jobs --- dpgen/auto_test/run.py | 16 ++++++++-------- dpgen/dispatcher/Dispatcher.py | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 0285063ee..f8f8c8e16 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -105,7 +105,7 @@ def run_equi(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -193,7 +193,7 @@ def run_eos(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -269,7 +269,7 @@ def run_elastic(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -344,7 +344,7 @@ def run_vacancy(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -447,7 +447,7 @@ def run_interstitial(task_type,jdata,mdata): for jj in run_tasks_: if ii in jj: run_tasks.append(os.path.basename(jj)) - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, ii, @@ -461,7 +461,7 @@ def run_interstitial(task_type,jdata,mdata): else: run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -549,7 +549,7 @@ def run_surf(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return machine,machine_type,ssh_sess,resources,command,group_size=util.get_machine_info(mdata,task_type) - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, @@ -608,7 +608,7 @@ def run_phonon(task_type,jdata,mdata): backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR','vasprun.xml'] common_files=['POSCAR'] - disp = make_dispatcher(machine, resources, run_tasks, group_size) + disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, command, work_path, diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index c6467caad..5c2d29d50 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -212,10 +212,10 @@ def all_finished(self, rjob['batch'].submit(task_chunks[idx], command, res = resources, outlog=outlog, errlog=errlog,restart=True) elif status == JobStatus.finished : dlog.info('job %s finished' % job_uuid) - rjob['context'].download(task_chunks[idx], backward_task_files) - rjob['context'].clean() - job_record.record_finish(cur_hash) - job_record.dump() + #rjob['context'].download(task_chunks[idx], backward_task_files) + #rjob['context'].clean() + #job_record.record_finish(cur_hash) + #job_record.dump() job_record.dump() return job_record.check_all_finished() From 3bbae54c2f7181bee80acd59126c418c572927ef Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 8 Feb 2020 10:41:24 +0800 Subject: [PATCH 043/201] fix bug --- dpgen/dispatcher/Dispatcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 5c2d29d50..c6467caad 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -212,10 +212,10 @@ def all_finished(self, rjob['batch'].submit(task_chunks[idx], command, res = resources, outlog=outlog, errlog=errlog,restart=True) elif status == JobStatus.finished : dlog.info('job %s finished' % job_uuid) - #rjob['context'].download(task_chunks[idx], backward_task_files) - #rjob['context'].clean() - #job_record.record_finish(cur_hash) - #job_record.dump() + rjob['context'].download(task_chunks[idx], backward_task_files) + rjob['context'].clean() + job_record.record_finish(cur_hash) + job_record.dump() job_record.dump() return job_record.check_all_finished() From 63aa1dbbc78d074dab4e0eee547cba86b3a99c31 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Feb 2020 11:21:44 +0800 Subject: [PATCH 044/201] fix bug in getting sleep time when res is None --- dpgen/dispatcher/Batch.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index e097997ad..b1924c56d 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -118,7 +118,10 @@ def submit(self, else: dlog.debug('new task') self.do_submit(job_dirs, cmd, args, res, outlog=outlog, errlog=errlog) - sleep = res.get('submit_wait_time', 0) + if res is None: + sleep = 0 + else: + sleep = res.get('submit_wait_time', 0) time.sleep(sleep) # For preventing the crash of the tasks while submitting def check_finish_tag(self) : From 26554cb270b2f0c163ae8d50cf802cc21d4baae2 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Feb 2020 11:32:06 +0800 Subject: [PATCH 045/201] travis print verbose unittest info --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b0963bb18..bad74118a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,6 @@ install: - pip install . # command to run tests script: - - coverage run --source=./dpgen -m unittest && coverage report + - coverage run --source=./dpgen -m unittest -v && coverage report after_success: - codecov From c3f6138b63a5fdc52369a6e040062baa312ba235 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Feb 2020 12:03:57 +0800 Subject: [PATCH 046/201] fix bug of getting the key machine_type from remote profile --- dpgen/dispatcher/Dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index c6467caad..a933ccf6a 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -154,7 +154,7 @@ def submit_jobs(self, job_list.append(rjob) ip = None instance_id = None - if self.remote_profile['machine_type'] == 'ALI': + if self.remote_profile.get('machine_type') == 'ALI': ip = self.remote_profile['hostname'] instance_id = self.remote_profile['instance_id'] job_record.record_remote_context(cur_hash, From 2bfa930620dc6a6a10ffe2b82fac6ff771351c0d Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Feb 2020 12:09:39 +0800 Subject: [PATCH 047/201] fix bug: submit script should exit with code 1 --- dpgen/dispatcher/Batch.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index b1924c56d..37ceed577 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -142,12 +142,12 @@ def _sub_script_inner(self, allow_failure = False for ii,jj in zip(job_dirs, args) : ret += 'cd %s\n' % ii - ret += 'test $? -ne 0 && exit\n\n' + ret += 'test $? -ne 0 && exit 1\n\n' if self.manual_cuda_devices <= 0: ret += 'if [ ! -f tag_%d_finished ] ;then\n' % idx ret += ' %s 1>> %s 2>> %s \n' % (self.sub_script_cmd(cmd, jj, res), outlog, errlog) if res['allow_failure'] is False: - ret += ' if test $? -ne 0; then exit; else touch tag_%d_finished; fi \n' % idx + ret += ' if test $? -ne 0; then exit 1; else touch tag_%d_finished; fi \n' % idx else : ret += ' touch tag_%d_finished \n' % idx ret += 'fi\n\n' @@ -157,7 +157,7 @@ def _sub_script_inner(self, ret += 'CUDA_VISIBLE_DEVICES=%d %s &\n\n' % ((self.cmd_cnt % self.manual_cuda_devices), tmp_cmd) self.cmd_cnt += 1 ret += 'cd %s\n' % self.context.remote_root - ret += 'test $? -ne 0 && exit\n' + ret += 'test $? -ne 0 && exit 1\n' if self.manual_cuda_devices > 0 and self.cmd_cnt % (self.manual_cuda_devices * self.manual_cuda_multiplicity) == 0: ret += '\nwait\n\n' ret += '\nwait\n\n' From 135ae81073e1bfb784e66f0298cf7596ae609a35 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 10 Feb 2020 17:49:54 +0800 Subject: [PATCH 048/201] dlog ip address in console, use parm task_per_node to control cpu cores. --- dpgen/dispatcher/ALI.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index ec6591177..9539bbbb8 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -167,7 +167,10 @@ def create_machine(self): strategy = self.adata["pay_strategy"] pwd = self.adata["password"] regionID = self.regionID - template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy) instance_name = self.adata["instance_name"] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) self.instance_list = [] @@ -239,6 +242,9 @@ def create_machine(self): response = client.do_action_with_exception(request) response = json.loads(response) self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + dlog.info('create machine successfully, here is the ip addresses\n') + for ip in self.ip_list: + dlog.info(ip) with open('machine_record.json', 'w') as fp: json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) From 72172ee3838f0f2987a2aceaf42b5d851c090f5c Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 10 Feb 2020 17:55:57 +0800 Subject: [PATCH 049/201] use task_per_node to control cpu_cores, numb_gpu control gpu_numbers, dlog ip address in console. --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 9539bbbb8..eab19f01d 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -242,7 +242,7 @@ def create_machine(self): response = client.do_action_with_exception(request) response = json.loads(response) self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - dlog.info('create machine successfully, here is the ip addresses\n') + dlog.info('create machine successfully, following are the ip addresses') for ip in self.ip_list: dlog.info(ip) with open('machine_record.json', 'w') as fp: From d459f3bc37994e372fa580e7dd296d2dc2321bfa Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 13 Feb 2020 15:24:36 -0500 Subject: [PATCH 050/201] add reference information --- README.md | 10 +++++++--- dpgen/__init__.py | 10 ++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0eeaf2677..c90f84187 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,14 @@ ## About DP-GEN [![GitHub release](https://img.shields.io/github/release/deepmodeling/dpgen.svg?maxAge=86400)](https://github.com/deepmodeling/dpgen/releases/) -[![arxiv:1910.12690](http://img.shields.io/badge/arXiv-1910.12690-B31B1B.svg?maxAge=86400)](https://arxiv.org/abs/1910.12690) +[![doi:10.1016/j.cpc.2020.107206](https://zenodo.org/badge/DOI/10.1016/j.cpc.2020.107206.svg)](https://doi.org/10.1016/j.cpc.2020.107206) + +DP-GEN (Deep Generator) is a software written in Python, delicately designed to generate a deep learning based model of interatomic potential energy and force field. DP-GEN is depedent on DeepMD-kit (https://github.com/deepmodeling/deepmd-kit/blob/master/README.md). With highly scalable interface with common softwares for molecular simulation, DP-GEN is capable to automatically prepare scripts and maintain job queues on HPC machines (High Performance Cluster) and analyze results. + +If you use this software in any publication, please cite: + +Yuzhi Zhang, Haidi Wang, Weijie Chen, Jinzhe Zeng, Linfeng Zhang, Han Wang, and Weinan E, DP-GEN: A concurrent learning platform for the generation of reliable deep learning based potential energy models, Computer Physics Communications, 2020, 107206. -DP-GEN (Deep Generator) is a software written in Python, delicately designed to generate a deep learning based model of interatomic potential energy and force field. DP-GEN is depedent on DeepMD-kit (https://github.com/deepmodeling/deepmd-kit/blob/master/README.md). With highly scalable interface with common softwares for molecular simulation, DP-GEN is capable to automatically prepare scripts and maintain job queues on HPC machines (High Performance Cluster) and analyze results ### Highlighted features + **Accurate and efficient**: DP-GEN is capable to sample more than tens of million structures and select only a few for first principles calculation. DP-GEN will finally obtain a uniformly accurate model. + **User-friendly and automatic**: Users may install and run DP-GEN easily. Once succusefully running, DP-GEN can dispatch and handle all jobs on HPCs, and thus there's no need for any personal effort. @@ -56,7 +61,6 @@ Options for TASK: * `test`: Auto-test for Deep Potential. * `db`: Collecting data from DP-GEN. - ## Download and Install One can download the source code of dpgen by ```bash diff --git a/dpgen/__init__.py b/dpgen/__init__.py index d77ec3898..b38875dee 100644 --- a/dpgen/__init__.py +++ b/dpgen/__init__.py @@ -45,3 +45,13 @@ def info(): except ImportError: print('%10s %10s Not Found' % (modui, '')) print() + + # reference + print("""Reference +------------ +Please cite: +Yuzhi Zhang, Haidi Wang, Weijie Chen, Jinzhe Zeng, Linfeng Zhang, Han Wang, and Weinan E, +DP-GEN: A concurrent learning platform for the generation of reliable deep learning +based potential energy models, Computer Physics Communications, 2020, 107206. +------------ +""") From 2b51e28454630594e6681fc6e83e7cff94a638f4 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 14 Feb 2020 14:28:53 +0800 Subject: [PATCH 051/201] use ali_auth to judge machine --- dpgen/dispatcher/Dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index a933ccf6a..9b9125d9a 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -154,7 +154,7 @@ def submit_jobs(self, job_list.append(rjob) ip = None instance_id = None - if self.remote_profile.get('machine_type') == 'ALI': + if 'ali_auth' in self.remote_profile:: ip = self.remote_profile['hostname'] instance_id = self.remote_profile['instance_id'] job_record.record_remote_context(cur_hash, From 1169552e434219048a64fd246f2757bd266a555a Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 14 Feb 2020 14:41:50 +0800 Subject: [PATCH 052/201] fix bug --- dpgen/dispatcher/Dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 9b9125d9a..00ded21c2 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -154,7 +154,7 @@ def submit_jobs(self, job_list.append(rjob) ip = None instance_id = None - if 'ali_auth' in self.remote_profile:: + if 'ali_auth' in self.remote_profile: ip = self.remote_profile['hostname'] instance_id = self.remote_profile['instance_id'] job_record.record_remote_context(cur_hash, From 66fafbc91578f78c6289a79ab5e11891a5120872 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 18 Feb 2020 12:26:40 +0800 Subject: [PATCH 053/201] support init model parameter from old models --- dpgen/generator/run.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 054d15440..9c82210ca 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -193,6 +193,12 @@ def make_train (iter_index, fp_task_min = jdata['fp_task_min'] model_devi_jobs = jdata['model_devi_jobs'] use_ele_temp = jdata.get('use_ele_temp', 0) + training_reuse_iter = jdata.get('training_reuse_iter') + training_reuse_old_ratio = jdata.get('training_reuse_old_ratio', 0.8) + training_reuse_stop_batch = jdata.get('training_reuse_stop_batch', 400000) + training_reuse_start_lr = jdata.get('training_reuse_start_lr', 1e-4) + training_reuse_start_pref_e = jdata.get('training_reuse_start_pref_e', 0.1) + training_reuse_start_pref_f = jdata.get('training_reuse_start_pref_f', 100) if iter_index > 0 and _check_empty_iter(iter_index-1, fp_task_min) : log_task('prev data is empty, copy prev model') @@ -242,8 +248,11 @@ def make_train (iter_index, else: init_data_sys.append(os.path.join('..', 'data.init', ii)) init_batch_size.append(detect_batch_size(ss, os.path.join(work_path, 'data.init', ii))) + old_range = None if iter_index > 0 : for ii in range(iter_index) : + if ii == iter_index - 1: + old_range = len(init_data_sys) fp_path = os.path.join(make_iter_name(ii), fp_name) fp_data_sys = glob.glob(os.path.join(fp_path, "data.*")) for jj in fp_data_sys : @@ -275,6 +284,7 @@ def make_train (iter_index, mdata["deepmd_version"] except: mdata = set_version(mdata) + # setup data systems if LooseVersion(mdata["deepmd_version"]) < LooseVersion('1'): # 0.x jinput['systems'] = init_data_sys @@ -296,6 +306,18 @@ def make_train (iter_index, jinput['model']['fitting_net'].pop('numb_fparam', None) else: raise RuntimeError('invalid setting for use_ele_temp ' + str(use_ele_temp)) + # set training reuse model + if training_reuse_iter is not None and iter_index >= training_reuse_iter: + jinput['training']['auto_prob_style'] \ + ="prob_sys_size; 0:%d:%f; %d:%d:%f" \ + %(old_range, training_reuse_old_ratio, old_range, len(init_data_sys), 1.-training_reuse_old_ratio) + if jinput['loss'].get('start_pref_e') is not None: + jinput['loss']['start_pref_e'] = training_reuse_start_pref_e + if jinput['loss'].get('start_pref_f') is not None: + jinput['loss']['start_pref_f'] = training_reuse_start_pref_f + jinput['learning_rate']['start_lr'] = training_reuse_start_lr + jinput['training']['stop_batch'] = training_reuse_stop_batch + # set random seed for each model, dump the input.json for ii in range(numb_models) : task_path = os.path.join(work_path, train_task_fmt % ii) create_path(task_path) @@ -351,6 +373,9 @@ def run_train (iter_index, numb_models = jdata['numb_models'] # train_param = jdata['train_param'] train_input_file = default_train_input_file + training_reuse_iter = jdata.get('training_reuse_iter') + if training_reuse_iter is not None and iter_index >= training_reuse_iter: + reuse_old = True try: mdata["deepmd_version"] except: @@ -387,6 +412,8 @@ def run_train (iter_index, elif python_path: # 1.x command = '%s -m deepmd train %s' % (python_path, train_input_file) + if reuse_old: + command += ' --init-model old/model.ckpt' commands.append(command) command = '%s -m deepmd freeze' % python_path commands.append(command) @@ -399,7 +426,6 @@ def run_train (iter_index, command = '%s freeze' % train_command commands.append(command) - #_tasks = [os.path.basename(ii) for ii in all_task] # run_tasks = [] # for ii in all_task: @@ -412,6 +438,11 @@ def run_train (iter_index, run_tasks = [os.path.basename(ii) for ii in all_task] forward_files = [train_input_file] + if reuse_old: + forward_files += [os.path.join('old', 'model.ckpt.meta'), + os.path.join('old', 'model.ckpt.index'), + os.path.join('old', 'model.ckpt.data-00000-of-00001') + ] backward_files = ['frozen_model.pb', 'lcurve.out', 'train.log'] init_data_sys_ = jdata['init_data_sys'] init_data_sys = [] From 5368f334a9d7c532af70b6956b23759036ce9786 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 18 Feb 2020 12:34:40 +0800 Subject: [PATCH 054/201] bug fixing: transfer type_map.raw. enforce the the same type_map in the training input.json --- dpgen/generator/run.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 9c82210ca..eabcc6563 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -295,6 +295,7 @@ def make_train (iter_index, # 1.x jinput['training']['systems'] = init_data_sys jinput['training']['batch_size'] = init_batch_size + jinput['model']['type_map'] = jdata['type_map'] # electron temperature if use_ele_temp == 0: pass @@ -459,21 +460,21 @@ def run_train (iter_index, if jdata.get('init_multi_systems', False): for single_sys in os.listdir(os.path.join(ii)): trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'set.*')) - trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'type.raw')) + trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'type*.raw')) trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'nopbc')) else: trans_comm_data += glob.glob(os.path.join(ii, 'set.*')) - trans_comm_data += glob.glob(os.path.join(ii, 'type.raw')) + trans_comm_data += glob.glob(os.path.join(ii, 'type*.raw')) trans_comm_data += glob.glob(os.path.join(ii, 'nopbc')) for ii in fp_data : if jdata.get('use_clusters', False): for single_sys in os.listdir(os.path.join(ii)): trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'set.*')) - trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'type.raw')) + trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'type*.raw')) trans_comm_data += glob.glob(os.path.join(ii, single_sys, 'nopbc')) else: trans_comm_data += glob.glob(os.path.join(ii, 'set.*')) - trans_comm_data += glob.glob(os.path.join(ii, 'type.raw')) + trans_comm_data += glob.glob(os.path.join(ii, 'type*.raw')) trans_comm_data += glob.glob(os.path.join(ii, 'nopbc')) os.chdir(cwd) From f26b40638289098c36d7510a3b28eb22b0555fa5 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 18 Feb 2020 15:29:36 +0800 Subject: [PATCH 055/201] add unit test for reuse --- tests/generator/test_make_train.py | 46 +++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/generator/test_make_train.py b/tests/generator/test_make_train.py index c64487f55..486ca40af 100644 --- a/tests/generator/test_make_train.py +++ b/tests/generator/test_make_train.py @@ -101,7 +101,7 @@ def _check_model_input_dict(testCase, input_dict, init_data_sys, init_batch_size testCase.assertEqual(input_dict[ii], default_training_param[ii]) -def _check_model_inputs_v1(testCase, iter_idx, jdata) : +def _check_model_inputs_v1(testCase, iter_idx, jdata, reuse = False) : train_param = jdata.get('train_param', 'input.json') numb_models = jdata['numb_models'] use_ele_temp = jdata.get('use_ele_temp', 0) @@ -133,6 +133,18 @@ def _check_model_inputs_v1(testCase, iter_idx, jdata) : _check_model_input_dict(testCase, jdata0['loss'], init_data_sys, init_batch_size, default_training_param['loss']) _check_model_input_dict(testCase, jdata0['learning_rate'], init_data_sys, init_batch_size, default_training_param['learning_rate']) _check_model_input_dict(testCase, jdata0['training'], init_data_sys, init_batch_size, default_training_param['training']) + if reuse: + testCase.assertEqual(jdata['training_reuse_stop_batch'], + jdata0['training']['stop_batch']) + testCase.assertEqual(jdata['training_reuse_start_lr'], + jdata0['learning_rate']['start_lr']) + testCase.assertEqual(jdata['training_reuse_start_pref_e'], + jdata0['loss']['start_pref_e']) + testCase.assertEqual(jdata['training_reuse_start_pref_f'], + jdata0['loss']['start_pref_f']) + old_ratio = jdata['training_reuse_old_ratio'] + testCase.assertEqual(jdata0['training']['auto_prob_style'], + "prob_sys_size; 0:1:%f; 1:2:%f" % (old_ratio, 1-old_ratio)) def _make_fake_fp(iter_idx, sys_idx, nframes): @@ -235,6 +247,38 @@ def test_1_data_v1(self) : # remove testing dirs shutil.rmtree('iter.000001') shutil.rmtree('iter.000000') + + + def test_1_data_reuse_v1(self) : + with open (param_file_v1, 'r') as fp : + jdata = json.load (fp) + jdata.pop('use_ele_temp', None) + jdata['training_reuse_iter'] = 1 + jdata['training_reuse_old_ratio'] = 0.8 + jdata['training_reuse_stop_batch'] = 400000 + jdata['training_reuse_start_lr'] = 1e-4 + jdata['training_reuse_start_pref_e'] = 0.1 + jdata['training_reuse_start_pref_f'] = 100 + with open (machine_file_v1, 'r') as fp: + mdata = json.load (fp) + make_train(0, jdata, mdata) + # make fake fp results #data == fp_task_min + _make_fake_fp(0, 0, jdata['fp_task_min']) + # make iter1 train + make_train(1, jdata, mdata) + # check data is linked + self.assertTrue(os.path.isdir(os.path.join('iter.000001', '00.train', 'data.iters', 'iter.000000', '02.fp'))) + # check old models are linked + self.assertTrue(os.path.isdir(os.path.join('iter.000001', '00.train', '000', 'old'))) + self.assertTrue(os.path.isdir(os.path.join('iter.000001', '00.train', '001', 'old'))) + self.assertTrue(os.path.isdir(os.path.join('iter.000001', '00.train', '002', 'old'))) + self.assertTrue(os.path.isdir(os.path.join('iter.000001', '00.train', '003', 'old'))) + # check models inputs + _check_model_inputs_v1(self, 1, jdata, reuse = True) + # remove testing dirs + shutil.rmtree('iter.000001') + shutil.rmtree('iter.000000') + def test_1_data_v1_eletron_temp(self) : with open (param_file_v1_et, 'r') as fp : From 7afdc02a728d534fdff3f6f1aab8b7b0a4aa243c Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 19 Feb 2020 14:48:13 +0800 Subject: [PATCH 056/201] support aniso kspacing for vasp --- README.md | 3 ++- dpgen/auto_test/lib/vasp.py | 4 +++- dpgen/generator/run.py | 12 ++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c90f84187..65d0fa588 100644 --- a/README.md +++ b/README.md @@ -497,7 +497,8 @@ The bold notation of key (such aas **type_map**) means that it's a necessary key | *fp_style == VASP* | **fp_pp_path** | String | "/sharedext4/.../ch4/" | Directory of psuedo-potential file to be used for 02.fp exists. | | **fp_pp_files** | List of string | ["POTCAR"] | Psuedo-potential file to be used for 02.fp. Note that the order of elements should correspond to the order in `type_map`. | -|**fp_incar** | String | "/sharedext4/../ch4/INCAR" | Input file for VASP. INCAR must specify KSPACING. +|**fp_incar** | String | "/sharedext4/../ch4/INCAR" | Input file for VASP. INCAR must specify KSPACING and KGAMMA. +|**fp_aniso_kspacing** | List of integer | [1.0,1.0,1.0] | Set anisotropic kspacing. Usually useful for 1-D or 2-D materials. Only support VASP. If it is setting the KSPACING key in INCAR will be ignored. |cvasp| Boolean | true | If `cvasp` is true, DP-GEN will use Custodian to help control VASP calculation. | *fp_style == Gaussian* | **use_clusters** | Boolean | false | If set to `true`, clusters will be taken instead of the whole system. This option does not work with DeePMD-kit 0.x. diff --git a/dpgen/auto_test/lib/vasp.py b/dpgen/auto_test/lib/vasp.py index f2610de82..b4e8408f0 100644 --- a/dpgen/auto_test/lib/vasp.py +++ b/dpgen/auto_test/lib/vasp.py @@ -102,6 +102,8 @@ def reciprocal_box(box) : return rbox def make_kspacing_kpoints(poscar, kspacing, kgamma) : + if type(kspacing) is not list: + kspacing = [kspacing, kspacing, kspacing] with open(poscar, 'r') as fp: lines = fp.read().split('\n') scale = float(lines[1]) @@ -111,7 +113,7 @@ def make_kspacing_kpoints(poscar, kspacing, kgamma) : box = np.array(box) box *= scale rbox = reciprocal_box(box) - kpoints = [(np.ceil(2 * np.pi * np.linalg.norm(ii) / kspacing).astype(int)) for ii in rbox] + kpoints = [(np.ceil(2 * np.pi * np.linalg.norm(ii) / ks).astype(int)) for ii,ks in zip(rbox,kspacing)] ret = make_vasp_kpoints(kpoints, kgamma) return ret diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index eabcc6563..f2cf31d91 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1237,6 +1237,7 @@ def _make_fp_pwmat_input (iter_index, def _make_fp_vasp_kp (iter_index,jdata): iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, fp_name) + fp_aniso_kspacing = jdata.get('fp_aniso_kspacing') fp_tasks = glob.glob(os.path.join(work_path, 'task.*')) fp_tasks.sort() @@ -1250,10 +1251,13 @@ def _make_fp_vasp_kp (iter_index,jdata): with open('INCAR') as fp: incar = fp.read() standard_incar = incar_upper(Incar.from_string(incar)) - try: - kspacing = standard_incar['KSPACING'] - except: - raise RuntimeError ("KSPACING must be given in INCAR") + if fp_aniso_kspacing is None: + try: + kspacing = standard_incar['KSPACING'] + except: + raise RuntimeError ("KSPACING must be given in INCAR") + else : + kspacing = fp_aniso_kspacing try: gamma = standard_incar['KGAMMA'] if isinstance(gamma,bool): From defab7f51b8e52dc2caf2b2b0fbbdf625a542c78 Mon Sep 17 00:00:00 2001 From: Manyi Yang Date: Thu, 20 Feb 2020 20:46:16 +0100 Subject: [PATCH 057/201] Added a Keyword "plm_path" --- dpgen/generator/run.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index f2cf31d91..809bf5f4e 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -698,6 +698,7 @@ def make_model_devi (iter_index, if "template" in cur_job: input_mode = "revise_template" use_plm = jdata.get('model_devi_plumed', False) + use_plm_path = jdata.get('model_devi_plumed_path', False) if input_mode == "native": _make_model_devi_native(iter_index, jdata, mdata, conf_systems) elif input_mode == "revise_template": @@ -718,6 +719,7 @@ def _make_model_devi_revmat(iter_index, jdata, mdata, conf_systems): raise RuntimeError("system index should be uniq") mass_map = jdata['mass_map'] use_plm = jdata.get('model_devi_plumed', False) + use_plm_path = jdata.get('model_devi_plumed_path', False) trj_freq = _get_param_alias(cur_job, ['t_freq', 'trj_freq','traj_freq']) rev_keys, rev_mat, num_lmp = parse_cur_job_revmat(cur_job, use_plm = use_plm) @@ -726,6 +728,9 @@ def _make_model_devi_revmat(iter_index, jdata, mdata, conf_systems): if use_plm: plm_templ = cur_job['template']['plm'] plm_templ = os.path.abspath(plm_templ) + if use_plm_path: + plm_path_templ = cur_job['template']['plm_path'] + plm_path_templ = os.path.abspath(plm_path_templ) iter_name = make_iter_name(iter_index) train_path = os.path.join(iter_name, train_name) @@ -777,6 +782,8 @@ def _make_model_devi_revmat(iter_index, jdata, mdata, conf_systems): plm_lines = revise_by_keys(plm_lines, rev_keys[num_lmp:], rev_item[num_lmp:]) with open('input.plumed', 'w') as fp: fp.write(''.join(plm_lines)) + if use_plm_path: + shutil.copyfile(plm_path_templ, 'mypath.pdb') # dump input of lammps with open('input.lammps', 'w') as fp: fp.write(''.join(lmp_lines)) @@ -915,6 +922,7 @@ def run_model_devi (iter_index, model_devi_group_size = mdata['model_devi_group_size'] model_devi_resources = mdata['model_devi_resources'] use_plm = jdata.get('model_devi_plumed', False) + use_plm_path = jdata.get('model_devi_plumed_path', False) iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, model_devi_name) @@ -947,7 +955,10 @@ def run_model_devi (iter_index, backward_files = ['model_devi.out', 'model_devi.log', 'traj'] if use_plm: forward_files += ['input.plumed'] - backward_files += ['output.plumed'] + # backward_files += ['output.plumed'] + backward_files += ['output.plumed','COLVAR','dump.0.xyz'] + if use_plm_path: + forward_files += ['mypath.pdb'] cwd = os.getcwd() dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], work_path, run_tasks, model_devi_group_size) From 1f6d890ce16a201277dccf800bbd70b37b528615 Mon Sep 17 00:00:00 2001 From: Manyi Yang Date: Thu, 20 Feb 2020 20:58:13 +0100 Subject: [PATCH 058/201] Added a Keyword "plm_path" to tranfer plumed pathfile --- dpgen/generator/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 809bf5f4e..f6a68a9a6 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -783,7 +783,7 @@ def _make_model_devi_revmat(iter_index, jdata, mdata, conf_systems): with open('input.plumed', 'w') as fp: fp.write(''.join(plm_lines)) if use_plm_path: - shutil.copyfile(plm_path_templ, 'mypath.pdb') + shutil.copyfile(plm_path_templ, 'plmpath.pdb') # dump input of lammps with open('input.lammps', 'w') as fp: fp.write(''.join(lmp_lines)) @@ -958,7 +958,7 @@ def run_model_devi (iter_index, # backward_files += ['output.plumed'] backward_files += ['output.plumed','COLVAR','dump.0.xyz'] if use_plm_path: - forward_files += ['mypath.pdb'] + forward_files += ['plmpath.pdb'] cwd = os.getcwd() dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], work_path, run_tasks, model_devi_group_size) From 0b2ef8a24db0b37f4da24bc4601c1da09b7adb29 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 21 Feb 2020 07:28:53 +0800 Subject: [PATCH 059/201] fix bug of uninitialized reuse_old --- dpgen/generator/run.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index f2cf31d91..c6e2d4799 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -377,6 +377,8 @@ def run_train (iter_index, training_reuse_iter = jdata.get('training_reuse_iter') if training_reuse_iter is not None and iter_index >= training_reuse_iter: reuse_old = True + else: + reuse_old = False try: mdata["deepmd_version"] except: From 538d915185d29b78934499abc89e6b861b34069c Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 21 Feb 2020 09:02:13 +0800 Subject: [PATCH 060/201] fix bug: trasfer back checkpoints --- dpgen/generator/run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index c6e2d4799..fba85a15d 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -447,6 +447,7 @@ def run_train (iter_index, os.path.join('old', 'model.ckpt.data-00000-of-00001') ] backward_files = ['frozen_model.pb', 'lcurve.out', 'train.log'] + backward_files+= ['model.ckpt.meta', 'model.ckpt.index', 'model.ckpt.data-00000-of-00001'] init_data_sys_ = jdata['init_data_sys'] init_data_sys = [] for ii in init_data_sys_ : From d4dcaeb000e65cd2192ed31523ec0de6afb10d51 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 21 Feb 2020 09:02:25 +0800 Subject: [PATCH 061/201] set default port to 22 --- dpgen/dispatcher/SSHContext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index 95f5ff363..57ed648c6 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -11,8 +11,8 @@ def __init__ (self, jdata) : # with open(remote_profile) as fp : # self.remote_profile = json.load(fp) self.remote_host = self.remote_profile['hostname'] - self.remote_port = self.remote_profile['port'] self.remote_uname = self.remote_profile['username'] + self.remote_port = self.remote_profile.get('port', 22) self.remote_password = None if 'password' in self.remote_profile : self.remote_password = self.remote_profile['password'] From 4ce4b673d4c57417ba6f5100565e8da1ee78a1ef Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 21 Feb 2020 14:44:33 +0800 Subject: [PATCH 062/201] add machine-ali.json in examples use manual_create(stage, num) to create machine --- dpgen/dispatcher/ALI.py | 71 ++++++++++++- .../machine/DeePMD-kit-1.0/machine-ali.json | 99 +++++++++++++++++++ 2 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 examples/machine/DeePMD-kit-1.0/machine-ali.json diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index eab19f01d..b642cd42b 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -10,6 +10,11 @@ from dpgen import dlog from hashlib import sha1 +real_name = { + 'LTAI4Fo5VhHMJUcEUgazCRMf':'wanrun', + 'LTAI4FjBXrYPPTLhNGjJCUrQ':'haidi', + 'LTAI4Fr876oV6kM9GR4ziXqQ':'zhaohan', +} def manual_delete(regionID): with open('machine-ali.json') as fp1: mdata = json.load(fp1) @@ -37,15 +42,73 @@ def manual_delete(regionID): response = client.do_action_with_exception(request) os.remove('machine_record.json') +def manual_create(stage, machine_number): + with open('machine-ali.json') as fp: + mdata = json.load(fp): + adata = mdata[stage][0]['machine']['ali_auth'] + mdata_resources = mdata[stage][0]['resources'] + mdata_machine = mdata[stage][0]['machine'] + AccessKey_ID = adata["AccessKey_ID"] + AccessKey_Secret = adata["AccessKey_Secret"] + strategy = adata["pay_strategy"] + pwd = mdata_machine["password"] + regionID = adata["regionID"] + if mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s' % (mdata_resources['partition'], mdata_resources['numb_gpu'], strategy) + elif mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s' % (mdata_resources['partition'], mdata_resources['task_per_node'], strategy) + instance_name = real_name[AccessKey_ID] + '_' + adata['task_name'] + client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) + instance_list = [] + ip_list = [] + request = RunInstancesRequest() + request.set_accept_format('json') + request.set_UniqueSuffix(True) + request.set_Password(pwd) + request.set_InstanceName(instance_name) + request.set_LaunchTemplateName(template_name) + request.set_Amount(machine_number) + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + instance_list.append(instanceID) + time.sleep(60) + request = DescribeInstancesRequest() + request.set_accept_format('json') + if len(instance_list) <= 10: + for i in range(len(instance_list)): + request.set_InstanceIds([instance_list[i]]) + response = client.do_action_with_exception(request) + response = json.loads(response) + ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + else: + iteration = len(instance_list) // 10 + for i in range(iteration): + for j in range(10): + request.set_InstanceIds([instance_list[i*10+j]]) + response = client.do_action_with_exception(request) + response = json.loads(response) + ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + if len(instance_list) - iteration * 10 != 0: + for j in range(len(instance_list) - iteration * 10): + request.set_InstanceIds([instance_list[iteration*10+j]]) + response = client.do_action_with_exception(request) + response = json.loads(response) + ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + for ip in ip_list: + print(ip) + with open('machine_record.json', 'w') as fp: + json.dump({'ip': ip_list, 'instance_id': instance_list}, fp, indent=4) + class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = None self.instance_list = None - self.regionID = mdata_machine["regionID"] self.dispatchers = None self.job_handlers = None self.task_chunks = None self.adata = adata + self.regionID = adata["regionID"] self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine self.nchunks = nchunks @@ -165,13 +228,13 @@ def create_machine(self): AccessKey_ID = self.adata["AccessKey_ID"] AccessKey_Secret = self.adata["AccessKey_Secret"] strategy = self.adata["pay_strategy"] - pwd = self.adata["password"] + pwd = self.mdata_machine["password"] regionID = self.regionID if self.mdata_resources['partition'] == 'gpu': template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) elif self.mdata_resources['partition'] == 'cpu': template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy) - instance_name = self.adata["instance_name"] + instance_name = real_name[AccessKey_ID] + '_' + self.adata['task_name'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) self.instance_list = [] self.ip_list = [] @@ -251,7 +314,7 @@ def create_machine(self): def delete_machine(self): AccessKey_ID = self.adata["AccessKey_ID"] AccessKey_Secret = self.adata["AccessKey_Secret"] - regionID = self.mdata_machine['regionID'] + regionID = self.regionID client = AcsClient(AccessKey_ID,AccessKey_Secret, regionID) request = DeleteInstancesRequest() request.set_accept_format('json') diff --git a/examples/machine/DeePMD-kit-1.0/machine-ali.json b/examples/machine/DeePMD-kit-1.0/machine-ali.json new file mode 100644 index 000000000..584f6ba21 --- /dev/null +++ b/examples/machine/DeePMD-kit-1.0/machine-ali.json @@ -0,0 +1,99 @@ +{ + "train": [ + { + "machine": { + "batch": "shell", + "hostname": "", + "password": "set you pwd here", + "port": 22, + "username": "root", + "work_path": "/root/dpgen_work", + "ali_auth": { + "AccessKey_ID":"", + "AccessKey_Secret":"", + "regionID": "cn-beijing", + "task_name": "CH4", + "pay_strategy": "spot", + } + }, + "resources": { + "numb_gpu": 1, + "numb_node": 1, + "task_per_node": 12, + "partition": "gpu", + "exclude_list": [], + "mem_limit": 32, + "source_list": [], + "module_list": [], + "time_limit": "23:0:0" + }, + "command": "/root/deepmd-kit/bin/dp", + "group_size": 2 + } + ], + + "model_devi": [ + { + "machine": { + "batch": "shell", + "hostname": "", + "password": "set your pwd here", + "port": 22, + "username": "root", + "work_path": "/root/dpgen_work", + "ali_auth": { + "AccessKey_ID":"", + "AccessKey_Secret":"", + "regionID": "cn-beijing", + "instance_name": "CH4", + "pay_strategy": "spot", + } + }, + "resources": { + "numb_gpu": 1, + "task_per_node": 4, + "partition": "gpu", + "exclude_list": [], + "mem_limit": 11, + "source_list": [], + "module_list": [], + "time_limit": "23:0:0" + }, + "command": "/root/deepmd-kit/bin/lmp", + "group_size": 2 + } + ], + + "fp": [ + { + "machine": { + "batch": "shell", + "hostname": "", + "password": "set your pwd here", + "port": 22, + "username": "root", + "work_path": "/root/dpgen_work", + "ali_auth": { + "AccessKey_ID":"", + "AccessKey_Secret":"", + "regionID": "cn-huhehaote", + "instance_name": "CH4", + "pay_strategy": "spot", + } + }, + "resources": { + "numb_gpu": 0, + "task_per_node": 16, + "with_mpi": "false", + "source_list": ["/opt/intel/parallel_studio_xe_2018/psxevars.sh"], + "module_list": [], + "partition": "cpu", + "envs" : {"PATH" : "/root/deepmd-pkg/vasp.5.4.4/bin:$PATH"} + }, + "command": "mpirun -n 16 /root/deepmd-pkg/vasp.5.4.4/bin/vasp_std", + "group_size": 1 + } + ] +} + + From 4b7329d7f20af61b01d244395eb988b729eb63a3 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 21 Feb 2020 18:24:33 +0800 Subject: [PATCH 063/201] add users --- dpgen/dispatcher/ALI.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index b642cd42b..758f728f5 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -12,8 +12,11 @@ real_name = { 'LTAI4Fo5VhHMJUcEUgazCRMf':'wanrun', - 'LTAI4FjBXrYPPTLhNGjJCUrQ':'haidi', - 'LTAI4Fr876oV6kM9GR4ziXqQ':'zhaohan', + 'LTAI4FeGeyPyxWiVZbTCS6Yq':'wanghan', + 'LTAI4FmDHRx5aF1JcqMyquUW':'haidi', + 'LTAI4FxNpH6hqE49XTmSzx8M':'zhaohan', + 'LTAI4FdwHX2gfSbrPSDz3KKe':'linfeng', + 'LTAI4FwFYYBi7MZs5KqSMzJf':'yuzhi', } def manual_delete(regionID): with open('machine-ali.json') as fp1: From 1b7ff1ce00a516d9abfc7ec4d876f2498f9f7a16 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 21 Feb 2020 19:22:48 +0800 Subject: [PATCH 064/201] add users --- dpgen/generator/run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index f2cf31d91..683a54706 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -375,6 +375,7 @@ def run_train (iter_index, # train_param = jdata['train_param'] train_input_file = default_train_input_file training_reuse_iter = jdata.get('training_reuse_iter') + reuse_old = False if training_reuse_iter is not None and iter_index >= training_reuse_iter: reuse_old = True try: From b6054cd5efc3bcd628f2151ea4219b092925b2d1 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 21 Feb 2020 19:53:19 +0800 Subject: [PATCH 065/201] add example machine-ali.json, add function manual_create to create machine, you need to provide machine-ali.json and stage(train, model_devi, fp), you should call this function like this: from dpgen.dispatcher.ALI import manual_create manual_create('train') --- dpgen/dispatcher/ALI.py | 9 +++++---- examples/machine/DeePMD-kit-1.0/machine-ali.json | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 758f728f5..902c44873 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -18,11 +18,12 @@ 'LTAI4FdwHX2gfSbrPSDz3KKe':'linfeng', 'LTAI4FwFYYBi7MZs5KqSMzJf':'yuzhi', } -def manual_delete(regionID): +def manual_delete(stage): with open('machine-ali.json') as fp1: mdata = json.load(fp1) - AccessKey_ID = mdata['train'][0]['machine']['ali_auth']['AccessKey_ID'] - AccessKey_Secret = mdata['train'][0]['machine']['ali_auth']['AccessKey_Secret'] + AccessKey_ID = mdata[stage][0]['machine']['ali_auth']['AccessKey_ID'] + AccessKey_Secret = mdata[stage][0]['machine']['ali_auth']['AccessKey_Secret'] + regionID = mdata[stage][0]['machine']['ali_auth']['regionID'] with open('machine_record.json', 'r') as fp2: machine_record = json.load(fp2) instance_list = machine_record['instance_id'] @@ -47,7 +48,7 @@ def manual_delete(regionID): def manual_create(stage, machine_number): with open('machine-ali.json') as fp: - mdata = json.load(fp): + mdata = json.load(fp) adata = mdata[stage][0]['machine']['ali_auth'] mdata_resources = mdata[stage][0]['resources'] mdata_machine = mdata[stage][0]['machine'] diff --git a/examples/machine/DeePMD-kit-1.0/machine-ali.json b/examples/machine/DeePMD-kit-1.0/machine-ali.json index 584f6ba21..df2c1650a 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-ali.json +++ b/examples/machine/DeePMD-kit-1.0/machine-ali.json @@ -4,7 +4,7 @@ "machine": { "batch": "shell", "hostname": "", - "password": "set you pwd here", + "password": "set you pwd here(example:123ABC!)", "port": 22, "username": "root", "work_path": "/root/dpgen_work", @@ -13,7 +13,7 @@ "AccessKey_Secret":"", "regionID": "cn-beijing", "task_name": "CH4", - "pay_strategy": "spot", + "pay_strategy": "spot" } }, "resources": { @@ -45,8 +45,8 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", - "instance_name": "CH4", - "pay_strategy": "spot", + "task_name": "CH4", + "pay_strategy": "spot" } }, "resources": { @@ -77,8 +77,8 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-huhehaote", - "instance_name": "CH4", - "pay_strategy": "spot", + "task_name": "CH4", + "pay_strategy": "spot" } }, "resources": { From 13530c8269eecac3d2ba96138173467487d50749 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 21 Feb 2020 21:06:47 +0800 Subject: [PATCH 066/201] fix bug --- dpgen/dispatcher/ALI.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 902c44873..09a354ccf 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -10,14 +10,6 @@ from dpgen import dlog from hashlib import sha1 -real_name = { - 'LTAI4Fo5VhHMJUcEUgazCRMf':'wanrun', - 'LTAI4FeGeyPyxWiVZbTCS6Yq':'wanghan', - 'LTAI4FmDHRx5aF1JcqMyquUW':'haidi', - 'LTAI4FxNpH6hqE49XTmSzx8M':'zhaohan', - 'LTAI4FdwHX2gfSbrPSDz3KKe':'linfeng', - 'LTAI4FwFYYBi7MZs5KqSMzJf':'yuzhi', -} def manual_delete(stage): with open('machine-ali.json') as fp1: mdata = json.load(fp1) @@ -61,7 +53,7 @@ def manual_create(stage, machine_number): template_name = '%s_%s_%s' % (mdata_resources['partition'], mdata_resources['numb_gpu'], strategy) elif mdata_resources['partition'] == 'cpu': template_name = '%s_%s_%s' % (mdata_resources['partition'], mdata_resources['task_per_node'], strategy) - instance_name = real_name[AccessKey_ID] + '_' + adata['task_name'] + instance_name = adata['instance_name'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) instance_list = [] ip_list = [] @@ -238,7 +230,7 @@ def create_machine(self): template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) elif self.mdata_resources['partition'] == 'cpu': template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy) - instance_name = real_name[AccessKey_ID] + '_' + self.adata['task_name'] + instance_name = self.adata['instance_name'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) self.instance_list = [] self.ip_list = [] From 3a2cce98d6e116472c334aa7542160e16c37d23d Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 21 Feb 2020 21:07:53 +0800 Subject: [PATCH 067/201] fix bug --- examples/machine/DeePMD-kit-1.0/machine-ali.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/machine/DeePMD-kit-1.0/machine-ali.json b/examples/machine/DeePMD-kit-1.0/machine-ali.json index df2c1650a..91da2f0a5 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-ali.json +++ b/examples/machine/DeePMD-kit-1.0/machine-ali.json @@ -12,7 +12,7 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", - "task_name": "CH4", + "instance_name": "CH4", "pay_strategy": "spot" } }, @@ -45,7 +45,7 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", - "task_name": "CH4", + "instance_name": "CH4", "pay_strategy": "spot" } }, @@ -77,7 +77,7 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-huhehaote", - "task_name": "CH4", + "instance_name": "CH4", "pay_strategy": "spot" } }, From b244958ff997d2064c24ec9385d72b58fcaf68ae Mon Sep 17 00:00:00 2001 From: zhaohan <32747623+dingzhaohan@users.noreply.github.com> Date: Fri, 21 Feb 2020 21:16:36 +0800 Subject: [PATCH 068/201] Update run.py --- dpgen/generator/run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 32ea9f3ef..16698fcd2 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -375,7 +375,6 @@ def run_train (iter_index, # train_param = jdata['train_param'] train_input_file = default_train_input_file training_reuse_iter = jdata.get('training_reuse_iter') - reuse_old = False if training_reuse_iter is not None and iter_index >= training_reuse_iter: reuse_old = True else: From 5b4e4bcf9741285441d1c2756a459be8a98ae2b3 Mon Sep 17 00:00:00 2001 From: AnguseZhang <529133328@qq.con> Date: Tue, 3 Mar 2020 14:27:05 +0800 Subject: [PATCH 069/201] update surf.py ; recover key z_min --- README.md | 1 + dpgen/data/surf.py | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0eeaf2677..92ffa05d5 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,7 @@ The bold notation of key (such as **Elements**) means that it's a necessary key. | **cell_type** | String | "hcp" | Specifying which typical structure to be generated. **Options** include fcc, hcp, bcc, sc, diamond. | **latt** | Float | 4.479 | Lattice constant for single cell. | **layer_numb** | Integer | 3 | Number of equavilent layers of slab. +| **z__min** | Float | 9.0 | Thickness of slab without vacuum (Angstrom). If the `layer_numb` and `z_min` are all setted, the `z_min` value will be ignored. | **vacuum_max** | Float | 9 | Maximal thickness of vacuum (Angstrom). | **vacuum_resol** | List of float | [0.5, 1 ] | Interval of thichness of vacuum. If size of `vacuum_resol` is 1, the interval is fixed to its value. If size of `vacuum_resol` is 2, the interval is `vacuum_resol[0]` before `mid_point`, otherwise `vacuum_resol[1]` after `mid_point`. | **millers** | List of list of Integer | [[1,0,0]] | Miller indices. diff --git a/dpgen/data/surf.py b/dpgen/data/surf.py index 78c6f918c..304403229 100644 --- a/dpgen/data/surf.py +++ b/dpgen/data/surf.py @@ -59,6 +59,8 @@ def replace (file_name, pattern, subst) : global_dirname_03 = '01.scale_pert' global_dirname_04 = '02.md' +max_layer_numb = 50 + def out_dir_name(jdata) : super_cell = jdata['super_cell'] @@ -232,8 +234,14 @@ def make_super_cell_pymatgen (jdata) : all_millers = jdata['millers'] path_sc = os.path.join(out_dir, global_dirname_02) - #z_min = jdata['z_min'] - layer_numb = jdata['layer_numb'] + + user_layer_numb = None # set default value + z_min = None + if 'layer_numb' in jdata: + user_layer_numb = jdata['layer_numb'] + else: + z_min = jdata['z_min'] + super_cell = jdata['super_cell'] cwd = os.getcwd() @@ -247,7 +255,16 @@ def make_super_cell_pymatgen (jdata) : path_cur_surf = create_path('surf-'+miller_str) os.chdir(path_cur_surf) #slabgen = SlabGenerator(ss, miller, z_min, 1e-3) - slab=general_surface.surface(ss,indices=miller,vacuum=1e-3,layers=layer_numb) + if user_layer_numb: + slab=general_surface.surface(ss,indices=miller,vacuum=1e-3,layers=user_layer_numb) + else: + # build slab according to z_min value + for layer_numb in range( 1,max_layer_numb+1): + slab=general_surface.surface(ss,indices=miller,vacuum=1e-3,layers=layer_numb) + if slab.cell.get_bravais_lattice().c >= z_min: + break + if layer_numb == max_layer_numb: + raise RuntimeError("can't build the required slab") #all_slabs = slabgen.get_slabs() dlog.info(os.getcwd()) #dlog.info("Miller %s: The slab has %s termination, use the first one" %(str(miller), len(all_slabs))) From 98207c163b01b4a5580c51f46d36a9ca32c0f7d0 Mon Sep 17 00:00:00 2001 From: haidi Date: Tue, 3 Mar 2020 15:02:56 +0800 Subject: [PATCH 070/201] fix bug for surf.py --- dpgen/data/surf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/data/surf.py b/dpgen/data/surf.py index 304403229..95b8e9156 100644 --- a/dpgen/data/surf.py +++ b/dpgen/data/surf.py @@ -261,7 +261,7 @@ def make_super_cell_pymatgen (jdata) : # build slab according to z_min value for layer_numb in range( 1,max_layer_numb+1): slab=general_surface.surface(ss,indices=miller,vacuum=1e-3,layers=layer_numb) - if slab.cell.get_bravais_lattice().c >= z_min: + if slab.cell.lengths()[-1] >= z_min: break if layer_numb == max_layer_numb: raise RuntimeError("can't build the required slab") From bdb9ef014ee1b41e81f9adea252436c3e28dc730 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Thu, 5 Mar 2020 21:30:46 +0800 Subject: [PATCH 071/201] change job status check interval to 60s --- dpgen/dispatcher/Dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 00ded21c2..fc2dddf36 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -87,7 +87,7 @@ def run_jobs(self, outlog, errlog) while not self.all_finished(job_handler) : - time.sleep(10) + time.sleep(60) # delete path map file when job finish # _pmap.delete() From ef6d84165c31183f4c9519f3a084e2f1b9218992 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 6 Mar 2020 15:07:58 +0800 Subject: [PATCH 072/201] fix bug for vol_path.sort --- dpgen/auto_test/cmpt_01_eos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/auto_test/cmpt_01_eos.py b/dpgen/auto_test/cmpt_01_eos.py index f5670c422..385475059 100755 --- a/dpgen/auto_test/cmpt_01_eos.py +++ b/dpgen/auto_test/cmpt_01_eos.py @@ -12,7 +12,7 @@ def comput_lmp_eos(jdata,conf_dir, task_name) : conf_path = os.path.abspath(conf_path) conf_path = os.path.join(conf_path, task_name) vol_paths = glob.glob(os.path.join(conf_path, 'vol-*')) - vol_paths.sort() + vol_paths.sort(key=lambda k : float(k.split('-')[-1])) result = os.path.join(conf_path,'result') print('Vpa(A^3)\tEpA(eV)') with open(result,'w') as fp: @@ -38,7 +38,7 @@ def comput_vasp_eos(jdata, conf_dir) : vasp_str='vasp-k%.2f' % kspacing task_path = os.path.join(conf_path, vasp_str) vol_paths = glob.glob(os.path.join(task_path, 'vol-*')) - vol_paths.sort() + vol_paths.sort(key=lambda k : float(k.split('-')[-1])) result = os.path.join(task_path,'result') print('Vpa(A^3)\tEpA(eV)') with open(result,'w') as fp: From b836164907ad5eec04deb6c7dbb773a0aab526b7 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 6 Mar 2020 23:52:49 +0800 Subject: [PATCH 073/201] support multi region --- dpgen/dispatcher/ALI.py | 126 +++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 27 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 09a354ccf..0d61c5ee3 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -50,9 +50,9 @@ def manual_create(stage, machine_number): pwd = mdata_machine["password"] regionID = adata["regionID"] if mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s' % (mdata_resources['partition'], mdata_resources['numb_gpu'], strategy) + template_name = '%s_%s_%s_%s_%s' % (mdata_resources['partition'], mdata_resources['numb_gpu'], strategy, adata["avail_district"][0], adata["avail_instance_type"][0]) elif mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s' % (mdata_resources['partition'], mdata_resources['task_per_node'], strategy) + template_name = '%s_%s_%s_%s_%s' % (mdata_resources['partition'], mdata_resources['task_per_node'], strategy, adata["avail_district"][0], adata["avail_instance_type"][0]) instance_name = adata['instance_name'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) instance_list = [] @@ -226,10 +226,6 @@ def create_machine(self): strategy = self.adata["pay_strategy"] pwd = self.mdata_machine["password"] regionID = self.regionID - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy) instance_name = self.adata['instance_name'] client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) self.instance_list = [] @@ -239,45 +235,121 @@ def create_machine(self): request.set_UniqueSuffix(True) request.set_Password(pwd) request.set_InstanceName(instance_name) - request.set_LaunchTemplateName(template_name) - if self.nchunks <= 100: + if self.nchunks <= 10: request.set_Amount(self.nchunks) try: + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) + request.set_LaunchTemplateName(template_name) response = client.do_action_with_exception(request) response = json.loads(response) for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: self.instance_list.append(instanceID) except: - dlog.info("Create failed, please check the console.") - if len(self.instance_list) > 0: - self.delete_machine() - exit() + for ii in range(len(self.adata["avail_district"])): + flag = False + for jj in range(1, len(self.adata["avail_instance_type"])): + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) + request.set_LaunchTemplateName(template_name) + try: + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + flag = True + break + except: + if ii == len(self.adata["avail_district"])-1 and jj == len(self.adata["avail_instance_type"]) - 1: + dlog.info("create_failed, please check the console") + if len(self.instance_list) > 0: + self.delete_machine() + exit() + else: + pass + if flag: + break else: - iteration = self.nchunks // 100 - try: - for i in range(iteration): - request.set_Amount(100) + iteration = self.nchunks // 10 + for i in range(iteration): + request.set_Amount(10) + try: + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) + request.set_LaunchTemplateName(template_name) response = client.do_action_with_exception(request) response = json.loads(response) for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: self.instance_list.append(instanceID) - except: - dlog.info("Create failed, please check the console.") - if len(self.instance_list) > 0: - self.delete_machine() - exit() - if self.nchunks - iteration * 100 != 0: + except: + for ii in range(len(self.adata["avail_district"])): + flag = False + for jj in range(1, len(self.adata["avail_instance_type"])): + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) + request.set_LaunchTemplateName(template_name) + try: + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + flag = True + break + except: + if ii == len(self.adata["avail_district"]) - 1 and jj == len(self.adata["avail_instance_type"]) - 1: + dlog.info("create_failed, please check the console") + if len(self.instance_list) > 0: + self.delete_machine() + exit() + else: + pass + if flag: + break + if self.nchunks - iteration * 10 != 0: try: - request.set_Amount(self.nchunks - iteration * 100) + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) + request.set_LaunchTemplateName(template_name) response = client.do_action_with_exception(request) response = json.loads(response) for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: self.instance_list.append(instanceID) except: - dlog.info("Create failed, please check the console.") - if len(self.instance_list) > 0: - self.delete_machine() - exit() + for ii in range(len(self.adata["avail_district"])): + flag = False + for jj in range(1, len(self.adata["avail_instance_type"])): + if self.mdata_resources['partition'] == 'gpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) + elif self.mdata_resources['partition'] == 'cpu': + template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) + request.set_LaunchTemplateName(template_name) + try: + response = client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + flag = True + break + except: + if ii == len(self.adata["avail_district"]) - 1 and jj == len(self.adata["avail_instance_type"]) - 1: + dlog.info("create_failed, please check the console") + if len(self.instance_list) > 0: + self.delete_machine() + exit() + else: + pass + if flag: + break time.sleep(60) request = DescribeInstancesRequest() request.set_accept_format('json') From 3f24f817fc0bd9ba7aae8e96fbac1342aafe0c7b Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 6 Mar 2020 23:55:50 +0800 Subject: [PATCH 074/201] update machine example --- examples/machine/DeePMD-kit-1.0/machine-ali.json | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/machine/DeePMD-kit-1.0/machine-ali.json b/examples/machine/DeePMD-kit-1.0/machine-ali.json index 91da2f0a5..f226c0076 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-ali.json +++ b/examples/machine/DeePMD-kit-1.0/machine-ali.json @@ -4,7 +4,7 @@ "machine": { "batch": "shell", "hostname": "", - "password": "set you pwd here(example:123ABC!)", + "password": "", "port": 22, "username": "root", "work_path": "/root/dpgen_work", @@ -12,6 +12,8 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", + "avail_district":["H"], + "avail_instance_type":["v100"], "instance_name": "CH4", "pay_strategy": "spot" } @@ -37,7 +39,7 @@ "machine": { "batch": "shell", "hostname": "", - "password": "set your pwd here", + "password": "", "port": 22, "username": "root", "work_path": "/root/dpgen_work", @@ -45,6 +47,8 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", + "avail_district":["H"], + "avail_instance_type":["v100"], "instance_name": "CH4", "pay_strategy": "spot" } @@ -69,7 +73,7 @@ "machine": { "batch": "shell", "hostname": "", - "password": "set your pwd here", + "password": "", "port": 22, "username": "root", "work_path": "/root/dpgen_work", @@ -77,6 +81,8 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-huhehaote", + "avail_district":["A","B"], + "avail_instance_type":["c5","c6"], "instance_name": "CH4", "pay_strategy": "spot" } @@ -87,7 +93,7 @@ "with_mpi": "false", "source_list": ["/opt/intel/parallel_studio_xe_2018/psxevars.sh"], "module_list": [], - "partition": "cpu", + "partition": "cpu", "envs" : {"PATH" : "/root/deepmd-pkg/vasp.5.4.4/bin:$PATH"} }, "command": "mpirun -n 16 /root/deepmd-pkg/vasp.5.4.4/bin/vasp_std", @@ -97,3 +103,4 @@ } + From cf7282089cf6be2f545334a1ce159c135137ca73 Mon Sep 17 00:00:00 2001 From: haidi Date: Sat, 7 Mar 2020 22:15:52 +0800 Subject: [PATCH 075/201] fix cvasp bug & update vasp_make_kp --- dpgen/auto_test/gen_00_equi.py | 5 +++ dpgen/dispatcher/Shell.py | 4 +- dpgen/dispatcher/Slurm.py | 4 +- dpgen/generator/run.py | 37 ++++++++++++++----- examples/run/dp-lammps-vasp/CH4/POT_C | 1 + examples/run/dp-lammps-vasp/CH4/POT_H | 1 + .../run/dp-lammps-vasp/CH4/param_CH4.json | 3 +- 7 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 examples/run/dp-lammps-vasp/CH4/POT_C create mode 100644 examples/run/dp-lammps-vasp/CH4/POT_H diff --git a/dpgen/auto_test/gen_00_equi.py b/dpgen/auto_test/gen_00_equi.py index 6f02c7096..7ae2572cc 100755 --- a/dpgen/auto_test/gen_00_equi.py +++ b/dpgen/auto_test/gen_00_equi.py @@ -5,6 +5,11 @@ import numpy as np import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps +# +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_kspacing_kpoints +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') global_task_name = '00.equi' diff --git a/dpgen/dispatcher/Shell.py b/dpgen/dispatcher/Shell.py index 81bff523c..2beb6ff87 100644 --- a/dpgen/dispatcher/Shell.py +++ b/dpgen/dispatcher/Shell.py @@ -91,9 +91,9 @@ def sub_script_cmd(self, _cmd = cmd.split('1>')[0].strip() if cvasp : if res['with_mpi']: - _cmd = 'python ../cvasp.py "mpirun -n %d %s %s" %s' % (res['task_per_node'], _cmd, arg, fp_max_errors) + _cmd = 'python cvasp.py "mpirun -n %d %s %s" %s' % (res['task_per_node'], _cmd, arg, fp_max_errors) else : - _cmd = 'python ../cvasp.py "%s %s" %s' % (_cmd, arg, fp_max_errors) + _cmd = 'python cvasp.py "%s %s" %s' % (_cmd, arg, fp_max_errors) else : if res['with_mpi']: _cmd = 'mpirun -n %d %s %s' % (res['task_per_node'], _cmd, arg) diff --git a/dpgen/dispatcher/Slurm.py b/dpgen/dispatcher/Slurm.py index 2852a2cce..f8bf91876 100644 --- a/dpgen/dispatcher/Slurm.py +++ b/dpgen/dispatcher/Slurm.py @@ -133,9 +133,9 @@ def sub_script_cmd(self, _cmd = cmd.split('1>')[0].strip() if cvasp : if res['with_mpi']: - _cmd = 'python ../cvasp.py "srun %s %s" %s' % (_cmd, arg, fp_max_errors) + _cmd = 'python cvasp.py "srun %s %s" %s' % (_cmd, arg, fp_max_errors) else : - _cmd = 'python ../cvasp.py "%s %s" %s' % (_cmd, arg, fp_max_errors) + _cmd = 'python cvasp.py "%s %s" %s' % (_cmd, arg, fp_max_errors) else : if res['with_mpi']: _cmd = 'srun %s %s' % (_cmd, arg) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 16698fcd2..aace20e25 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1248,6 +1248,25 @@ def _make_fp_pwmat_input (iter_index, os.system("sed -i '1,2c 4 1' etot.input") os.chdir(cwd) +def _copy_cvasp(iter_index,jdata): + # Move cvasp interface to jdata + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + pass + else: + return + iter_name = make_iter_name(iter_index) + work_path = os.path.join(iter_name, fp_name) + fp_tasks = glob.glob(os.path.join(work_path, 'task.*')) + fp_tasks.sort() + if len(fp_tasks) == 0 : + return + cwd = os.getcwd() + for ii in fp_tasks: + os.chdir(ii) + #copy cvasp.py + shutil.copyfile(cvasp_file, 'cvasp.py') + os.chdir(cwd) + def _make_fp_vasp_kp (iter_index,jdata): iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, fp_name) @@ -1268,7 +1287,7 @@ def _make_fp_vasp_kp (iter_index,jdata): if fp_aniso_kspacing is None: try: kspacing = standard_incar['KSPACING'] - except: + except KeyError: raise RuntimeError ("KSPACING must be given in INCAR") else : kspacing = fp_aniso_kspacing @@ -1281,7 +1300,7 @@ def _make_fp_vasp_kp (iter_index,jdata): gamma=True else: gamma=False - except: + except KeyError: raise RuntimeError ("KGAMMA must be given in INCAR") # check poscar assert(os.path.exists('POSCAR')) @@ -1367,10 +1386,6 @@ def _make_fp_vasp_configs(iter_index, work_path = os.path.join(iter_name, fp_name) create_path(work_path) - #copy cvasp.py - # Move cvasp interface to jdata - if ('cvasp' in jdata) and (jdata['cvasp'] == True): - shutil.copyfile(cvasp_file, os.path.join(work_path,'cvasp.py')) modd_path = os.path.join(iter_name, model_devi_name) task_min = -1 @@ -1410,6 +1425,8 @@ def make_fp_vasp (iter_index, _make_fp_vasp_incar(iter_index, jdata, nbands_esti = nbe) # 3, create kpoints _make_fp_vasp_kp(iter_index, jdata) + # 4, copy cvasp + _copy_cvasp(iter_index,jdata) def make_fp_pwscf(iter_index, @@ -1672,15 +1689,15 @@ def run_fp (iter_index, fp_pp_files = jdata['fp_pp_files'] if fp_style == "vasp" : - forward_files = ['POSCAR', 'INCAR', 'POTCAR'] + forward_files = ['POSCAR', 'INCAR', 'POTCAR','KPOINTS'] backward_files = ['OUTCAR','vasprun.xml'] # Move cvasp interface to jdata if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True if ('cvasp' in mdata["fp_resources"] ) and (mdata["fp_resources"]["cvasp"]==True): - #dlog.info("cvasp is on !") - forward_common_files=['cvasp.py'] - forward_files.append('KPOINTS') + dlog.info("cvasp is on !") + forward_files.append('cvasp.py') + forward_common_files=[] else: forward_common_files=[] run_fp_inner(iter_index, jdata, mdata, forward_files, backward_files, _vasp_check_fin, diff --git a/examples/run/dp-lammps-vasp/CH4/POT_C b/examples/run/dp-lammps-vasp/CH4/POT_C new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/run/dp-lammps-vasp/CH4/POT_C @@ -0,0 +1 @@ + diff --git a/examples/run/dp-lammps-vasp/CH4/POT_H b/examples/run/dp-lammps-vasp/CH4/POT_H new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/run/dp-lammps-vasp/CH4/POT_H @@ -0,0 +1 @@ + diff --git a/examples/run/dp-lammps-vasp/CH4/param_CH4.json b/examples/run/dp-lammps-vasp/CH4/param_CH4.json index 2ec6216d5..086af510d 100644 --- a/examples/run/dp-lammps-vasp/CH4/param_CH4.json +++ b/examples/run/dp-lammps-vasp/CH4/param_CH4.json @@ -88,12 +88,13 @@ ], "_comment": " 02.fp ", + "cvasp": true, "fp_style": "vasp", "shuffle_poscar": false, "fp_task_max": 20, "fp_task_min": 5, "fp_pp_path": "/gpfs/share/home/1600017784/yuzhi/methane/", - "fp_pp_files": ["POTCAR"], + "fp_pp_files": ["POT_H","POT_C"], "fp_incar" : "/gpfs/share/home/1600017784/yuzhi/methane/INCAR_methane", "_comment": " that's all " } From 95f8743f2641c06299d3d87409ae8b081c8a83bb Mon Sep 17 00:00:00 2001 From: haidi Date: Sat, 7 Mar 2020 22:34:42 +0800 Subject: [PATCH 076/201] update param_CH4.json --- examples/run/dp-lammps-vasp/CH4/param_CH4.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/run/dp-lammps-vasp/CH4/param_CH4.json b/examples/run/dp-lammps-vasp/CH4/param_CH4.json index 086af510d..f69b4652d 100644 --- a/examples/run/dp-lammps-vasp/CH4/param_CH4.json +++ b/examples/run/dp-lammps-vasp/CH4/param_CH4.json @@ -88,7 +88,7 @@ ], "_comment": " 02.fp ", - "cvasp": true, + "cvasp": false, "fp_style": "vasp", "shuffle_poscar": false, "fp_task_max": 20, From 0b0fb96366027a2aa3c19084ead39c2ee1383a9c Mon Sep 17 00:00:00 2001 From: haidi Date: Sun, 8 Mar 2020 14:22:25 +0800 Subject: [PATCH 077/201] fix bug NSW in gen.py --- dpgen/data/gen.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dpgen/data/gen.py b/dpgen/data/gen.py index 73bb8e25f..caf8571aa 100644 --- a/dpgen/data/gen.py +++ b/dpgen/data/gen.py @@ -20,7 +20,9 @@ import dpgen.data.tools.bcc as bcc import dpgen.data.tools.diamond as diamond import dpgen.data.tools.sc as sc +from dpgen.generator.lib.vasp import incar_upper from pymatgen import Structure +from pymatgen.io.vasp import Incar from dpgen.remote.decide_machine import decide_fp_machine from dpgen import ROOT_PATH from dpgen.dispatcher.Dispatcher import Dispatcher, make_dispatcher @@ -646,14 +648,11 @@ def gen_init_bulk(args) : try: md_incar = jdata['md_incar'] if os.path.isfile(md_incar): - with open(md_incar , "r") as fr: - md_incar_lines = fr.readlines() + standard_incar = incar_upper(Incar.from_file(md_incar)) nsw_flag = False - for incar_line in md_incar_lines: - line = incar_line.split() - if "NSW" in line: + if "NSW" in standard_incar: nsw_flag = True - nsw_steps = int(incar_line.split()[-1]) + nsw_steps = standard_incar['NSW'] break #dlog.info("nsw_steps is", nsw_steps) #dlog.info("md_nstep_jdata is", md_nstep_jdata) @@ -664,7 +663,7 @@ def gen_init_bulk(args) : dlog.info("MD steps in md_incar is %d"%(nsw_steps)) dlog.info("DP-GEN will use settings in md_incar!") jdata['md_nstep'] = nsw_steps - except: + except KeyError: pass ## correct element name temp_elements = [] From 5b4f81af603c03e1697031c59c0b0a142ef51ea8 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 8 Mar 2020 23:16:36 +0800 Subject: [PATCH 078/201] optimize code --- dpgen/dispatcher/ALI.py | 338 +++++++++++----------------------------- 1 file changed, 91 insertions(+), 247 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 0d61c5ee3..0b4f82eb7 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -13,30 +13,14 @@ def manual_delete(stage): with open('machine-ali.json') as fp1: mdata = json.load(fp1) - AccessKey_ID = mdata[stage][0]['machine']['ali_auth']['AccessKey_ID'] - AccessKey_Secret = mdata[stage][0]['machine']['ali_auth']['AccessKey_Secret'] - regionID = mdata[stage][0]['machine']['ali_auth']['regionID'] + adata = mdata[stage][0]['machine']['ali_auth'] + mdata_resources = mdata[stage][0]['resources'] + mdata_machine = mdata[stage][0]['machine'] + ali = ALI(adata, mdata_resources, mdata_machine, machine_number) with open('machine_record.json', 'r') as fp2: machine_record = json.load(fp2) - instance_list = machine_record['instance_id'] - client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) - request = DeleteInstancesRequest() - request.set_accept_format('json') - if len(instance_list) <= 100: - request.set_InstanceIds(instance_list) - request.set_Force(True) - response = client.do_action_with_exception(request) - else: - iteration = len(instance_list) // 100 - for i in range(iteration): - request.set_InstanceIds(instance_list[i*100:(i+1)*100]) - request.set_Force(True) - response = client.do_action_with_exception(request) - if len(instance_list) - iteration * 100 != 0: - request.set_InstanceIds(instance_list[iteration*100:]) - request.set_Force(True) - response = client.do_action_with_exception(request) - os.remove('machine_record.json') + ali.instance_list = machine_record['instance_id'] + ali.delete_machine() def manual_create(stage, machine_number): with open('machine-ali.json') as fp: @@ -44,67 +28,20 @@ def manual_create(stage, machine_number): adata = mdata[stage][0]['machine']['ali_auth'] mdata_resources = mdata[stage][0]['resources'] mdata_machine = mdata[stage][0]['machine'] - AccessKey_ID = adata["AccessKey_ID"] - AccessKey_Secret = adata["AccessKey_Secret"] - strategy = adata["pay_strategy"] - pwd = mdata_machine["password"] - regionID = adata["regionID"] - if mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (mdata_resources['partition'], mdata_resources['numb_gpu'], strategy, adata["avail_district"][0], adata["avail_instance_type"][0]) - elif mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (mdata_resources['partition'], mdata_resources['task_per_node'], strategy, adata["avail_district"][0], adata["avail_instance_type"][0]) - instance_name = adata['instance_name'] - client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) - instance_list = [] - ip_list = [] - request = RunInstancesRequest() - request.set_accept_format('json') - request.set_UniqueSuffix(True) - request.set_Password(pwd) - request.set_InstanceName(instance_name) - request.set_LaunchTemplateName(template_name) - request.set_Amount(machine_number) - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - instance_list.append(instanceID) - time.sleep(60) - request = DescribeInstancesRequest() - request.set_accept_format('json') - if len(instance_list) <= 10: - for i in range(len(instance_list)): - request.set_InstanceIds([instance_list[i]]) - response = client.do_action_with_exception(request) - response = json.loads(response) - ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - else: - iteration = len(instance_list) // 10 - for i in range(iteration): - for j in range(10): - request.set_InstanceIds([instance_list[i*10+j]]) - response = client.do_action_with_exception(request) - response = json.loads(response) - ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - if len(instance_list) - iteration * 10 != 0: - for j in range(len(instance_list) - iteration * 10): - request.set_InstanceIds([instance_list[iteration*10+j]]) - response = client.do_action_with_exception(request) - response = json.loads(response) - ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - for ip in ip_list: - print(ip) - with open('machine_record.json', 'w') as fp: - json.dump({'ip': ip_list, 'instance_id': instance_list}, fp, indent=4) + ali = ALI(adata, mdata_resources, mdata_machine, machine_number) + ali.alloc_machine() + print(ali.ip_list) class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks): - self.ip_list = None - self.instance_list = None + self.ip_list = [] + self.instance_list = [] self.dispatchers = None self.job_handlers = None self.task_chunks = None self.adata = adata self.regionID = adata["regionID"] + self.clinet = AcsClient(adata["AccessKey_ID"], adata["AccessKey_Secret"], self.regionID) self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine self.nchunks = nchunks @@ -114,7 +51,7 @@ def init(self, work_path, tasks, group_size): if self.check_restart(work_path, tasks, group_size): pass else: - self.create_machine() + self.alloc_machine() self.dispatchers = self.make_dispatchers() def check_restart(self, work_path, tasks, group_size): @@ -151,6 +88,79 @@ def check_restart(self, work_path, tasks, group_size): else: return False + def alloc_machine(self): + for district, type_num in self.adata["avail_resources"].items(): + for machine_type, number in type_num: + if self.nchunks > number: + self.nchunks -= number + self.create_machine(machine_type, number, district) + elif self.nchunks > 0: + self.create_machine(machine_type, self.nchunks, district) + self.get_ip() + + def create_machine(self, machine_type, number, district): + request = RunInstancesRequest() + request.set_accept_format('json') + request.set_UniqueSuffix(True) + request.set_Password(self.mdata_machine["password"]) + request.set_InstanceName(self.adata['instance_name']) + + if self.mdata_resources['partition'] == 'gpu': + template_name = 'gpu_%s_%s_%s_%s' % (self.mdata_resources['numb_gpu'], self.adata["pay_strategy"], district, machine_type) + elif self.mdata_resources['partition'] == 'cpu': + template_name = 'cpu_%s_%s_%s_%s' % (self.mdata_resources['task_per_node'], self.adata["pay_strategy"], district, machine_type) + request.set_LaunchTemplateName(template_name) + + if number <= 100 and number > 0: + request.set_Amount(number) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + else: + iteration = number // 100 + for i in range(iteration): + request.set_Amount(100) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + if number - iteration * 100 != 0: + request.set_Amount(number - iteration * 100) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: + self.instance_list.append(instanceID) + + def get_ip(self): + request = DescribeInstancesRequest() + request.set_accept_format('json') + if len(self.instance_list) <= 10: + for i in range(len(self.instance_list)): + request.set_InstanceIds([self.instance_list[i]]) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + else: + iteration = len(self.instance_list) // 10 + for i in range(iteration): + for j in range(10): + request.set_InstanceIds([self.instance_list[i*10+j]]) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + if len(self.instance_list) - iteration * 10 != 0: + for j in range(len(self.instance_list) - iteration * 10): + request.set_InstanceIds([self.instance_list[iteration*10+j]]) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + dlog.info('create machine successfully, following are the ip addresses') + for ip in self.ip_list: + dlog.info(ip) + with open('machine_record.json', 'w') as fp: + json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) + def run_jobs(self, resources, command, @@ -190,15 +200,11 @@ def run_jobs(self, time.sleep(10) def delete(self, ii): - AccessKey_ID = self.adata["AccessKey_ID"] - AccessKey_Secret = self.adata["AccessKey_Secret"] - regionID = self.regionID - client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) request = DeleteInstancesRequest() request.set_accept_format('json') request.set_InstanceIds([self.instance_list[ii]]) request.set_Force(True) - response = client.do_action_with_exception(request) + response = self.client.do_action_with_exception(request) self.nchunks -= 1 self.instance_list.pop(ii) self.ip_list.pop(ii) @@ -220,186 +226,24 @@ def make_dispatchers(self): dispatchers.append(disp) return dispatchers - def create_machine(self): - AccessKey_ID = self.adata["AccessKey_ID"] - AccessKey_Secret = self.adata["AccessKey_Secret"] - strategy = self.adata["pay_strategy"] - pwd = self.mdata_machine["password"] - regionID = self.regionID - instance_name = self.adata['instance_name'] - client = AcsClient(AccessKey_ID, AccessKey_Secret, regionID) - self.instance_list = [] - self.ip_list = [] - request = RunInstancesRequest() - request.set_accept_format('json') - request.set_UniqueSuffix(True) - request.set_Password(pwd) - request.set_InstanceName(instance_name) - if self.nchunks <= 10: - request.set_Amount(self.nchunks) - try: - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) - request.set_LaunchTemplateName(template_name) - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - except: - for ii in range(len(self.adata["avail_district"])): - flag = False - for jj in range(1, len(self.adata["avail_instance_type"])): - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) - request.set_LaunchTemplateName(template_name) - try: - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - flag = True - break - except: - if ii == len(self.adata["avail_district"])-1 and jj == len(self.adata["avail_instance_type"]) - 1: - dlog.info("create_failed, please check the console") - if len(self.instance_list) > 0: - self.delete_machine() - exit() - else: - pass - if flag: - break - else: - iteration = self.nchunks // 10 - for i in range(iteration): - request.set_Amount(10) - try: - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) - request.set_LaunchTemplateName(template_name) - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - except: - for ii in range(len(self.adata["avail_district"])): - flag = False - for jj in range(1, len(self.adata["avail_instance_type"])): - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) - request.set_LaunchTemplateName(template_name) - try: - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - flag = True - break - except: - if ii == len(self.adata["avail_district"]) - 1 and jj == len(self.adata["avail_instance_type"]) - 1: - dlog.info("create_failed, please check the console") - if len(self.instance_list) > 0: - self.delete_machine() - exit() - else: - pass - if flag: - break - if self.nchunks - iteration * 10 != 0: - try: - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][0], self.adata["avail_instance_type"][0]) - request.set_LaunchTemplateName(template_name) - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - except: - for ii in range(len(self.adata["avail_district"])): - flag = False - for jj in range(1, len(self.adata["avail_instance_type"])): - if self.mdata_resources['partition'] == 'gpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['numb_gpu'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) - elif self.mdata_resources['partition'] == 'cpu': - template_name = '%s_%s_%s_%s_%s' % (self.mdata_resources['partition'], self.mdata_resources['task_per_node'], strategy, self.adata["avail_district"][ii], self.adata["avail_instance_type"][jj]) - request.set_LaunchTemplateName(template_name) - try: - response = client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - flag = True - break - except: - if ii == len(self.adata["avail_district"]) - 1 and jj == len(self.adata["avail_instance_type"]) - 1: - dlog.info("create_failed, please check the console") - if len(self.instance_list) > 0: - self.delete_machine() - exit() - else: - pass - if flag: - break - time.sleep(60) - request = DescribeInstancesRequest() - request.set_accept_format('json') - if len(self.instance_list) <= 10: - for i in range(len(self.instance_list)): - request.set_InstanceIds([self.instance_list[i]]) - response = client.do_action_with_exception(request) - response = json.loads(response) - self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - else: - iteration = len(self.instance_list) // 10 - for i in range(iteration): - for j in range(10): - request.set_InstanceIds([self.instance_list[i*10+j]]) - response = client.do_action_with_exception(request) - response = json.loads(response) - self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - if len(self.instance_list) - iteration * 10 != 0: - for j in range(len(self.instance_list) - iteration * 10): - request.set_InstanceIds([self.instance_list[iteration*10+j]]) - response = client.do_action_with_exception(request) - response = json.loads(response) - self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - dlog.info('create machine successfully, following are the ip addresses') - for ip in self.ip_list: - dlog.info(ip) - with open('machine_record.json', 'w') as fp: - json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) - def delete_machine(self): - AccessKey_ID = self.adata["AccessKey_ID"] - AccessKey_Secret = self.adata["AccessKey_Secret"] - regionID = self.regionID - client = AcsClient(AccessKey_ID,AccessKey_Secret, regionID) request = DeleteInstancesRequest() request.set_accept_format('json') if len(self.instance_list) <= 100: request.set_InstanceIds(self.instance_list) request.set_Force(True) - response = client.do_action_with_exception(request) + response = self.client.do_action_with_exception(request) else: iteration = len(self.instance_list) // 100 for i in range(iteration): request.set_InstanceIds(self.instance_list[i*100:(i+1)*100]) request.set_Force(True) - response = client.do_action_with_exception(request) + response = self.client.do_action_with_exception(request) if len(self.instance_list) - iteration * 100 != 0: request.set_InstanceIds(self.instance_list[iteration*100:]) request.set_Force(True) - response = client.do_action_with_exception(request) + response = self.client.do_action_with_exception(request) self.instance_list = [] + self.ip_list = [] os.remove('machine_record.json') dlog.debug("Successfully free the machine!") From c28a2e7bc8f19fb7f2dbbc8040d598d9cbbeba53 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 8 Mar 2020 23:37:55 +0800 Subject: [PATCH 079/201] optimize code --- dpgen/dispatcher/ALI.py | 6 +-- .../machine/DeePMD-kit-1.0/machine-ali.json | 38 +++++++------------ 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 0b4f82eb7..d3ffb8dc8 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -41,12 +41,11 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.task_chunks = None self.adata = adata self.regionID = adata["regionID"] - self.clinet = AcsClient(adata["AccessKey_ID"], adata["AccessKey_Secret"], self.regionID) + self.client = AcsClient(adata["AccessKey_ID"], adata["AccessKey_Secret"], self.regionID) self.mdata_resources = mdata_resources self.mdata_machine = mdata_machine self.nchunks = nchunks - def init(self, work_path, tasks, group_size): if self.check_restart(work_path, tasks, group_size): pass @@ -90,12 +89,13 @@ def check_restart(self, work_path, tasks, group_size): def alloc_machine(self): for district, type_num in self.adata["avail_resources"].items(): - for machine_type, number in type_num: + for machine_type, number in type_num.items(): if self.nchunks > number: self.nchunks -= number self.create_machine(machine_type, number, district) elif self.nchunks > 0: self.create_machine(machine_type, self.nchunks, district) + time.sleep(60) self.get_ip() def create_machine(self, machine_type, number, district): diff --git a/examples/machine/DeePMD-kit-1.0/machine-ali.json b/examples/machine/DeePMD-kit-1.0/machine-ali.json index f226c0076..7e8e58050 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-ali.json +++ b/examples/machine/DeePMD-kit-1.0/machine-ali.json @@ -12,22 +12,16 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", - "avail_district":["H"], - "avail_instance_type":["v100"], + "avail_resources":{ + "H": {"v100": 20} + }, "instance_name": "CH4", "pay_strategy": "spot" } }, "resources": { - "numb_gpu": 1, - "numb_node": 1, - "task_per_node": 12, - "partition": "gpu", - "exclude_list": [], - "mem_limit": 32, - "source_list": [], - "module_list": [], - "time_limit": "23:0:0" + "numb_gpu": 1, + "partition": "gpu" }, "command": "/root/deepmd-kit/bin/dp", "group_size": 2 @@ -47,21 +41,16 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-beijing", - "avail_district":["H"], - "avail_instance_type":["v100"], + "avail_resources":{ + "H": {"v100": 20} + }, "instance_name": "CH4", "pay_strategy": "spot" } }, "resources": { "numb_gpu": 1, - "task_per_node": 4, - "partition": "gpu", - "exclude_list": [], - "mem_limit": 11, - "source_list": [], - "module_list": [], - "time_limit": "23:0:0" + "partition": "gpu" }, "command": "/root/deepmd-kit/bin/lmp", "group_size": 2 @@ -81,8 +70,10 @@ "AccessKey_ID":"", "AccessKey_Secret":"", "regionID": "cn-huhehaote", - "avail_district":["A","B"], - "avail_instance_type":["c5","c6"], + "avail_resources":{ + "A": {"c5": 500, "c6": 160}, + "B": {"c6": 200} + }, "instance_name": "CH4", "pay_strategy": "spot" } @@ -92,7 +83,6 @@ "task_per_node": 16, "with_mpi": "false", "source_list": ["/opt/intel/parallel_studio_xe_2018/psxevars.sh"], - "module_list": [], "partition": "cpu", "envs" : {"PATH" : "/root/deepmd-pkg/vasp.5.4.4/bin:$PATH"} }, @@ -102,5 +92,3 @@ ] } - - From 8cbb9479bcf362a91309fb3587ec2f48b5234859 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 9 Mar 2020 00:00:55 +0800 Subject: [PATCH 080/201] fix bug --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index d3ffb8dc8..5a2921117 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -16,7 +16,7 @@ def manual_delete(stage): adata = mdata[stage][0]['machine']['ali_auth'] mdata_resources = mdata[stage][0]['resources'] mdata_machine = mdata[stage][0]['machine'] - ali = ALI(adata, mdata_resources, mdata_machine, machine_number) + ali = ALI(adata, mdata_resources, mdata_machine, 0) with open('machine_record.json', 'r') as fp2: machine_record = json.load(fp2) ali.instance_list = machine_record['instance_id'] From 993ff61f7bf6365e3370b232ef0aaa2b8e9db10c Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 9 Mar 2020 00:47:38 +0800 Subject: [PATCH 081/201] fix bug --- dpgen/dispatcher/ALI.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 5a2921117..f63f56ad8 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -109,6 +109,8 @@ def create_machine(self, machine_type, number, district): template_name = 'gpu_%s_%s_%s_%s' % (self.mdata_resources['numb_gpu'], self.adata["pay_strategy"], district, machine_type) elif self.mdata_resources['partition'] == 'cpu': template_name = 'cpu_%s_%s_%s_%s' % (self.mdata_resources['task_per_node'], self.adata["pay_strategy"], district, machine_type) + elif self.mdata_resources['partition'] == 'gmx': + template_name = 'gmx_%s_%s_%s_%s' % (self.mdata_resources['numb_gpu'], self.adata["pay_strategy"], district, machine_type) request.set_LaunchTemplateName(template_name) if number <= 100 and number > 0: From e134612604357c912e3a230ea422e9342770751d Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 9 Mar 2020 16:42:26 -0400 Subject: [PATCH 082/201] check if the length of batch size list is enought --- dpgen/generator/run.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index aace20e25..b0e486af2 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -234,12 +234,18 @@ def make_train (iter_index, init_batch_size = [] if 'init_batch_size' in jdata: init_batch_size_ = list(jdata['init_batch_size']) + if len(init_data_sys_) > len(init_batch_size): + warnings.warn("The batch sizes are not enough. Assume auto for those not spefified.") + init_batch_size.extend(["auto" for aa in range(len(init_data_sys_)-len(init_batch_size))]) else: init_batch_size_ = ["auto" for aa in range(len(jdata['init_data_sys']))] if 'sys_batch_size' in jdata: sys_batch_size = jdata['sys_batch_size'] else: sys_batch_size = ["auto" for aa in range(len(jdata['sys_configs']))] + + # make sure all init_data_sys has the batch size -- for the following `zip` + assert (len(init_data_sys)_ <= len(init_batch_size)) for ii, ss in zip(init_data_sys_, init_batch_size_) : if jdata.get('init_multi_systems', False): for single_sys in os.listdir(os.path.join(work_path, 'data.init', ii)): From e98b430c26a3d9eeb023e2de2c46a5418a853c42 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 9 Mar 2020 16:58:56 -0400 Subject: [PATCH 083/201] fix typo in dpgen/generator/run.py --- dpgen/generator/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index b0e486af2..d9ac58949 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -245,7 +245,7 @@ def make_train (iter_index, sys_batch_size = ["auto" for aa in range(len(jdata['sys_configs']))] # make sure all init_data_sys has the batch size -- for the following `zip` - assert (len(init_data_sys)_ <= len(init_batch_size)) + assert (len(init_data_sys_) <= len(init_batch_size)) for ii, ss in zip(init_data_sys_, init_batch_size_) : if jdata.get('init_multi_systems', False): for single_sys in os.listdir(os.path.join(work_path, 'data.init', ii)): From 03bfeef41b8768c4d7ee2831c1d2c9ff8ff6434b Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 9 Mar 2020 17:09:21 -0400 Subject: [PATCH 084/201] Apply suggestions from code review --- dpgen/generator/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index d9ac58949..f229f3eda 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -234,7 +234,7 @@ def make_train (iter_index, init_batch_size = [] if 'init_batch_size' in jdata: init_batch_size_ = list(jdata['init_batch_size']) - if len(init_data_sys_) > len(init_batch_size): + if len(init_data_sys_) > len(init_batch_size_): warnings.warn("The batch sizes are not enough. Assume auto for those not spefified.") init_batch_size.extend(["auto" for aa in range(len(init_data_sys_)-len(init_batch_size))]) else: @@ -245,7 +245,7 @@ def make_train (iter_index, sys_batch_size = ["auto" for aa in range(len(jdata['sys_configs']))] # make sure all init_data_sys has the batch size -- for the following `zip` - assert (len(init_data_sys_) <= len(init_batch_size)) + assert (len(init_data_sys_) <= len(init_batch_size_)) for ii, ss in zip(init_data_sys_, init_batch_size_) : if jdata.get('init_multi_systems', False): for single_sys in os.listdir(os.path.join(work_path, 'data.init', ii)): From 6dbf0fe77ff5700b8ba9c3231977663b1db2d18c Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 10 Mar 2020 08:38:33 +0800 Subject: [PATCH 085/201] check bad confs --- dpgen/generator/run.py | 28 ++++++++++++++++++++++++++ tests/generator/test_check_bad_conf.py | 15 ++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/generator/test_check_bad_conf.py diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 16698fcd2..9007bf8b9 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -982,6 +982,24 @@ def post_model_devi (iter_index, mdata) : pass +def check_bad_conf(conf_name, + criteria, + fmt = 'lammps/dump'): + all_c = criteria.split(';') + sys = dpdata.System(conf_name, fmt) + assert(sys.get_nframes() == 1) + is_bad = False + for ii in all_c: + [key, value] = ii.split(':') + if key == 'length_ratio': + lengths = np.linalg.norm(sys['cells'][0], axis = 1) + ratio = np.max(lengths) / np.min(lengths) + if ratio > float(value): + is_bad = True + else: + raise RuntimeError('unknow key', key) + return is_bad + def _make_fp_vasp_inner (modd_path, work_path, model_devi_skip, @@ -1015,6 +1033,8 @@ def _make_fp_vasp_inner (modd_path, cluster_cutoff = jdata['cluster_cutoff'] if jdata.get('use_clusters', False) else None # skip save *.out if detailed_report_make_fp is False, default is True detailed_report_make_fp = jdata.get("detailed_report_make_fp", True) + # skip bad conf criteria + skip_bad_conf = jdata.get('fp_skip_bad_conf') for ss in system_index : fp_candidate = [] if detailed_report_make_fp: @@ -1081,6 +1101,7 @@ def _make_fp_vasp_inner (modd_path, for ii in fp_rest_failed: fp.write(" ".join([str(nn) for nn in ii]) + "\n") numb_task = min(fp_task_max, len(fp_candidate)) + count_bad_conf = 0 for cc in range(numb_task) : tt = fp_candidate[cc][0] ii = fp_candidate[cc][1] @@ -1088,6 +1109,11 @@ def _make_fp_vasp_inner (modd_path, conf_name = os.path.join(tt, "traj") conf_name = os.path.join(conf_name, str(ii) + '.lammpstrj') conf_name = os.path.abspath(conf_name) + if skip_bad_conf is not None: + skip = check_bad_conf(conf_name, skip_bad_conf) + if skip: + count_bad_conf += 1 + continue # link job.json job_name = os.path.join(tt, "job.json") @@ -1114,6 +1140,8 @@ def _make_fp_vasp_inner (modd_path, for pair in fp_link_files : os.symlink(pair[0], pair[1]) os.chdir(cwd) + if count_bad_conf > 0: + dlog.info("system {0:s} skipped {1:6d} bad confs, {2:6d} remains".format(ss, count_bad_conf, numb_task - count_bad_conf)) if cluster_cutoff is None: cwd = os.getcwd() for ii in fp_tasks: diff --git a/tests/generator/test_check_bad_conf.py b/tests/generator/test_check_bad_conf.py new file mode 100644 index 000000000..4c49ddb6c --- /dev/null +++ b/tests/generator/test_check_bad_conf.py @@ -0,0 +1,15 @@ +import os,sys,json,glob,shutil +import dpdata +import numpy as np +import unittest + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +__package__ = 'generator' +from .context import check_bad_conf + +class TestCheckBadConf(unittest.TestCase): + def test_length_ratio(self): + conf_bad = os.path.join('check_bad_conf', 'bad.lammpstrj') + conf_good = os.path.join('check_bad_conf', 'good.lammpstrj') + self.assertTrue(check_bad_conf(conf_bad, 'length_ratio:5')) + self.assertFalse(check_bad_conf(conf_good, 'length_ratio:5')) From 544ddbd805900a94a0249121440be46e63022fbd Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 10 Mar 2020 08:41:49 +0800 Subject: [PATCH 086/201] set counter 0. print item if counter is 0 --- dpgen/generator/run.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 4c0140882..c171b8555 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1045,6 +1045,9 @@ def _make_fp_vasp_inner (modd_path, modd_system_task.sort() cc = 0 counter = Counter() + counter['candidate'] = 0 + counter['failed'] = 0 + counter['accurate'] = 0 for tt in modd_system_task : with warnings.catch_warnings(): warnings.simplefilter("ignore") From f611c31f0f0692e5f968c03d9aac63722a4ac6cc Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 10 Mar 2020 08:55:28 +0800 Subject: [PATCH 087/201] fix bug of test --- tests/generator/check_bad_conf/bad.lammpstrj | 17 +++++++++++++++++ tests/generator/check_bad_conf/good.lammpstrj | 17 +++++++++++++++++ tests/generator/test_check_bad_conf.py | 5 +++-- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/generator/check_bad_conf/bad.lammpstrj create mode 100644 tests/generator/check_bad_conf/good.lammpstrj diff --git a/tests/generator/check_bad_conf/bad.lammpstrj b/tests/generator/check_bad_conf/bad.lammpstrj new file mode 100644 index 000000000..72219f300 --- /dev/null +++ b/tests/generator/check_bad_conf/bad.lammpstrj @@ -0,0 +1,17 @@ +ITEM: TIMESTEP +5280 +ITEM: NUMBER OF ATOMS +8 +ITEM: BOX BOUNDS xy xz yz pp pp pp +-9.8598738203143235e+00 2.5613607315592056e+01 2.2564569351176282e+00 +2.0941875002685144e+00 4.1475715876280130e+00 8.0019818936636522e+00 +1.1929631605542741e+00 4.3055923370457059e+00 7.7239367239652201e-01 +ITEM: ATOMS id type x y z +1 1 -4.99391 3.35438 2.10847 +2 1 -0.969424 2.43249 2.0988 +3 1 7.28407 2.8735 2.62347 +4 1 12.7535 2.79479 2.52587 +5 1 11.2843 3.48997 4.38948 +6 1 18.5323 3.79371 3.76776 +7 1 22.4848 3.28742 3.92357 +8 1 4.68871 3.71 3.677 diff --git a/tests/generator/check_bad_conf/good.lammpstrj b/tests/generator/check_bad_conf/good.lammpstrj new file mode 100644 index 000000000..de83eb30d --- /dev/null +++ b/tests/generator/check_bad_conf/good.lammpstrj @@ -0,0 +1,17 @@ +ITEM: TIMESTEP +1000 +ITEM: NUMBER OF ATOMS +8 +ITEM: BOX BOUNDS xy xz yz pp pp pp +1.8139178622650964e-01 5.3315679568790921e+00 6.0140898224392482e-02 +1.5585645904442166e-01 5.2801927596078837e+00 -4.2475821618813588e-02 +1.7257600456519429e-01 5.3259794930348008e+00 -3.3316196847694178e-02 +ITEM: ATOMS id type x y z +1 1 1.56205 1.38625 1.45027 +2 1 2.81674 2.78587 0.383858 +3 1 1.42276 3.82071 4.00808 +4 1 2.70217 5.14268 2.79767 +5 1 3.8694 1.49549 4.01017 +6 1 5.26061 2.60465 2.6905 +7 1 4.17094 4.22066 1.50687 +8 1 0.254383 0.28016 5.15933 diff --git a/tests/generator/test_check_bad_conf.py b/tests/generator/test_check_bad_conf.py index 4c49ddb6c..8bb594eae 100644 --- a/tests/generator/test_check_bad_conf.py +++ b/tests/generator/test_check_bad_conf.py @@ -9,7 +9,8 @@ class TestCheckBadConf(unittest.TestCase): def test_length_ratio(self): - conf_bad = os.path.join('check_bad_conf', 'bad.lammpstrj') - conf_good = os.path.join('check_bad_conf', 'good.lammpstrj') + dirname = os.path.dirname(__file__) + conf_bad = os.path.join(dirname, 'check_bad_conf', 'bad.lammpstrj') + conf_good = os.path.join(dirname, 'check_bad_conf', 'good.lammpstrj') self.assertTrue(check_bad_conf(conf_bad, 'length_ratio:5')) self.assertFalse(check_bad_conf(conf_good, 'length_ratio:5')) From 08ac3f0ca258b5170fb14c4416cfae7eed15ca3a Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 10 Mar 2020 09:26:00 +0800 Subject: [PATCH 088/201] fix bug of reuse_old. remove support of python_path --- dpgen/generator/run.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index c171b8555..b1c04b7b9 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -282,7 +282,7 @@ def make_train (iter_index, jinput = jdata['default_training_param'] try: mdata["deepmd_version"] - except: + except KeyError: mdata = set_version(mdata) # setup data systems if LooseVersion(mdata["deepmd_version"]) < LooseVersion('1'): @@ -381,14 +381,13 @@ def run_train (iter_index, reuse_old = False try: mdata["deepmd_version"] - except: + except KeyError: mdata = set_version(mdata) if LooseVersion(mdata["deepmd_version"]) < LooseVersion('1'): # 0.x deepmd_path = mdata['deepmd_path'] else: # 1.x - python_path = mdata.get('python_path', None) train_command = mdata.get('train_command', 'dp') train_resources = mdata['train_resources'] @@ -412,19 +411,14 @@ def run_train (iter_index, commands.append(command) command = os.path.join(deepmd_path, 'bin/dp_frz') commands.append(command) - elif python_path: - # 1.x - command = '%s -m deepmd train %s' % (python_path, train_input_file) - if reuse_old: - command += ' --init-model old/model.ckpt' - commands.append(command) - command = '%s -m deepmd freeze' % python_path - commands.append(command) else: + # 1.x ## Commands are like `dp train` and `dp freeze` ## train_command should not be None assert(train_command) command = '%s train %s' % (train_command, train_input_file) + if reuse_old: + command += ' --init-model old/model.ckpt' commands.append(command) command = '%s freeze' % train_command commands.append(command) @@ -745,7 +739,7 @@ def _make_model_devi_revmat(iter_index, jdata, mdata, conf_systems): work_path = os.path.join(iter_name, model_devi_name) try: mdata["deepmd_version"] - except: + except KeyError: mdata = set_version(mdata) deepmd_version = mdata['deepmd_version'] @@ -877,7 +871,7 @@ def _make_model_devi_native(iter_index, jdata, mdata, conf_systems): os.chdir(task_path) try: mdata["deepmd_version"] - except: + except KeyError: mdata = set_version(mdata) deepmd_version = mdata['deepmd_version'] file_c = make_lammps_input(ensemble, From 2d3caff3fb819252f1d8126de2a97ca71305ca66 Mon Sep 17 00:00:00 2001 From: haidi Date: Tue, 10 Mar 2020 15:09:10 +0800 Subject: [PATCH 089/201] cvasp support for autotest --- dpgen/__init__.py | 9 +++-- dpgen/auto_test/gen_00_equi.py | 14 ++++++-- dpgen/auto_test/gen_01_eos.py | 14 +++++++- dpgen/auto_test/gen_02_elastic.py | 14 +++++++- dpgen/auto_test/gen_03_vacancy.py | 15 +++++++- dpgen/auto_test/gen_04_interstitial.py | 15 +++++++- dpgen/auto_test/gen_05_surf.py | 15 +++++++- dpgen/auto_test/gen_06_phonon.py | 19 ++++++++--- dpgen/auto_test/lib/vasp.py | 47 +++++++++++++++++++++++--- dpgen/auto_test/run.py | 33 ++++++++++++++---- 10 files changed, 168 insertions(+), 27 deletions(-) diff --git a/dpgen/__init__.py b/dpgen/__init__.py index b38875dee..2827ff230 100644 --- a/dpgen/__init__.py +++ b/dpgen/__init__.py @@ -8,11 +8,10 @@ SHORT_CMD="dpgen" dlog = logging.getLogger(__name__) dlog.setLevel(logging.INFO) -dlogf = logging.FileHandler(os.getcwd()+os.sep+SHORT_CMD+'.log') -dlogf_formatter=logging.Formatter('%(asctime)s - %(levelname)s : %(message)s') -#dlogf_formatter=logging.Formatter('%(asctime)s - %(name)s - [%(filename)s:%(funcName)s - %(lineno)d ] - %(levelname)s \n %(message)s') -dlogf.setFormatter(dlogf_formatter) -dlog.addHandler(dlogf) +sh = logging.StreamHandler() +formatter=logging.Formatter('%(asctime)s - %(name)s - [%(filename)s:%(funcName)s - %(lineno)d ] - %(levelname)s \n %(message)s') +sh.setFormatter(formatter) +dlog.addHandler(sh) __author__ = "Han Wang" __copyright__ = "Copyright 2019" diff --git a/dpgen/auto_test/gen_00_equi.py b/dpgen/auto_test/gen_00_equi.py index 7ae2572cc..b64409232 100755 --- a/dpgen/auto_test/gen_00_equi.py +++ b/dpgen/auto_test/gen_00_equi.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob +import os, re, argparse, filecmp, json, glob, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp @@ -8,7 +8,7 @@ # from dpgen import ROOT_PATH from pymatgen.io.vasp import Incar,Kpoints,Potcar -from dpgen.auto_test.lib.vasp import make_kspacing_kpoints +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') global_task_name = '00.equi' @@ -70,12 +70,22 @@ def make_vasp(jdata, conf_dir) : os.chdir(vasp_path) print(vasp_path) + # write incar with open('INCAR', 'w') as fp : fp.write(fc) + # gen poscar if os.path.exists('POSCAR') : os.remove('POSCAR') os.symlink(os.path.relpath(to_poscar), 'POSCAR') + + # write kp + make_vasp_kpoints_from_incar(vasp_path,jdata) + + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, 'cvasp.py') + # gen potcar with open('POTCAR', 'w') as outfile: for fname in potcar_list: diff --git a/dpgen/auto_test/gen_01_eos.py b/dpgen/auto_test/gen_01_eos.py index 253792aee..196313c6f 100755 --- a/dpgen/auto_test/gen_01_eos.py +++ b/dpgen/auto_test/gen_01_eos.py @@ -1,12 +1,17 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob +import os, re, argparse, filecmp, json, glob, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps from pymatgen.core.structure import Structure +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') + global_equi_name = '00.equi' global_task_name = '01.eos' @@ -98,6 +103,13 @@ def make_vasp(jdata, conf_dir) : # print(scale) vasp.poscar_scale('POSCAR.orig', 'POSCAR', scale) # print(vol_path, vasp.poscar_vol('POSCAR') / vasp.poscar_natoms('POSCAR')) + # write kp + make_vasp_kpoints_from_incar(vol_path,jdata) + + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, os.path.join(vol_path,'cvasp.py')) + os.chdir(cwd) def make_lammps (jdata, conf_dir,task_type) : diff --git a/dpgen/auto_test/gen_02_elastic.py b/dpgen/auto_test/gen_02_elastic.py index 45553e49d..5938f3c08 100755 --- a/dpgen/auto_test/gen_02_elastic.py +++ b/dpgen/auto_test/gen_02_elastic.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob +import os, re, argparse, filecmp, json, glob, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp @@ -8,6 +8,11 @@ from pymatgen.core.structure import Structure from pymatgen.analysis.elasticity.strain import Deformation, DeformedStructureSet, Strain +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') + global_equi_name = '00.equi' global_task_name = '02.elastic' @@ -107,6 +112,13 @@ def make_vasp(jdata, conf_dir, norm_def = 2e-3, shear_def = 5e-3) : os.symlink(os.path.relpath(os.path.join(task_path, 'INCAR')), 'INCAR') os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')), 'POTCAR') os.symlink(os.path.relpath(os.path.join(task_path, 'KPOINTS')), 'KPOINTS') + + # write kp + make_vasp_kpoints_from_incar(dfm_path,jdata) + + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, os.path.join(dfm_path,'cvasp.py')) cwd = os.getcwd() def make_lammps(jdata, conf_dir,task_type) : diff --git a/dpgen/auto_test/gen_03_vacancy.py b/dpgen/auto_test/gen_03_vacancy.py index f464ef148..ce087ede4 100755 --- a/dpgen/auto_test/gen_03_vacancy.py +++ b/dpgen/auto_test/gen_03_vacancy.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob +import os, re, argparse, filecmp, json, glob, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp @@ -9,6 +9,12 @@ from pymatgen.analysis.defects.core import Vacancy from pymatgen.analysis.defects.generators import VacancyGenerator +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') + + global_equi_name = '00.equi' global_task_name = '03.vacancy' @@ -91,6 +97,13 @@ def make_vasp(jdata, conf_dir, supercell = [1,1,1]) : os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')), 'POTCAR') # save supercell np.savetxt('supercell.out', supercell, fmt='%d') + + # write kp + make_vasp_kpoints_from_incar(struct_path,jdata) + + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, os.path.join(struct_path,'cvasp.py')) os.chdir(cwd) def make_lammps(jdata, conf_dir, task_type, supercell) : diff --git a/dpgen/auto_test/gen_04_interstitial.py b/dpgen/auto_test/gen_04_interstitial.py index ae56de0eb..fb745c76c 100755 --- a/dpgen/auto_test/gen_04_interstitial.py +++ b/dpgen/auto_test/gen_04_interstitial.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob, warnings +import os, re, argparse, filecmp, json, glob, warnings, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp @@ -9,6 +9,12 @@ from pymatgen.analysis.defects.core import Interstitial from pymatgen.analysis.defects.generators import InterstitialGenerator +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') + + global_equi_name = '00.equi' global_task_name = '04.interstitial' @@ -100,6 +106,13 @@ def _make_vasp(jdata, conf_dir, supercell, insert_ele) : os.symlink(os.path.relpath(os.path.join(task_path, 'INCAR')), 'INCAR') # save supercell np.savetxt('supercell.out', supercell, fmt='%d') + + # write kp + make_vasp_kpoints_from_incar(struct_path,jdata) + + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, os.path.join(struct_path,'cvasp.py')) os.chdir(cwd) diff --git a/dpgen/auto_test/gen_05_surf.py b/dpgen/auto_test/gen_05_surf.py index eff97a622..164588d2f 100755 --- a/dpgen/auto_test/gen_05_surf.py +++ b/dpgen/auto_test/gen_05_surf.py @@ -1,12 +1,18 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob +import os, re, argparse, filecmp, json, glob, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps from pymatgen.core.surface import generate_all_slabs, Structure +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') + + global_equi_name = '00.equi' global_task_name = '05.surf' @@ -122,6 +128,13 @@ def make_vasp(jdata, conf_dir, max_miller = 2, relax_box = False, static = False # link incar, potcar, kpoints os.symlink(os.path.relpath(os.path.join(task_path, 'INCAR')), 'INCAR') os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')), 'POTCAR') + + # write kp + make_vasp_kpoints_from_incar(struct_path,jdata) + + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, os.path.join(struct_path,'cvasp.py')) cwd = os.getcwd() def make_lammps(jdata, conf_dir, max_miller = 2, static = False, relax_box = False, task_type = 'wrong-task') : diff --git a/dpgen/auto_test/gen_06_phonon.py b/dpgen/auto_test/gen_06_phonon.py index 3be4427dc..5bd6e0691 100644 --- a/dpgen/auto_test/gen_06_phonon.py +++ b/dpgen/auto_test/gen_06_phonon.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os, re, argparse, filecmp, json, glob +import os, re, argparse, filecmp, json, glob, shutil import subprocess as sp import numpy as np import dpgen.auto_test.lib.vasp as vasp @@ -8,6 +8,10 @@ from phonopy.structure.atoms import PhonopyAtoms import yaml +from dpgen import ROOT_PATH +from pymatgen.io.vasp import Incar,Kpoints,Potcar +from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') global_equi_name = '00.equi' @@ -145,9 +149,16 @@ def make_vasp(jdata, conf_dir) : with open(fname) as infile: outfile.write(infile.read()) # gen kpoints - fc = vasp.make_kspacing_kpoints(task_poscar, kspacing, kgamma) - with open(os.path.join(task_path,'KPOINTS'), 'w') as fp: - fp.write(fc) +# fc = vasp.make_kspacing_kpoints(task_poscar, kspacing, kgamma) +# with open(os.path.join(task_path,'KPOINTS'), 'w') as fp: +# fp.write(fc) + + # write kp + make_vasp_kpoints_from_incar(task_path,jdata) + #copy cvasp + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + shutil.copyfile(cvasp_file, os.path.join(task_path,'cvasp.py')) + # gen band.conf os.chdir(task_path) with open('band.conf','w') as fp: diff --git a/dpgen/auto_test/lib/vasp.py b/dpgen/auto_test/lib/vasp.py index b4e8408f0..2e234b3c1 100644 --- a/dpgen/auto_test/lib/vasp.py +++ b/dpgen/auto_test/lib/vasp.py @@ -1,9 +1,11 @@ #!/usr/bin/python3 - +import os import warnings import numpy as np import dpgen.auto_test.lib.lammps as lammps import dpgen.auto_test.lib.util as util +from dpgen.generator.lib.vasp import incar_upper +from pymatgen.io.vasp import Incar,Kpoints,Potcar class OutcarItemError(Exception): pass @@ -113,7 +115,7 @@ def make_kspacing_kpoints(poscar, kspacing, kgamma) : box = np.array(box) box *= scale rbox = reciprocal_box(box) - kpoints = [(np.ceil(2 * np.pi * np.linalg.norm(ii) / ks).astype(int)) for ii,ks in zip(rbox,kspacing)] + kpoints = [max(1,(np.ceil(2 * np.pi * np.linalg.norm(ii) / ks).astype(int))) for ii,ks in zip(rbox,kspacing)] ret = make_vasp_kpoints(kpoints, kgamma) return ret @@ -249,7 +251,7 @@ def make_vasp_static_incar (ecut, ediff, ret += 'PREC=A\n' ret += 'ENCUT=%d\n' % ecut ret += '# ISYM=0\n' - ret += 'ALGO=fast\n' + ret += 'ALGO=normal\n' ret += 'EDIFF=%e\n' % ediff ret += 'EDIFFG=-0.01\n' ret += 'LREAL=A\n' @@ -290,7 +292,7 @@ def make_vasp_relax_incar (ecut, ediff, ret += 'PREC=A\n' ret += 'ENCUT=%d\n' % ecut ret += '# ISYM=0\n' - ret += 'ALGO=fast\n' + ret += 'ALGO=normal\n' ret += 'EDIFF=%e\n' % ediff ret += 'EDIFFG=-0.01\n' ret += 'LREAL=A\n' @@ -331,7 +333,7 @@ def make_vasp_phonon_incar (ecut, ediff, ret += 'PREC=A\n' ret += 'ENCUT=%d\n' % ecut ret += '# ISYM=0\n' - ret += 'ALGO=fast\n' + ret += 'ALGO=normal\n' ret += 'EDIFF=%e\n' % ediff ret += 'EDIFFG=-0.01\n' ret += 'LREAL=A\n' @@ -458,3 +460,38 @@ def make_vasp_kpoints (kpoints, kgamma = False) : ret = _make_vasp_kp_mp(kpoints) return ret + +def make_vasp_kpoints_from_incar(work_dir,jdata): + cwd=os.getcwd() + fp_aniso_kspacing = jdata.get('fp_aniso_kspacing') + os.chdir(work_dir) + # get kspacing and kgamma from incar + assert(os.path.exists('INCAR')) + with open('INCAR') as fp: + incar = fp.read() + standard_incar = incar_upper(Incar.from_string(incar)) + if fp_aniso_kspacing is None: + try: + kspacing = standard_incar['KSPACING'] + except KeyError: + raise RuntimeError ("KSPACING must be given in INCAR") + else : + kspacing = fp_aniso_kspacing + try: + gamma = standard_incar['KGAMMA'] + if isinstance(gamma,bool): + pass + else: + if gamma[0].upper()=="T": + gamma=True + else: + gamma=False + except KeyError: + raise RuntimeError ("KGAMMA must be given in INCAR") + # check poscar + assert(os.path.exists('POSCAR')) + # make kpoints + ret=make_kspacing_kpoints('POSCAR', kspacing, gamma) + kp=Kpoints.from_string(ret) + kp.write_file("KPOINTS") + os.chdir(cwd) diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 12a250dd1..040fdcf4b 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -72,7 +72,10 @@ def run_equi(task_type,jdata,mdata): if task_type=="vasp": mdata=decide_fp_machine(mdata) - forward_files = ['INCAR', 'POTCAR'] + forward_files = ['INCAR', 'POTCAR', 'KPOINTS'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] common_files=['POSCAR'] @@ -162,9 +165,12 @@ def run_eos(task_type,jdata,mdata): if task_type=="vasp": mdata=decide_fp_machine(mdata) - forward_files = ['INCAR', 'POSCAR','POTCAR'] + forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') #lammps elif task_type in lammps_task_type: @@ -241,6 +247,9 @@ def run_elastic(task_type,jdata,mdata): forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] common_files=['INCAR','POTCAR','KPOINTS'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') #lammps elif task_type in lammps_task_type: @@ -312,9 +321,12 @@ def run_vacancy(task_type,jdata,mdata): if task_type == "vasp": mdata=decide_fp_machine(mdata) - forward_files = ['INCAR', 'POSCAR','POTCAR'] + forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') #lammps elif task_type in lammps_task_type: @@ -396,9 +408,12 @@ def run_interstitial(task_type,jdata,mdata): if task_type == "vasp": mdata=decide_fp_machine(mdata) - forward_files = ['INCAR', 'POSCAR','POTCAR'] + forward_files = ['INCAR', 'POSCAR','POTCAR',"KPOINTS"] backward_files = ['OUTCAR', 'autotest.out' , 'XDATCAR','OSZICAR'] common_files=['INCAR'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') #lammps elif task_type in lammps_task_type: @@ -518,9 +533,12 @@ def run_surf(task_type,jdata,mdata): if task_type == "vasp": mdata=decide_fp_machine(mdata) - forward_files = ['INCAR', 'POSCAR','POTCAR'] + forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') #lammps elif task_type in lammps_task_type: @@ -603,9 +621,12 @@ def run_phonon(task_type,jdata,mdata): machine,resources,command,group_size=util.get_machine_info(mdata,task_type) run_tasks = util.collect_task(all_task,task_type) - forward_files = ['INCAR', 'POTCAR','KPOINTS'] + forward_files = ['INCAR', 'POTCAR','KPOINTS','KPOINTS'] backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR','vasprun.xml'] common_files=['POSCAR'] + if ('cvasp' in jdata) and (jdata['cvasp'] == True): + mdata['fp_resources']['cvasp'] = True + forward_files.append('cvasp.py') disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) disp.run_jobs(resources, From 20c94ceb2a2ff606ccaa8da0cf7603939d6f3310 Mon Sep 17 00:00:00 2001 From: haidi Date: Tue, 10 Mar 2020 16:49:27 +0800 Subject: [PATCH 090/201] recover __init__.py --- dpgen/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dpgen/__init__.py b/dpgen/__init__.py index 2827ff230..b38875dee 100644 --- a/dpgen/__init__.py +++ b/dpgen/__init__.py @@ -8,10 +8,11 @@ SHORT_CMD="dpgen" dlog = logging.getLogger(__name__) dlog.setLevel(logging.INFO) -sh = logging.StreamHandler() -formatter=logging.Formatter('%(asctime)s - %(name)s - [%(filename)s:%(funcName)s - %(lineno)d ] - %(levelname)s \n %(message)s') -sh.setFormatter(formatter) -dlog.addHandler(sh) +dlogf = logging.FileHandler(os.getcwd()+os.sep+SHORT_CMD+'.log') +dlogf_formatter=logging.Formatter('%(asctime)s - %(levelname)s : %(message)s') +#dlogf_formatter=logging.Formatter('%(asctime)s - %(name)s - [%(filename)s:%(funcName)s - %(lineno)d ] - %(levelname)s \n %(message)s') +dlogf.setFormatter(dlogf_formatter) +dlog.addHandler(dlogf) __author__ = "Han Wang" __copyright__ = "Copyright 2019" From 77e5860fd4f3a65cd2c4dc66e42d004d369fee80 Mon Sep 17 00:00:00 2001 From: haidi Date: Tue, 10 Mar 2020 21:11:34 +0800 Subject: [PATCH 091/201] update vasp std output --- dpgen/auto_test/run.py | 58 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 040fdcf4b..9402c6fe1 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -76,7 +76,7 @@ def run_equi(task_type,jdata,mdata): if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True forward_files.append('cvasp.py') - backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] + backward_files = ['OUTCAR', 'vasp.out' , 'CONTCAR','OSZICAR'] common_files=['POSCAR'] #lammps @@ -84,7 +84,7 @@ def run_equi(task_type,jdata,mdata): mdata = decide_model_devi_machine(mdata) forward_files = ['conf.lmp', 'lammps.in'] - backward_files = ['dump.relax','log.lammps', 'autotest.out'] + backward_files = ['dump.relax','log.lammps', 'vasp.out'] fp_params = jdata['lammps_params'] model_dir = fp_params['model_dir'] @@ -116,8 +116,8 @@ def run_equi(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') def cmpt_equi(task_type,jdata,mdata): @@ -166,7 +166,7 @@ def run_eos(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] + backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -186,7 +186,7 @@ def run_eos(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps', 'autotest.out'] + backward_files = ['log.lammps', 'vasp.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -207,8 +207,8 @@ def run_eos(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') def cmpt_eos(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -245,7 +245,7 @@ def run_elastic(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] + backward_files = ['OUTCAR', 'vasp.out' , 'CONTCAR','OSZICAR'] common_files=['INCAR','POTCAR','KPOINTS'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -265,7 +265,7 @@ def run_elastic(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in','strain.out']+model_name - backward_files = ['log.lammps', 'autotest.out'] + backward_files = ['log.lammps', 'vasp.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -286,8 +286,8 @@ def run_elastic(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') def cmpt_elastic(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -322,7 +322,7 @@ def run_vacancy(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] + backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -343,7 +343,7 @@ def run_vacancy(task_type,jdata,mdata): models = [os.path.join(model_dir,ii) for ii in model_name] common_files = model_name forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps','autotest.out'] + backward_files = ['log.lammps','vasp.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -364,8 +364,8 @@ def run_vacancy(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') def cmpt_vacancy(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -409,7 +409,7 @@ def run_interstitial(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR',"KPOINTS"] - backward_files = ['OUTCAR', 'autotest.out' , 'XDATCAR','OSZICAR'] + backward_files = ['OUTCAR', 'vasp.out' , 'XDATCAR','OSZICAR'] common_files=['INCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -445,7 +445,7 @@ def run_interstitial(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps', 'autotest.out'] + backward_files = ['log.lammps', 'vasp.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -470,8 +470,8 @@ def run_interstitial(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') else: run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return @@ -484,8 +484,8 @@ def run_interstitial(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') def cmpt_interstitial(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -534,7 +534,7 @@ def run_surf(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] + backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -554,7 +554,7 @@ def run_surf(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps','autotest.out'] + backward_files = ['log.lammps','vasp.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -575,8 +575,8 @@ def run_surf(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') def cmpt_surf(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -622,7 +622,7 @@ def run_phonon(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) forward_files = ['INCAR', 'POTCAR','KPOINTS','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR','vasprun.xml'] + backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR','vasprun.xml'] common_files=['POSCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -637,8 +637,8 @@ def run_phonon(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog='vasp.out', + errlog='vasp.err') #lammps elif task_type in lammps_task_type: None From 99b39f018aece23920cab093bf4b44624bfed6bd Mon Sep 17 00:00:00 2001 From: haidi Date: Tue, 10 Mar 2020 21:17:57 +0800 Subject: [PATCH 092/201] recover autotest.out --- dpgen/auto_test/run.py | 58 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 9402c6fe1..040fdcf4b 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -76,7 +76,7 @@ def run_equi(task_type,jdata,mdata): if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True forward_files.append('cvasp.py') - backward_files = ['OUTCAR', 'vasp.out' , 'CONTCAR','OSZICAR'] + backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] common_files=['POSCAR'] #lammps @@ -84,7 +84,7 @@ def run_equi(task_type,jdata,mdata): mdata = decide_model_devi_machine(mdata) forward_files = ['conf.lmp', 'lammps.in'] - backward_files = ['dump.relax','log.lammps', 'vasp.out'] + backward_files = ['dump.relax','log.lammps', 'autotest.out'] fp_params = jdata['lammps_params'] model_dir = fp_params['model_dir'] @@ -116,8 +116,8 @@ def run_equi(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') def cmpt_equi(task_type,jdata,mdata): @@ -166,7 +166,7 @@ def run_eos(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR'] + backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -186,7 +186,7 @@ def run_eos(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps', 'vasp.out'] + backward_files = ['log.lammps', 'autotest.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -207,8 +207,8 @@ def run_eos(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') def cmpt_eos(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -245,7 +245,7 @@ def run_elastic(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'vasp.out' , 'CONTCAR','OSZICAR'] + backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] common_files=['INCAR','POTCAR','KPOINTS'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -265,7 +265,7 @@ def run_elastic(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in','strain.out']+model_name - backward_files = ['log.lammps', 'vasp.out'] + backward_files = ['log.lammps', 'autotest.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -286,8 +286,8 @@ def run_elastic(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') def cmpt_elastic(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -322,7 +322,7 @@ def run_vacancy(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR'] + backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -343,7 +343,7 @@ def run_vacancy(task_type,jdata,mdata): models = [os.path.join(model_dir,ii) for ii in model_name] common_files = model_name forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps','vasp.out'] + backward_files = ['log.lammps','autotest.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -364,8 +364,8 @@ def run_vacancy(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') def cmpt_vacancy(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -409,7 +409,7 @@ def run_interstitial(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR',"KPOINTS"] - backward_files = ['OUTCAR', 'vasp.out' , 'XDATCAR','OSZICAR'] + backward_files = ['OUTCAR', 'autotest.out' , 'XDATCAR','OSZICAR'] common_files=['INCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -445,7 +445,7 @@ def run_interstitial(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps', 'vasp.out'] + backward_files = ['log.lammps', 'autotest.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -470,8 +470,8 @@ def run_interstitial(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') else: run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return @@ -484,8 +484,8 @@ def run_interstitial(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') def cmpt_interstitial(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -534,7 +534,7 @@ def run_surf(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR'] + backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -554,7 +554,7 @@ def run_surf(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps','vasp.out'] + backward_files = ['log.lammps','autotest.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -575,8 +575,8 @@ def run_surf(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') def cmpt_surf(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -622,7 +622,7 @@ def run_phonon(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) forward_files = ['INCAR', 'POTCAR','KPOINTS','KPOINTS'] - backward_files = ['OUTCAR', 'vasp.out' , 'OSZICAR','vasprun.xml'] + backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR','vasprun.xml'] common_files=['POSCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -637,8 +637,8 @@ def run_phonon(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='vasp.out', - errlog='vasp.err') + outlog='autotest.out', + errlog='autotest.err') #lammps elif task_type in lammps_task_type: None From 66ff2209ee7787f8d301dc5087530fd7e8497e65 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 11 Mar 2020 08:03:48 +0800 Subject: [PATCH 093/201] tag failures on allow_failure --- dpgen/dispatcher/Batch.py | 6 +- dpgen/dispatcher/Dispatcher.py | 4 +- dpgen/dispatcher/LazyLocalContext.py | 16 +++- dpgen/dispatcher/LocalContext.py | 10 ++- dpgen/dispatcher/SSHContext.py | 13 +++- dpgen/generator/run.py | 33 +++++++-- tests/dispatcher/test_lazy_local_context.py | 77 +++++++++++++++++++ tests/dispatcher/test_local_context.py | 82 +++++++++++++++++++++ tests/dispatcher/test_ssh_context.py | 82 +++++++++++++++++++++ 9 files changed, 310 insertions(+), 13 deletions(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index 37ceed577..51576fac4 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -136,10 +136,7 @@ def _sub_script_inner(self, outlog = 'log', errlog = 'err') : ret = "" - try: - allow_failure = res['allow_failure'] - except: - allow_failure = False + allow_failure = res.get('allow_failure', False) for ii,jj in zip(job_dirs, args) : ret += 'cd %s\n' % ii ret += 'test $? -ne 0 && exit 1\n\n' @@ -150,6 +147,7 @@ def _sub_script_inner(self, ret += ' if test $? -ne 0; then exit 1; else touch tag_%d_finished; fi \n' % idx else : ret += ' touch tag_%d_finished \n' % idx + ret += ' touch tag_failure_%d \n' % idx ret += 'fi\n\n' else : # do not support task-wise restart diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index fc2dddf36..e0b2f2fb1 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -189,6 +189,7 @@ def all_finished(self, job_list = job_handler['job_list'] job_record = job_handler['job_record'] command = job_handler['command'] + tag_failure_list = ['tag_failure_%d' % ii for ii in range(len(command))] resources = job_handler['resources'] outlog = job_handler['outlog'] errlog = job_handler['errlog'] @@ -212,7 +213,8 @@ def all_finished(self, rjob['batch'].submit(task_chunks[idx], command, res = resources, outlog=outlog, errlog=errlog,restart=True) elif status == JobStatus.finished : dlog.info('job %s finished' % job_uuid) - rjob['context'].download(task_chunks[idx], backward_task_files) + rjob['context'].download(task_chunks[idx], tag_failure_list, check_exists = True, mark_failure = False) + rjob['context'].download(task_chunks[idx], backward_task_files, check_exists = True) rjob['context'].clean() job_record.record_finish(cur_hash) job_record.dump() diff --git a/dpgen/dispatcher/LazyLocalContext.py b/dpgen/dispatcher/LazyLocalContext.py index e42ad1ac8..0f79a8161 100644 --- a/dpgen/dispatcher/LazyLocalContext.py +++ b/dpgen/dispatcher/LazyLocalContext.py @@ -47,8 +47,22 @@ def upload(self, def download(self, job_dirs, remote_down_files, + check_exists = False, + mark_failure = True, back_error=False) : - pass + for ii in job_dirs : + for jj in remote_down_files : + fname = os.path.join(self.local_root, ii, jj) + exists = os.path.exists(fname) + if not exists: + if check_exists: + if mark_failure: + with open(os.path.join(self.local_root, ii, 'tag_failure_download_%s' % jj), 'w') as fp: pass + else: + pass + else: + raise RuntimeError('do not find download file ' + fname) + def block_checkcall(self, cmd) : diff --git a/dpgen/dispatcher/LocalContext.py b/dpgen/dispatcher/LocalContext.py index 4549b307a..457ae659b 100644 --- a/dpgen/dispatcher/LocalContext.py +++ b/dpgen/dispatcher/LocalContext.py @@ -88,6 +88,8 @@ def upload(self, def download(self, job_dirs, remote_down_files, + check_exists = False, + mark_failure = True, back_error=False) : cwd = os.getcwd() for ii in job_dirs : @@ -103,7 +105,13 @@ def download(self, lfile = os.path.join(local_job, jj) if not os.path.realpath(rfile) == os.path.realpath(lfile) : if (not os.path.exists(rfile)) and (not os.path.exists(lfile)): - raise RuntimeError('do not find download file ' + rfile) + if check_exists : + if mark_failure: + with open(os.path.join(self.local_root, ii, 'tag_failure_download_%s' % jj), 'w') as fp: pass + else : + pass + else : + raise RuntimeError('do not find download file ' + rfile) elif (not os.path.exists(rfile)) and (os.path.exists(lfile)) : # already downloaded pass diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index 57ed648c6..0cc590018 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -121,6 +121,8 @@ def upload(self, def download(self, job_dirs, remote_down_files, + check_exists = False, + mark_failure = True, back_error=False) : self.ssh_session.ensure_alive() cwd = os.getcwd() @@ -128,7 +130,16 @@ def download(self, file_list = [] for ii in job_dirs : for jj in remote_down_files : - file_list.append(os.path.join(ii,jj)) + file_name = os.path.join(ii,jj) + if check_exists: + if self.check_file_exists(file_name): + file_list.append(file_name) + elif mark_failure : + with open(os.path.join(self.local_root, ii, 'tag_failure_download_%s' % jj), 'w') as fp: pass + else: + pass + else: + file_list.append(file_name) if back_error: errors=glob(os.path.join(ii,'error*')) file_list.extend(errors) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 709932526..85ad5abe6 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1757,6 +1757,29 @@ def run_fp (iter_index, raise RuntimeError ("unsupported fp style") +def post_fp_check_fail(iter_index, + jdata, + rfailed = None) : + ratio_failed = rfailed if rfailed else jdata.get('ratio_failed',0.05) + iter_name = make_iter_name(iter_index) + work_path = os.path.join(iter_name, fp_name) + fp_tasks = glob.glob(os.path.join(work_path, 'task.*')) + fp_tasks.sort() + if len(fp_tasks) == 0 : + return + # check fail according to tag_failure + fp_failed_tags = glob.glob(os.path.join(work_path, 'task.*', 'tag_failure*')) + fp_failed_tasks = [os.path.dirname(ii) for ii in fp_failed_tags] + fp_failed_tasks = list(set(fp_failed_tasks)) + + ntask = len(fp_tasks) + nfail = len(fp_failed_tasks) + rfail = float(nfail) / float(ntask) + dlog.info("failed tasks: %6d in %6d %6.2f %% " % (nfail, ntask, rfail * 100.)) + if rfail > ratio_failed: + raise RuntimeError("find too many unsuccessfully terminated jobs") + + def post_fp_vasp (iter_index, jdata, rfailed=None): @@ -1836,12 +1859,10 @@ def post_fp_vasp (iter_index, else: raise RuntimeError('invalid setting of use_ele_temp ' + str(use_ele_temp)) - dlog.info("failed frame number: %s "%icount) - dlog.info("total frame number: %s "%tcount) - reff=icount/tcount - dlog.info('ratio of failed frame: {:.2%}'.format(reff)) + rfail=float(icount)/float(tcount) + dlog.info("failed frame: %6d in %6d %6.2f %% " % (icount, tcount, rfail * 100.)) - if reff>ratio_failed: + if rfail>ratio_failed: raise RuntimeError("find too many unsuccessfully terminated jobs") @@ -2070,9 +2091,11 @@ def post_fp_pwmat (iter_index, if reff>ratio_failed: raise RuntimeError("find too many unsuccessfully terminated jobs") + def post_fp (iter_index, jdata) : fp_style = jdata['fp_style'] + post_fp_check_fail(iter_index, jdata) if fp_style == "vasp" : post_fp_vasp(iter_index, jdata) elif fp_style == "pwscf" : diff --git a/tests/dispatcher/test_lazy_local_context.py b/tests/dispatcher/test_lazy_local_context.py index 6f34f2f14..8de1875d8 100644 --- a/tests/dispatcher/test_lazy_local_context.py +++ b/tests/dispatcher/test_lazy_local_context.py @@ -39,6 +39,83 @@ def test_download(self): self.job.upload(tasks, ['test0', 'dir0']) self.job.download(tasks, ['test0', 'dir0']) + def test_download_check_mark(self): + # upload files + self.job = LazyLocalContext('loc', None) + tasks = ['task0', 'task1'] + self.job.upload(tasks, ['test0', 'dir0']) + record_uuid = [] + # generate extra donwload files + for ii in tasks : + for jj in ['test6', 'test7'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6'): + continue + with open(os.path.join('loc',ii,jj), 'w') as fp: + tmp = str(uuid.uuid4()) + fp.write(tmp) + record_uuid.append(tmp) + self.job.download(tasks, ['test6', 'test7', 'dir1'], check_exists = True, mark_failure = True) + # check dlded + cc = 0 + for ii in tasks : + for jj in ['test6', 'test7'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertTrue(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'failed to find ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + with open(os.path.join('loc',ii,jj), 'r') as fp: + tmp = fp.read() + self.assertEqual(tmp, record_uuid[cc]) + cc += 1 + for ii in tasks : + for jj in ['dir1'] : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj))) + self.assertTrue(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj))) + + + def test_download_check_nomark(self): + # upload files + self.job = LazyLocalContext('loc', None) + tasks = ['task0', 'task1'] + self.job.upload(tasks, ['test0', 'dir0']) + record_uuid = [] + # generate extra donwload files + for ii in tasks : + for jj in ['test6', 'test7'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6'): + continue + with open(os.path.join('loc',ii,jj), 'w') as fp: + tmp = str(uuid.uuid4()) + fp.write(tmp) + record_uuid.append(tmp) + self.job.download(tasks, ['test6', 'test7', 'dir1'], check_exists = True, mark_failure = False) + # check dlded + cc = 0 + for ii in tasks : + for jj in ['test6', 'test7'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'found ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + with open(os.path.join('loc',ii,jj), 'r') as fp: + tmp = fp.read() + self.assertEqual(tmp, record_uuid[cc]) + cc += 1 + for ii in tasks : + for jj in ['dir1'] : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj))) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj))) + + + def test_block_call(self) : self.job = LazyLocalContext('loc', None) tasks = ['task0', 'task1'] diff --git a/tests/dispatcher/test_local_context.py b/tests/dispatcher/test_local_context.py index 49d4e2efc..694d48fa2 100644 --- a/tests/dispatcher/test_local_context.py +++ b/tests/dispatcher/test_local_context.py @@ -217,6 +217,88 @@ def test_download(self): rmtf = os.path.join('rmt', self.job.job_uuid, ii, jj, kk) self.assertEqual(os.path.realpath(locf), os.path.realpath(rmtf)) + + def test_download_check_mark(self): + # upload files + work_profile = LocalSession({'work_path':'rmt'}) + self.job = LocalContext('loc', work_profile) + tasks = ['task0', 'task1'] + self.job.upload(tasks, ['test0', 'dir0']) + # generate extra donwload files + record_uuid = [] + for ii in tasks : + for jj in ['test7', 'test8'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + continue + with open(os.path.join('rmt',self.job.job_uuid,ii,jj), 'w') as fp: + tmp = str(uuid.uuid4()) + fp.write(tmp) + record_uuid.append(tmp) + # donwload + files = ['test7', 'test8', 'dir1'] + self.job.download(tasks, files, check_exists = True, mark_failure = True) + # check dlded + cc = 0 + for ii in tasks : + for jj in ['test7', 'test8'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertTrue(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'failed to find ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + with open(os.path.join('loc',ii,jj), 'r') as fp: + tmp = fp.read() + self.assertEqual(tmp, record_uuid[cc]) + cc += 1 + for ii in tasks : + for jj in ['dir1'] : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj))) + self.assertTrue(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj))) + + + def test_download_check_nomark(self): + # upload files + work_profile = LocalSession({'work_path':'rmt'}) + self.job = LocalContext('loc', work_profile) + tasks = ['task0', 'task1'] + self.job.upload(tasks, ['test0', 'dir0']) + # generate extra donwload files + record_uuid = [] + for ii in tasks : + for jj in ['test7', 'test8'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + continue + with open(os.path.join('rmt',self.job.job_uuid,ii,jj), 'w') as fp: + tmp = str(uuid.uuid4()) + fp.write(tmp) + record_uuid.append(tmp) + # donwload + files = ['test7', 'test8', 'dir1'] + self.job.download(tasks, files, check_exists = True, mark_failure = False) + # check dlded + cc = 0 + for ii in tasks : + for jj in ['test7', 'test8'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'found ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + with open(os.path.join('loc',ii,jj), 'r') as fp: + tmp = fp.read() + self.assertEqual(tmp, record_uuid[cc]) + cc += 1 + for ii in tasks : + for jj in ['dir1'] : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj))) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj))) + def test_block_call(self) : work_profile = LocalSession({'work_path':'rmt'}) diff --git a/tests/dispatcher/test_ssh_context.py b/tests/dispatcher/test_ssh_context.py index 909923d2d..550fcaa12 100644 --- a/tests/dispatcher/test_ssh_context.py +++ b/tests/dispatcher/test_ssh_context.py @@ -110,6 +110,87 @@ def test_donwload(self): cc += 1 + def test_donwload_check_mark(self): + tasks = ['task0', 'task1'] + self.job.upload(tasks, ['test0', 'dir0']) + # generate extra donwload files + record_uuid = [] + for ii in tasks : + for jj in ['test6', 'test7'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6'): + continue + with open(os.path.join('rmt',self.job.job_uuid,ii,jj), 'w') as fp: + tmp = str(uuid.uuid4()) + fp.write(tmp) + record_uuid.append(tmp) + # donwload + files = ['test6', 'test7', 'dir1'] + self.job.download(tasks, files, check_exists = True, mark_failure = True) + # check dlded + cc = 0 + for ii in tasks : + for jj in ['test6', 'test7'] : + if (ii == 'task1' and jj == 'test7') or \ + (ii == 'task0' and jj == 'test6') : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertTrue(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'failed to find ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + with open(os.path.join('loc',ii,jj), 'r') as fp: + tmp = fp.read() + self.assertEqual(tmp, record_uuid[cc]) + cc += 1 + for ii in tasks : + for jj in ['dir1'] : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj))) + self.assertTrue(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj))) + + + def test_donwload_check_nomark(self): + tasks = ['task0', 'task1'] + self.job.upload(tasks, ['test0', 'dir0']) + # generate extra donwload files + record_uuid = [] + for ii in tasks : + for jj in ['test6', 'test7'] : + if ii == 'task1' and jj == 'test7' : + continue + if ii == 'task0' and jj == 'test6' : + continue + with open(os.path.join('rmt',self.job.job_uuid,ii,jj), 'w') as fp: + tmp = str(uuid.uuid4()) + fp.write(tmp) + record_uuid.append(tmp) + # donwload + files = ['test6', 'test7', 'dir1'] + self.job.download(tasks, files, check_exists = True, mark_failure = False) + # check dlded + cc = 0 + for ii in tasks : + for jj in ['test6', 'test7'] : + if ii == 'task1' and jj == 'test7' : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'found ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + if ii == 'task0' and jj == 'test6' : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj)), + msg = 'found ' + os.path.join('loc', ii, jj)) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj)), + msg = 'found ' + os.path.join('loc', ii, 'tag_failure_download_%s' % jj)) + continue + with open(os.path.join('loc',ii,jj), 'r') as fp: + tmp = fp.read() + self.assertEqual(tmp, record_uuid[cc]) + cc += 1 + for ii in tasks : + for jj in ['dir1'] : + self.assertFalse(os.path.exists(os.path.join('loc', ii, jj))) + self.assertFalse(os.path.exists(os.path.join('loc', ii, 'tag_failure_download_%s' % jj))) + def test_block_call(self) : tasks = ['task0', 'task1'] files = ['test0', 'test1'] @@ -147,3 +228,4 @@ def test_file(self) : tmp1 = self.job.read_file('aaa') self.assertEqual(tmp, tmp1) + From 5e7df5270c349a937a1bd0345ba3d37203469974 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 11 Mar 2020 10:54:25 +0800 Subject: [PATCH 094/201] fix bug of downloading no tags --- dpgen/dispatcher/SSHContext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index 0cc590018..ce171c4bb 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -143,7 +143,8 @@ def download(self, if back_error: errors=glob(os.path.join(ii,'error*')) file_list.extend(errors) - self._get_files(file_list) + if len(file_list) > 0: + self._get_files(file_list) os.chdir(cwd) def block_checkcall(self, From c05b0af27feb494766a5424cfb5156b9e6db00b7 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 11 Mar 2020 11:08:33 +0800 Subject: [PATCH 095/201] fix bug of failure tag --- dpgen/dispatcher/Batch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index 51576fac4..ad1a15e79 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -146,8 +146,8 @@ def _sub_script_inner(self, if res['allow_failure'] is False: ret += ' if test $? -ne 0; then exit 1; else touch tag_%d_finished; fi \n' % idx else : + ret += ' if test $? -ne 0; then touch tag_failure_%d; fi \n' % idx ret += ' touch tag_%d_finished \n' % idx - ret += ' touch tag_failure_%d \n' % idx ret += 'fi\n\n' else : # do not support task-wise restart From 1dd96c00398fbc64ca72b06cedd6026561f62394 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 13 Mar 2020 12:46:51 +0800 Subject: [PATCH 096/201] check failure tags only when the key `mark_failure` is set. --- dpgen/dispatcher/Dispatcher.py | 13 +++++++++---- dpgen/generator/run.py | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index e0b2f2fb1..ebf189216 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -73,6 +73,7 @@ def run_jobs(self, forward_task_files, backward_task_files, forward_task_deference = True, + mark_failure = False, outlog = 'log', errlog = 'err') : job_handler = self.submit_jobs(resources, @@ -86,7 +87,7 @@ def run_jobs(self, forward_task_deference, outlog, errlog) - while not self.all_finished(job_handler) : + while not self.all_finished(job_handler, mark_failure) : time.sleep(60) # delete path map file when job finish # _pmap.delete() @@ -182,7 +183,8 @@ def submit_jobs(self, def all_finished(self, - job_handler): + job_handler, + mark_failure): task_chunks = job_handler['task_chunks'] task_chunks_str = ['+'.join(ii) for ii in task_chunks] task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] @@ -213,8 +215,11 @@ def all_finished(self, rjob['batch'].submit(task_chunks[idx], command, res = resources, outlog=outlog, errlog=errlog,restart=True) elif status == JobStatus.finished : dlog.info('job %s finished' % job_uuid) - rjob['context'].download(task_chunks[idx], tag_failure_list, check_exists = True, mark_failure = False) - rjob['context'].download(task_chunks[idx], backward_task_files, check_exists = True) + if mark_failure: + rjob['context'].download(task_chunks[idx], tag_failure_list, check_exists = True, mark_failure = False) + rjob['context'].download(task_chunks[idx], backward_task_files, check_exists = True) + else: + rjob['context'].download(task_chunks[idx], backward_task_files) rjob['context'].clean() job_record.record_finish(cur_hash) job_record.dump() diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 85ad5abe6..55cf548cc 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1685,6 +1685,7 @@ def run_fp_inner (iter_index, fp_command = mdata['fp_command'] fp_group_size = mdata['fp_group_size'] fp_resources = mdata['fp_resources'] + mark_failure = fp_resources.get('mark_failure', False) iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, fp_name) @@ -1708,6 +1709,7 @@ def run_fp_inner (iter_index, forward_common_files, forward_files, backward_files, + mark_failure = mark_failure, outlog = log_file, errlog = log_file) From e825e390b8ed44ac61388587dacdd8b02152ec9c Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 13 Mar 2020 14:02:13 +0800 Subject: [PATCH 097/201] update autotest --- dpgen/auto_test/run.py | 179 +++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 78 deletions(-) diff --git a/dpgen/auto_test/run.py b/dpgen/auto_test/run.py index 040fdcf4b..9c5380893 100644 --- a/dpgen/auto_test/run.py +++ b/dpgen/auto_test/run.py @@ -76,7 +76,7 @@ def run_equi(task_type,jdata,mdata): if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True forward_files.append('cvasp.py') - backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] + backward_files = ['OUTCAR', task_type+'.out' , 'CONTCAR','OSZICAR'] common_files=['POSCAR'] #lammps @@ -84,7 +84,7 @@ def run_equi(task_type,jdata,mdata): mdata = decide_model_devi_machine(mdata) forward_files = ['conf.lmp', 'lammps.in'] - backward_files = ['dump.relax','log.lammps', 'autotest.out'] + backward_files = ['dump.relax','log.lammps', task_type+'.out'] fp_params = jdata['lammps_params'] model_dir = fp_params['model_dir'] @@ -107,7 +107,10 @@ def run_equi(task_type,jdata,mdata): if len(run_tasks)==0: return machine,resources,command,group_size=util.get_machine_info(mdata,task_type) disp = make_dispatcher(machine, resources, work_path, run_tasks, group_size) - + #debug# + #print(' '.join(common_files)) + #print(' '.join(forward_files)) + #print(' '.join(backward_files)) disp.run_jobs(resources, command, work_path, @@ -116,8 +119,8 @@ def run_equi(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') def cmpt_equi(task_type,jdata,mdata): @@ -166,7 +169,7 @@ def run_eos(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] + backward_files = ['OUTCAR', task_type+'.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -186,7 +189,7 @@ def run_eos(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps', 'autotest.out'] + backward_files = ['log.lammps', task_type+'.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -207,8 +210,8 @@ def run_eos(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') def cmpt_eos(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -245,7 +248,7 @@ def run_elastic(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'CONTCAR','OSZICAR'] + backward_files = ['OUTCAR', task_type+'.out' , 'CONTCAR','OSZICAR'] common_files=['INCAR','POTCAR','KPOINTS'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -265,7 +268,7 @@ def run_elastic(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in','strain.out']+model_name - backward_files = ['log.lammps', 'autotest.out'] + backward_files = ['log.lammps', task_type+'.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -286,8 +289,8 @@ def run_elastic(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') def cmpt_elastic(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -322,7 +325,7 @@ def run_vacancy(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] + backward_files = ['OUTCAR', task_type+'.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -343,7 +346,7 @@ def run_vacancy(task_type,jdata,mdata): models = [os.path.join(model_dir,ii) for ii in model_name] common_files = model_name forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps','autotest.out'] + backward_files = ['log.lammps',task_type+'.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -364,8 +367,8 @@ def run_vacancy(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') def cmpt_vacancy(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -409,7 +412,7 @@ def run_interstitial(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR',"KPOINTS"] - backward_files = ['OUTCAR', 'autotest.out' , 'XDATCAR','OSZICAR'] + backward_files = ['OUTCAR', task_type+'.out' , 'XDATCAR','OSZICAR'] common_files=['INCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -445,7 +448,7 @@ def run_interstitial(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps', 'autotest.out'] + backward_files = ['log.lammps', task_type+'.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -470,8 +473,8 @@ def run_interstitial(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') else: run_tasks = util.collect_task(all_task,task_type) if len(run_tasks)==0: return @@ -484,8 +487,8 @@ def run_interstitial(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') def cmpt_interstitial(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -534,7 +537,7 @@ def run_surf(task_type,jdata,mdata): mdata=decide_fp_machine(mdata) forward_files = ['INCAR', 'POSCAR','POTCAR','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR'] + backward_files = ['OUTCAR', task_type+'.out' , 'OSZICAR'] common_files=['INCAR','POTCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -554,7 +557,7 @@ def run_surf(task_type,jdata,mdata): else: models = [os.path.join(model_dir,ii) for ii in model_name] forward_files = ['conf.lmp', 'lammps.in']+model_name - backward_files = ['log.lammps','autotest.out'] + backward_files = ['log.lammps',task_type+'.out'] common_files=['lammps.in']+model_name if len(model_name)>1 and task_type == 'deepmd': @@ -575,8 +578,8 @@ def run_surf(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') def cmpt_surf(task_type,jdata,mdata): conf_dir=jdata['conf_dir'] @@ -622,7 +625,7 @@ def run_phonon(task_type,jdata,mdata): run_tasks = util.collect_task(all_task,task_type) forward_files = ['INCAR', 'POTCAR','KPOINTS','KPOINTS'] - backward_files = ['OUTCAR', 'autotest.out' , 'OSZICAR','vasprun.xml'] + backward_files = ['OUTCAR', task_type+'.out' , 'OSZICAR','vasprun.xml'] common_files=['POSCAR'] if ('cvasp' in jdata) and (jdata['cvasp'] == True): mdata['fp_resources']['cvasp'] = True @@ -637,8 +640,8 @@ def run_phonon(task_type,jdata,mdata): common_files, forward_files, backward_files, - outlog='autotest.out', - errlog='autotest.err') + outlog=task_type+'.out', + errlog=task_type+'.err') #lammps elif task_type in lammps_task_type: None @@ -672,10 +675,29 @@ def run_task (json_file, machine_file) : ii = jdata['task_type'] jj=jdata['task'] - task_list=['equi','eos','elastic','vacancy','interstitial','surf','phonon','all'] + task_list=['equi','eos','elastic','vacancy','interstitial','surf','phonon'] + + if isinstance(jj,str): + if jj=='all': + all_task_list=task_list.copy() + else: + try: + assert jj in task_list + except AssertionError: + raise RuntimeError ("unknow task %s, something wrong" % jj) + all_task_list=[jj] + elif isinstance(jj,list): + try: + assert set(jj).issubset(set(task_list)) + except AssertionError: + raise RuntimeError ("unknow task %s, some tasks may not supported" % ' '.join(jj)) + all_task_list=jj.copy() + else: + raise RuntimeError ('unknow format for task, it must be a string or list') + task_type_list=['vasp']+lammps_task_type - if jj not in task_list : - raise RuntimeError ("unknow task %s, something wrong" % jj) + #if jj not in task_list : + # raise RuntimeError ("unknow task %s, something wrong" % jj) if ii not in task_type_list : raise RuntimeError ("unknow task type %s, something wrong" % ii) @@ -693,51 +715,52 @@ def run_task (json_file, machine_file) : run_equi (ii, jdata, mdata) log_iter ("cmpt_equi", ii,"equi") cmpt_equi (ii, jdata, mdata) - if jj == "eos" or jj=="all": - log_iter ("gen_eos", ii, "eos") - gen_eos (ii, jdata, mdata) - log_iter ("run_eos", ii, "eos") - run_eos (ii, jdata, mdata) - log_iter ("cmpt_eos", ii, "eos") - cmpt_eos (ii, jdata, mdata) - if jj=="elastic" or jj=="all": - log_iter ("gen_elastic", ii, "elastic") - gen_elastic (ii, jdata, mdata) - log_iter ("run_elastic", ii, "elastic") - run_elastic (ii, jdata, mdata) - log_iter ("cmpt_elastic", ii, "elastic") - cmpt_elastic (ii, jdata, mdata) - if jj=="vacancy" or jj=="all": - log_iter ("gen_vacancy", ii, "vacancy") - gen_vacancy (ii, jdata, mdata) - log_iter ("run_vacancy", ii, "vacancy") - run_vacancy (ii, jdata, mdata) - log_iter ("cmpt_vacancy", ii, "vacancy") - cmpt_vacancy (ii, jdata, mdata) - if jj=="interstitial" or jj=="all": - log_iter ("gen_interstitial", ii, "interstitial") - gen_interstitial (ii, jdata, mdata) - log_iter ("run_interstitial", ii, "interstitial") - run_interstitial (ii, jdata, mdata) - log_iter ("cmpt_interstitial", ii, "interstitial") - cmpt_interstitial (ii, jdata, mdata) - if jj=="surf" or jj=="all": - log_iter ("gen_surf", ii, "surf") - gen_surf (ii, jdata, mdata) - log_iter ("run_surf", ii, "surf") - run_surf (ii, jdata, mdata) - log_iter ("cmpt_surf", ii, "surf") - cmpt_surf (ii, jdata, mdata) - ''' - if jj=="phonon": - log_iter ("gen_phonon", ii, "phonon") - gen_phonon (ii, jdata, mdata) - log_iter ("run_phonon", ii, "phonon") - run_phonon (ii, jdata, mdata) - log_iter ("cmpt_phonon", ii, "phonon") - cmpt_phonon (ii, jdata, mdata) - ''' - record_iter (record, confs, ii, jj) + for jj in all_task_list: + if jj == "eos": + log_iter ("gen_eos", ii, "eos") + gen_eos (ii, jdata, mdata) + log_iter ("run_eos", ii, "eos") + run_eos (ii, jdata, mdata) + log_iter ("cmpt_eos", ii, "eos") + cmpt_eos (ii, jdata, mdata) + if jj=="elastic": + log_iter ("gen_elastic", ii, "elastic") + gen_elastic (ii, jdata, mdata) + log_iter ("run_elastic", ii, "elastic") + run_elastic (ii, jdata, mdata) + log_iter ("cmpt_elastic", ii, "elastic") + cmpt_elastic (ii, jdata, mdata) + if jj=="vacancy": + log_iter ("gen_vacancy", ii, "vacancy") + gen_vacancy (ii, jdata, mdata) + log_iter ("run_vacancy", ii, "vacancy") + run_vacancy (ii, jdata, mdata) + log_iter ("cmpt_vacancy", ii, "vacancy") + cmpt_vacancy (ii, jdata, mdata) + if jj=="interstitial": + log_iter ("gen_interstitial", ii, "interstitial") + gen_interstitial (ii, jdata, mdata) + log_iter ("run_interstitial", ii, "interstitial") + run_interstitial (ii, jdata, mdata) + log_iter ("cmpt_interstitial", ii, "interstitial") + cmpt_interstitial (ii, jdata, mdata) + if jj=="surf": + log_iter ("gen_surf", ii, "surf") + gen_surf (ii, jdata, mdata) + log_iter ("run_surf", ii, "surf") + run_surf (ii, jdata, mdata) + log_iter ("cmpt_surf", ii, "surf") + cmpt_surf (ii, jdata, mdata) + ''' + if jj=="phonon": + log_iter ("gen_phonon", ii, "phonon") + gen_phonon (ii, jdata, mdata) + log_iter ("run_phonon", ii, "phonon") + run_phonon (ii, jdata, mdata) + log_iter ("cmpt_phonon", ii, "phonon") + cmpt_phonon (ii, jdata, mdata) + ''' + record_iter (record, confs, ii, jj) def gen_test(args): logging.info ("start auto-testing") From c8b264ae3b8ebe4a00be093b8e071b26c3975f6c Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 13 Mar 2020 14:05:16 +0800 Subject: [PATCH 098/201] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a080c9f74..03a60e0f0 100644 --- a/README.md +++ b/README.md @@ -639,7 +639,7 @@ param.json in a dictionary. | conf_dir | path like string | "confs/Al/std-fcc" | the dir which contains vasp's POSCAR | | key_id | string| "DZIwdXCXg1fiXXXXXX" |the API key of Material project| | task_type | string | "vasp" | task type, one of deepmd vasp meam | -| task | string | "equi" | task, one of equi, eos, elastic, vacancy, interstitial, surf or all | +| task | string or list | "equi" | task, one or several tasks from { equi, eos, elastic, vacancy, interstitial, surf } or all stands for all tasks | | vasp_params| dict | seeing below | params relating to vasp INCAR| | lammps_params | dict| seeing below| params relating to lammps | From 2ceb8d44b4ea37f4374cc56cd4ab29039126edca Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 14 Mar 2020 14:09:58 +0800 Subject: [PATCH 099/201] add height ratio --- dpgen/generator/run.py | 16 ++++++++++++++++ tests/generator/check_bad_conf/bad.height.POSCAR | 16 ++++++++++++++++ .../{bad.lammpstrj => bad.length.lammpstrj} | 0 tests/generator/test_check_bad_conf.py | 8 +++++++- 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/generator/check_bad_conf/bad.height.POSCAR rename tests/generator/check_bad_conf/{bad.lammpstrj => bad.length.lammpstrj} (100%) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 55cf548cc..b56c1081f 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -982,6 +982,16 @@ def post_model_devi (iter_index, mdata) : pass + +def _to_face_dist(box_): + box = np.reshape(box_, [3,3]) + vol = np.abs(np.linalg.det(box)) + dists = [] + for [ii,jj] in [[0, 1], [1, 2], [2, 0]]: + vv = np.cross(box[ii], box[jj]) + dists.append(vol / np.linalg.norm(vv)) + return np.array(dists) + def check_bad_conf(conf_name, criteria, fmt = 'lammps/dump'): @@ -996,6 +1006,12 @@ def check_bad_conf(conf_name, ratio = np.max(lengths) / np.min(lengths) if ratio > float(value): is_bad = True + elif key == 'height_ratio': + lengths = np.linalg.norm(sys['cells'][0], axis = 1) + dists = _to_face_dist(sys['cells'][0]) + ratio = np.max(lengths) / np.min(dists) + if ratio > float(value): + is_bad = True else: raise RuntimeError('unknow key', key) return is_bad diff --git a/tests/generator/check_bad_conf/bad.height.POSCAR b/tests/generator/check_bad_conf/bad.height.POSCAR new file mode 100644 index 000000000..7bffef9c3 --- /dev/null +++ b/tests/generator/check_bad_conf/bad.height.POSCAR @@ -0,0 +1,16 @@ +Si8 +1.0 +1.5044712174663648e+01 0.0000000000000000e+00 0.0000000000000000e+00 +2.1550292128393154e+00 4.6052924123850527e+00 0.0000000000000000e+00 +-4.8723010805707583e+00 -2.0033682930713113e+00 1.5924563614826326e+00 +Si +8 +Cartesian + 4.9743339600 0.3464058271 1.0711743625 + -0.3457810400 2.5396598271 1.1364543625 + 8.0859689600 0.8673568271 0.7907843625 + 8.6812089600 -1.3051601729 1.3749843625 + 3.4236889600 2.1462798271 0.9487443625 + 10.9770889600 0.2924648271 0.3900343625 + 2.4104489600 3.8329998271 -0.0757256375 + 5.9863139600 2.6933598271 -0.0617356375 diff --git a/tests/generator/check_bad_conf/bad.lammpstrj b/tests/generator/check_bad_conf/bad.length.lammpstrj similarity index 100% rename from tests/generator/check_bad_conf/bad.lammpstrj rename to tests/generator/check_bad_conf/bad.length.lammpstrj diff --git a/tests/generator/test_check_bad_conf.py b/tests/generator/test_check_bad_conf.py index 8bb594eae..f75af662b 100644 --- a/tests/generator/test_check_bad_conf.py +++ b/tests/generator/test_check_bad_conf.py @@ -10,7 +10,13 @@ class TestCheckBadConf(unittest.TestCase): def test_length_ratio(self): dirname = os.path.dirname(__file__) - conf_bad = os.path.join(dirname, 'check_bad_conf', 'bad.lammpstrj') + conf_bad = os.path.join(dirname, 'check_bad_conf', 'bad.length.lammpstrj') conf_good = os.path.join(dirname, 'check_bad_conf', 'good.lammpstrj') self.assertTrue(check_bad_conf(conf_bad, 'length_ratio:5')) self.assertFalse(check_bad_conf(conf_good, 'length_ratio:5')) + + def test_height_ratio(self): + dirname = os.path.dirname(__file__) + conf_bad = os.path.join(dirname, 'check_bad_conf', 'bad.height.POSCAR') + self.assertTrue(check_bad_conf(conf_bad, 'height_ratio:5', fmt = 'vasp/POSCAR')) + self.assertFalse(check_bad_conf(conf_bad, 'length_ratio:5', fmt = 'vasp/POSCAR')) From f5bbfafd13ad51ea7abba3e8ae20ecd70d82e8b7 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 14 Mar 2020 14:12:04 +0800 Subject: [PATCH 100/201] change bad_conf -> bad_box --- dpgen/generator/run.py | 16 +++++++------- .../bad.height.POSCAR | 0 .../bad.length.lammpstrj | 0 .../good.lammpstrj | 0 tests/generator/test_check_bad_box.py | 22 +++++++++++++++++++ tests/generator/test_check_bad_conf.py | 22 ------------------- 6 files changed, 30 insertions(+), 30 deletions(-) rename tests/generator/{check_bad_conf => check_bad_box}/bad.height.POSCAR (100%) rename tests/generator/{check_bad_conf => check_bad_box}/bad.length.lammpstrj (100%) rename tests/generator/{check_bad_conf => check_bad_box}/good.lammpstrj (100%) create mode 100644 tests/generator/test_check_bad_box.py delete mode 100644 tests/generator/test_check_bad_conf.py diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index b56c1081f..ea524ae53 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -992,7 +992,7 @@ def _to_face_dist(box_): dists.append(vol / np.linalg.norm(vv)) return np.array(dists) -def check_bad_conf(conf_name, +def check_bad_box(conf_name, criteria, fmt = 'lammps/dump'): all_c = criteria.split(';') @@ -1050,7 +1050,7 @@ def _make_fp_vasp_inner (modd_path, # skip save *.out if detailed_report_make_fp is False, default is True detailed_report_make_fp = jdata.get("detailed_report_make_fp", True) # skip bad conf criteria - skip_bad_conf = jdata.get('fp_skip_bad_conf') + skip_bad_box = jdata.get('fp_skip_bad_box') for ss in system_index : fp_candidate = [] if detailed_report_make_fp: @@ -1120,7 +1120,7 @@ def _make_fp_vasp_inner (modd_path, for ii in fp_rest_failed: fp.write(" ".join([str(nn) for nn in ii]) + "\n") numb_task = min(fp_task_max, len(fp_candidate)) - count_bad_conf = 0 + count_bad_box = 0 for cc in range(numb_task) : tt = fp_candidate[cc][0] ii = fp_candidate[cc][1] @@ -1128,10 +1128,10 @@ def _make_fp_vasp_inner (modd_path, conf_name = os.path.join(tt, "traj") conf_name = os.path.join(conf_name, str(ii) + '.lammpstrj') conf_name = os.path.abspath(conf_name) - if skip_bad_conf is not None: - skip = check_bad_conf(conf_name, skip_bad_conf) + if skip_bad_box is not None: + skip = check_bad_box(conf_name, skip_bad_box) if skip: - count_bad_conf += 1 + count_bad_box += 1 continue # link job.json @@ -1159,8 +1159,8 @@ def _make_fp_vasp_inner (modd_path, for pair in fp_link_files : os.symlink(pair[0], pair[1]) os.chdir(cwd) - if count_bad_conf > 0: - dlog.info("system {0:s} skipped {1:6d} bad confs, {2:6d} remains".format(ss, count_bad_conf, numb_task - count_bad_conf)) + if count_bad_box > 0: + dlog.info("system {0:s} skipped {1:6d} bad confs, {2:6d} remains".format(ss, count_bad_box, numb_task - count_bad_box)) if cluster_cutoff is None: cwd = os.getcwd() for ii in fp_tasks: diff --git a/tests/generator/check_bad_conf/bad.height.POSCAR b/tests/generator/check_bad_box/bad.height.POSCAR similarity index 100% rename from tests/generator/check_bad_conf/bad.height.POSCAR rename to tests/generator/check_bad_box/bad.height.POSCAR diff --git a/tests/generator/check_bad_conf/bad.length.lammpstrj b/tests/generator/check_bad_box/bad.length.lammpstrj similarity index 100% rename from tests/generator/check_bad_conf/bad.length.lammpstrj rename to tests/generator/check_bad_box/bad.length.lammpstrj diff --git a/tests/generator/check_bad_conf/good.lammpstrj b/tests/generator/check_bad_box/good.lammpstrj similarity index 100% rename from tests/generator/check_bad_conf/good.lammpstrj rename to tests/generator/check_bad_box/good.lammpstrj diff --git a/tests/generator/test_check_bad_box.py b/tests/generator/test_check_bad_box.py new file mode 100644 index 000000000..ba210128e --- /dev/null +++ b/tests/generator/test_check_bad_box.py @@ -0,0 +1,22 @@ +import os,sys,json,glob,shutil +import dpdata +import numpy as np +import unittest + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +__package__ = 'generator' +from .context import check_bad_box + +class TestCheckBadBox(unittest.TestCase): + def test_length_ratio(self): + dirname = os.path.dirname(__file__) + conf_bad = os.path.join(dirname, 'check_bad_box', 'bad.length.lammpstrj') + conf_good = os.path.join(dirname, 'check_bad_box', 'good.lammpstrj') + self.assertTrue(check_bad_box(conf_bad, 'length_ratio:5')) + self.assertFalse(check_bad_box(conf_good, 'length_ratio:5')) + + def test_height_ratio(self): + dirname = os.path.dirname(__file__) + conf_bad = os.path.join(dirname, 'check_bad_box', 'bad.height.POSCAR') + self.assertTrue(check_bad_box(conf_bad, 'height_ratio:5', fmt = 'vasp/POSCAR')) + self.assertFalse(check_bad_box(conf_bad, 'length_ratio:5', fmt = 'vasp/POSCAR')) diff --git a/tests/generator/test_check_bad_conf.py b/tests/generator/test_check_bad_conf.py deleted file mode 100644 index f75af662b..000000000 --- a/tests/generator/test_check_bad_conf.py +++ /dev/null @@ -1,22 +0,0 @@ -import os,sys,json,glob,shutil -import dpdata -import numpy as np -import unittest - -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -__package__ = 'generator' -from .context import check_bad_conf - -class TestCheckBadConf(unittest.TestCase): - def test_length_ratio(self): - dirname = os.path.dirname(__file__) - conf_bad = os.path.join(dirname, 'check_bad_conf', 'bad.length.lammpstrj') - conf_good = os.path.join(dirname, 'check_bad_conf', 'good.lammpstrj') - self.assertTrue(check_bad_conf(conf_bad, 'length_ratio:5')) - self.assertFalse(check_bad_conf(conf_good, 'length_ratio:5')) - - def test_height_ratio(self): - dirname = os.path.dirname(__file__) - conf_bad = os.path.join(dirname, 'check_bad_conf', 'bad.height.POSCAR') - self.assertTrue(check_bad_conf(conf_bad, 'height_ratio:5', fmt = 'vasp/POSCAR')) - self.assertFalse(check_bad_conf(conf_bad, 'length_ratio:5', fmt = 'vasp/POSCAR')) From b7c3818046d950ec66b4f1fef3e9fe43e7c7ab2f Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 14 Mar 2020 14:13:29 +0800 Subject: [PATCH 101/201] fixed message --- dpgen/generator/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index ea524ae53..38ce80dab 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1049,7 +1049,7 @@ def _make_fp_vasp_inner (modd_path, cluster_cutoff = jdata['cluster_cutoff'] if jdata.get('use_clusters', False) else None # skip save *.out if detailed_report_make_fp is False, default is True detailed_report_make_fp = jdata.get("detailed_report_make_fp", True) - # skip bad conf criteria + # skip bad box criteria skip_bad_box = jdata.get('fp_skip_bad_box') for ss in system_index : fp_candidate = [] @@ -1160,7 +1160,7 @@ def _make_fp_vasp_inner (modd_path, os.symlink(pair[0], pair[1]) os.chdir(cwd) if count_bad_box > 0: - dlog.info("system {0:s} skipped {1:6d} bad confs, {2:6d} remains".format(ss, count_bad_box, numb_task - count_bad_box)) + dlog.info("system {0:s} skipped {1:6d} confs with bad box, {2:6d} remains".format(ss, count_bad_box, numb_task - count_bad_box)) if cluster_cutoff is None: cwd = os.getcwd() for ii in fp_tasks: From f78971e041c530c236533df2e979373a0e451256 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 15 Mar 2020 19:59:52 +0800 Subject: [PATCH 102/201] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8E=92=E9=98=9F?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=BC=B9=E6=80=A7?= =?UTF-8?q?=E4=BE=9B=E5=BA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dpgen/dispatcher/ALI.py | 340 +++++++++++++----- .../machine/DeePMD-kit-1.0/machine-ali.json | 59 ++- 2 files changed, 284 insertions(+), 115 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index f63f56ad8..302162eee 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -4,7 +4,17 @@ from aliyunsdkcore.acs_exception.exceptions import ServerException from aliyunsdkecs.request.v20140526.RunInstancesRequest import RunInstancesRequest from aliyunsdkecs.request.v20140526.DeleteInstancesRequest import DeleteInstancesRequest -import time, json, os, glob +from aliyunsdkecs.request.v20140526.DescribeAutoProvisioningGroupInstancesRequest import DescribeAutoProvisioningGroupInstancesRequest +from aliyunsdkecs.request.v20140526.CreateAutoProvisioningGroupRequest import CreateAutoProvisioningGroupRequest +from aliyunsdkecs.request.v20140526.DeleteAutoProvisioningGroupRequest import DeleteAutoProvisioningGroupRequest +from aliyunsdkecs.request.v20140526.ModifyAutoProvisioningGroupRequest import ModifyAutoProvisioningGroupRequest +from aliyunsdkecs.request.v20140526.DeleteLaunchTemplateRequest import DeleteLaunchTemplateRequest +from aliyunsdkvpc.request.v20160428.DescribeVpcsRequest import DescribeVpcsRequest +from aliyunsdkecs.request.v20140526.DescribeLaunchTemplatesRequest import DescribeLaunchTemplatesRequest +from aliyunsdkecs.request.v20140526.CreateLaunchTemplateRequest import CreateLaunchTemplateRequest +from aliyunsdkecs.request.v20140526.DescribeImagesRequest import DescribeImagesRequest +from aliyunsdkecs.request.v20140526.DescribeSecurityGroupsRequest import DescribeSecurityGroupsRequest +import time, json, os, glob, string, random from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks, JobRecord from os.path import join from dpgen import dlog @@ -40,6 +50,9 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.job_handlers = None self.task_chunks = None self.adata = adata + self.apg_id = None + self.template_id = None + self.vsw_id = None self.regionID = adata["regionID"] self.client = AcsClient(adata["AccessKey_ID"], adata["AccessKey_Secret"], self.regionID) self.mdata_resources = mdata_resources @@ -50,11 +63,155 @@ def init(self, work_path, tasks, group_size): if self.check_restart(work_path, tasks, group_size): pass else: - self.alloc_machine() + self.create_ess() self.dispatchers = self.make_dispatchers() + def create_ess(self): + img_id = self.get_image_id(self.adata["img_name"]) + sg_id, vpc_id = self.get_sg_vpc_id() + self.template_id = self.create_template(img_id, sg_id, vpc_id) + self.vsw_id = self.get_vsw_id(vpc_id) + self.apg_id = self.create_apg() + dlog.info("begin to create ess") + time.sleep(60) + self.instance_list = self.describe_apg_instances() + self.ip_list = self.get_ip(self.instance_list) + + def delete_apg(self): + request = DeleteAutoProvisioningGroupRequest() + request.set_accept_format('json') + request.set_AutoProvisioningGroupId(self.apg_id) + request.set_TerminateInstances(True) + response = self.client.do_action_with_exception(request) + + def create_apg(self): + request = CreateAutoProvisioningGroupRequest() + request.set_accept_format('json') + request.set_TotalTargetCapacity(str(self.nchunks)) + request.set_LaunchTemplateId(self.template_id) + request.set_AutoProvisioningGroupName(''.join(random.choice(string.ascii_uppercase) for _ in range(20))) + request.set_AutoProvisioningGroupType("maintain") + request.set_SpotAllocationStrategy("lowest-price") + request.set_SpotInstanceInterruptionBehavior("terminate") + request.set_SpotInstancePoolsToUseCount(1) + request.set_ExcessCapacityTerminationPolicy("termination") + request.set_TerminateInstances(True) + request.set_PayAsYouGoTargetCapacity("0") + request.set_SpotTargetCapacity(str(self.nchunks)) + config = self.generate_config() + request.set_LaunchTemplateConfigs(config) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + with open('apg_id.json', 'w') as fp: + json.dump({'apg_id': response["AutoProvisioningGroupId"]}, fp, indent=4) + return response["AutoProvisioningGroupId"] + + def update_instance_list(self): + instance_list = self.describe_apg_instances() + ip_list = [] + if len(set(instance_list) - set(self.instance_list)) > 0: + self.instance_list += list(set(instance_list) - set(self.instance_list)) + ip_list = self.get_ip(list(set(instance_list) - set(self.instance_list))) + self.ip_list += ip_list + return True + return False + + def describe_apg_instances(self): + request = DescribeAutoProvisioningGroupInstancesRequest() + request.set_accept_format('json') + request.set_AutoProvisioningGroupId(self.apg_id) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + instance_list = [] + for ins in response["Instances"]["Instance"]: + instance_list.append(ins["InstanceId"]) + return instance_list + + def generate_config(self): + machine_config = self.adata["machine_type_price"] + config = [] + for conf in machine_config: + for vsw in self.vsw_id: + tmp = { + "InstanceType": conf["machine_type"], + "MaxPrice": str(conf["price_limit"] * conf["numb"]), + "VSwitchId": vsw, + "WeightedCapacity": "1", + "Priority": str(conf["priority"]) + } + config.append(tmp) + return config + + def create_template(self, image_id, sg_id, vpc_id): + request = CreateLaunchTemplateRequest() + request.set_accept_format('json') + request.set_LaunchTemplateName(''.join(random.choice(string.ascii_uppercase) for _ in range(20))) + request.set_ImageId(image_id) + request.set_ImageOwnerAlias("self") + request.set_PasswordInherit(True) + request.set_InstanceType("ecs.c6.large") + request.set_SecurityGroupId(sg_id) + request.set_VpcId(vpc_id) + request.set_InternetMaxBandwidthIn(10) + request.set_InternetMaxBandwidthOut(10) + request.set_SystemDiskCategory("cloud_efficiency") + request.set_SystemDiskSize(40) + request.set_IoOptimized("optimized") + request.set_InstanceChargeType("PostPaid") + request.set_NetworkType("vpc") + request.set_SpotStrategy("SpotWithPriceLimit") + request.set_SpotPriceLimit(100) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + return response["LaunchTemplateId"] + + def delete_template(self): + request = DeleteLaunchTemplateRequest() + request.set_accept_format('json') + request.set_LaunchTemplateId(self.template_id) + response = self.client.do_action_with_exception(request) + + def get_image_id(self, img_name): + request = DescribeImagesRequest() + request.set_accept_format('json') + request.set_ImageOwnerAlias("self") + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for img in response["Images"]["Image"]: + if img["ImageName"] == img_name: + return img["ImageId"] + + def get_sg_vpc_id(self): + request = DescribeSecurityGroupsRequest() + request.set_accept_format('json') + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for sg in response["SecurityGroups"]["SecurityGroup"]: + if sg["SecurityGroupName"] == "sg": + return sg["SecurityGroupId"], sg["VpcId"] + + def get_vsw_id(self, vpc_id): + request = DescribeVpcsRequest() + request.set_accept_format('json') + request.set_VpcId(vpc_id) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for vpc in response["Vpcs"]["Vpc"]: + if vpc["VpcId"] == vpc_id: + return vpc["VSwitchIds"]["VSwitchId"] + + def change_apg_capasity(self, capasity): + request = ModifyAutoProvisioningGroupRequest() + request.set_accept_format('json') + request.set_TotalTargetCapacity(str(capasity)) + request.set_SpotTargetCapacity(str(capasity)) + response = self.client.do_action_with_exception(request) + + def spot_data_callback(): + pass + def check_restart(self, work_path, tasks, group_size): - if os.path.exists('machine_record.json'): + if os.path.exists('apg_id.json'): dispatchers = [] instance_list = [] ip_list = [] @@ -87,81 +244,43 @@ def check_restart(self, work_path, tasks, group_size): else: return False - def alloc_machine(self): - for district, type_num in self.adata["avail_resources"].items(): - for machine_type, number in type_num.items(): - if self.nchunks > number: - self.nchunks -= number - self.create_machine(machine_type, number, district) - elif self.nchunks > 0: - self.create_machine(machine_type, self.nchunks, district) - time.sleep(60) - self.get_ip() - - def create_machine(self, machine_type, number, district): - request = RunInstancesRequest() - request.set_accept_format('json') - request.set_UniqueSuffix(True) - request.set_Password(self.mdata_machine["password"]) - request.set_InstanceName(self.adata['instance_name']) - - if self.mdata_resources['partition'] == 'gpu': - template_name = 'gpu_%s_%s_%s_%s' % (self.mdata_resources['numb_gpu'], self.adata["pay_strategy"], district, machine_type) - elif self.mdata_resources['partition'] == 'cpu': - template_name = 'cpu_%s_%s_%s_%s' % (self.mdata_resources['task_per_node'], self.adata["pay_strategy"], district, machine_type) - elif self.mdata_resources['partition'] == 'gmx': - template_name = 'gmx_%s_%s_%s_%s' % (self.mdata_resources['numb_gpu'], self.adata["pay_strategy"], district, machine_type) - request.set_LaunchTemplateName(template_name) - - if number <= 100 and number > 0: - request.set_Amount(number) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - else: - iteration = number // 100 - for i in range(iteration): - request.set_Amount(100) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - if number - iteration * 100 != 0: - request.set_Amount(number - iteration * 100) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - for instanceID in response["InstanceIdSets"]["InstanceIdSet"]: - self.instance_list.append(instanceID) - - def get_ip(self): + def get_ip(self, instance_list): request = DescribeInstancesRequest() request.set_accept_format('json') - if len(self.instance_list) <= 10: - for i in range(len(self.instance_list)): - request.set_InstanceIds([self.instance_list[i]]) + ip_list = [] + if len(instance_list) <= 10: + for i in range(len(instance_list)): + request.set_InstanceIds([instance_list[i]]) response = self.client.do_action_with_exception(request) response = json.loads(response) - self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) else: - iteration = len(self.instance_list) // 10 + iteration = len(instance_list) // 10 for i in range(iteration): for j in range(10): - request.set_InstanceIds([self.instance_list[i*10+j]]) + request.set_InstanceIds([instance_list[i*10+j]]) response = self.client.do_action_with_exception(request) response = json.loads(response) - self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - if len(self.instance_list) - iteration * 10 != 0: - for j in range(len(self.instance_list) - iteration * 10): - request.set_InstanceIds([self.instance_list[iteration*10+j]]) + ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + if len(instance_list) - iteration * 10 != 0: + for j in range(len(instance_list) - iteration * 10): + request.set_InstanceIds([instance_list[iteration*10+j]]) response = self.client.do_action_with_exception(request) response = json.loads(response) - self.ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) dlog.info('create machine successfully, following are the ip addresses') - for ip in self.ip_list: + for ip in ip_list: dlog.info(ip) - with open('machine_record.json', 'w') as fp: - json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) + return ip_list + # with open('machine_record.json', 'w') as fp: + # json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) + + def get_finished_job_num(self): + finished_num = 0 + for ii in range(len(self.dispatchers)): + if self.dispatchers[ii][1] == "finished": + finished_num += 1 + return finished_num def run_jobs(self, resources, @@ -178,53 +297,82 @@ def run_jobs(self, if not self.task_chunks: self.task_chunks = _split_tasks(tasks, group_size) self.job_handlers = [] - for ii in range(len(self.dispatchers)): - job_handler = self.dispatchers[ii].submit_jobs(resources, - command, - work_path, - self.task_chunks[ii], - group_size, - forward_common_files, - forward_task_files, - backward_task_files, - forward_task_deference, - outlog, - errlog) - self.job_handlers.append(job_handler) + for ii in range(self.nchunks): + if self.dispatchers[ii][1] == "working": + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers.append(job_handler) while True: - for ii in range(len(self.dispatchers)): - if self.dispatchers[ii].all_finished(self.job_handlers[ii]): + for ii in range(self.nchunks): + if self.dispatchers[ii][1] == "working" and self.dispatchers[ii][0].all_finished(self.job_handlers[ii]): + self.dispatchers[ii][1] = "finished" + self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) self.delete(ii) - break - if len(self.dispatchers) == 0: + elif self.dispatchers[ii][1] == "finished": + continue + elif self.dispatchers[ii][1] == "unalloc" and self.update_instance_list(): + if ii < len(self.ip_list): + profile = self.mdata_machine.copy() + profile["hostname"] = self.ip_list[ii] + disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + self.dispatchers[ii][0] = disp + self.dispatchers[ii][1] == "working" + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers.append(job_handler) + if self.check_dispatcher_finished(): + os.remove('apg_id.json') + self.delete_template() + self.delete_apg() break else: time.sleep(10) +# status = ["unalloc", "working", "finished"] + + def check_dispatcher_finished(self): + for ii in range(len(self.dispatchers)): + if self.dispatchers[ii][1] == "unalloc" or self.dispatchers[ii][1] == "working": + return False + return True + def delete(self, ii): request = DeleteInstancesRequest() request.set_accept_format('json') request.set_InstanceIds([self.instance_list[ii]]) request.set_Force(True) response = self.client.do_action_with_exception(request) - self.nchunks -= 1 - self.instance_list.pop(ii) - self.ip_list.pop(ii) - self.dispatchers.pop(ii) - self.job_handlers.pop(ii) - if len(self.ip_list) == 0: - os.remove('machine_record.json') - else: - with open('machine_record.json', 'w') as fp: - json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) def make_dispatchers(self): dispatchers = [] + if len(self.ip_list) < self.nchunks: + dlog.info("machine resources unsuffient, %d jobs are running, %d jobs are pending" %(len(self.ip_list), self.nchunks-len(self.ip_list))) for ii in range(self.nchunks): - profile = self.mdata_machine.copy() - profile['hostname'] = self.ip_list[ii] - profile['instance_id'] = self.instance_list[ii] - disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + if ii >= len(self.ip_list): + disp = [None, "unalloc"] + else: + profile = self.mdata_machine.copy() + profile['hostname'] = self.ip_list[ii] + profile['instance_id'] = self.instance_list[ii] + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] dispatchers.append(disp) return dispatchers diff --git a/examples/machine/DeePMD-kit-1.0/machine-ali.json b/examples/machine/DeePMD-kit-1.0/machine-ali.json index 7e8e58050..e539ee74e 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-ali.json +++ b/examples/machine/DeePMD-kit-1.0/machine-ali.json @@ -4,24 +4,33 @@ "machine": { "batch": "shell", "hostname": "", - "password": "", + "password": "975481DING!", "port": 22, "username": "root", "work_path": "/root/dpgen_work", "ali_auth": { "AccessKey_ID":"", "AccessKey_Secret":"", - "regionID": "cn-beijing", - "avail_resources":{ - "H": {"v100": 20} - }, + "regionID": "cn-shenzhen", + "img_name": "kit", + "machine_type_price": [ + {"machine_type": "ecs.gn6v-c8g1.2xlarge", "price_limit": 20.00, "numb": 1, "priority": 0}, + {"machine_type": "ecs.gn5-c4g1.xlarge", "price_limit": 20.00, "numb": 1, "priority": 1} + ], "instance_name": "CH4", "pay_strategy": "spot" } }, "resources": { - "numb_gpu": 1, - "partition": "gpu" + "numb_gpu": 1, + "numb_node": 1, + "task_per_node": 12, + "partition": "gpu", + "exclude_list": [], + "mem_limit": 32, + "source_list": [], + "module_list": [], + "time_limit": "23:0:0" }, "command": "/root/deepmd-kit/bin/dp", "group_size": 2 @@ -33,24 +42,32 @@ "machine": { "batch": "shell", "hostname": "", - "password": "", + "password": "975481DING!", "port": 22, "username": "root", "work_path": "/root/dpgen_work", "ali_auth": { "AccessKey_ID":"", "AccessKey_Secret":"", - "regionID": "cn-beijing", - "avail_resources":{ - "H": {"v100": 20} - }, + "regionID": "cn-shenzhen", + "img_name": "kit", + "machine_type_price": [ + {"machine_type": "ecs.gn6v-c8g1.2xlarge", "price_limit": 20.00, "numb": 1, "priority": 0}, + {"machine_type": "ecs.gn5-c4g1.xlarge", "price_limit": 20.00, "numb": 1, "priority": 1} + ], "instance_name": "CH4", "pay_strategy": "spot" } }, "resources": { "numb_gpu": 1, - "partition": "gpu" + "task_per_node": 4, + "partition": "gpu", + "exclude_list": [], + "mem_limit": 11, + "source_list": [], + "module_list": [], + "time_limit": "23:0:0" }, "command": "/root/deepmd-kit/bin/lmp", "group_size": 2 @@ -62,18 +79,19 @@ "machine": { "batch": "shell", "hostname": "", - "password": "", + "password": "975481DING!", "port": 22, "username": "root", "work_path": "/root/dpgen_work", "ali_auth": { "AccessKey_ID":"", "AccessKey_Secret":"", - "regionID": "cn-huhehaote", - "avail_resources":{ - "A": {"c5": 500, "c6": 160}, - "B": {"c6": 200} - }, + "regionID": "cn-shenzhen", + "img_name": "vasp", + "machine_type_price": [ + {"machine_type": "ecs.c6.4xlarge", "price_limit": 0.2, "numb": 16, "priority": 0}, + {"machine_type": "ecs.g6.4xlarge", "price_limit": 0.2, "numb": 16, "priority": 1} + ], "instance_name": "CH4", "pay_strategy": "spot" } @@ -83,6 +101,7 @@ "task_per_node": 16, "with_mpi": "false", "source_list": ["/opt/intel/parallel_studio_xe_2018/psxevars.sh"], + "module_list": [], "partition": "cpu", "envs" : {"PATH" : "/root/deepmd-pkg/vasp.5.4.4/bin:$PATH"} }, @@ -92,3 +111,5 @@ ] } + + From 73577ef17094fd2c0740c10a445c5082a0ce5a65 Mon Sep 17 00:00:00 2001 From: haidi Date: Mon, 16 Mar 2020 09:29:03 +0800 Subject: [PATCH 103/201] support restart for init_data --- dpgen/data/gen.py | 50 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/dpgen/data/gen.py b/dpgen/data/gen.py index 033058d2b..470710601 100644 --- a/dpgen/data/gen.py +++ b/dpgen/data/gen.py @@ -29,18 +29,24 @@ -def create_path (path) : +def create_path (path,back=False) : if path[-1] != "/": path += '/' if os.path.isdir(path) : - dirname = os.path.dirname(path) - counter = 0 - while True : - bk_dirname = dirname + ".bk%03d" % counter - if not os.path.isdir(bk_dirname) : - shutil.move (dirname, bk_dirname) - break - counter += 1 + if back: + dirname = os.path.dirname(path) + counter = 0 + while True : + bk_dirname = dirname + ".bk%03d" % counter + if not os.path.isdir(bk_dirname) : + shutil.move (dirname, bk_dirname) + break + counter += 1 + os.makedirs (path) + return path + else: + return path + os.makedirs (path) return path @@ -240,7 +246,10 @@ def make_super_cell_poscar(jdata) : cwd = os.getcwd() to_file = os.path.abspath(to_file) os.chdir(path_work) - os.symlink(os.path.relpath(to_file), 'POSCAR') + try: + os.symlink(os.path.relpath(to_file), 'POSCAR') + except FileExistsError: + pass os.chdir(cwd) def make_combines (dim, natoms) : @@ -319,9 +328,15 @@ def make_vasp_relax (jdata, mdata) : for ss in sys_list: os.chdir(ss) ln_src = os.path.relpath(os.path.join(work_dir,'INCAR')) - os.symlink(ln_src, 'INCAR') + try: + os.symlink(ln_src, 'INCAR') + except FileExistsError: + pass ln_src = os.path.relpath(os.path.join(work_dir,'POTCAR')) - os.symlink(ln_src, 'POTCAR') + try: + os.symlink(ln_src, 'POTCAR') + except FileExistsError: + pass os.chdir(work_dir) os.chdir(cwd) @@ -454,8 +469,15 @@ def make_vasp_md(jdata) : shutil.copy2 (init_pos, 'POSCAR') file_incar = os.path.join(path_md, 'INCAR') file_potcar = os.path.join(path_md, 'POTCAR') - os.symlink(os.path.relpath(file_incar), 'INCAR') - os.symlink(os.path.relpath(file_potcar), 'POTCAR') + try: + os.symlink(os.path.relpath(file_incar), 'INCAR') + except FileExistsError: + pass + try: + os.symlink(os.path.relpath(file_potcar), 'POTCAR') + except FileExistsError: + pass + os.chdir(cwd) def coll_vasp_md(jdata) : From 68711be45cf186985aa4b5eaf6b5669ead59c136 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 17 Mar 2020 00:04:22 +0800 Subject: [PATCH 104/201] add job queuing function and implement ALI-ess interface. --- dpgen/dispatcher/ALI.py | 43 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 302162eee..d05ea49f4 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -73,7 +73,7 @@ def create_ess(self): self.vsw_id = self.get_vsw_id(vpc_id) self.apg_id = self.create_apg() dlog.info("begin to create ess") - time.sleep(60) + time.sleep(90) self.instance_list = self.describe_apg_instances() self.ip_list = self.get_ip(self.instance_list) @@ -221,22 +221,28 @@ def check_restart(self, work_path, tasks, group_size): task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] nchunks = len(task_chunks) for ii in range(nchunks): - job_record = JobRecord(work_path, task_chunks, fname = 'jr.%.06d.json' % ii) - cur_chunk = task_chunks[ii] - cur_hash = task_hashes[ii] - if not job_record.check_finished(cur_hash): - self.task_chunks.append(cur_chunk) - with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: - jr = json.load(fp) - ip = jr[cur_hash]['context'][3] - instance_id = jr[cur_hash]['context'][4] - ip_list.append(ip) - instance_list.append(instance_id) - profile = self.mdata_machine.copy() - profile['hostname'] = ip - profile['instance_id'] = instance_id - disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) - dispatchers.append(disp) + fn = 'jr.%.06d.json' % ii + if not os.path.exists(os.path.join(os.path.abspath(work_path), fn)): + dispatchers.append([None, "unalloc"]) + else: + job_record = JobRecord(work_path, task_chunks, fname = fn) + cur_chunk = task_chunks[ii] + cur_hash = task_hashes[ii] + if not job_record.check_finished(cur_hash): + self.task_chunks.append(cur_chunk) + with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: + jr = json.load(fp) + ip = jr[cur_hash]['context'][3] + instance_id = jr[cur_hash]['context'][4] + ip_list.append(ip) + instance_list.append(instance_id) + profile = self.mdata_machine.copy() + profile['hostname'] = ip + profile['instance_id'] = instance_id + disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + dispatchers.append([disp, "working"]) + else: + dispatchers.append([None, "finished"]) self.dispatchers = dispatchers self.instance_list = instance_list self.ip_list = ip_list @@ -292,6 +298,7 @@ def run_jobs(self, forward_task_files, backward_task_files, forward_task_deference = True, + mark_failure = False, outlog = 'log', errlog = 'err'): if not self.task_chunks: @@ -313,7 +320,7 @@ def run_jobs(self, self.job_handlers.append(job_handler) while True: for ii in range(self.nchunks): - if self.dispatchers[ii][1] == "working" and self.dispatchers[ii][0].all_finished(self.job_handlers[ii]): + if self.dispatchers[ii][1] == "working" and self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.dispatchers[ii][1] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) self.delete(ii) From dd9f5b11a2f8fe366f6b650c653607ce6d5cd258 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 17 Mar 2020 00:41:52 +0800 Subject: [PATCH 105/201] fix bug --- dpgen/dispatcher/ALI.py | 49 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index d05ea49f4..7597974ef 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -46,8 +46,8 @@ class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = [] self.instance_list = [] - self.dispatchers = None - self.job_handlers = None + self.dispatchers = [] + self.job_handlers = [] self.task_chunks = None self.adata = adata self.apg_id = None @@ -201,8 +201,11 @@ def get_vsw_id(self, vpc_id): return vpc["VSwitchIds"]["VSwitchId"] def change_apg_capasity(self, capasity): + if capasity == 0: + pass request = ModifyAutoProvisioningGroupRequest() request.set_accept_format('json') + request.set_AutoProvisioningGroupId(self.apg_id) request.set_TotalTargetCapacity(str(capasity)) request.set_SpotTargetCapacity(str(capasity)) response = self.client.do_action_with_exception(request) @@ -212,40 +215,43 @@ def spot_data_callback(): def check_restart(self, work_path, tasks, group_size): if os.path.exists('apg_id.json'): - dispatchers = [] - instance_list = [] - ip_list = [] - self.task_chunks = [] - task_chunks = _split_tasks(tasks, group_size) - task_chunks_str = ['+'.join(ii) for ii in task_chunks] + with open('apg_id.json') as fp: + apg = json.load(fp) + self.apg_id = apg["apg_id"] + self.task_chunks = _split_tasks(tasks, group_size) + task_chunks_str = ['+'.join(ii) for ii in self.task_chunks] task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] - nchunks = len(task_chunks) + nchunks = len(self.task_chunks) for ii in range(nchunks): fn = 'jr.%.06d.json' % ii if not os.path.exists(os.path.join(os.path.abspath(work_path), fn)): - dispatchers.append([None, "unalloc"]) + self.dispatchers.append([None, "unalloc"]) else: - job_record = JobRecord(work_path, task_chunks, fname = fn) - cur_chunk = task_chunks[ii] + job_record = JobRecord(work_path, self.task_chunks, fname = fn) + cur_chunk = self.task_chunks[ii] cur_hash = task_hashes[ii] if not job_record.check_finished(cur_hash): - self.task_chunks.append(cur_chunk) with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: jr = json.load(fp) ip = jr[cur_hash]['context'][3] instance_id = jr[cur_hash]['context'][4] - ip_list.append(ip) - instance_list.append(instance_id) + self.ip_list.append(ip) + self.instance_list.append(instance_id) profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) - dispatchers.append([disp, "working"]) + self.dispatchers.append([disp, "working"]) + self.job_handlers.append(None) else: - dispatchers.append([None, "finished"]) - self.dispatchers = dispatchers - self.instance_list = instance_list - self.ip_list = ip_list + with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: + jr = json.load(fp) + ip = jr[cur_hash]['context'][3] + instance_id = jr[cur_hash]['context'][4] + self.ip_list.append(ip) + self.instance_list.append(instance_id) + self.dispatchers.append([None, "finished"]) + self.job_handlers.append(None) return True else: return False @@ -303,7 +309,6 @@ def run_jobs(self, errlog = 'err'): if not self.task_chunks: self.task_chunks = _split_tasks(tasks, group_size) - self.job_handlers = [] for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working": job_handler = self.dispatchers[ii][0].submit_jobs(resources, @@ -317,7 +322,7 @@ def run_jobs(self, forward_task_deference, outlog, errlog) - self.job_handlers.append(job_handler) + self.job_handlers[ii] = job_handler while True: for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working" and self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): From adf4a993f938926905250f42e320ed5b2779f806 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 17 Mar 2020 00:55:21 +0800 Subject: [PATCH 106/201] fix bug --- dpgen/dispatcher/ALI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 7597974ef..903b07e85 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -254,6 +254,8 @@ def check_restart(self, work_path, tasks, group_size): self.job_handlers.append(None) return True else: + self.task_chunks = _split_tasks(tasks, group_size) + self.job_handlers = [None for i in range(len(self.task_chunks))] return False def get_ip(self, instance_list): @@ -307,8 +309,6 @@ def run_jobs(self, mark_failure = False, outlog = 'log', errlog = 'err'): - if not self.task_chunks: - self.task_chunks = _split_tasks(tasks, group_size) for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working": job_handler = self.dispatchers[ii][0].submit_jobs(resources, From ea3a292d5936a824c18ccb7b34d6f29a95b353c3 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 17 Mar 2020 14:11:13 +0800 Subject: [PATCH 107/201] fix bug add task queuing function and implement ALI-ess interface. solving problem in job restart that machine can't be released before jr.json generates,which means you can restart the dpgen in any situations. --- dpgen/dispatcher/ALI.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 903b07e85..a52696521 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -47,7 +47,7 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = [] self.instance_list = [] self.dispatchers = [] - self.job_handlers = [] + self.job_handlers = [None for i in range(nchunks)] self.task_chunks = None self.adata = adata self.apg_id = None @@ -110,11 +110,9 @@ def update_instance_list(self): instance_list = self.describe_apg_instances() ip_list = [] if len(set(instance_list) - set(self.instance_list)) > 0: - self.instance_list += list(set(instance_list) - set(self.instance_list)) ip_list = self.get_ip(list(set(instance_list) - set(self.instance_list))) + self.instance_list += list(set(instance_list) - set(self.instance_list)) self.ip_list += ip_list - return True - return False def describe_apg_instances(self): request = DescribeAutoProvisioningGroupInstancesRequest() @@ -201,13 +199,12 @@ def get_vsw_id(self, vpc_id): return vpc["VSwitchIds"]["VSwitchId"] def change_apg_capasity(self, capasity): - if capasity == 0: - pass request = ModifyAutoProvisioningGroupRequest() request.set_accept_format('json') request.set_AutoProvisioningGroupId(self.apg_id) request.set_TotalTargetCapacity(str(capasity)) request.set_SpotTargetCapacity(str(capasity)) + request.set_PayAsYouGoTargetCapacity("0") response = self.client.do_action_with_exception(request) def spot_data_callback(): @@ -242,7 +239,6 @@ def check_restart(self, work_path, tasks, group_size): profile['instance_id'] = instance_id disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) self.dispatchers.append([disp, "working"]) - self.job_handlers.append(None) else: with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: jr = json.load(fp) @@ -251,11 +247,11 @@ def check_restart(self, work_path, tasks, group_size): self.ip_list.append(ip) self.instance_list.append(instance_id) self.dispatchers.append([None, "finished"]) - self.job_handlers.append(None) + #dlog.info(self.ip_list) + #dlog.info(self.dispatchers) return True else: self.task_chunks = _split_tasks(tasks, group_size) - self.job_handlers = [None for i in range(len(self.task_chunks))] return False def get_ip(self, instance_list): @@ -282,12 +278,10 @@ def get_ip(self, instance_list): response = self.client.do_action_with_exception(request) response = json.loads(response) ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - dlog.info('create machine successfully, following are the ip addresses') + #dlog.info('create machine successfully, following are the ip addresses') for ip in ip_list: dlog.info(ip) return ip_list - # with open('machine_record.json', 'w') as fp: - # json.dump({'ip': self.ip_list, 'instance_id': self.instance_list}, fp, indent=4) def get_finished_job_num(self): finished_num = 0 @@ -327,17 +321,20 @@ def run_jobs(self, for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working" and self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.dispatchers[ii][1] = "finished" - self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) self.delete(ii) + self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) elif self.dispatchers[ii][1] == "finished": continue - elif self.dispatchers[ii][1] == "unalloc" and self.update_instance_list(): + elif self.dispatchers[ii][1] == "unalloc": + self.update_instance_list() + #dlog.info(self.instance_list) + #dlog.info(self.ip_list) if ii < len(self.ip_list): profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] - disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) - self.dispatchers[ii][0] = disp - self.dispatchers[ii][1] == "working" + profile["instance_id"] = self.instance_list[ii] + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + self.dispatchers[ii] = disp job_handler = self.dispatchers[ii][0].submit_jobs(resources, command, work_path, @@ -349,7 +346,7 @@ def run_jobs(self, forward_task_deference, outlog, errlog) - self.job_handlers.append(job_handler) + self.job_handlers[ii] = job_handler if self.check_dispatcher_finished(): os.remove('apg_id.json') self.delete_template() From 339a7dd513208946be8348d2a4498d53108996fb Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 17 Mar 2020 16:39:44 +0800 Subject: [PATCH 108/201] fix bug --- dpgen/dispatcher/ALI.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index a52696521..7d1b2ba8b 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -108,21 +108,25 @@ def create_apg(self): def update_instance_list(self): instance_list = self.describe_apg_instances() - ip_list = [] - if len(set(instance_list) - set(self.instance_list)) > 0: - ip_list = self.get_ip(list(set(instance_list) - set(self.instance_list))) - self.instance_list += list(set(instance_list) - set(self.instance_list)) - self.ip_list += ip_list + ip_list = self.get_ip(instance_list) + self.instance_list += list(set(instance_list) - set(self.instance_list)) + self.ip_list += list(set(ip_list) - set(self.ip_list)) + #dlog.info("%d,%d,%d,%d"%(len(instance_list), len(ip_list), len(self.instance_list), len(self.ip_list))) def describe_apg_instances(self): request = DescribeAutoProvisioningGroupInstancesRequest() request.set_accept_format('json') request.set_AutoProvisioningGroupId(self.apg_id) - response = self.client.do_action_with_exception(request) - response = json.loads(response) + request.set_PageSize(100) + iteration = self.nchunks // 100 instance_list = [] - for ins in response["Instances"]["Instance"]: - instance_list.append(ins["InstanceId"]) + for i in range(iteration + 1): + request.set_PageNumber(i+1) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for ins in response["Instances"]["Instance"]: + instance_list.append(ins["InstanceId"]) + #dlog.info(instance_list) return instance_list def generate_config(self): @@ -215,6 +219,7 @@ def check_restart(self, work_path, tasks, group_size): with open('apg_id.json') as fp: apg = json.load(fp) self.apg_id = apg["apg_id"] + dlog.info(self.apg_id) self.task_chunks = _split_tasks(tasks, group_size) task_chunks_str = ['+'.join(ii) for ii in self.task_chunks] task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] @@ -279,8 +284,8 @@ def get_ip(self, instance_list): response = json.loads(response) ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) #dlog.info('create machine successfully, following are the ip addresses') - for ip in ip_list: - dlog.info(ip) + # for ip in ip_list: + # dlog.info(ip) return ip_list def get_finished_job_num(self): @@ -305,6 +310,7 @@ def run_jobs(self, errlog = 'err'): for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working": + dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, command, work_path, @@ -327,14 +333,13 @@ def run_jobs(self, continue elif self.dispatchers[ii][1] == "unalloc": self.update_instance_list() - #dlog.info(self.instance_list) - #dlog.info(self.ip_list) if ii < len(self.ip_list): profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] profile["instance_id"] = self.instance_list[ii] disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] self.dispatchers[ii] = disp + dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, command, work_path, From 0163b222f84569f5a09e0a70f1e9cb477ac7f7fb Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 17 Mar 2020 16:45:44 +0800 Subject: [PATCH 109/201] fix bug --- dpgen/dispatcher/ALI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 7d1b2ba8b..9e25bea90 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -89,7 +89,7 @@ def create_apg(self): request.set_accept_format('json') request.set_TotalTargetCapacity(str(self.nchunks)) request.set_LaunchTemplateId(self.template_id) - request.set_AutoProvisioningGroupName(''.join(random.choice(string.ascii_uppercase) for _ in range(20))) + request.set_AutoProvisioningGroupName(self.adata["instance_name"] + ''.join(random.choice(string.ascii_uppercase) for _ in range(20))) request.set_AutoProvisioningGroupType("maintain") request.set_SpotAllocationStrategy("lowest-price") request.set_SpotInstanceInterruptionBehavior("terminate") @@ -219,7 +219,7 @@ def check_restart(self, work_path, tasks, group_size): with open('apg_id.json') as fp: apg = json.load(fp) self.apg_id = apg["apg_id"] - dlog.info(self.apg_id) + #dlog.info(self.apg_id) self.task_chunks = _split_tasks(tasks, group_size) task_chunks_str = ['+'.join(ii) for ii in self.task_chunks] task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] From 2a06362f493cf2a631eff1c63890027cf5346d22 Mon Sep 17 00:00:00 2001 From: yuzhi <529133328@qq.com> Date: Wed, 18 Mar 2020 21:33:16 +0800 Subject: [PATCH 110/201] restart shell by uuid --- dpgen/dispatcher/ALI.py | 6 +++--- dpgen/dispatcher/Batch.py | 1 + dpgen/dispatcher/Dispatcher.py | 16 ++++++++++++---- dpgen/dispatcher/SSHContext.py | 13 ++++++++++++- dpgen/dispatcher/Shell.py | 11 ++++++++++- .../machine/DeePMD-kit-1.0/machine-local.json | 3 +++ 6 files changed, 41 insertions(+), 9 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 9e25bea90..dfab5f640 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -242,7 +242,7 @@ def check_restart(self, work_path, tasks, group_size): profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id - disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) self.dispatchers.append([disp, "working"]) else: with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: @@ -337,7 +337,7 @@ def run_jobs(self, profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] profile["instance_id"] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] self.dispatchers[ii] = disp dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, @@ -386,7 +386,7 @@ def make_dispatchers(self): profile = self.mdata_machine.copy() profile['hostname'] = self.ip_list[ii] profile['instance_id'] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] dispatchers.append(disp) return dispatchers diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index ad1a15e79..2e2a7c1f3 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -9,6 +9,7 @@ def __init__ (self, context, uuid_names = False) : self.context = context + self.uuid_names = uuid_names if uuid_names: self.finish_tag_name = '%s_tag_finished' % self.context.job_uuid self.sub_script_name = '%s.sub' % self.context.job_uuid diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index ebf189216..00b1bff60 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -47,6 +47,10 @@ def __init__ (self, self.session = SSHSession(remote_profile) self.context = SSHContext self.uuid_names = False + elif context_type == 'ssh-uuid': + self.session = SSHSession(remote_profile) + self.context = SSHContext + self.uuid_names = True else : raise RuntimeError('unknown context') if batch_type == 'slurm': @@ -308,10 +312,14 @@ def make_dispatcher(mdata, mdata_resource=None, work_path=None, run_tasks=None, dispatcher.init(work_path, run_tasks, group_size) return dispatcher else: - try: - hostname = mdata['hostname'] - context_type = 'ssh' - except: + hostname = mdata.get('hostname', None) + use_uuid = mdata.get('use_uuid', False) + if hostname: + if use_uuid: + context_type = 'ssh-uuid' + else: + context_type = 'ssh' + else: context_type = 'local' try: batch_type = mdata['batch'] diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index ce171c4bb..e1c8ec135 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -205,7 +205,18 @@ def call(self, cmd): def check_finish(self, cmd_pipes): return cmd_pipes['stdout'].channel.exit_status_ready() - + + def check_running_uuid(self, uuid_names): + ## Check if the uuid.sub is running on remote machine + cnt = 0 + ret, stdin, stdout, stderr = self.block_call("ps aux | grep %s"%uuid_names) + response_list = stdout.read().decode('utf-8').split("\n") + for response in response_list: + if uuid_names + ".sub" in response: + return True + return False + + def get_return(self, cmd_pipes): if not self.check_finish(cmd_pipes): return None, None, None diff --git a/dpgen/dispatcher/Shell.py b/dpgen/dispatcher/Shell.py index 2beb6ff87..c9060d2f6 100644 --- a/dpgen/dispatcher/Shell.py +++ b/dpgen/dispatcher/Shell.py @@ -1,6 +1,7 @@ import os,getpass,time from dpgen.dispatcher.Batch import Batch from dpgen.dispatcher.JobStatus import JobStatus +import datetime def _default_item(resources, key, value) : if key not in resources : @@ -11,7 +12,15 @@ class Shell(Batch) : def check_status(self) : if not hasattr(self, 'proc'): - return JobStatus.unsubmitted + if (self.uuid_names): + if self.context.check_file_exists("%s_tag_finished"%self.context.job_uuid): + return JobStatus.finished + elif self.context.check_running_uuid(self.context.job_uuid): + return JobStatus.running + else: + return JobStatus.terminated + else: + return JobStatus.unsubmitted if not self.context.check_finish(self.proc) : return JobStatus.running elif (self.context.get_return(self.proc))[0] == 0 : diff --git a/examples/machine/DeePMD-kit-1.0/machine-local.json b/examples/machine/DeePMD-kit-1.0/machine-local.json index d418a783e..291d90662 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-local.json +++ b/examples/machine/DeePMD-kit-1.0/machine-local.json @@ -4,6 +4,9 @@ "python_path": "/home/wanghan/local/deepmd/1.*/python", "train_machine": { "batch": "shell", + "_comment" : "If use_uuid is True, the batch script will be named as UUID.sub", + "_comment" : "In this way , shell can be restarted if DP-GEN is terminated ", + "use_uuid" : false, "work_path" : "/home/wanghan/tmp/subs/" }, "train_resources": { From 0bd7f4dfe851b6fd60b81a2d99da45f608e47d8c Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 19 Mar 2020 01:24:20 +0800 Subject: [PATCH 111/201] fix terminal bug --- dpgen/dispatcher/ALI.py | 91 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 9e25bea90..da76b37ab 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -16,6 +16,7 @@ from aliyunsdkecs.request.v20140526.DescribeSecurityGroupsRequest import DescribeSecurityGroupsRequest import time, json, os, glob, string, random from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks, JobRecord +from dpgen.dispatcher.SSHContext import SSHSession from os.path import join from dpgen import dlog from hashlib import sha1 @@ -106,7 +107,7 @@ def create_apg(self): json.dump({'apg_id': response["AutoProvisioningGroupId"]}, fp, indent=4) return response["AutoProvisioningGroupId"] - def update_instance_list(self): + def update_instance_ip_list(self): instance_list = self.describe_apg_instances() ip_list = self.get_ip(instance_list) self.instance_list += list(set(instance_list) - set(self.instance_list)) @@ -323,16 +324,60 @@ def run_jobs(self, outlog, errlog) self.job_handlers[ii] = job_handler + machine_exception_num = 0 + exception_task_chunks = [] + exception_jr_name = [] while True: - for ii in range(self.nchunks): - if self.dispatchers[ii][1] == "working" and self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): - self.dispatchers[ii][1] = "finished" - self.delete(ii) - self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) + dlog.info(self.ip_list) + dlog.info(self.task_chunks) + dlog.info(exception_task_chunks) + if machine_exception_num / self.nchunks > 0.05: + self.update_instance_ip_list() + dlog.info(self.ip_list) + if len(self.ip_list) == len(self.task_chunks) + len(exception_task_chunks): + available_machine_num = len(self.ip_list) - len(self.task_chunks) + for ii in range(len(self.task_chunks), len(self.ip_list)): + profile = self.mdata_machine.copy() + profile["hostname"] = self.ip_list[ii] + profile["instance_id"] = self.instance_list[ii] + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception_jr_name[ii-len(self.task_chunks)]), "working"] + self.dispatchers.append(disp) + dlog.info(self.ip_list[ii]) + job_handler = disp[0].submit_jobs(resources, + command, + work_path, + exception_task_chunks[ii-len(self.task_chunks)], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers.append(job_handler) + exception_jr_name.pop(ii-len(self.task_chunks)) + self.task_chunks.append(exception_task_chunks.pop(ii-len(self.task_chunks))) + for ii in range(len(self.task_chunks)): + if self.dispatchers[ii][1] == "working": + if self.check_server(self.dispatchers[ii][0]): + if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): + self.dispatchers[ii][1] = "finished" + self.delete(ii) + self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) + else: + machine_exception_num += 1 + exception_task_chunks.append(self.task_chunks.pop(ii)) + exception_jr_name.append(self.job_handlers[ii]["job_record"].fname[-14:]) + self.dispatchers.pop(ii) + self.ip_list.pop(ii) + self.instance_list.pop(ii) + self.job_handlers.pop(ii) + os.remove(self.job_handlers[ii]["job_record"].fname) + break elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": - self.update_instance_list() + self.update_instance_ip_list() if ii < len(self.ip_list): profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] @@ -360,7 +405,37 @@ def run_jobs(self, else: time.sleep(10) -# status = ["unalloc", "working", "finished"] +# status = ["unalloc", "working", "finished", "exception"] + def check_server(self, dispatcher): + dlog.info("check server") + #dlog.info(dispatcher.remote_profile) + try: + session = SSHSession(dispatcher.remote_profile) + except: + dlog.info(False) + return False + dlog.info(True) + return True + + def dispatcher_finish(self, dispatcher, job_handler, mark_failure): + job_record = job_handler['job_record'] + rjob = job_handler['job_list'][0] + status = rjob['batch'].check_status() + if status == JobStatus.terminated : + machine_status = True + ssh_active_count = 0 + while True: + if rjob['context'].ssh_session._check_alive(): + break + if not rjob['context'].ssh_session._check_alive(): + ssh_active_count += 1 + if ssh_active_count == 3: + machine_status = False + break + if machine_status == False: + pass + else: + return dispatcher.all_finished(job_handler, mark_failure) def check_dispatcher_finished(self): for ii in range(len(self.dispatchers)): From 4b3c20959a6bf64d0b4aa7ecb2017354481487bb Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 19 Mar 2020 01:28:55 +0800 Subject: [PATCH 112/201] fix bug --- dpgen/dispatcher/ALI.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 16ae23b79..9f9bcbce5 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -328,12 +328,12 @@ def run_jobs(self, exception_task_chunks = [] exception_jr_name = [] while True: - dlog.info(self.ip_list) - dlog.info(self.task_chunks) - dlog.info(exception_task_chunks) + #dlog.info(self.ip_list) + #dlog.info(self.task_chunks) + #dlog.info(exception_task_chunks) if machine_exception_num / self.nchunks > 0.05: self.update_instance_ip_list() - dlog.info(self.ip_list) + #dlog.info(self.ip_list) if len(self.ip_list) == len(self.task_chunks) + len(exception_task_chunks): available_machine_num = len(self.ip_list) - len(self.task_chunks) for ii in range(len(self.task_chunks), len(self.ip_list)): @@ -407,14 +407,14 @@ def run_jobs(self, # status = ["unalloc", "working", "finished", "exception"] def check_server(self, dispatcher): - dlog.info("check server") + #dlog.info("check server") #dlog.info(dispatcher.remote_profile) try: session = SSHSession(dispatcher.remote_profile) except: - dlog.info(False) + #dlog.info(False) return False - dlog.info(True) + #dlog.info(True) return True def dispatcher_finish(self, dispatcher, job_handler, mark_failure): From 0422a4856f239cbff9f2521c49937272681e5452 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Thu, 19 Mar 2020 17:53:09 +0800 Subject: [PATCH 113/201] uuid named upload tag --- dpgen/dispatcher/Dispatcher.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index ebf189216..40a5da76c 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -134,13 +134,14 @@ def submit_jobs(self, batch = self.batch(context, uuid_names = self.uuid_names) rjob = {'context':context, 'batch':batch} # upload files - if not rjob['context'].check_file_exists('tag_upload'): + tag_upload = '%s_tag_upload' % rjob['context'].job_uuid + if not rjob['context'].check_file_exists(tag_upload): rjob['context'].upload('.', forward_common_files) rjob['context'].upload(cur_chunk, forward_task_files, dereference = forward_task_deference) - rjob['context'].write_file('tag_upload', '') + rjob['context'].write_file(tag_upload, '') dlog.debug('uploaded files for %s' % task_chunks_str[ii]) # submit new or recover old submission if not submitted: From 5de77e59d01dc26736522199c8c8ec293101c47b Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 19 Mar 2020 20:45:52 +0800 Subject: [PATCH 114/201] fix bug --- dpgen/dispatcher/ALI.py | 47 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 9f9bcbce5..aaa7f8154 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -331,22 +331,29 @@ def run_jobs(self, #dlog.info(self.ip_list) #dlog.info(self.task_chunks) #dlog.info(exception_task_chunks) + #dlog.info(exception_jr_name) if machine_exception_num / self.nchunks > 0.05: self.update_instance_ip_list() #dlog.info(self.ip_list) if len(self.ip_list) == len(self.task_chunks) + len(exception_task_chunks): - available_machine_num = len(self.ip_list) - len(self.task_chunks) - for ii in range(len(self.task_chunks), len(self.ip_list)): + restart_ip_list = self.ip_list[-len(exception_task_chunks):] + restart_instance_list = self.instance_list[-len(exception_task_chunks):] + for ii in range(len(exception_task_chunks)): + #dlog.info(exception_task_chunks[ii]) + #dlog.info(exception_jr_name[ii]) profile = self.mdata_machine.copy() - profile["hostname"] = self.ip_list[ii] - profile["instance_id"] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception_jr_name[ii-len(self.task_chunks)]), "working"] - self.dispatchers.append(disp) - dlog.info(self.ip_list[ii]) + profile["hostname"] = restart_ip_list[ii] + profile["instance_id"] = restart_instance_list[ii] + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception_jr_name[ii]), "working"] + pos = int(exception_task_chunks[ii][0]) + self.dispatchers.insert(pos, disp) + self.ip_list.insert(pos, self.ip_list.pop(self.ip_list.index(profile["hostname"]))) + self.instance_list.insert(pos, self.instance_list.pop(self.instance_list.index(profile["instance_id"]))) + dlog.info(restart_ip_list[ii]) job_handler = disp[0].submit_jobs(resources, command, work_path, - exception_task_chunks[ii-len(self.task_chunks)], + exception_task_chunks[ii], group_size, forward_common_files, forward_task_files, @@ -354,9 +361,9 @@ def run_jobs(self, forward_task_deference, outlog, errlog) - self.job_handlers.append(job_handler) - exception_jr_name.pop(ii-len(self.task_chunks)) - self.task_chunks.append(exception_task_chunks.pop(ii-len(self.task_chunks))) + self.job_handlers.insert(pos, job_handler) + exception_jr_name.pop(ii) + self.task_chunks.insert(pos, exception_task_chunks.pop(ii)) for ii in range(len(self.task_chunks)): if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0]): @@ -372,6 +379,7 @@ def run_jobs(self, self.ip_list.pop(ii) self.instance_list.pop(ii) self.job_handlers.pop(ii) + dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) os.remove(self.job_handlers[ii]["job_record"].fname) break elif self.dispatchers[ii][1] == "finished": @@ -408,14 +416,15 @@ def run_jobs(self, # status = ["unalloc", "working", "finished", "exception"] def check_server(self, dispatcher): #dlog.info("check server") - #dlog.info(dispatcher.remote_profile) - try: - session = SSHSession(dispatcher.remote_profile) - except: - #dlog.info(False) - return False - #dlog.info(True) - return True + for ii in range(3): + try: + session = SSHSession(dispatcher.remote_profile) + #dlog.info(True) + return True + except: + pass + #dlog.info(False) + return False def dispatcher_finish(self, dispatcher, job_handler, mark_failure): job_record = job_handler['job_record'] From 2a2028a465ac364583011b5c7c564256f3b4242a Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 19 Mar 2020 23:30:05 +0800 Subject: [PATCH 115/201] fix bug --- dpgen/dispatcher/ALI.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index aaa7f8154..79194ce34 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -325,8 +325,7 @@ def run_jobs(self, errlog) self.job_handlers[ii] = job_handler machine_exception_num = 0 - exception_task_chunks = [] - exception_jr_name = [] + exception = [] while True: #dlog.info(self.ip_list) #dlog.info(self.task_chunks) @@ -335,17 +334,18 @@ def run_jobs(self, if machine_exception_num / self.nchunks > 0.05: self.update_instance_ip_list() #dlog.info(self.ip_list) - if len(self.ip_list) == len(self.task_chunks) + len(exception_task_chunks): - restart_ip_list = self.ip_list[-len(exception_task_chunks):] - restart_instance_list = self.instance_list[-len(exception_task_chunks):] - for ii in range(len(exception_task_chunks)): + if len(self.ip_list) == len(self.task_chunks) + len(exception): + exception = sorted(exception, key=lambda x:x[2]) + restart_ip_list = self.ip_list[-len(exception):] + restart_instance_list = self.instance_list[-len(exception):] + for ii in range(len(exception)): #dlog.info(exception_task_chunks[ii]) #dlog.info(exception_jr_name[ii]) profile = self.mdata_machine.copy() profile["hostname"] = restart_ip_list[ii] profile["instance_id"] = restart_instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception_jr_name[ii]), "working"] - pos = int(exception_task_chunks[ii][0]) + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception[ii][1]), "working"] + pos = exception[ii][2] self.dispatchers.insert(pos, disp) self.ip_list.insert(pos, self.ip_list.pop(self.ip_list.index(profile["hostname"]))) self.instance_list.insert(pos, self.instance_list.pop(self.instance_list.index(profile["instance_id"]))) @@ -353,7 +353,7 @@ def run_jobs(self, job_handler = disp[0].submit_jobs(resources, command, work_path, - exception_task_chunks[ii], + exception[ii][0], group_size, forward_common_files, forward_task_files, @@ -373,8 +373,7 @@ def run_jobs(self, self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: machine_exception_num += 1 - exception_task_chunks.append(self.task_chunks.pop(ii)) - exception_jr_name.append(self.job_handlers[ii]["job_record"].fname[-14:]) + exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) self.dispatchers.pop(ii) self.ip_list.pop(ii) self.instance_list.pop(ii) From add151c0cfbc9da62152d6dcfdbb4e4509e68761 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 19 Mar 2020 23:34:48 +0800 Subject: [PATCH 116/201] fix bug --- dpgen/dispatcher/ALI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 79194ce34..c2913fff2 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -362,8 +362,8 @@ def run_jobs(self, outlog, errlog) self.job_handlers.insert(pos, job_handler) - exception_jr_name.pop(ii) - self.task_chunks.insert(pos, exception_task_chunks.pop(ii)) + self.task_chunks.insert(pos, exception[ii][0]) + exception.pop(ii) for ii in range(len(self.task_chunks)): if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0]): From 457bc63e9030cb7cf01956bcb9137f5f6457ee40 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 20 Mar 2020 04:24:34 +0800 Subject: [PATCH 117/201] fix bug --- dpgen/dispatcher/ALI.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index c2913fff2..2f671a37b 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -243,8 +243,11 @@ def check_restart(self, work_path, tasks, group_size): profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id - disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) - self.dispatchers.append([disp, "working"]) + if self.check_server(profile): + disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) + self.dispatchers.append([disp, "working"]) + else: + self.dispatchers.append([None, "unalloc"]) else: with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: jr = json.load(fp) @@ -363,10 +366,10 @@ def run_jobs(self, errlog) self.job_handlers.insert(pos, job_handler) self.task_chunks.insert(pos, exception[ii][0]) - exception.pop(ii) + exception = [] for ii in range(len(self.task_chunks)): if self.dispatchers[ii][1] == "working": - if self.check_server(self.dispatchers[ii][0]): + if self.check_server(self.dispatchers[ii][0].remote_profile): if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.dispatchers[ii][1] = "finished" self.delete(ii) @@ -413,11 +416,11 @@ def run_jobs(self, time.sleep(10) # status = ["unalloc", "working", "finished", "exception"] - def check_server(self, dispatcher): + def check_server(self, profile): #dlog.info("check server") for ii in range(3): try: - session = SSHSession(dispatcher.remote_profile) + session = SSHSession(profile) #dlog.info(True) return True except: From 1dd2c3d13ece19ff074cc3839e19e8ea3d21d43a Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 20 Mar 2020 04:41:32 +0800 Subject: [PATCH 118/201] fix bug --- dpgen/dispatcher/ALI.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 2f671a37b..df671a070 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -392,21 +392,31 @@ def run_jobs(self, profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] profile["instance_id"] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] - self.dispatchers[ii] = disp - dlog.info(self.ip_list[ii]) - job_handler = self.dispatchers[ii][0].submit_jobs(resources, - command, - work_path, - self.task_chunks[ii], - group_size, - forward_common_files, - forward_task_files, - backward_task_files, - forward_task_deference, - outlog, - errlog) - self.job_handlers[ii] = job_handler + if self.check_server(profile): + disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + self.dispatchers[ii] = disp + dlog.info(self.ip_list[ii]) + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers[ii] = job_handler + else: + machine_exception_num += 1 + exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) + self.dispatchers.pop(ii) + self.ip_list.pop(ii) + self.instance_list.pop(ii) + self.job_handlers.pop(ii) + dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) + break if self.check_dispatcher_finished(): os.remove('apg_id.json') self.delete_template() From 1da3763f7e7f2fa441ae587f06454777a8ccc83b Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 20 Mar 2020 05:02:08 +0800 Subject: [PATCH 119/201] fix bug --- dpgen/dispatcher/ALI.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index df671a070..a223e4d6c 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -410,12 +410,11 @@ def run_jobs(self, self.job_handlers[ii] = job_handler else: machine_exception_num += 1 - exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) + exception.append([self.task_chunks.pop(ii), "jr.%.06d.json" % ii, ii]) self.dispatchers.pop(ii) self.ip_list.pop(ii) self.instance_list.pop(ii) self.job_handlers.pop(ii) - dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) break if self.check_dispatcher_finished(): os.remove('apg_id.json') From c013ef2370ac0beb023bf46ff5e3dff5d3460100 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 20 Mar 2020 10:38:59 +0800 Subject: [PATCH 120/201] fix bug in 02.elastic, user provided INCAR --- dpgen/auto_test/gen_02_elastic.py | 26 ++- .../si/mp-149/vasp-relax_incar/CONTCAR | 25 +++ .../00.equi/si/mp-149/vasp-relax_incar/OUTCAR | 204 ++++++++++++++++++ tests/auto_test/confs/si/mp-149/POSCAR | 25 +++ tests/auto_test/test_elastic_incar.py | 52 +++++ tests/auto_test/vasp_input/INCAR | 30 +++ tests/auto_test/vasp_input/INCAR.md | 38 ++++ tests/auto_test/vasp_input/INCAR.rlx | 32 +++ tests/auto_test/vasp_input/POTCAR | 1 + 9 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/CONTCAR create mode 100644 tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/OUTCAR create mode 100644 tests/auto_test/confs/si/mp-149/POSCAR create mode 100644 tests/auto_test/test_elastic_incar.py create mode 100644 tests/auto_test/vasp_input/INCAR create mode 100644 tests/auto_test/vasp_input/INCAR.md create mode 100644 tests/auto_test/vasp_input/INCAR.rlx create mode 100644 tests/auto_test/vasp_input/POTCAR diff --git a/dpgen/auto_test/gen_02_elastic.py b/dpgen/auto_test/gen_02_elastic.py index 45553e49d..21c1267e4 100755 --- a/dpgen/auto_test/gen_02_elastic.py +++ b/dpgen/auto_test/gen_02_elastic.py @@ -3,17 +3,22 @@ import os, re, argparse, filecmp, json, glob import subprocess as sp import numpy as np +from dpgen import dlog import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps +from dpgen.generator.lib.vasp import incar_upper from pymatgen.core.structure import Structure from pymatgen.analysis.elasticity.strain import Deformation, DeformedStructureSet, Strain +from pymatgen.io.vasp import Incar global_equi_name = '00.equi' global_task_name = '02.elastic' -def make_vasp(jdata, conf_dir, norm_def = 2e-3, shear_def = 5e-3) : - norm_def = jdata['norm_deform'] - shear_def = jdata['shear_deform'] +def make_vasp(jdata, conf_dir) : + default_norm_def = 2e-3 + default_shear_def = 5e-3 + norm_def = jdata.get('norm_deform', default_norm_def) + shear_def = jdata.get('shear_deform', default_shear_def) conf_path = os.path.abspath(conf_dir) conf_poscar = os.path.join(conf_path, 'POSCAR') @@ -55,10 +60,14 @@ def make_vasp(jdata, conf_dir, norm_def = 2e-3, shear_def = 5e-3) : if 'relax_incar' in jdata.keys(): relax_incar_path = jdata['relax_incar'] assert(os.path.exists(relax_incar_path)) - relax_incar_path = os.path.abspath(relax_incar_path) - fc = open(relax_incar_path).read() - kspacing =float(re.findall((r"KSPACING(.+?)\n"),fc)[0].replace('=','')) - kgamma =('T' in re.findall((r"KGAMMA(.+?)\n"),fc)[0]) + relax_incar_path = os.path.abspath(relax_incar_path) + incar = incar_upper(Incar.from_file(relax_incar_path)) + if incar.get('ISIF') != 2: + dlog.info("%s:%s setting ISIF to 2" % (__file__, make_vasp.__name__)) + incar['ISIF'] = 2 + fc = incar.get_string() + kspacing = incar['KSPACING'] + kgamma = incar['KGAMMA'] else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -107,7 +116,8 @@ def make_vasp(jdata, conf_dir, norm_def = 2e-3, shear_def = 5e-3) : os.symlink(os.path.relpath(os.path.join(task_path, 'INCAR')), 'INCAR') os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')), 'POTCAR') os.symlink(os.path.relpath(os.path.join(task_path, 'KPOINTS')), 'KPOINTS') - cwd = os.getcwd() + os.chdir(cwd) + def make_lammps(jdata, conf_dir,task_type) : fp_params = jdata['lammps_params'] diff --git a/tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/CONTCAR b/tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/CONTCAR new file mode 100644 index 000000000..4fe904343 --- /dev/null +++ b/tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/CONTCAR @@ -0,0 +1,25 @@ +Si8 + 1.00000000000000 + 5.4687279999999996 0.0000000000000000 0.0000000000000000 + 0.0000000000000000 5.4687279999999996 0.0000000000000000 + 0.0000000000000000 0.0000000000000000 5.4687279999999996 + Si + 8 +Direct + 0.2500000000000000 0.2500000000000000 0.2500000000000000 + 0.5000000000000000 0.5000000000000000 0.0000000000000000 + 0.2500000000000000 0.7500000000000000 0.7500000000000000 + 0.5000000000000000 0.0000000000000000 0.5000000000000000 + 0.7500000000000000 0.2500000000000000 0.7500000000000000 + 0.0000000000000000 0.5000000000000000 0.5000000000000000 + 0.7500000000000000 0.7500000000000000 0.2500000000000000 + 0.0000000000000000 0.0000000000000000 0.0000000000000000 + + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 diff --git a/tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/OUTCAR b/tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/OUTCAR new file mode 100644 index 000000000..0bbd2d456 --- /dev/null +++ b/tests/auto_test/00.equi/si/mp-149/vasp-relax_incar/OUTCAR @@ -0,0 +1,204 @@ + + + INCAR: + POTCAR: PAW_PBE Si 05Jan2001 + + POTCAR: PAW_PBE Si 05Jan2001 + VRHFIN =Si: s2p2 + LEXCH = PE + EATOM = 103.0669 eV, 7.5752 Ry + + TITEL = PAW_PBE Si 05Jan2001 + LULTRA = F use ultrasoft PP ? + IUNSCR = 1 unscreen: 0-lin 1-nonlin 2-no + RPACOR = 1.500 partial core radius + POMASS = 28.085; ZVAL = 4.000 mass and valenz + RCORE = 1.900 outmost cutoff radius + RWIGS = 2.480; RWIGS = 1.312 wigner-seitz radius (au A) + ENMAX = 245.345; ENMIN = 184.009 eV + ICORE = 2 local potential + LCOR = T correct aug charges + LPAW = T paw PP + EAUG = 322.069 + DEXC = 0.000 + RMAX = 1.950 core radius for proj-oper + RAUG = 1.300 factor for augmentation sphere + RDEP = 1.993 radius for radial grids + RDEPT = 1.837 core radius for aug-charge + + Atomic configuration + 6 entries + n l j E occ. + 1 0 0.50 -1785.8828 2.0000 + 2 0 0.50 -139.4969 2.0000 + 2 1 1.50 -95.5546 6.0000 + 3 0 0.50 -10.8127 2.0000 + 3 1 0.50 -4.0811 2.0000 + 3 2 1.50 -4.0817 0.0000 + Description + l E TYP RCUT TYP RCUT + 0 -10.8127223 23 1.900 + 0 -7.6451159 23 1.900 + 1 -4.0811372 23 1.900 + 1 2.4879257 23 1.900 + 2 -4.0817478 7 1.900 + local pseudopotential read in + partial core-charges read in + partial kinetic energy density read in + atomic valenz-charges read in + non local Contribution for L= 0 read in + real space projection operators read in + non local Contribution for L= 0 read in + real space projection operators read in + non local Contribution for L= 1 read in + real space projection operators read in + non local Contribution for L= 1 read in + real space projection operators read in + PAW grid and wavefunctions read in + + number of l-projection operators is LMAX = 4 + number of lm-projection operators is LMMAX = 8 + + PAW_PBE Si 05Jan2001 : + energy of atom 1 EATOM= -103.0669 + kinetic energy error for atom= 0.0003 (will be added to EATOM!!) + + + POSCAR: Si8 + positions in direct lattice + velocities in cartesian coordinates + exchange correlation table for LEXCH = 8 + RHO(1)= 0.500 N(1) = 2000 + RHO(2)= 100.500 N(2) = 4000 + + + +------------------------ aborting loop because EDIFF is reached ---------------------------------------- + + + CHARGE: cpu time 0.2963: real time 0.2963 + FORLOC: cpu time 0.0049: real time 0.0049 + FORNL : cpu time 0.9680: real time 0.9681 + STRESS: cpu time 3.5438: real time 3.5442 + FORCOR: cpu time 0.0514: real time 0.0514 + FORHAR: cpu time 0.0141: real time 0.0141 + MIXING: cpu time 0.0020: real time 0.0020 + OFIELD: cpu time 0.0000: real time 0.0000 + + FORCE on cell =-STRESS in cart. coord. units (eV): + Direction XX YY ZZ XY YZ ZX + -------------------------------------------------------------------------------------- + Alpha Z 13.17272 13.17272 13.17272 + Ewald -302.59373 -302.59373 -302.59373 0.00000 0.00000 -0.00000 + Hartree 20.22818 20.22818 20.22818 -0.00000 0.00000 -0.00001 + E(xc) -100.98430 -100.98430 -100.98430 0.00000 -0.00000 0.00000 + Local -118.00021 -118.00021 -118.00020 0.00001 -0.00000 0.00002 + n-local 309.78388 309.78388 309.78388 -0.00000 0.00000 -0.00000 + augment -46.59684 -46.59684 -46.59684 -0.00000 -0.00000 -0.00000 + Kinetic 224.99250 224.99250 224.99250 0.00000 0.00000 0.00001 + Fock 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 + ------------------------------------------------------------------------------------- + Total 0.00220 0.00220 0.00220 0.00001 0.00000 0.00002 + in kB 0.02151 0.02155 0.02156 0.00008 0.00001 0.00019 + external pressure = 0.02 kB Pullay stress = 0.00 kB + + + VOLUME and BASIS-vectors are now : + ----------------------------------------------------------------------------- + energy-cutoff : 650.00 + volume of cell : 163.55 + direct lattice vectors reciprocal lattice vectors + 5.468728000 0.000000000 0.000000000 0.182857878 0.000000000 0.000000000 + 0.000000000 5.468728000 0.000000000 0.000000000 0.182857878 0.000000000 + 0.000000000 0.000000000 5.468728000 0.000000000 0.000000000 0.182857878 + + length of vectors + 5.468728000 5.468728000 5.468728000 0.182857878 0.182857878 0.182857878 + + + FORCES acting on ions + electron-ion (+dipol) ewald-force non-local-force convergence-correction + ----------------------------------------------------------------------------------------------- + -.236E-04 -.111E-04 -.458E-04 0.249E-13 0.107E-13 -.355E-14 -.871E-05 -.478E-05 0.920E-05 0.337E-04 0.158E-04 0.387E-04 + -.255E-04 -.104E-04 -.182E-04 0.180E-13 0.384E-14 -.710E-14 0.241E-05 0.915E-05 -.846E-06 0.256E-04 0.474E-05 0.222E-04 + -.805E-04 0.107E-05 0.374E-04 0.107E-13 0.355E-14 0.000E+00 0.146E-04 0.648E-05 -.650E-05 0.733E-04 -.793E-05 -.346E-04 + -.760E-05 -.183E-04 -.565E-05 0.341E-14 -.357E-14 -.362E-14 -.135E-05 0.548E-05 0.133E-04 0.888E-05 0.154E-04 -.541E-05 + 0.555E-04 0.406E-05 0.204E-04 0.355E-14 0.355E-14 0.355E-14 -.750E-05 -.781E-05 0.356E-05 -.512E-04 0.202E-05 -.277E-04 + -.321E-05 0.309E-04 0.493E-04 -.178E-13 -.350E-14 -.718E-16 0.836E-05 -.964E-05 -.109E-04 -.534E-05 -.228E-04 -.407E-04 + 0.481E-04 0.281E-04 -.852E-05 -.107E-13 -.355E-14 0.711E-14 -.257E-05 -.775E-05 -.798E-05 -.479E-04 -.223E-04 0.139E-04 + 0.234E-04 -.320E-04 -.298E-04 -.320E-13 -.106E-13 0.356E-14 -.448E-05 0.995E-05 0.233E-05 -.207E-04 0.245E-04 0.287E-04 + ----------------------------------------------------------------------------------------------- + -.134E-04 -.763E-05 -.867E-06 0.124E-15 0.361E-15 -.131E-15 0.759E-06 0.107E-05 0.221E-05 0.165E-04 0.934E-05 -.503E-05 + + + POSITION TOTAL-FORCE (eV/Angst) + ----------------------------------------------------------------------------------- + 1.36718 1.36718 1.36718 0.000001 -0.000000 0.000003 + 2.73436 2.73436 0.00000 0.000002 0.000003 0.000004 + 1.36718 4.10155 4.10155 0.000007 -0.000001 -0.000003 + 2.73436 0.00000 2.73436 -0.000001 0.000002 0.000003 + 4.10155 1.36718 4.10155 -0.000004 -0.000002 -0.000003 + 0.00000 2.73436 2.73436 -0.000001 -0.000002 -0.000002 + 4.10155 4.10155 1.36718 -0.000003 -0.000002 -0.000002 + 0.00000 0.00000 0.00000 -0.000002 0.000002 0.000002 + ----------------------------------------------------------------------------------- + total drift: 0.000004 0.000003 -0.000004 + + +-------------------------------------------------------------------------------------------------------- + + + + FREE ENERGIE OF THE ION-ELECTRON SYSTEM (eV) + --------------------------------------------------- + free energy TOTEN = -43.40017315 eV + + energy without entropy= -43.40017315 energy(sigma->0) = -43.40017315 + + + +-------------------------------------------------------------------------------------------------------- + + + POTLOK: cpu time 0.0496: real time 0.0496 + + +-------------------------------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------------------------------- + + + + reached required accuracy - stopping structural energy minimisation + LOOP+: cpu time 73.7767: real time 73.8176 + 4ORBIT: cpu time 0.0000: real time 0.0000 + + total amount of memory used by VASP MPI-rank0 119933. kBytes +======================================================================= + + base : 30000. kBytes + nonl-proj : 43479. kBytes + fftplans : 4240. kBytes + grid : 9842. kBytes + one-center: 24. kBytes + wavefun : 32348. kBytes + + + + General timing and accounting informations for this job: + ======================================================== + + Total CPU time used (sec): 75.718 + User time (sec): 75.184 + System time (sec): 0.534 + Elapsed time (sec): 75.841 + + Maximum memory used (kb): 186724. + Average memory used (kb): 0. + + Minor page faults: 71438 + Major page faults: 0 + Voluntary context switches: 409 diff --git a/tests/auto_test/confs/si/mp-149/POSCAR b/tests/auto_test/confs/si/mp-149/POSCAR new file mode 100644 index 000000000..4fe904343 --- /dev/null +++ b/tests/auto_test/confs/si/mp-149/POSCAR @@ -0,0 +1,25 @@ +Si8 + 1.00000000000000 + 5.4687279999999996 0.0000000000000000 0.0000000000000000 + 0.0000000000000000 5.4687279999999996 0.0000000000000000 + 0.0000000000000000 0.0000000000000000 5.4687279999999996 + Si + 8 +Direct + 0.2500000000000000 0.2500000000000000 0.2500000000000000 + 0.5000000000000000 0.5000000000000000 0.0000000000000000 + 0.2500000000000000 0.7500000000000000 0.7500000000000000 + 0.5000000000000000 0.0000000000000000 0.5000000000000000 + 0.7500000000000000 0.2500000000000000 0.7500000000000000 + 0.0000000000000000 0.5000000000000000 0.5000000000000000 + 0.7500000000000000 0.7500000000000000 0.2500000000000000 + 0.0000000000000000 0.0000000000000000 0.0000000000000000 + + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 diff --git a/tests/auto_test/test_elastic_incar.py b/tests/auto_test/test_elastic_incar.py new file mode 100644 index 000000000..53a8d57a2 --- /dev/null +++ b/tests/auto_test/test_elastic_incar.py @@ -0,0 +1,52 @@ +import os,sys,json,glob,shutil +import dpdata +import numpy as np +import unittest + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +__package__ = 'auto_test' +from .context import make_kspacing_kpoints +from .context import setUpModule + +from pymatgen.io.vasp import Incar +from dpgen.auto_test.gen_02_elastic import make_vasp + +class Test02(unittest.TestCase): + def tearDown(self): + if os.path.exists('02.elastic'): + shutil.rmtree('02.elastic') + + def test_make_vasp (self): + jdata = { + 'relax_incar' : 'vasp_input/INCAR.rlx', + 'potcar_map': {'Si': 'vasp_input/POTCAR' }, + } + make_vasp(jdata, 'confs/si/mp-149') + + target_path = '02.elastic/si/mp-149/vasp-relax_incar' + equi_path = '00.equi/si/mp-149/vasp-relax_incar' + dfm_dirs = glob.glob(os.path.join(target_path, 'dfm*')) + + # check root INCAR + incar0 = Incar.from_file(os.path.join('vasp_input', 'INCAR.rlx')) + incar1 = Incar.from_file(os.path.join(target_path, 'INCAR')) + self.assertFalse(incar0 == incar1) + incar0['ISIF'] = 2 + self.assertTrue(incar0 == incar1) + # check root POTCAR + with open(os.path.join('vasp_input', 'POTCAR')) as fp: + pot0 = fp.read() + with open(os.path.join(target_path, 'POTCAR')) as fp: + pot1 = fp.read() + self.assertEqual(pot0, pot1) + # check root POSCAR + self.assertEqual(os.path.realpath(os.path.join(target_path, 'POSCAR')), + os.path.realpath(os.path.join(equi_path, 'CONTCAR'))) + # check subdir + for ii in dfm_dirs: + self.assertEqual(os.path.realpath(os.path.join(ii, 'INCAR')), + os.path.realpath(os.path.join(target_path, 'INCAR'))) + self.assertEqual(os.path.realpath(os.path.join(ii, 'KPOINTS')), + os.path.realpath(os.path.join(target_path, 'KPOINTS'))) + self.assertEqual(os.path.realpath(os.path.join(ii, 'POTCAR')), + os.path.realpath(os.path.join(target_path, 'POTCAR'))) diff --git a/tests/auto_test/vasp_input/INCAR b/tests/auto_test/vasp_input/INCAR new file mode 100644 index 000000000..1e2a28323 --- /dev/null +++ b/tests/auto_test/vasp_input/INCAR @@ -0,0 +1,30 @@ +#Parameters +SYSTEM = dpgen +PREC = A +ISART = 0 +ICHARG = 2 +#Electronic Relaxation +ENCUT = 650 +NELM = 100 +NELMIN = 6 +NELMDL = -5 +EDIFF = 1e-06 +LREAL = False +ALGO = Fast # or normal +#Ionic relaxation +IBRION = -1 +ISIF = 2 +ISYM = 0 +NSW = 0 +ISMEAR = 0 +SIGMA = 0.1 +# Write flags +LWAVE = False +LCHARG = False +#parallel related +#KPAR = 4 +#NPAR = 1 +KSPACING = 0.1 +KGAMMA = False + +PSTRESS = 0.0 diff --git a/tests/auto_test/vasp_input/INCAR.md b/tests/auto_test/vasp_input/INCAR.md new file mode 100644 index 000000000..b7b00d64b --- /dev/null +++ b/tests/auto_test/vasp_input/INCAR.md @@ -0,0 +1,38 @@ +#Parameters +SYSTEM = dpgen +PREC = A +ISART = 0 +ICHARG = 2 +#Electronic Relaxation +ENCUT = 650 +NELM = 100 +NELMIN = 6 +NELMDL = -5 +EDIFF = 1e-06 +LREAL = False +ALGO = Fast # or normal +#Ionic relaxation +IBRION = 0 +ISIF = 2 +#EDIFFG = -0.01 # useless for MD +ISYM = 0 +NSW = 10 +ISMEAR = 0 +SIGMA = 0.1 +# MD related +SMASS = 0 +POTIM = 2 +TEBEG = 100 +TEEND = 100 +NBLOCK = 1 +KBLOCK = 100 +# Write flags +LWAVE = False +LCHARG = False +#parallel related +#KPAR = 4 +#NPAR = 1 +KSPACING = 0.1 +KGAMMA = False + +PSTRESS = 0.0 diff --git a/tests/auto_test/vasp_input/INCAR.rlx b/tests/auto_test/vasp_input/INCAR.rlx new file mode 100644 index 000000000..b871170ff --- /dev/null +++ b/tests/auto_test/vasp_input/INCAR.rlx @@ -0,0 +1,32 @@ +#Parameters +SYSTEM = dpgen +PREC = A +ISART = 0 +ICHARG = 2 +#Electronic Relaxation +ENCUT = 650 +NELM = 100 +NELMIN = 6 +NELMDL = -5 +EDIFF = 1e-06 +LREAL = False +ALGO = Fast # or normal +#Ionic relaxation +IBRION = 2 +POTIM = 0.2 +ISIF = 3 +EDIFFG = -0.01 +ISYM = 0 +NSW = 100 +ISMEAR = 0 +SIGMA = 0.1 +# Write flags +LWAVE = False +LCHARG = False +#parallel related +#KPAR = 4 +#NPAR = 1 +KSPACING = 0.2 +KGAMMA = False + +PSTRESS = 0.0 diff --git a/tests/auto_test/vasp_input/POTCAR b/tests/auto_test/vasp_input/POTCAR new file mode 100644 index 000000000..7bfb856bd --- /dev/null +++ b/tests/auto_test/vasp_input/POTCAR @@ -0,0 +1 @@ +bf8505dd-84b6-4fcc-a32c-3e1768b8a119 From 1d0d107d0a72cdfdb9a8a6ac1d48b3981f76c2c0 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 20 Mar 2020 11:29:08 +0800 Subject: [PATCH 121/201] fix bug in 01.eos, user provided INCAR --- dpgen/auto_test/gen_01_eos.py | 47 ++++++++--- dpgen/auto_test/gen_02_elastic.py | 5 +- ...{test_elastic_incar.py => test_elastic.py} | 0 tests/auto_test/test_eos.py | 81 +++++++++++++++++++ 4 files changed, 118 insertions(+), 15 deletions(-) rename tests/auto_test/{test_elastic_incar.py => test_elastic.py} (100%) create mode 100644 tests/auto_test/test_eos.py diff --git a/dpgen/auto_test/gen_01_eos.py b/dpgen/auto_test/gen_01_eos.py index 253792aee..e4acba157 100755 --- a/dpgen/auto_test/gen_01_eos.py +++ b/dpgen/auto_test/gen_01_eos.py @@ -5,7 +5,11 @@ import numpy as np import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps + +from dpgen import dlog +from dpgen.generator.lib.vasp import incar_upper from pymatgen.core.structure import Structure +from pymatgen.io.vasp import Incar global_equi_name = '00.equi' global_task_name = '01.eos' @@ -19,12 +23,26 @@ def make_vasp(jdata, conf_dir) : vol_start = jdata['vol_start'] vol_end = jdata['vol_end'] vol_step = jdata['vol_step'] - + eos_relax_cell_shape = jdata.get('eos_relax_cell_shape', True) conf_path = os.path.abspath(conf_dir) + conf_poscar = os.path.join(conf_path, 'POSCAR') + + if 'relax_incar' in jdata.keys(): + vasp_str='vasp-relax_incar' + else: + kspacing = jdata['vasp_params']['kspacing'] + vasp_str='vasp-k%.2f' % kspacing + + # get equi poscar + equi_path = re.sub('confs', global_equi_name, conf_path) + equi_path = os.path.join(equi_path, vasp_str) + equi_contcar = os.path.join(equi_path, 'CONTCAR') task_path = re.sub('confs', global_task_name, conf_path) - os.makedirs(task_path, exist_ok = True) + task_path = os.path.join(task_path, vasp_str) + os.makedirs(task_path, exist_ok = True) + # link poscar cwd = os.getcwd() - from_poscar = os.path.join(conf_path, 'POSCAR') + from_poscar = os.path.join(equi_contcar) to_poscar = os.path.join(task_path, 'POSCAR') if os.path.exists(to_poscar) : assert(filecmp.cmp(from_poscar, to_poscar)) @@ -56,8 +74,15 @@ def make_vasp(jdata, conf_dir) : relax_incar_path = jdata['relax_incar'] assert(os.path.exists(relax_incar_path)) relax_incar_path = os.path.abspath(relax_incar_path) - fc = open(relax_incar_path).read() - vasp_path = os.path.join(task_path, 'vasp-relax_incar' ) + incar = incar_upper(Incar.from_file(relax_incar_path)) + if eos_relax_cell_shape: + isif = 4 + else: + isif = 2 + if incar.get('ISIF') != isif: + dlog.info("%s:%s setting ISIF to %d" % (__file__, make_vasp.__name__, isif)) + incar['ISIF'] = isif + fc = incar.get_string() else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -67,11 +92,8 @@ def make_vasp(jdata, conf_dir) : kspacing = fp_params['kspacing'] kgamma = fp_params['kgamma'] fc = vasp.make_vasp_relax_incar(ecut, ediff, is_alloy, True, False, npar, kpar, kspacing, kgamma) - vasp_path = os.path.join(task_path, 'vasp-k%.2f' % kspacing) - os.makedirs(vasp_path, exist_ok = True) - os.chdir(vasp_path) - print(vasp_path) + os.chdir(task_path) with open('INCAR', 'w') as fp : fp.write(fc) @@ -82,16 +104,15 @@ def make_vasp(jdata, conf_dir) : outfile.write(infile.read()) # loop over volumes for vol in np.arange(vol_start, vol_end, vol_step) : - vol_path = os.path.join(vasp_path, 'vol-%.2f' % vol) + vol_path = os.path.join(task_path, 'vol-%.2f' % vol) os.makedirs(vol_path, exist_ok = True) os.chdir(vol_path) - print(vol_path) for ii in ['INCAR', 'POTCAR', 'POSCAR.orig', 'POSCAR'] : if os.path.exists(ii) : os.remove(ii) # link incar, potcar - os.symlink(os.path.relpath(os.path.join(vasp_path, 'INCAR')), 'INCAR') - os.symlink(os.path.relpath(os.path.join(vasp_path, 'POTCAR')), 'POTCAR') + os.symlink(os.path.relpath(os.path.join(task_path, 'INCAR')), 'INCAR') + os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')), 'POTCAR') # gen poscar os.symlink(os.path.relpath(to_poscar), 'POSCAR.orig') scale = (vol / vol_to_poscar) ** (1./3.) diff --git a/dpgen/auto_test/gen_02_elastic.py b/dpgen/auto_test/gen_02_elastic.py index 21c1267e4..a41b9fc79 100755 --- a/dpgen/auto_test/gen_02_elastic.py +++ b/dpgen/auto_test/gen_02_elastic.py @@ -3,9 +3,10 @@ import os, re, argparse, filecmp, json, glob import subprocess as sp import numpy as np -from dpgen import dlog import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps + +from dpgen import dlog from dpgen.generator.lib.vasp import incar_upper from pymatgen.core.structure import Structure from pymatgen.analysis.elasticity.strain import Deformation, DeformedStructureSet, Strain @@ -22,13 +23,13 @@ def make_vasp(jdata, conf_dir) : conf_path = os.path.abspath(conf_dir) conf_poscar = os.path.join(conf_path, 'POSCAR') - # get equi poscar if 'relax_incar' in jdata.keys(): vasp_str='vasp-relax_incar' else: kspacing = jdata['vasp_params']['kspacing'] vasp_str='vasp-k%.2f' % kspacing + # get equi poscar equi_path = re.sub('confs', global_equi_name, conf_path) equi_path = os.path.join(equi_path, vasp_str) equi_contcar = os.path.join(equi_path, 'CONTCAR') diff --git a/tests/auto_test/test_elastic_incar.py b/tests/auto_test/test_elastic.py similarity index 100% rename from tests/auto_test/test_elastic_incar.py rename to tests/auto_test/test_elastic.py diff --git a/tests/auto_test/test_eos.py b/tests/auto_test/test_eos.py new file mode 100644 index 000000000..fb0c73480 --- /dev/null +++ b/tests/auto_test/test_eos.py @@ -0,0 +1,81 @@ +import os,sys,json,glob,shutil +import dpdata +import numpy as np +import unittest + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +__package__ = 'auto_test' +from .context import make_kspacing_kpoints +from .context import setUpModule + +from pymatgen.io.vasp import Incar +from dpgen.auto_test.gen_01_eos import make_vasp + +class Test01(unittest.TestCase): + def tearDown(self): + if os.path.exists('01.eos'): + shutil.rmtree('01.eos') + + def test_make_vasp_rlx_cell_shape (self): + jdata = { + 'relax_incar' : 'vasp_input/INCAR.rlx', + 'potcar_map': {'Si': 'vasp_input/POTCAR' }, + 'vol_start': 15, + 'vol_end': 25, + 'vol_step': 1.0, + 'eos_relax_cell_shape': True, + } + make_vasp(jdata, 'confs/si/mp-149') + + target_path = '01.eos/si/mp-149/vasp-relax_incar' + equi_path = '00.equi/si/mp-149/vasp-relax_incar' + dfm_dirs = glob.glob(os.path.join(target_path, 'vol*')) + + # check root INCAR + incar0 = Incar.from_file(os.path.join('vasp_input', 'INCAR.rlx')) + incar1 = Incar.from_file(os.path.join(target_path, 'INCAR')) + self.assertFalse(incar0 == incar1) + incar0['ISIF'] = 4 + self.assertTrue(incar0 == incar1) + # check root POTCAR + with open(os.path.join('vasp_input', 'POTCAR')) as fp: + pot0 = fp.read() + with open(os.path.join(target_path, 'POTCAR')) as fp: + pot1 = fp.read() + self.assertEqual(pot0, pot1) + # check subdir + for ii in dfm_dirs: + self.assertFalse(os.path.isfile(os.path.join(ii, 'KPOINTS'))) + self.assertEqual(os.path.realpath(os.path.join(ii, 'POSCAR.orig')), + os.path.realpath(os.path.join(equi_path, 'CONTCAR'))) + self.assertEqual(os.path.realpath(os.path.join(ii, 'INCAR')), + os.path.realpath(os.path.join(target_path, 'INCAR'))) + self.assertEqual(os.path.realpath(os.path.join(ii, 'POTCAR')), + os.path.realpath(os.path.join(target_path, 'POTCAR'))) + sys = dpdata.System(os.path.join(ii, 'POSCAR')) + vol = float(ii.split('/')[-1].split('-')[1]) + natoms = sys.get_natoms() + self.assertAlmostEqual(vol, np.linalg.det(sys['cells'][0]) / natoms) + + + def test_make_vasp_norlx_cell_shape (self): + jdata = { + 'relax_incar' : 'vasp_input/INCAR.rlx', + 'potcar_map': {'Si': 'vasp_input/POTCAR' }, + 'vol_start': 15, + 'vol_end': 25, + 'vol_step': 1.0, + 'eos_relax_cell_shape': False, + } + make_vasp(jdata, 'confs/si/mp-149') + + target_path = '01.eos/si/mp-149/vasp-relax_incar' + equi_path = '00.equi/si/mp-149/vasp-relax_incar' + dfm_dirs = glob.glob(os.path.join(target_path, 'vol*')) + + # check root INCAR + incar0 = Incar.from_file(os.path.join('vasp_input', 'INCAR.rlx')) + incar1 = Incar.from_file(os.path.join(target_path, 'INCAR')) + self.assertFalse(incar0 == incar1) + incar0['ISIF'] = 2 + self.assertTrue(incar0 == incar1) From be97c1d61140b392723a4f5c28a27c5d2666275e Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 20 Mar 2020 13:48:50 +0800 Subject: [PATCH 122/201] fix bug --- dpgen/dispatcher/ALI.py | 152 +++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 78 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index a223e4d6c..39ed776de 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -243,11 +243,9 @@ def check_restart(self, work_path, tasks, group_size): profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id - if self.check_server(profile): - disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) - self.dispatchers.append([disp, "working"]) - else: - self.dispatchers.append([None, "unalloc"]) + if self.check_server(profile) + disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) + self.dispatchers.append([disp, "working"]) else: with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: jr = json.load(fp) @@ -334,39 +332,42 @@ def run_jobs(self, #dlog.info(self.task_chunks) #dlog.info(exception_task_chunks) #dlog.info(exception_jr_name) - if machine_exception_num / self.nchunks > 0.05: - self.update_instance_ip_list() - #dlog.info(self.ip_list) - if len(self.ip_list) == len(self.task_chunks) + len(exception): - exception = sorted(exception, key=lambda x:x[2]) - restart_ip_list = self.ip_list[-len(exception):] - restart_instance_list = self.instance_list[-len(exception):] - for ii in range(len(exception)): - #dlog.info(exception_task_chunks[ii]) - #dlog.info(exception_jr_name[ii]) - profile = self.mdata_machine.copy() - profile["hostname"] = restart_ip_list[ii] - profile["instance_id"] = restart_instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception[ii][1]), "working"] - pos = exception[ii][2] - self.dispatchers.insert(pos, disp) - self.ip_list.insert(pos, self.ip_list.pop(self.ip_list.index(profile["hostname"]))) - self.instance_list.insert(pos, self.instance_list.pop(self.instance_list.index(profile["instance_id"]))) - dlog.info(restart_ip_list[ii]) - job_handler = disp[0].submit_jobs(resources, - command, - work_path, - exception[ii][0], - group_size, - forward_common_files, - forward_task_files, - backward_task_files, - forward_task_deference, - outlog, - errlog) - self.job_handlers.insert(pos, job_handler) - self.task_chunks.insert(pos, exception[ii][0]) - exception = [] + # if machine_exception_num / self.nchunks > 0.05: + # try: + # self.update_instance_ip_list() + # except: + # pass + # #dlog.info(self.ip_list) + # if len(self.ip_list) == len(self.task_chunks) + len(exception): + # exception = sorted(exception, key=lambda x:x[2]) + # restart_ip_list = self.ip_list[-len(exception):] + # restart_instance_list = self.instance_list[-len(exception):] + # for ii in range(len(exception)): + # #dlog.info(exception_task_chunks[ii]) + # #dlog.info(exception_jr_name[ii]) + # profile = self.mdata_machine.copy() + # profile["hostname"] = restart_ip_list[ii] + # profile["instance_id"] = restart_instance_list[ii] + # disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception[ii][1]), "working"] + # pos = exception[ii][2] + # self.dispatchers.insert(pos, disp) + # self.ip_list.insert(pos, self.ip_list.pop(self.ip_list.index(profile["hostname"]))) + # self.instance_list.insert(pos, self.instance_list.pop(self.instance_list.index(profile["instance_id"]))) + # dlog.info(restart_ip_list[ii]) + # job_handler = disp[0].submit_jobs(resources, + # command, + # work_path, + # exception[ii][0], + # group_size, + # forward_common_files, + # forward_task_files, + # backward_task_files, + # forward_task_deference, + # outlog, + # errlog) + # self.job_handlers.insert(pos, job_handler) + # self.task_chunks.insert(pos, exception[ii][0]) + # exception = [] for ii in range(len(self.task_chunks)): if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): @@ -374,48 +375,43 @@ def run_jobs(self, self.dispatchers[ii][1] = "finished" self.delete(ii) self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) - else: - machine_exception_num += 1 - exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) - self.dispatchers.pop(ii) - self.ip_list.pop(ii) - self.instance_list.pop(ii) - self.job_handlers.pop(ii) - dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) - os.remove(self.job_handlers[ii]["job_record"].fname) - break + # else: + # machine_exception_num += 1 + # exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) + # self.dispatchers.pop(ii) + # self.ip_list.pop(ii) + # self.instance_list.pop(ii) + # self.job_handlers.pop(ii) + # dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) + # os.remove(self.job_handlers[ii]["job_record"].fname) + # break elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": - self.update_instance_ip_list() - if ii < len(self.ip_list): - profile = self.mdata_machine.copy() - profile["hostname"] = self.ip_list[ii] - profile["instance_id"] = self.instance_list[ii] - if self.check_server(profile): - disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] - self.dispatchers[ii] = disp - dlog.info(self.ip_list[ii]) - job_handler = self.dispatchers[ii][0].submit_jobs(resources, - command, - work_path, - self.task_chunks[ii], - group_size, - forward_common_files, - forward_task_files, - backward_task_files, - forward_task_deference, - outlog, - errlog) - self.job_handlers[ii] = job_handler - else: - machine_exception_num += 1 - exception.append([self.task_chunks.pop(ii), "jr.%.06d.json" % ii, ii]) - self.dispatchers.pop(ii) - self.ip_list.pop(ii) - self.instance_list.pop(ii) - self.job_handlers.pop(ii) - break + continue + # try: + # self.update_instance_ip_list() + # except: + # pass + # if ii < len(self.ip_list): + # profile = self.mdata_machine.copy() + # profile["hostname"] = self.ip_list[ii] + # profile["instance_id"] = self.instance_list[ii] + # disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + # self.dispatchers[ii] = disp + # dlog.info(self.ip_list[ii]) + # job_handler = self.dispatchers[ii][0].submit_jobs(resources, + # command, + # work_path, + # self.task_chunks[ii], + # group_size, + # forward_common_files, + # forward_task_files, + # backward_task_files, + # forward_task_deference, + # outlog, + # errlog) + # self.job_handlers[ii] = job_handler if self.check_dispatcher_finished(): os.remove('apg_id.json') self.delete_template() @@ -433,7 +429,7 @@ def check_server(self, profile): #dlog.info(True) return True except: - pass + time.sleep(60) #dlog.info(False) return False From 6e6887ddb19f97a9f60799a6fd38c18d68c2913f Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 20 Mar 2020 13:59:21 +0800 Subject: [PATCH 123/201] fix bug --- dpgen/dispatcher/ALI.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 39ed776de..60383cda7 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -243,9 +243,9 @@ def check_restart(self, work_path, tasks, group_size): profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id - if self.check_server(profile) - disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) - self.dispatchers.append([disp, "working"]) + if self.check_server(profile): + disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) + self.dispatchers.append([disp, "working"]) else: with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: jr = json.load(fp) @@ -375,16 +375,16 @@ def run_jobs(self, self.dispatchers[ii][1] = "finished" self.delete(ii) self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) - # else: - # machine_exception_num += 1 - # exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) - # self.dispatchers.pop(ii) - # self.ip_list.pop(ii) - # self.instance_list.pop(ii) - # self.job_handlers.pop(ii) - # dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) - # os.remove(self.job_handlers[ii]["job_record"].fname) - # break + else: + machine_exception_num += 1 + exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) + self.dispatchers.pop(ii) + self.ip_list.pop(ii) + self.instance_list.pop(ii) + self.job_handlers.pop(ii) + dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) + os.remove(self.job_handlers[ii]["job_record"].fname) + break elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": From 6f12418af151df47978105160158fd685f689a54 Mon Sep 17 00:00:00 2001 From: yuzhi <529133328@qq.com> Date: Fri, 20 Mar 2020 14:34:59 +0800 Subject: [PATCH 124/201] modify check_status of shell --- dpgen/dispatcher/SSHContext.py | 10 ---------- dpgen/dispatcher/Shell.py | 30 +++++++++++++++--------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index e1c8ec135..14cfbb770 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -205,16 +205,6 @@ def call(self, cmd): def check_finish(self, cmd_pipes): return cmd_pipes['stdout'].channel.exit_status_ready() - - def check_running_uuid(self, uuid_names): - ## Check if the uuid.sub is running on remote machine - cnt = 0 - ret, stdin, stdout, stderr = self.block_call("ps aux | grep %s"%uuid_names) - response_list = stdout.read().decode('utf-8').split("\n") - for response in response_list: - if uuid_names + ".sub" in response: - return True - return False def get_return(self, cmd_pipes): diff --git a/dpgen/dispatcher/Shell.py b/dpgen/dispatcher/Shell.py index c9060d2f6..731ad52f5 100644 --- a/dpgen/dispatcher/Shell.py +++ b/dpgen/dispatcher/Shell.py @@ -11,22 +11,13 @@ def _default_item(resources, key, value) : class Shell(Batch) : def check_status(self) : - if not hasattr(self, 'proc'): - if (self.uuid_names): - if self.context.check_file_exists("%s_tag_finished"%self.context.job_uuid): - return JobStatus.finished - elif self.context.check_running_uuid(self.context.job_uuid): - return JobStatus.running - else: - return JobStatus.terminated - else: - return JobStatus.unsubmitted - if not self.context.check_finish(self.proc) : - return JobStatus.running - elif (self.context.get_return(self.proc))[0] == 0 : + if self.check_finish_tag(): return JobStatus.finished - else : + elif self.check_running(): + return JobStatus.running + else: return JobStatus.terminated + ## warn: cannont distinguish terminated from unsubmitted. def do_submit(self, job_dirs, @@ -41,6 +32,16 @@ def do_submit(self, self.context.write_file(self.sub_script_name, script_str) self.proc = self.context.call('cd %s && exec bash %s' % (self.context.remote_root, self.sub_script_name)) + def check_running(self): + uuid_names = self.context.job_uuid + ## Check if the uuid.sub is running on remote machine + cnt = 0 + ret, stdin, stdout, stderr = self.context.block_call("ps aux | grep %s"%uuid_names) + response_list = stdout.read().decode('utf-8').split("\n") + for response in response_list: + if uuid_names + ".sub" in response: + return True + return False def default_resources(self, res_) : if res_ == None : @@ -109,4 +110,3 @@ def sub_script_cmd(self, else : _cmd = '%s %s' % (_cmd, arg) return _cmd - From 4286164b8baa75cff960cac65dcafefc08f642c8 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 20 Mar 2020 15:04:15 +0800 Subject: [PATCH 125/201] fix autotest kspacing bug --- dpgen/auto_test/gen_00_equi.py | 1 + dpgen/auto_test/gen_03_vacancy.py | 12 ++++++++---- dpgen/auto_test/gen_04_interstitial.py | 13 +++++++++---- dpgen/auto_test/gen_05_surf.py | 17 ++++++++++++----- dpgen/auto_test/gen_06_phonon.py | 14 ++++++++------ 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/dpgen/auto_test/gen_00_equi.py b/dpgen/auto_test/gen_00_equi.py index bcd135458..67e837b75 100755 --- a/dpgen/auto_test/gen_00_equi.py +++ b/dpgen/auto_test/gen_00_equi.py @@ -8,6 +8,7 @@ from dpgen import ROOT_PATH from pymatgen.io.vasp import Incar +from dpgen.generator.lib.vasp import incar_upper cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') diff --git a/dpgen/auto_test/gen_03_vacancy.py b/dpgen/auto_test/gen_03_vacancy.py index ce087ede4..f50591b45 100755 --- a/dpgen/auto_test/gen_03_vacancy.py +++ b/dpgen/auto_test/gen_03_vacancy.py @@ -10,8 +10,8 @@ from pymatgen.analysis.defects.generators import VacancyGenerator from dpgen import ROOT_PATH -from pymatgen.io.vasp import Incar,Kpoints,Potcar -from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +from pymatgen.io.vasp import Incar +from dpgen.generator.lib.vasp import incar_upper cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') @@ -54,7 +54,10 @@ def make_vasp(jdata, conf_dir, supercell = [1,1,1]) : relax_incar_path = jdata['relax_incar'] assert(os.path.exists(relax_incar_path)) relax_incar_path = os.path.abspath(relax_incar_path) - fc = open(relax_incar_path).read() + incar = incar_upper(Incar.from_file(relax_incar_path)) + fc = incar.get_string() + kspacing = incar['KSPACING'] + kgamma = incar['KGAMMA'] else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -99,7 +102,8 @@ def make_vasp(jdata, conf_dir, supercell = [1,1,1]) : np.savetxt('supercell.out', supercell, fmt='%d') # write kp - make_vasp_kpoints_from_incar(struct_path,jdata) + fc = vasp.make_kspacing_kpoints('POSCAR', kspacing, kgamma) + with open('KPOINTS', 'w') as fp: fp.write(fc) #copy cvasp if ('cvasp' in jdata) and (jdata['cvasp'] == True): diff --git a/dpgen/auto_test/gen_04_interstitial.py b/dpgen/auto_test/gen_04_interstitial.py index fb745c76c..f1c10c4c3 100755 --- a/dpgen/auto_test/gen_04_interstitial.py +++ b/dpgen/auto_test/gen_04_interstitial.py @@ -10,8 +10,8 @@ from pymatgen.analysis.defects.generators import InterstitialGenerator from dpgen import ROOT_PATH -from pymatgen.io.vasp import Incar,Kpoints,Potcar -from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +from pymatgen.io.vasp import Incar +from dpgen.generator.lib.vasp import incar_upper cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') @@ -59,7 +59,11 @@ def _make_vasp(jdata, conf_dir, supercell, insert_ele) : relax_incar_path = jdata['relax_incar'] assert(os.path.exists(relax_incar_path)) relax_incar_path = os.path.abspath(relax_incar_path) - fc = open(relax_incar_path).read() + incar = incar_upper(Incar.from_file(relax_incar_path)) + fc = incar.get_string() + kspacing = incar['KSPACING'] + kgamma = incar['KGAMMA'] + else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -108,7 +112,8 @@ def _make_vasp(jdata, conf_dir, supercell, insert_ele) : np.savetxt('supercell.out', supercell, fmt='%d') # write kp - make_vasp_kpoints_from_incar(struct_path,jdata) + fc = vasp.make_kspacing_kpoints('POSCAR', kspacing, kgamma) + with open('KPOINTS', 'w') as fp: fp.write(fc) #copy cvasp if ('cvasp' in jdata) and (jdata['cvasp'] == True): diff --git a/dpgen/auto_test/gen_05_surf.py b/dpgen/auto_test/gen_05_surf.py index 164588d2f..ee6cd662d 100755 --- a/dpgen/auto_test/gen_05_surf.py +++ b/dpgen/auto_test/gen_05_surf.py @@ -8,8 +8,8 @@ from pymatgen.core.surface import generate_all_slabs, Structure from dpgen import ROOT_PATH -from pymatgen.io.vasp import Incar,Kpoints,Potcar -from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +from pymatgen.io.vasp import Incar +from dpgen.generator.lib.vasp import incar_upper cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') @@ -65,7 +65,10 @@ def make_vasp(jdata, conf_dir, max_miller = 2, relax_box = False, static = False scf_incar_path = jdata['scf_incar'] assert(os.path.exists(scf_incar_path)) scf_incar_path = os.path.abspath(scf_incar_path) - fc = open(scf_incar_path).read() + incar = incar_upper(Incar.from_file(scf_incar_path)) + fc = incar.get_string() + kspacing = incar['KSPACING'] + kgamma = incar['KGAMMA'] else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -80,7 +83,10 @@ def make_vasp(jdata, conf_dir, max_miller = 2, relax_box = False, static = False relax_incar_path = jdata['relax_incar'] assert(os.path.exists(relax_incar_path)) relax_incar_path = os.path.abspath(relax_incar_path) - fc = open(relax_incar_path).read() + incar = incar_upper(Incar.from_file(relax_incar_path)) + fc = incar.get_string() + kspacing = incar['KSPACING'] + kgamma = incar['KGAMMA'] else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -130,7 +136,8 @@ def make_vasp(jdata, conf_dir, max_miller = 2, relax_box = False, static = False os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')), 'POTCAR') # write kp - make_vasp_kpoints_from_incar(struct_path,jdata) + fc = vasp.make_kspacing_kpoints('POSCAR', kspacing, kgamma) + with open('KPOINTS', 'w') as fp: fp.write(fc) #copy cvasp if ('cvasp' in jdata) and (jdata['cvasp'] == True): diff --git a/dpgen/auto_test/gen_06_phonon.py b/dpgen/auto_test/gen_06_phonon.py index 5bd6e0691..09e9ef9b3 100644 --- a/dpgen/auto_test/gen_06_phonon.py +++ b/dpgen/auto_test/gen_06_phonon.py @@ -9,8 +9,8 @@ import yaml from dpgen import ROOT_PATH -from pymatgen.io.vasp import Incar,Kpoints,Potcar -from dpgen.auto_test.lib.vasp import make_vasp_kpoints_from_incar +from pymatgen.io.vasp import Incar +from dpgen.generator.lib.vasp import incar_upper cvasp_file=os.path.join(ROOT_PATH,'generator/lib/cvasp.py') @@ -121,9 +121,10 @@ def make_vasp(jdata, conf_dir) : user_incar_path = jdata['user_incar'] assert(os.path.exists(user_incar_path)) user_incar_path = os.path.abspath(user_incar_path) - fc = open(user_incar_path).read() - kspacing =float(re.findall((r"KSPACING(.+?)\n"),fc)[0].replace('=','')) - kgamma =('T' in re.findall((r"KGAMMA(.+?)\n"),fc)[0]) + incar = incar_upper(Incar.from_file(user_incar_path)) + fc = incar.get_string() + kspacing = incar['KSPACING'] + kgamma = incar['KGAMMA'] else : fp_params = jdata['vasp_params'] ecut = fp_params['ecut'] @@ -154,7 +155,8 @@ def make_vasp(jdata, conf_dir) : # fp.write(fc) # write kp - make_vasp_kpoints_from_incar(task_path,jdata) + fc = vasp.make_kspacing_kpoints('POSCAR', kspacing, kgamma) + with open('KPOINTS', 'w') as fp: fp.write(fc) #copy cvasp if ('cvasp' in jdata) and (jdata['cvasp'] == True): shutil.copyfile(cvasp_file, os.path.join(task_path,'cvasp.py')) From 592d033328c965062c7a5157a0faa070ce69a57e Mon Sep 17 00:00:00 2001 From: yuzhi <529133328@qq.com> Date: Fri, 20 Mar 2020 15:11:10 +0800 Subject: [PATCH 126/201] Delete ssh-uuid mode --- dpgen/dispatcher/ALI.py | 6 +++--- dpgen/dispatcher/Dispatcher.py | 17 ++++------------- .../machine/DeePMD-kit-1.0/machine-local.json | 3 --- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index dfab5f640..9e25bea90 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -242,7 +242,7 @@ def check_restart(self, work_path, tasks, group_size): profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id - disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) + disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) self.dispatchers.append([disp, "working"]) else: with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: @@ -337,7 +337,7 @@ def run_jobs(self, profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] profile["instance_id"] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] self.dispatchers[ii] = disp dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, @@ -386,7 +386,7 @@ def make_dispatchers(self): profile = self.mdata_machine.copy() profile['hostname'] = self.ip_list[ii] profile['instance_id'] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] dispatchers.append(disp) return dispatchers diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 00b1bff60..2434378c0 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -35,6 +35,7 @@ def __init__ (self, batch_type = 'slurm', job_record = 'jr.json'): self.remote_profile = remote_profile + if context_type == 'local': self.session = LocalSession(remote_profile) self.context = LocalContext @@ -44,10 +45,6 @@ def __init__ (self, self.context = LazyLocalContext self.uuid_names = True elif context_type == 'ssh': - self.session = SSHSession(remote_profile) - self.context = SSHContext - self.uuid_names = False - elif context_type == 'ssh-uuid': self.session = SSHSession(remote_profile) self.context = SSHContext self.uuid_names = True @@ -313,12 +310,9 @@ def make_dispatcher(mdata, mdata_resource=None, work_path=None, run_tasks=None, return dispatcher else: hostname = mdata.get('hostname', None) - use_uuid = mdata.get('use_uuid', False) + #use_uuid = mdata.get('use_uuid', False) if hostname: - if use_uuid: - context_type = 'ssh-uuid' - else: - context_type = 'ssh' + context_type = 'ssh' else: context_type = 'local' try: @@ -326,10 +320,7 @@ def make_dispatcher(mdata, mdata_resource=None, work_path=None, run_tasks=None, except: dlog.info('cannot find key "batch" in machine file, try to use deprecated key "machine_type"') batch_type = mdata['machine_type'] - try: - lazy_local = mdata['lazy_local'] - except: - lazy_local = False + lazy_local = (mdata.get('lazy-local', False)) or (mdata.get('lazy_local', True)) if lazy_local and context_type == 'local': dlog.info('Dispatcher switches to the lazy local mode') context_type = 'lazy-local' diff --git a/examples/machine/DeePMD-kit-1.0/machine-local.json b/examples/machine/DeePMD-kit-1.0/machine-local.json index 291d90662..d418a783e 100644 --- a/examples/machine/DeePMD-kit-1.0/machine-local.json +++ b/examples/machine/DeePMD-kit-1.0/machine-local.json @@ -4,9 +4,6 @@ "python_path": "/home/wanghan/local/deepmd/1.*/python", "train_machine": { "batch": "shell", - "_comment" : "If use_uuid is True, the batch script will be named as UUID.sub", - "_comment" : "In this way , shell can be restarted if DP-GEN is terminated ", - "use_uuid" : false, "work_path" : "/home/wanghan/tmp/subs/" }, "train_resources": { From 5493f9a8d0de0cee284ae6abf94542b34b8991e0 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 20 Mar 2020 15:12:57 +0800 Subject: [PATCH 127/201] fix bug --- dpgen/auto_test/gen_00_equi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpgen/auto_test/gen_00_equi.py b/dpgen/auto_test/gen_00_equi.py index bcd135458..ddfcf9d11 100755 --- a/dpgen/auto_test/gen_00_equi.py +++ b/dpgen/auto_test/gen_00_equi.py @@ -6,6 +6,8 @@ import dpgen.auto_test.lib.vasp as vasp import dpgen.auto_test.lib.lammps as lammps +from dpgen import dlog +from dpgen.generator.lib.vasp import incar_upper from dpgen import ROOT_PATH from pymatgen.io.vasp import Incar From 92d5c7be9c91e42cf79e67b52a4ae0699663085b Mon Sep 17 00:00:00 2001 From: yuzhi <529133328@qq.com> Date: Fri, 20 Mar 2020 15:15:56 +0800 Subject: [PATCH 128/201] Fix bug --- dpgen/dispatcher/Dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 2434378c0..3495d2f39 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -320,7 +320,7 @@ def make_dispatcher(mdata, mdata_resource=None, work_path=None, run_tasks=None, except: dlog.info('cannot find key "batch" in machine file, try to use deprecated key "machine_type"') batch_type = mdata['machine_type'] - lazy_local = (mdata.get('lazy-local', False)) or (mdata.get('lazy_local', True)) + lazy_local = (mdata.get('lazy-local', False)) or (mdata.get('lazy_local', False)) if lazy_local and context_type == 'local': dlog.info('Dispatcher switches to the lazy local mode') context_type = 'lazy-local' From 0f8c2b747036dff0e196ecbafdd8a1b02717b6ec Mon Sep 17 00:00:00 2001 From: yuzhi <529133328@qq.com> Date: Fri, 20 Mar 2020 15:41:16 +0800 Subject: [PATCH 129/201] Change uuid_names to True for all batches. Fix tag_upload bug --- dpgen/dispatcher/Batch.py | 4 +++- dpgen/dispatcher/Dispatcher.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dpgen/dispatcher/Batch.py b/dpgen/dispatcher/Batch.py index 2e2a7c1f3..02b09e68e 100644 --- a/dpgen/dispatcher/Batch.py +++ b/dpgen/dispatcher/Batch.py @@ -7,14 +7,16 @@ class Batch(object) : def __init__ (self, context, - uuid_names = False) : + uuid_names = True) : self.context = context self.uuid_names = uuid_names if uuid_names: + self.upload_tag_name = '%s_tag_upload' % self.context.job_uuid self.finish_tag_name = '%s_tag_finished' % self.context.job_uuid self.sub_script_name = '%s.sub' % self.context.job_uuid self.job_id_name = '%s_job_id' % self.context.job_uuid else: + self.upload_tag_name = 'tag_upload' self.finish_tag_name = 'tag_finished' self.sub_script_name = 'run.sub' self.job_id_name = 'job_id' diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 3495d2f39..a7e13b7d0 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -39,7 +39,7 @@ def __init__ (self, if context_type == 'local': self.session = LocalSession(remote_profile) self.context = LocalContext - self.uuid_names = False + self.uuid_names = True elif context_type == 'lazy-local': self.session = None self.context = LazyLocalContext @@ -135,13 +135,13 @@ def submit_jobs(self, batch = self.batch(context, uuid_names = self.uuid_names) rjob = {'context':context, 'batch':batch} # upload files - if not rjob['context'].check_file_exists('tag_upload'): + if not rjob['context'].check_file_exists(rjob['batch'].upload_tag_name): rjob['context'].upload('.', forward_common_files) rjob['context'].upload(cur_chunk, forward_task_files, dereference = forward_task_deference) - rjob['context'].write_file('tag_upload', '') + rjob['context'].write_file(rjob['batch'].upload_tag_name, '') dlog.debug('uploaded files for %s' % task_chunks_str[ii]) # submit new or recover old submission if not submitted: From 3efb931a32b6767d3ffa55d214f3ec9c36bf7315 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Mar 2020 17:17:35 +0800 Subject: [PATCH 130/201] fix bug --- dpgen/dispatcher/ALI.py | 220 +++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 116 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 60383cda7..755772f63 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -45,10 +45,10 @@ def manual_create(stage, machine_number): class ALI(): def __init__(self, adata, mdata_resources, mdata_machine, nchunks): - self.ip_list = [] - self.instance_list = [] - self.dispatchers = [] - self.job_handlers = [None for i in range(nchunks)] + self.ip_list = ["unalloc" for i in range(nchunks)] + self.instance_list = ["unalloc" for i in range(nchunks)] + self.dispatchers = [[None, "unalloc"] for i in range(nchunks)] + self.job_handlers = ["unalloc" for i in range(nchunks)] self.task_chunks = None self.adata = adata self.apg_id = None @@ -107,12 +107,9 @@ def create_apg(self): json.dump({'apg_id': response["AutoProvisioningGroupId"]}, fp, indent=4) return response["AutoProvisioningGroupId"] - def update_instance_ip_list(self): + def update_server_list(self): instance_list = self.describe_apg_instances() - ip_list = self.get_ip(instance_list) - self.instance_list += list(set(instance_list) - set(self.instance_list)) - self.ip_list += list(set(ip_list) - set(self.ip_list)) - #dlog.info("%d,%d,%d,%d"%(len(instance_list), len(ip_list), len(self.instance_list), len(self.ip_list))) + return list(set(instance_list) - set(self.instance_list)) def describe_apg_instances(self): request = DescribeAutoProvisioningGroupInstancesRequest() @@ -220,7 +217,6 @@ def check_restart(self, work_path, tasks, group_size): with open('apg_id.json') as fp: apg = json.load(fp) self.apg_id = apg["apg_id"] - #dlog.info(self.apg_id) self.task_chunks = _split_tasks(tasks, group_size) task_chunks_str = ['+'.join(ii) for ii in self.task_chunks] task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] @@ -228,32 +224,38 @@ def check_restart(self, work_path, tasks, group_size): for ii in range(nchunks): fn = 'jr.%.06d.json' % ii if not os.path.exists(os.path.join(os.path.abspath(work_path), fn)): - self.dispatchers.append([None, "unalloc"]) + pass else: - job_record = JobRecord(work_path, self.task_chunks, fname = fn) + job_record = JobRecord(work_path, self.task_chunks[ii], fname = fn) cur_chunk = self.task_chunks[ii] cur_hash = task_hashes[ii] if not job_record.check_finished(cur_hash): - with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: + with open(os.path.join(work_path, fn)) as fp: jr = json.load(fp) ip = jr[cur_hash]['context'][3] instance_id = jr[cur_hash]['context'][4] - self.ip_list.append(ip) - self.instance_list.append(instance_id) profile = self.mdata_machine.copy() profile['hostname'] = ip profile['instance_id'] = instance_id if self.check_server(profile): disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) - self.dispatchers.append([disp, "working"]) + self.dispatchers[ii] = [disp, "working"] + self.ip_list[ii] = ip + self.instance_list[ii] = instance_id + # spot callback + else: + os.remove(os.path.join(work_path, fn)) else: - with open(os.path.join(work_path, 'jr.%.06d.json' % ii)) as fp: - jr = json.load(fp) - ip = jr[cur_hash]['context'][3] - instance_id = jr[cur_hash]['context'][4] - self.ip_list.append(ip) - self.instance_list.append(instance_id) - self.dispatchers.append([None, "finished"]) + self.ip_list[ii] = "finished" + self.instance_list[ii] = "finished" + self.dispatchers[ii] = [None, "finished"] + # with open(os.path.join(work_path, fn)) as fp: + # jr = json.load(fp) + # ip = jr[cur_hash]['context'][3] + # instance_id = jr[cur_hash]['context'][4] + # self.ip_list.append("finished") + # self.instance_list.append("finished") + # self.dispatchers.append([None, "finished"]) #dlog.info(self.ip_list) #dlog.info(self.dispatchers) return True @@ -326,92 +328,78 @@ def run_jobs(self, errlog) self.job_handlers[ii] = job_handler machine_exception_num = 0 - exception = [] while True: - #dlog.info(self.ip_list) - #dlog.info(self.task_chunks) - #dlog.info(exception_task_chunks) - #dlog.info(exception_jr_name) - # if machine_exception_num / self.nchunks > 0.05: - # try: - # self.update_instance_ip_list() - # except: - # pass - # #dlog.info(self.ip_list) - # if len(self.ip_list) == len(self.task_chunks) + len(exception): - # exception = sorted(exception, key=lambda x:x[2]) - # restart_ip_list = self.ip_list[-len(exception):] - # restart_instance_list = self.instance_list[-len(exception):] - # for ii in range(len(exception)): - # #dlog.info(exception_task_chunks[ii]) - # #dlog.info(exception_jr_name[ii]) - # profile = self.mdata_machine.copy() - # profile["hostname"] = restart_ip_list[ii] - # profile["instance_id"] = restart_instance_list[ii] - # disp = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record=exception[ii][1]), "working"] - # pos = exception[ii][2] - # self.dispatchers.insert(pos, disp) - # self.ip_list.insert(pos, self.ip_list.pop(self.ip_list.index(profile["hostname"]))) - # self.instance_list.insert(pos, self.instance_list.pop(self.instance_list.index(profile["instance_id"]))) - # dlog.info(restart_ip_list[ii]) - # job_handler = disp[0].submit_jobs(resources, - # command, - # work_path, - # exception[ii][0], - # group_size, - # forward_common_files, - # forward_task_files, - # backward_task_files, - # forward_task_deference, - # outlog, - # errlog) - # self.job_handlers.insert(pos, job_handler) - # self.task_chunks.insert(pos, exception[ii][0]) - # exception = [] - for ii in range(len(self.task_chunks)): + if machine_exception_num / self.nchunks > 0.05: + try: + new_server_list = self.update_server_list() + except: + pass + if len(new_server_list) + len(self.instance_list) == self.nchunks: + new_ip_list = self.get_ip(new_server_list) + for ii in range(self.nchunks): + if self.dispatchers[ii][1] = "exception": + self.ip_list[ii] = new_ip_list.pop() + self.instance_list[ii] = new_server_list.pop() + profile = self.mdata_machine.copy() + profile["hostname"] = self.ip_list[ii] + profile["instance_id"] = self.instance_list[ii] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record="jr.%.06d.json" % ii), "working"] + dlog.info(self.ip_list[ii]) + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers[ii] = job_handler + for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.dispatchers[ii][1] = "finished" + self.ip_list[ii] = "finished" + self.instance_list[ii] = "finished" self.delete(ii) self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: machine_exception_num += 1 - exception.append([self.task_chunks.pop(ii), self.job_handlers[ii]["job_record"].fname[-14:], ii]) - self.dispatchers.pop(ii) - self.ip_list.pop(ii) - self.instance_list.pop(ii) - self.job_handlers.pop(ii) - dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname[-14:]) + self.dispatchers[ii] = [None, "exception"] + self.ip_list[ii] = "unalloc" + self.instance_list[ii] = "unalloc" os.remove(self.job_handlers[ii]["job_record"].fname) - break elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": - continue - # try: - # self.update_instance_ip_list() - # except: - # pass - # if ii < len(self.ip_list): - # profile = self.mdata_machine.copy() - # profile["hostname"] = self.ip_list[ii] - # profile["instance_id"] = self.instance_list[ii] - # disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] - # self.dispatchers[ii] = disp - # dlog.info(self.ip_list[ii]) - # job_handler = self.dispatchers[ii][0].submit_jobs(resources, - # command, - # work_path, - # self.task_chunks[ii], - # group_size, - # forward_common_files, - # forward_task_files, - # backward_task_files, - # forward_task_deference, - # outlog, - # errlog) - # self.job_handlers[ii] = job_handler + try: + new_server_list = self.update_server_list() + except: + pass + if new_server_list: + new_ip_list = self.get_ip(new_server_list) + self.ip_list[ii] = new_ip_list.pop() + self.instance_list[ii] = new_server_list.pop() + profile = self.mdata_machine.copy() + profile["hostname"] = self.ip_list[ii] + profile["instance_id"] = self.instance_list[ii] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + dlog.info(self.ip_list[ii]) + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers[ii] = job_handler if self.check_dispatcher_finished(): os.remove('apg_id.json') self.delete_template() @@ -430,28 +418,28 @@ def check_server(self, profile): return True except: time.sleep(60) - #dlog.info(False) + dlog.info(False) return False - def dispatcher_finish(self, dispatcher, job_handler, mark_failure): - job_record = job_handler['job_record'] - rjob = job_handler['job_list'][0] - status = rjob['batch'].check_status() - if status == JobStatus.terminated : - machine_status = True - ssh_active_count = 0 - while True: - if rjob['context'].ssh_session._check_alive(): - break - if not rjob['context'].ssh_session._check_alive(): - ssh_active_count += 1 - if ssh_active_count == 3: - machine_status = False - break - if machine_status == False: - pass - else: - return dispatcher.all_finished(job_handler, mark_failure) + # def dispatcher_finish(self, dispatcher, job_handler, mark_failure): + # job_record = job_handler['job_record'] + # rjob = job_handler['job_list'][0] + # status = rjob['batch'].check_status() + # if status == JobStatus.terminated : + # machine_status = True + # ssh_active_count = 0 + # while True: + # if rjob['context'].ssh_session._check_alive(): + # break + # if not rjob['context'].ssh_session._check_alive(): + # ssh_active_count += 1 + # if ssh_active_count == 3: + # machine_status = False + # break + # if machine_status == False: + # pass + # else: + # return dispatcher.all_finished(job_handler, mark_failure) def check_dispatcher_finished(self): for ii in range(len(self.dispatchers)): From 782fbb3449ece5b1b5d71794a84e3269be4c4040 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Mar 2020 17:20:14 +0800 Subject: [PATCH 131/201] fix bug --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 755772f63..748155f51 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -337,7 +337,7 @@ def run_jobs(self, if len(new_server_list) + len(self.instance_list) == self.nchunks: new_ip_list = self.get_ip(new_server_list) for ii in range(self.nchunks): - if self.dispatchers[ii][1] = "exception": + if self.dispatchers[ii][1] == "exception": self.ip_list[ii] = new_ip_list.pop() self.instance_list[ii] = new_server_list.pop() profile = self.mdata_machine.copy() From e5007c9b92a28191082815511dd546dd27dc8622 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Mar 2020 17:30:45 +0800 Subject: [PATCH 132/201] fix bug --- dpgen/dispatcher/ALI.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 748155f51..7a52664f5 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -65,7 +65,7 @@ def init(self, work_path, tasks, group_size): pass else: self.create_ess() - self.dispatchers = self.make_dispatchers() + self.make_dispatchers() def create_ess(self): img_id = self.get_image_id(self.adata["img_name"]) @@ -75,8 +75,11 @@ def create_ess(self): self.apg_id = self.create_apg() dlog.info("begin to create ess") time.sleep(90) - self.instance_list = self.describe_apg_instances() - self.ip_list = self.get_ip(self.instance_list) + new_server_list = self.describe_apg_instances() + new_ip_list = self.get_ip(new_server_list) + for ii in range(len(new_server_list)): + self.instance_list[ii] = new_server_list[ii] + self.ip_list[ii] = new_ip_list[ii] def delete_apg(self): request = DeleteAutoProvisioningGroupRequest() @@ -150,6 +153,7 @@ def create_template(self, image_id, sg_id, vpc_id): request.set_ImageOwnerAlias("self") request.set_PasswordInherit(True) request.set_InstanceType("ecs.c6.large") + request.set_InstanceName(self.adata["instance_name"]) request.set_SecurityGroupId(sg_id) request.set_VpcId(vpc_id) request.set_InternetMaxBandwidthIn(10) @@ -455,19 +459,14 @@ def delete(self, ii): response = self.client.do_action_with_exception(request) def make_dispatchers(self): - dispatchers = [] - if len(self.ip_list) < self.nchunks: - dlog.info("machine resources unsuffient, %d jobs are running, %d jobs are pending" %(len(self.ip_list), self.nchunks-len(self.ip_list))) for ii in range(self.nchunks): if ii >= len(self.ip_list): - disp = [None, "unalloc"] + break else: profile = self.mdata_machine.copy() profile['hostname'] = self.ip_list[ii] profile['instance_id'] = self.instance_list[ii] - disp = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] - dispatchers.append(disp) - return dispatchers + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] def delete_machine(self): request = DeleteInstancesRequest() From d461f39d895255cddab7a47da3f1c9f4a6236024 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Mar 2020 19:34:07 +0800 Subject: [PATCH 133/201] fix bug --- dpgen/dispatcher/ALI.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 7a52664f5..75843c7e6 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -338,7 +338,11 @@ def run_jobs(self, new_server_list = self.update_server_list() except: pass - if len(new_server_list) + len(self.instance_list) == self.nchunks: + count = 0 + for ii in range(self.nchunks): + if self.instance_list[ii] == "exception": + count += 1 + if len(new_server_list) == count and len(new_server_list) > 0: new_ip_list = self.get_ip(new_server_list) for ii in range(self.nchunks): if self.dispatchers[ii][1] == "exception": @@ -361,21 +365,31 @@ def run_jobs(self, outlog, errlog) self.job_handlers[ii] = job_handler + # dlog.info(self.ip_list) + # dlog.info(self.instance_list) + # dlog.info(self.dispatchers) for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): + dlog.info(self.dispatchers) if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): + self.delete(ii) self.dispatchers[ii][1] = "finished" self.ip_list[ii] = "finished" self.instance_list[ii] = "finished" - self.delete(ii) self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: + os.remove(self.job_handlers[ii]["job_record"].fname) machine_exception_num += 1 + dlog.info("exception") + dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname) + # dlog.info(self.ip_list) + # dlog.info(self.instance_list) + # dlog.info(self.dispatchers) self.dispatchers[ii] = [None, "exception"] - self.ip_list[ii] = "unalloc" - self.instance_list[ii] = "unalloc" - os.remove(self.job_handlers[ii]["job_record"].fname) + self.job_handlers[ii] = "exception" + self.ip_list[ii] = "exception" + self.instance_list[ii] = "exception" elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": @@ -421,7 +435,9 @@ def check_server(self, profile): #dlog.info(True) return True except: - time.sleep(60) + #time.sleep(60) + dlog.info(False) + pass dlog.info(False) return False @@ -460,9 +476,7 @@ def delete(self, ii): def make_dispatchers(self): for ii in range(self.nchunks): - if ii >= len(self.ip_list): - break - else: + if self.ip_list[ii] != "unalloc": profile = self.mdata_machine.copy() profile['hostname'] = self.ip_list[ii] profile['instance_id'] = self.instance_list[ii] From 637522e368b10d72e2028a906cf7c814517e40ee Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Mar 2020 19:36:34 +0800 Subject: [PATCH 134/201] fix bug --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 75843c7e6..75c500dff 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -435,7 +435,7 @@ def check_server(self, profile): #dlog.info(True) return True except: - #time.sleep(60) + time.sleep(60) dlog.info(False) pass dlog.info(False) From fb8c7414f5ea132dfd76b133acdf5576a2b84b34 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 21 Mar 2020 19:42:35 +0800 Subject: [PATCH 135/201] fix bug --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 75c500dff..cc8430cad 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -463,7 +463,7 @@ def check_server(self, profile): def check_dispatcher_finished(self): for ii in range(len(self.dispatchers)): - if self.dispatchers[ii][1] == "unalloc" or self.dispatchers[ii][1] == "working": + if self.dispatchers[ii][1] == "unalloc" or self.dispatchers[ii][1] == "working" or self.dispatchers[ii][1] == "exception": return False return True From 7869952d92fc5bd149224027bc2576aac1df3cfc Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 21 Mar 2020 23:31:06 +0800 Subject: [PATCH 136/201] fix bugs in slurm test --- tests/dispatcher/slurm/test_dispatcher_lazy_local.py | 11 +++++------ tests/dispatcher/slurm/test_slurm_local.py | 6 +++--- tests/dispatcher/slurm/test_slurm_ssh.py | 8 ++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/dispatcher/slurm/test_dispatcher_lazy_local.py b/tests/dispatcher/slurm/test_dispatcher_lazy_local.py index 7d21a2fe7..89fd9b9a4 100644 --- a/tests/dispatcher/slurm/test_dispatcher_lazy_local.py +++ b/tests/dispatcher/slurm/test_dispatcher_lazy_local.py @@ -22,15 +22,14 @@ def setUp(self) : for ii in ['loc/task0', 'loc/task1', 'loc/task2']: with open(os.path.join(ii, 'test0'),'w') as fp: fp.write('this is test0 from ' + ii + '\n') - work_profile = None + work_profile = {} self.disp = Dispatcher(work_profile, 'lazy-local', 'slurm') def tearDown(self): - # shutil.rmtree('loc') - # shutil.rmtree('rmt') - # if os.path.exists('dpgen.log'): - # os.remove('dpgen.log') - pass + shutil.rmtree('loc') + shutil.rmtree('rmt') + if os.path.exists('dpgen.log'): + os.remove('dpgen.log') def test_sub_success(self): tasks = ['task0', 'task1', 'task2'] diff --git a/tests/dispatcher/slurm/test_slurm_local.py b/tests/dispatcher/slurm/test_slurm_local.py index bca72a54a..0aeca1f75 100644 --- a/tests/dispatcher/slurm/test_slurm_local.py +++ b/tests/dispatcher/slurm/test_slurm_local.py @@ -50,7 +50,7 @@ def test_sub_success(self) : self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'tag_finished'))) + self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, '%s_tag_finished' % self.slurm.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test2'))) @@ -76,7 +76,7 @@ def test_sub_scancel(self) : self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_0_finished'))) self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_1_finished'))) - self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'tag_finished'))) + self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, '%s_tag_finished' % self.slurm.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/test1'))) self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test2'))) @@ -92,7 +92,7 @@ def test_sub_scancel(self) : self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'tag_finished'))) + self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, '%s_tag_finished' % self.slurm.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test2'))) diff --git a/tests/dispatcher/slurm/test_slurm_ssh.py b/tests/dispatcher/slurm/test_slurm_ssh.py index 204e2191f..774650110 100644 --- a/tests/dispatcher/slurm/test_slurm_ssh.py +++ b/tests/dispatcher/slurm/test_slurm_ssh.py @@ -37,7 +37,7 @@ def test_gen_sub_script(self): self.slurm.context.write_file('run.sub', ret) with open('run.sub', 'w') as fp: fp.write(ret) - ret1 = self.slurm.sub_script(job_dirs, ['touch', 'touch'], [['test1 ', 'test2 '], ['test1 ', 'test2 ']]) + ret1 = self.slurm.sub_script(job_dirs, ['touch', 'touch'], [['test1 ', 'test1 '], ['test2 ', 'test2 ']]) with open('run.sub.1', 'w') as fp: fp.write(ret1) my_file_cmp(self, 'run.sub.1', 'run.sub') @@ -55,7 +55,7 @@ def test_sub_success(self) : self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'tag_finished'))) + self.assertTrue(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, '%s_tag_finished' % self.slurm.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test2'))) @@ -81,7 +81,7 @@ def test_sub_scancel(self) : self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_0_finished'))) self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_1_finished'))) - self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'tag_finished'))) + self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, '%s_tag_finished' % self.slurm.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/test1'))) self.assertFalse(os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test2'))) @@ -97,7 +97,7 @@ def test_sub_scancel(self) : self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'tag_finished'))) + self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, '%s_tag_finished' % self.slurm.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.slurm.context.remote_root, 'task0/test2'))) From 8b01955283b61d14bb31c5b92257f345995c22a4 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 22 Mar 2020 17:01:42 +0800 Subject: [PATCH 137/201] fix bug --- dpgen/dispatcher/ALI.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index cc8430cad..62ac2a19c 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -44,12 +44,13 @@ def manual_create(stage, machine_number): print(ali.ip_list) class ALI(): - def __init__(self, adata, mdata_resources, mdata_machine, nchunks): + def __init__(self, adata, mdata_resources, mdata_machine, nchunks, stage): self.ip_list = ["unalloc" for i in range(nchunks)] self.instance_list = ["unalloc" for i in range(nchunks)] self.dispatchers = [[None, "unalloc"] for i in range(nchunks)] self.job_handlers = ["unalloc" for i in range(nchunks)] self.task_chunks = None + self.stage = stage self.adata = adata self.apg_id = None self.template_id = None @@ -333,7 +334,7 @@ def run_jobs(self, self.job_handlers[ii] = job_handler machine_exception_num = 0 while True: - if machine_exception_num / self.nchunks > 0.05: + if machine_exception_num > 2: try: new_server_list = self.update_server_list() except: @@ -371,7 +372,7 @@ def run_jobs(self, for ii in range(self.nchunks): if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): - dlog.info(self.dispatchers) + #dlog.info(self.dispatchers) if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.delete(ii) self.dispatchers[ii][1] = "finished" @@ -379,10 +380,13 @@ def run_jobs(self, self.instance_list[ii] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: + try: + self.delete(ii) + except: + pass os.remove(self.job_handlers[ii]["job_record"].fname) machine_exception_num += 1 - dlog.info("exception") - dlog.info("remove %s" % self.job_handlers[ii]["job_record"].fname) + dlog.info("exception accured in %s, remove %s, %s" % (self.ip_list[ii], self.job_handlers[ii]["job_record"].fname,self.ip_list[ii])) # dlog.info(self.ip_list) # dlog.info(self.instance_list) # dlog.info(self.dispatchers) @@ -395,10 +399,10 @@ def run_jobs(self, elif self.dispatchers[ii][1] == "unalloc": try: new_server_list = self.update_server_list() + new_ip_list = self.get_ip(new_server_list) except: pass - if new_server_list: - new_ip_list = self.get_ip(new_server_list) + if new_ip_list: self.ip_list[ii] = new_ip_list.pop() self.instance_list[ii] = new_server_list.pop() profile = self.mdata_machine.copy() @@ -436,9 +440,9 @@ def check_server(self, profile): return True except: time.sleep(60) - dlog.info(False) + #dlog.info(False) pass - dlog.info(False) + #dlog.info(False) return False # def dispatcher_finish(self, dispatcher, job_handler, mark_failure): From c725ebceae489eaadfbbf2f351ca13ff8837c0d6 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 22 Mar 2020 17:11:55 +0800 Subject: [PATCH 138/201] fix bug --- dpgen/dispatcher/ALI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 62ac2a19c..088ce43e3 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -157,8 +157,8 @@ def create_template(self, image_id, sg_id, vpc_id): request.set_InstanceName(self.adata["instance_name"]) request.set_SecurityGroupId(sg_id) request.set_VpcId(vpc_id) - request.set_InternetMaxBandwidthIn(10) - request.set_InternetMaxBandwidthOut(10) + request.set_InternetMaxBandwidthIn(50) + request.set_InternetMaxBandwidthOut(50) request.set_SystemDiskCategory("cloud_efficiency") request.set_SystemDiskSize(40) request.set_IoOptimized("optimized") From 88939206d25deda2b1bc9283e50533e527f5645b Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 22 Mar 2020 17:19:55 +0800 Subject: [PATCH 139/201] fix bug --- dpgen/dispatcher/ALI.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 68cdabc58..21f2ce5eb 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -397,6 +397,7 @@ def run_jobs(self, elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": + new_ip_list = [] try: new_server_list = self.update_server_list() new_ip_list = self.get_ip(new_server_list) From 7e1a5a72c2c2e0006f4b2bf0051f1b258ed6a34f Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 22 Mar 2020 18:25:54 +0800 Subject: [PATCH 140/201] fix bug --- dpgen/dispatcher/ALI.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 21f2ce5eb..063177f73 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -44,13 +44,12 @@ def manual_create(stage, machine_number): print(ali.ip_list) class ALI(): - def __init__(self, adata, mdata_resources, mdata_machine, nchunks, stage): + def __init__(self, adata, mdata_resources, mdata_machine, nchunks): self.ip_list = ["unalloc" for i in range(nchunks)] self.instance_list = ["unalloc" for i in range(nchunks)] self.dispatchers = [[None, "unalloc"] for i in range(nchunks)] self.job_handlers = ["unalloc" for i in range(nchunks)] self.task_chunks = None - self.stage = stage self.adata = adata self.apg_id = None self.template_id = None @@ -243,7 +242,7 @@ def check_restart(self, work_path, tasks, group_size): profile['hostname'] = ip profile['instance_id'] = instance_id if self.check_server(profile): - disp = Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii) + disp = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) self.dispatchers[ii] = [disp, "working"] self.ip_list[ii] = ip self.instance_list[ii] = instance_id @@ -352,7 +351,7 @@ def run_jobs(self, profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] profile["instance_id"] = self.instance_list[ii] - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record="jr.%.06d.json" % ii), "working"] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record="jr.%.06d.json" % ii), "working"] dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, command, @@ -409,7 +408,7 @@ def run_jobs(self, profile = self.mdata_machine.copy() profile["hostname"] = self.ip_list[ii] profile["instance_id"] = self.instance_list[ii] - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, command, @@ -485,7 +484,7 @@ def make_dispatchers(self): profile = self.mdata_machine.copy() profile['hostname'] = self.ip_list[ii] profile['instance_id'] = self.instance_list[ii] - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh-uuid', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] def delete_machine(self): From d90b1d66eb1f0ccb0ba58b57e625ec5871a36fed Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 23 Mar 2020 15:46:38 +0800 Subject: [PATCH 141/201] add collect data --- dpgen/collect/__init__.py | 0 dpgen/collect/collect.py | 92 +++++++++++++++++++++++++++++++++++++++ dpgen/main.py | 15 +++++++ setup.py | 1 + 4 files changed, 108 insertions(+) create mode 100644 dpgen/collect/__init__.py create mode 100644 dpgen/collect/collect.py diff --git a/dpgen/collect/__init__.py b/dpgen/collect/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dpgen/collect/collect.py b/dpgen/collect/collect.py new file mode 100644 index 000000000..b60be8c3b --- /dev/null +++ b/dpgen/collect/collect.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +import os,sys,json,glob,argparse,dpdata +import numpy as np +from dpgen.generator.run import data_system_fmt + +def collect_data(target_folder, param_file, output, verbose = True, shuffle = True) : + target_folder = os.path.abspath(target_folder) + output = os.path.abspath(output) + # goto input + cwd = os.getcwd() + os.chdir(target_folder) + jdata = json.load(open(param_file)) + sys_configs_prefix = jdata.get('sys_configs_prefix', '') + sys_configs = jdata.get('sys_configs', []) + if verbose : + max_str_len = max([len(str(ii)) for ii in sys_configs]) + max_form_len = 16 + ptr_fmt = '%%%ds %%%ds natoms %%6d nframes %%6d' % (max_str_len+5, max_form_len) + # init systems + init_data = [] + init_data_prefix = jdata.get('init_data_prefix', '') + init_data_sys = jdata.get('init_data_sys', []) + for ii in init_data_sys: + init_data.append(dpdata.LabeledSystem(os.path.join(init_data_prefix, ii), fmt='deepmd/npy')) + # collect systems from iter dirs + coll_data = {} + numb_sys = len(sys_configs) + model_devi_jobs = jdata.get('model_devi_jobs', {}) + numb_jobs = len(model_devi_jobs) + iters = ['iter.%06d' % ii for ii in range(numb_jobs)] + # loop over iters to collect data + for ii in range(len(iters)) : + iter_data = glob.glob(os.path.join(iters[ii], '02.fp', 'data.[0-9]*[0-9]')) + iter_data.sort() + for jj in iter_data : + sys = dpdata.LabeledSystem(jj, fmt = 'deepmd/npy') + sys_str = (os.path.basename(jj).split('.')[-1]) + if sys_str in coll_data.keys(): + coll_data[sys_str].append(sys) + else: + coll_data[sys_str] = sys + # print information + if verbose: + for ii in range(len(init_data)): + print(ptr_fmt % (str(init_data_sys[ii]), + init_data[ii].formula, + init_data[ii].get_natoms(), + init_data[ii].get_nframes() )) + keys = list(coll_data.keys()) + keys.sort() + for idx,ii in enumerate(keys): + print(ptr_fmt % (str(sys_configs[idx]), + coll_data[ii].formula, + coll_data[ii].get_natoms(), + coll_data[ii].get_nframes() )) + # shuffle system data + if shuffle: + for kk in coll_data.keys(): + coll_data[kk].shuffle() + # create output dir + os.chdir(cwd) + os.makedirs(output, exist_ok = True) + # dump init data + for idx,ii in enumerate(init_data): + out_dir = 'init.' + (data_system_fmt % idx) + ii.to('deepmd/npy', os.path.join(output, out_dir)) + # dump iter data + for kk in coll_data.keys(): + out_dir = 'sys.%s' % kk + coll_data[kk].to('deepmd/npy', os.path.join(output, out_dir)) + +def gen_collect(args): + collect_data(args.JOB_DIR, args.parameter, args.OUTPUT, args.verbose) + +def _main() : + parser = argparse.ArgumentParser(description='Collect data from DP-GEN iterations') + parser.add_argument("JOB_DIR", type=str, + help="the directory of the DP-GEN job") + parser.add_argument("OUTPUT", type=str, + help="the output directory of data") + parser.add_argument('-p',"--parameter", type=str, default = 'param.json', + help="the json file provides DP-GEN paramters, should be located in JOB_DIR") + parser.add_argument('-v',"--verbose", action = 'store_true', + help="print number of data in each system") + args = parser.parse_args() + gen_collect(args) + +if __name__ == '__main__': + _main() + + diff --git a/dpgen/main.py b/dpgen/main.py index f5aff6583..e7c731244 100644 --- a/dpgen/main.py +++ b/dpgen/main.py @@ -10,6 +10,7 @@ from dpgen.data.gen import gen_init_bulk from dpgen.data.surf import gen_init_surf from dpgen.data.reaction import gen_init_reaction +from dpgen.collect.collect import gen_collect from dpgen.simplify.simplify import gen_simplify from dpgen.auto_test.run import gen_test from dpgen.database.run import db_run @@ -115,6 +116,20 @@ def main(): help="being loud") parser_rr.set_defaults(func=run_report) + # collect + parser_coll = subparsers.add_parser( + "collect", + help="Collect data.") + parser_coll.add_argument("JOB_DIR", type=str, + help="the directory of the DP-GEN job") + parser_coll.add_argument("OUTPUT", type=str, + help="the output directory of data") + parser_coll.add_argument('-p',"--parameter", type=str, default = 'param.json', + help="the json file provides DP-GEN paramters, should be located in JOB_DIR") + parser_coll.add_argument('-v',"--verbose", action = 'store_true', + help="print number of data in each system") + parser_coll.set_defaults(func=gen_collect) + # simplify parser_run = subparsers.add_parser( "simplify", diff --git a/setup.py b/setup.py index 3af033482..1016662a9 100755 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ 'dpgen/database', 'dpgen/tools', 'dpgen/simplify', + 'dpgen/collect', ], # data_files = [('dpgen/tools/', ['dpgen/tools/update_time.sh', ])], # package_data={'example':['*.json']}, From 35e0b7cceec4bc519fabbde328535f07b2752b96 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 23 Mar 2020 16:05:56 +0800 Subject: [PATCH 142/201] add options for merging and shuffling the system --- dpgen/collect/collect.py | 19 ++++++++++++++++--- dpgen/main.py | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/dpgen/collect/collect.py b/dpgen/collect/collect.py index b60be8c3b..792c13793 100644 --- a/dpgen/collect/collect.py +++ b/dpgen/collect/collect.py @@ -4,7 +4,10 @@ import numpy as np from dpgen.generator.run import data_system_fmt -def collect_data(target_folder, param_file, output, verbose = True, shuffle = True) : +def collect_data(target_folder, param_file, output, + verbose = True, + shuffle = True, + merge = True) : target_folder = os.path.abspath(target_folder) output = os.path.abspath(output) # goto input @@ -35,7 +38,10 @@ def collect_data(target_folder, param_file, output, verbose = True, shuffle = Tr iter_data.sort() for jj in iter_data : sys = dpdata.LabeledSystem(jj, fmt = 'deepmd/npy') - sys_str = (os.path.basename(jj).split('.')[-1]) + if merge: + sys_str = sys.formula + else: + sys_str = (os.path.basename(jj).split('.')[-1]) if sys_str in coll_data.keys(): coll_data[sys_str].append(sys) else: @@ -71,7 +77,10 @@ def collect_data(target_folder, param_file, output, verbose = True, shuffle = Tr coll_data[kk].to('deepmd/npy', os.path.join(output, out_dir)) def gen_collect(args): - collect_data(args.JOB_DIR, args.parameter, args.OUTPUT, args.verbose) + collect_data(args.JOB_DIR, args.parameter, args.OUTPUT, + verbose = args.verbose, + shuffle = args.shuffle, + merge = args.merge) def _main() : parser = argparse.ArgumentParser(description='Collect data from DP-GEN iterations') @@ -83,6 +92,10 @@ def _main() : help="the json file provides DP-GEN paramters, should be located in JOB_DIR") parser.add_argument('-v',"--verbose", action = 'store_true', help="print number of data in each system") + parser.add_argument('-m',"--merge", action = 'store_true', + help="merge the systems with the same chemical formula") + parser.add_argument('-s',"--shuffle", action = 'store_true', + help="shuffle the data systems") args = parser.parse_args() gen_collect(args) diff --git a/dpgen/main.py b/dpgen/main.py index e7c731244..7d180b596 100644 --- a/dpgen/main.py +++ b/dpgen/main.py @@ -128,6 +128,10 @@ def main(): help="the json file provides DP-GEN paramters, should be located in JOB_DIR") parser_coll.add_argument('-v',"--verbose", action = 'store_true', help="print number of data in each system") + parser_coll.add_argument('-m',"--merge", action = 'store_true', + help="merge the systems with the same chemical formula") + parser_coll.add_argument('-s',"--shuffle", action = 'store_true', + help="shuffle the data systems") parser_coll.set_defaults(func=gen_collect) # simplify From 97141fb75d114f2b299da951e644a1ef356f2270 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 23 Mar 2020 16:13:53 +0800 Subject: [PATCH 143/201] fix bug of information presenting --- dpgen/collect/collect.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dpgen/collect/collect.py b/dpgen/collect/collect.py index 792c13793..91d13b857 100644 --- a/dpgen/collect/collect.py +++ b/dpgen/collect/collect.py @@ -55,8 +55,12 @@ def collect_data(target_folder, param_file, output, init_data[ii].get_nframes() )) keys = list(coll_data.keys()) keys.sort() - for idx,ii in enumerate(keys): - print(ptr_fmt % (str(sys_configs[idx]), + for ii in keys: + if merge: + sys_str = ii + else : + sys_str = str(sys_configs[int(ii)]) + print(ptr_fmt % (sys_str, coll_data[ii].formula, coll_data[ii].get_natoms(), coll_data[ii].get_nframes() )) From c2c9daf2e14600b292d38bbf4ee3c779bfbbf326 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 23 Mar 2020 21:17:49 +0800 Subject: [PATCH 144/201] add options to initialize model parameters from existing models --- README.md | 2 ++ dpgen/generator/run.py | 53 +++++++++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 03a60e0f0..3563b1418 100644 --- a/README.md +++ b/README.md @@ -471,6 +471,8 @@ The bold notation of key (such aas **type_map**) means that it's a necessary key | **sys_batch_size** | List of integer | [8, 8] | Each number is the batch_size for training of corresponding system in `sys_configs`. If set to `auto`, batch size will be 32 divided by number of atoms. | | *#Training* | **numb_models** | Integer | 4 (recommend) | Number of models to be trained in `00.train`. | +| training_iter0_model_path | list of string | ["/path/to/model0_ckpt/", ...] | The model used to init the first iter training. Number of element should be equal to `numb_models` | +| training_init_model | bool | False | Iteration > 0, the model parameters will be initilized from the model trained at the previous iteration. Iteration == 0, the model parameters will be initialized from `training_iter0_model_path`. | | **default_training_param** | Dict | {
...
"use_smooth": true,
"sel_a": [16, 4],
"rcut_smth": 0.5,
"rcut": 5,
"filter_neuron": [10, 20, 40],
...
} | Training parameters for `deepmd-kit` in `00.train`.
You can find instructions from here: (https://github.com/deepmodeling/deepmd-kit)..
We commonly let `stop_batch` = 200 * `decay_steps`. | | *#Exploration* | **model_devi_dt** | Float | 0.002 (recommend) | Timestep for MD | diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 38ce80dab..c3e8998d2 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -193,8 +193,9 @@ def make_train (iter_index, fp_task_min = jdata['fp_task_min'] model_devi_jobs = jdata['model_devi_jobs'] use_ele_temp = jdata.get('use_ele_temp', 0) + training_iter0_model = jdata.get('training_iter0_model_path', []) training_reuse_iter = jdata.get('training_reuse_iter') - training_reuse_old_ratio = jdata.get('training_reuse_old_ratio', 0.8) + training_reuse_old_ratio = jdata.get('training_reuse_old_ratio', 0.2) training_reuse_stop_batch = jdata.get('training_reuse_stop_batch', 400000) training_reuse_start_lr = jdata.get('training_reuse_start_lr', 1e-4) training_reuse_start_pref_e = jdata.get('training_reuse_start_pref_e', 0.1) @@ -352,16 +353,37 @@ def make_train (iter_index, prev_task_path = os.path.join(prev_work_path, train_task_fmt%ii) old_model_files = glob.glob( os.path.join(prev_task_path, "model.ckpt*")) - task_path = os.path.join(work_path, train_task_fmt % ii) - task_old_path = os.path.join(task_path, 'old') - create_path(task_old_path) - cwd = os.getcwd() - for jj in old_model_files: - absjj = os.path.abspath(jj) - basejj = os.path.basename(jj) - os.chdir(task_old_path) - os.symlink(os.path.relpath(absjj), basejj) - os.chdir(cwd) + _link_old_models(work_path, old_model_files, ii) + else: + if type(training_iter0_model) == str: + training_iter0_model = [training_iter0_model] + iter0_models = [] + for ii in training_iter0_model: + model_is = glob.glob(ii) + model_is.sort() + iter0_models += [os.path.abspath(ii) for ii in model_is] + assert(numb_models == len(iter0_models)) + for ii in range(len(iter0_models)): + old_model_files = glob.glob(os.path.join(iter0_models[ii], 'model.ckpt*')) + _link_old_models(work_path, old_model_files, ii) + + +def _link_old_models(work_path, old_model_files, ii): + """ + link the `ii`th old model given by `old_model_files` to + the `ii`th training task in `work_path` + """ + task_path = os.path.join(work_path, train_task_fmt % ii) + task_old_path = os.path.join(task_path, 'old') + create_path(task_old_path) + cwd = os.getcwd() + for jj in old_model_files: + absjj = os.path.abspath(jj) + basejj = os.path.basename(jj) + os.chdir(task_old_path) + os.symlink(os.path.relpath(absjj), basejj) + os.chdir(cwd) + def detect_batch_size(batch_size, system=None): if type(batch_size) == int: @@ -381,10 +403,9 @@ def run_train (iter_index, # train_param = jdata['train_param'] train_input_file = default_train_input_file training_reuse_iter = jdata.get('training_reuse_iter') + training_init_model = jdata.get('training_init_model', False) if training_reuse_iter is not None and iter_index >= training_reuse_iter: - reuse_old = True - else: - reuse_old = False + training_init_model = True try: mdata["deepmd_version"] except KeyError: @@ -423,7 +444,7 @@ def run_train (iter_index, ## train_command should not be None assert(train_command) command = '%s train %s' % (train_command, train_input_file) - if reuse_old: + if training_init_model: command += ' --init-model old/model.ckpt' commands.append(command) command = '%s freeze' % train_command @@ -441,7 +462,7 @@ def run_train (iter_index, run_tasks = [os.path.basename(ii) for ii in all_task] forward_files = [train_input_file] - if reuse_old: + if training_init_model: forward_files += [os.path.join('old', 'model.ckpt.meta'), os.path.join('old', 'model.ckpt.index'), os.path.join('old', 'model.ckpt.data-00000-of-00001') From 06108cf3fc18f36236eef9712656f15b2e6d9f67 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 23 Mar 2020 21:20:29 +0800 Subject: [PATCH 145/201] fix bug --- dpgen/generator/run.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index c3e8998d2..7b6b6a2a2 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -362,7 +362,8 @@ def make_train (iter_index, model_is = glob.glob(ii) model_is.sort() iter0_models += [os.path.abspath(ii) for ii in model_is] - assert(numb_models == len(iter0_models)) + if len(iter0_models) > 0: + assert(numb_models == len(iter0_models)) for ii in range(len(iter0_models)): old_model_files = glob.glob(os.path.join(iter0_models[ii], 'model.ckpt*')) _link_old_models(work_path, old_model_files, ii) From fffcda58165f4680398bad9af56419550b508bef Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 23 Mar 2020 21:24:04 +0800 Subject: [PATCH 146/201] check number of iter0 models if init_model is required --- dpgen/generator/run.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 7b6b6a2a2..03db5c8e6 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -194,6 +194,7 @@ def make_train (iter_index, model_devi_jobs = jdata['model_devi_jobs'] use_ele_temp = jdata.get('use_ele_temp', 0) training_iter0_model = jdata.get('training_iter0_model_path', []) + training_init_model = jdata.get('training_init_model', False) training_reuse_iter = jdata.get('training_reuse_iter') training_reuse_old_ratio = jdata.get('training_reuse_old_ratio', 0.2) training_reuse_stop_batch = jdata.get('training_reuse_stop_batch', 400000) @@ -362,8 +363,8 @@ def make_train (iter_index, model_is = glob.glob(ii) model_is.sort() iter0_models += [os.path.abspath(ii) for ii in model_is] - if len(iter0_models) > 0: - assert(numb_models == len(iter0_models)) + if training_init_model: + assert(numb_models == len(iter0_models)), "training_iter0_model should be provided, and the number of models should be equal to %d" % numb_models for ii in range(len(iter0_models)): old_model_files = glob.glob(os.path.join(iter0_models[ii], 'model.ckpt*')) _link_old_models(work_path, old_model_files, ii) From 2c77fcf817f4731da156ded8f355b59ada8eb52c Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 24 Mar 2020 13:41:40 +0800 Subject: [PATCH 147/201] fix bug of getting nframes in make_train --- dpgen/generator/run.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 03db5c8e6..0e982f7e5 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -278,9 +278,7 @@ def make_train (iter_index, init_data_sys.append(os.path.join('..', 'data.iters', jj, sys_single)) init_batch_size.append(detect_batch_size(sys_batch_size[sys_idx], os.path.join(jj, sys_single))) else: - tmp_box = np.loadtxt(os.path.join(jj, 'box.raw')) - tmp_box = np.reshape(tmp_box, [-1,9]) - nframes = tmp_box.shape[0] + nframes = dpdata.System(jj, 'deepmd/npy').get_nframes() if nframes < fp_task_min : log_task('nframes (%d) in data sys %s is too small, skip' % (nframes, jj)) continue From 11ffd5fcef2858413a34ac459850aa44deeb1104 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Tue, 24 Mar 2020 15:01:55 +0800 Subject: [PATCH 148/201] fix bug in test cases --- tests/dispatcher/shell/test_shell_local.py | 6 +++--- tests/dispatcher/shell/test_shell_ssh.py | 2 +- tests/generator/test_make_train.py | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/dispatcher/shell/test_shell_local.py b/tests/dispatcher/shell/test_shell_local.py index b4d02ba67..4c47136c1 100644 --- a/tests/dispatcher/shell/test_shell_local.py +++ b/tests/dispatcher/shell/test_shell_local.py @@ -74,7 +74,7 @@ def test_sub_success(self) : self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'tag_finished'))) + self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, '%s_tag_finished' % self.shell.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test2'))) @@ -101,7 +101,7 @@ def test_sub_scancel(self) : self.assertFalse(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_0_finished'))) self.assertFalse(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_1_finished'))) - self.assertFalse(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'tag_finished'))) + self.assertFalse(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, '%s_tag_finished' % self.shell.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/test1'))) self.assertFalse(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test2'))) @@ -117,7 +117,7 @@ def test_sub_scancel(self) : self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'tag_finished'))) + self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, '%s_tag_finished' % self.shell.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test2'))) diff --git a/tests/dispatcher/shell/test_shell_ssh.py b/tests/dispatcher/shell/test_shell_ssh.py index 10da439a4..d80bb5688 100644 --- a/tests/dispatcher/shell/test_shell_ssh.py +++ b/tests/dispatcher/shell/test_shell_ssh.py @@ -59,7 +59,7 @@ def test_sub_success(self) : self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/tag_1_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_0_finished'))) self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/tag_1_finished'))) - self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'tag_finished'))) + self.assertTrue(os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, '%s_tag_finished' % self.shell.context.job_uuid))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task1/test1'))) self.assertTrue (os.path.isfile(os.path.join('rmt', self.shell.context.remote_root, 'task0/test2'))) diff --git a/tests/generator/test_make_train.py b/tests/generator/test_make_train.py index 486ca40af..3fa820d82 100644 --- a/tests/generator/test_make_train.py +++ b/tests/generator/test_make_train.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os,sys,json,glob,shutil +import os,sys,json,glob,shutil,dpdata import numpy as np import unittest @@ -155,13 +155,14 @@ def _make_fake_fp(iter_idx, sys_idx, nframes): os.makedirs(dirname, exist_ok = True) dirname = os.path.join('iter.%06d' % iter_idx, '02.fp', - 'data.%03d' % sys_idx) + 'data.%03d' % sys_idx) os.makedirs(dirname, exist_ok = True) - box_str = ['0' for ii in range(9)] - box_str = ' '.join(box_str) - with open(os.path.join(dirname, 'box.raw'), 'w') as fp : - for ii in range(nframes) : - fp.write(box_str + '\n') + tmp_sys = dpdata.LabeledSystem('out_data_post_fp_vasp/02.fp/task.000.000000/OUTCAR') + tmp_sys1 = tmp_sys.sub_system([0]) + tmp_sys2 = tmp_sys1 + for ii in range(1, nframes): + tmp_sys2.append(tmp_sys1) + tmp_sys2.to('deepmd/npy', dirname) def _check_pb_link(testCase, iter_idx, numb_models) : From c81180253ebe8e1dfac5f5cfbeb3a9f7e831f1a3 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 24 Mar 2020 20:03:55 +0800 Subject: [PATCH 149/201] fix bug --- dpgen/dispatcher/ALI.py | 135 ++++++++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 45 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 063177f73..ea2426938 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -213,8 +213,16 @@ def change_apg_capasity(self, capasity): request.set_PayAsYouGoTargetCapacity("0") response = self.client.do_action_with_exception(request) - def spot_data_callback(): - pass + def check_spot_callback(self, instance_id): + request = DescribeInstancesRequest() + request.set_accept_format('json') + request.set_InstanceIds([instance_id]) + response = self.client.do_action_with_exception(request) + response = json.loads(response) + if "Recycling" in response["Instances"]["Instance"][0]["OperationLocks"]["LockReason"]: + return True + return False + def check_restart(self, work_path, tasks, group_size): if os.path.exists('apg_id.json'): @@ -303,6 +311,70 @@ def get_finished_job_num(self): finished_num += 1 return finished_num + def resubmission(self): + if self.adata["img_name"] == "kit": + new_ip_list = [] + try: + new_server_list = self.update_server_list() + new_ip_list = self.get_ip(new_server_list) + except: + pass + for ii in range(self.nchunks): + if len(new_ip_list) == 0: + break + if self.dispatchers[ii][1] == "exception": + self.ip_list[ii] = new_ip_list.pop() + self.instance_list[ii] = new_server_list.pop() + profile = self.mdata_machine.copy() + profile["hostname"] = self.ip_list[ii] + profile["instance_id"] = self.instance_list[ii] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record="jr.%.06d.json" % ii), "working"] + dlog.info(self.ip_list[ii]) + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers[ii] = job_handler + elif self.adata["img_name"] == "vasp": + if machine_exception_num / self.nchunks > 0.05: + self.change_apg_capasity(self.nchunks - self.get_finished_job_num() + machine_exception_num) + time.sleep(90) + new_ip_list = [] + try: + new_server_list = self.update_server_list() + new_ip_list = self.get_ip(new_server_list) + except: + pass + if len(new_ip_list) == machine_exception_num: + dlog.info("new submission of callback machine") + for ii in range(self.nchunks): + self.ip_list[ii] = new_ip_list.pop() + self.instance_list[ii] = new_server_list.pop() + profile = self.mdata_machine.copy() + profile["hostname"] = self.ip_list[ii] + profile["instance_id"] = self.instance_list[ii] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record="jr.%.06d.json" % ii), "working"] + dlog.info(self.ip_list[ii]) + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers[ii] = job_handler + def run_jobs(self, resources, command, @@ -333,42 +405,20 @@ def run_jobs(self, self.job_handlers[ii] = job_handler machine_exception_num = 0 while True: - if machine_exception_num > 2: - try: - new_server_list = self.update_server_list() - except: - pass - count = 0 - for ii in range(self.nchunks): - if self.instance_list[ii] == "exception": - count += 1 - if len(new_server_list) == count and len(new_server_list) > 0: - new_ip_list = self.get_ip(new_server_list) - for ii in range(self.nchunks): - if self.dispatchers[ii][1] == "exception": - self.ip_list[ii] = new_ip_list.pop() - self.instance_list[ii] = new_server_list.pop() - profile = self.mdata_machine.copy() - profile["hostname"] = self.ip_list[ii] - profile["instance_id"] = self.instance_list[ii] - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record="jr.%.06d.json" % ii), "working"] - dlog.info(self.ip_list[ii]) - job_handler = self.dispatchers[ii][0].submit_jobs(resources, - command, - work_path, - self.task_chunks[ii], - group_size, - forward_common_files, - forward_task_files, - backward_task_files, - forward_task_deference, - outlog, - errlog) - self.job_handlers[ii] = job_handler + if machine_exception_num > 0: + self.resubmission() # dlog.info(self.ip_list) # dlog.info(self.instance_list) # dlog.info(self.dispatchers) for ii in range(self.nchunks): + if self.check_spot_callback(self.instance_list[ii]): + machine_exception_num += 1 + dlog.info("machine %s callback" % self.instance_list[ii]) + self.job_handlers[ii] = "exception" + self.ip_list[ii] = "exception" + self.instance_list[ii] = "exception" + if self.adata["img_name"] == "vasp": + self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): #dlog.info(self.dispatchers) @@ -379,20 +429,15 @@ def run_jobs(self, self.instance_list[ii] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: - try: - self.delete(ii) - except: - pass - os.remove(self.job_handlers[ii]["job_record"].fname) - machine_exception_num += 1 - dlog.info("exception accured in %s, remove %s, %s" % (self.ip_list[ii], self.job_handlers[ii]["job_record"].fname,self.ip_list[ii])) + #os.remove(self.job_handlers[ii]["job_record"].fname) + #machine_exception_num += 1 + dlog.info("ssh exception accured in %s" % self.ip_list[ii]) # dlog.info(self.ip_list) # dlog.info(self.instance_list) # dlog.info(self.dispatchers) - self.dispatchers[ii] = [None, "exception"] - self.job_handlers[ii] = "exception" - self.ip_list[ii] = "exception" - self.instance_list[ii] = "exception" + # self.job_handlers[ii] = "exception" + # self.ip_list[ii] = "exception" + # self.instance_list[ii] = "exception" elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": From 97fa52c9adef57f4f8484421645648f0e7a0fa21 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 24 Mar 2020 20:09:53 +0800 Subject: [PATCH 150/201] fix bug --- dpgen/dispatcher/ALI.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index ea2426938..a1b45c4c2 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -223,7 +223,6 @@ def check_spot_callback(self, instance_id): return True return False - def check_restart(self, work_path, tasks, group_size): if os.path.exists('apg_id.json'): with open('apg_id.json') as fp: @@ -417,6 +416,8 @@ def run_jobs(self, self.job_handlers[ii] = "exception" self.ip_list[ii] = "exception" self.instance_list[ii] = "exception" + self.dispatchers[ii][1] = "exception" + os.remove(self.job_handlers[ii]["job_record"].fname) if self.adata["img_name"] == "vasp": self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) if self.dispatchers[ii][1] == "working": From 9f69907c551bfbb92fb667cf88fcbe4a6b264c03 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 24 Mar 2020 20:20:14 +0800 Subject: [PATCH 151/201] fix bug --- dpgen/dispatcher/ALI.py | 47 +---------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index a1b45c4c2..5a7b5db09 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -219,7 +219,7 @@ def check_spot_callback(self, instance_id): request.set_InstanceIds([instance_id]) response = self.client.do_action_with_exception(request) response = json.loads(response) - if "Recycling" in response["Instances"]["Instance"][0]["OperationLocks"]["LockReason"]: + if len(response["Instances"]["Instance"]) == 1 and "Recycling" in response["Instances"]["Instance"][0]["OperationLocks"]["LockReason"]: return True return False @@ -260,15 +260,6 @@ def check_restart(self, work_path, tasks, group_size): self.ip_list[ii] = "finished" self.instance_list[ii] = "finished" self.dispatchers[ii] = [None, "finished"] - # with open(os.path.join(work_path, fn)) as fp: - # jr = json.load(fp) - # ip = jr[cur_hash]['context'][3] - # instance_id = jr[cur_hash]['context'][4] - # self.ip_list.append("finished") - # self.instance_list.append("finished") - # self.dispatchers.append([None, "finished"]) - #dlog.info(self.ip_list) - #dlog.info(self.dispatchers) return True else: self.task_chunks = _split_tasks(tasks, group_size) @@ -298,9 +289,6 @@ def get_ip(self, instance_list): response = self.client.do_action_with_exception(request) response = json.loads(response) ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) - #dlog.info('create machine successfully, following are the ip addresses') - # for ip in ip_list: - # dlog.info(ip) return ip_list def get_finished_job_num(self): @@ -406,9 +394,6 @@ def run_jobs(self, while True: if machine_exception_num > 0: self.resubmission() - # dlog.info(self.ip_list) - # dlog.info(self.instance_list) - # dlog.info(self.dispatchers) for ii in range(self.nchunks): if self.check_spot_callback(self.instance_list[ii]): machine_exception_num += 1 @@ -422,7 +407,6 @@ def run_jobs(self, self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): - #dlog.info(self.dispatchers) if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.delete(ii) self.dispatchers[ii][1] = "finished" @@ -430,15 +414,7 @@ def run_jobs(self, self.instance_list[ii] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: - #os.remove(self.job_handlers[ii]["job_record"].fname) - #machine_exception_num += 1 dlog.info("ssh exception accured in %s" % self.ip_list[ii]) - # dlog.info(self.ip_list) - # dlog.info(self.instance_list) - # dlog.info(self.dispatchers) - # self.job_handlers[ii] = "exception" - # self.ip_list[ii] = "exception" - # self.instance_list[ii] = "exception" elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": @@ -491,26 +467,6 @@ def check_server(self, profile): #dlog.info(False) return False - # def dispatcher_finish(self, dispatcher, job_handler, mark_failure): - # job_record = job_handler['job_record'] - # rjob = job_handler['job_list'][0] - # status = rjob['batch'].check_status() - # if status == JobStatus.terminated : - # machine_status = True - # ssh_active_count = 0 - # while True: - # if rjob['context'].ssh_session._check_alive(): - # break - # if not rjob['context'].ssh_session._check_alive(): - # ssh_active_count += 1 - # if ssh_active_count == 3: - # machine_status = False - # break - # if machine_status == False: - # pass - # else: - # return dispatcher.all_finished(job_handler, mark_failure) - def check_dispatcher_finished(self): for ii in range(len(self.dispatchers)): if self.dispatchers[ii][1] == "unalloc" or self.dispatchers[ii][1] == "working" or self.dispatchers[ii][1] == "exception": @@ -532,7 +488,6 @@ def make_dispatchers(self): profile['instance_id'] = self.instance_list[ii] self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] - def delete_machine(self): request = DeleteInstancesRequest() request.set_accept_format('json') From 3f7a813828d916468e5ab514d45fd36ec1add132 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Tue, 24 Mar 2020 20:32:44 +0800 Subject: [PATCH 152/201] fix bug --- dpgen/dispatcher/ALI.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 5a7b5db09..8cf1020ba 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -217,11 +217,14 @@ def check_spot_callback(self, instance_id): request = DescribeInstancesRequest() request.set_accept_format('json') request.set_InstanceIds([instance_id]) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - if len(response["Instances"]["Instance"]) == 1 and "Recycling" in response["Instances"]["Instance"][0]["OperationLocks"]["LockReason"]: - return True - return False + try: + response = self.client.do_action_with_exception(request) + response = json.loads(response) + if len(response["Instances"]["Instance"]) == 1 and "Recycling" in response["Instances"]["Instance"][0]["OperationLocks"]["LockReason"]: + return True + return False + except: + return False def check_restart(self, work_path, tasks, group_size): if os.path.exists('apg_id.json'): From cc3ffdd79ff2e42350daa5cc477db3beb93168d8 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 25 Mar 2020 08:46:18 +0800 Subject: [PATCH 153/201] implement simplify for LabeledSystem and System --- dpgen/simplify/simplify.py | 388 ++++++++++++++++++++++++++++++------- 1 file changed, 313 insertions(+), 75 deletions(-) diff --git a/dpgen/simplify/simplify.py b/dpgen/simplify/simplify.py index ba99b0b6c..89c052ff4 100644 --- a/dpgen/simplify/simplify.py +++ b/dpgen/simplify/simplify.py @@ -15,7 +15,7 @@ import argparse import pickle import glob - +import fnmatch import dpdata import numpy as np @@ -24,7 +24,7 @@ from dpgen.util import sepline from dpgen.remote.decide_machine import decide_train_machine from dpgen.dispatcher.Dispatcher import Dispatcher, make_dispatcher -from dpgen.generator.run import make_train, run_train, post_train, run_fp, post_fp, fp_name, model_devi_name, train_name +from dpgen.generator.run import make_train, run_train, post_train, run_fp, post_fp, fp_name, model_devi_name, train_name, sys_link_fp_vasp_pp, make_fp_vasp_incar, make_fp_vasp_kp, make_fp_vasp_cp_cvasp # TODO: maybe the following functions can be moved to dpgen.util from dpgen.generator.lib.utils import log_iter, make_iter_name, create_path, record_iter from dpgen.remote.decide_machine import decide_train_machine, decide_fp_machine, decide_model_devi_machine @@ -35,6 +35,19 @@ rest_data_name = "data.rest" accurate_data_name = "data.accurate" detail_file_name_prefix = "details" +sys_name_fmt = 'sys.%03d' +sys_name_pattern = 'sys.[0-9]*[0-9]' + +def expand_sys_str(root_dir): + matches = [] + for root, dirnames, filenames in os.walk(root_dir, followlinks=True): + for filename in fnmatch.filter(filenames, 'type.raw'): + matches.append(root) + matches.sort() + dirnames = [os.path.basename(ii) for ii in matches] + if (len(list(set(dirnames))) != len(matches)) : + raise RuntimeError('duplicated system name: it is highly recommend to place all systems in the same level of directory and has different names') + return matches def get_system_cls(jdata): @@ -43,24 +56,49 @@ def get_system_cls(jdata): return dpdata.System -def get_systems(path, jdata): +def get_multi_system(path, jdata): system = get_system_cls(jdata) systems = dpdata.MultiSystems( *[system(os.path.join(path, s), fmt='deepmd/npy') for s in os.listdir(path)]) return systems +def get_systems(path, jdata): + system_cls = get_system_cls(jdata) + system_paths = expand_sys_str(path) + systems = {} + for ii in system_paths: + systems[os.path.basename(ii)] = system_cls(ii, fmt='deepmd/npy') + return systems + + +def get_system_idx(path): + system_paths = expand_sys_str(path) + sys_idx_map = {} + for idx,ii in enumerate(system_paths): + sys_idx_map[os.path.basename(ii)] = idx + return sys_idx_map + + def init_pick(iter_index, jdata, mdata): """pick up init data from dataset randomly""" pick_data = jdata['pick_data'] init_pick_number = jdata['init_pick_number'] + use_clusters = jdata.get('use_clusters', False) # use MultiSystems with System # TODO: support System and LabeledSystem # TODO: support other format - systems = get_systems(pick_data, jdata) + if use_clusters: + systems = get_multi_system(pick_data, jdata) + else: + systems = get_systems(pick_data, jdata) # label the system labels = [] - for key, system in systems.systems.items(): + if use_clusters: + items = systems.systems.items() + else: + items = systems.items() + for key, system in items: labels.extend([(key, j) for j in range(len(system))]) # random pick @@ -74,27 +112,56 @@ def init_pick(iter_index, jdata, mdata): rest_idx = idx[init_pick_number:] # dump the init data - picked_systems = dpdata.MultiSystems() - for j in pick_idx: - sys_name, sys_id = labels[j] - picked_systems.append(systems[sys_name][sys_id]) sys_data_path = os.path.join(work_path, picked_data_name) - - picked_systems.to_deepmd_raw(sys_data_path) - picked_systems.to_deepmd_npy(sys_data_path, set_size=init_pick_number) + _init_dump_selected_frames(systems, labels, pick_idx, sys_data_path, jdata) # dump the rest data - rest_systems = dpdata.MultiSystems() - for j in rest_idx: - sys_name, sys_id = labels[j] - rest_systems.append(systems[sys_name][sys_id]) sys_data_path = os.path.join(work_path, rest_data_name) - rest_systems.to_deepmd_raw(sys_data_path) - rest_systems.to_deepmd_npy(sys_data_path, set_size=rest_idx.size) + _init_dump_selected_frames(systems, labels, rest_idx, sys_data_path, jdata) + + +def _add_system(systems, key, system): + if key in systems.keys(): + systems[key].append(system) + else: + systems[key] = system + return systems + + +def _init_dump_selected_frames(systems, labels, selc_idx, sys_data_path, jdata): + pick_data = jdata['pick_data'] + use_clusters = jdata.get('use_clusters', False) + if use_clusters: + selc_systems = dpdata.MultiSystems() + for j in selc_idx: + sys_name, sys_id = labels[j] + selc_systems.append(systems[sys_name][sys_id]) + selc_systems.to_deepmd_raw(sys_data_path) + selc_systems.to_deepmd_npy(sys_data_path, set_size=selc_idx.size) + else: + selc_systems = {} + for j in selc_idx: + sys_name, sys_id = labels[j] + selc_systems = _add_system(selc_systems, sys_name, systems[sys_name][sys_id]) + sys_idx_map = get_system_idx(pick_data) + for kk in selc_systems.keys(): + sub_path = os.path.join(sys_data_path, sys_name_fmt % sys_idx_map[kk]) + selc_systems[kk].to_deepmd_raw(sub_path) + selc_systems[kk].to_deepmd_npy(sub_path, set_size=selc_idx.size) + with open(os.path.join(sys_data_path, 'sys_idx_map.json'), 'w') as fp: + json.dump(sys_idx_map, fp, indent=4) + +def _dump_system_dict(systems, path): + for kk in systems: + sub_path = os.path.join(path, sys_name_fmt % (int(kk))) + systems[kk].to_deepmd_raw(sub_path) + systems[kk].to_deepmd_npy(sub_path, set_size=systems[kk].get_nframes()) def make_model_devi(iter_index, jdata, mdata): """calculate the model deviation of the rest idx""" + pick_data = jdata['pick_data'] + use_clusters = jdata.get('use_clusters', False) iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, model_devi_name) create_path(work_path) @@ -110,12 +177,25 @@ def make_model_devi(iter_index, jdata, mdata): rest_data_path = os.path.join(last_iter_name, model_devi_name, rest_data_name) if not os.path.exists(rest_data_path): return False - for jj, subsystem in enumerate(os.listdir(rest_data_path)): - task_name = "task.%03d.%06d" % (0, jj) - task_path = os.path.join(work_path, task_name) - create_path(task_path) - os.symlink(os.path.abspath(os.path.join(rest_data_path, subsystem)), - os.path.abspath(os.path.join(task_path, rest_data_name))) + if use_clusters: + for jj, subsystem in enumerate(os.listdir(rest_data_path)): + task_name = "task.%03d.%06d" % (0, jj) + task_path = os.path.join(work_path, task_name) + create_path(task_path) + os.symlink(os.path.abspath(os.path.join(rest_data_path, subsystem)), + os.path.abspath(os.path.join(task_path, rest_data_name))) + else: + rest_data_path = os.path.abspath(rest_data_path) + sys_path = glob.glob(os.path.join(rest_data_path, sys_name_pattern)) + cwd = os.getcwd() + for ii in sys_path: + task_name = "task.%s.%06d" % (os.path.basename(ii).split('.')[1], 0) + task_path = os.path.join(work_path, task_name) + create_path(task_path) + os.chdir(task_path) + os.symlink(os.path.relpath(ii), rest_data_name) + os.chdir(cwd) + os.chdir(cwd) return True @@ -177,20 +257,31 @@ def run_model_devi(iter_index, jdata, mdata, dispatcher): def post_model_devi(iter_index, jdata, mdata): """calculate the model deviation""" + use_clusters = jdata.get('use_clusters', False) iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, model_devi_name) tasks = glob.glob(os.path.join(work_path, "task.*")) + tasks.sort() e_trust_lo = jdata['e_trust_lo'] e_trust_hi = jdata['e_trust_hi'] f_trust_lo = jdata['f_trust_lo'] f_trust_hi = jdata['f_trust_hi'] - sys_accurate = dpdata.MultiSystems() - sys_candinate = dpdata.MultiSystems() - sys_failed = dpdata.MultiSystems() + if use_clusters: + sys_accurate = dpdata.MultiSystems() + sys_candinate = dpdata.MultiSystems() + sys_failed = dpdata.MultiSystems() + else: + sys_accurate = {} + sys_candinate = {} + sys_failed = {} + all_names = set() for task in tasks: + if not use_clusters: + sys_name = os.path.basename(task).split('.')[1] + all_names.add(sys_name) # e.out details_e = glob.glob(os.path.join(task, "{}.*.e.out".format(detail_file_name_prefix))) e_all = np.array([np.loadtxt(detail_e, ndmin=2)[:, 1] for detail_e in details_e]) @@ -211,86 +302,233 @@ def post_model_devi(iter_index, jdata, mdata): system_cls = get_system_cls(jdata) for subsys, e_devi, f_devi in zip(system_cls(os.path.join(task, rest_data_name), fmt='deepmd/npy'), e_std, f_std): if (e_devi < e_trust_hi and e_devi >= e_trust_lo) or (f_devi < f_trust_hi and f_devi >= f_trust_lo) : - sys_candinate.append(subsys) + if use_clusters: + sys_candinate.append(subsys) + else: + sys_candinate = _add_system(sys_candinate, sys_name, subsys) elif (e_devi >= e_trust_hi ) or (f_devi >= f_trust_hi ): - sys_failed.append(subsys) + if use_clusters: + sys_failed.append(subsys) + else: + sys_failed = _add_system(sys_failed, sys_name, subsys) elif (e_devi < e_trust_lo and f_devi < f_trust_lo ): - sys_accurate.append(subsys) - counter = {"candidate": sys_candinate.get_nframes(), "accurate": sys_accurate.get_nframes(), "failed": sys_failed.get_nframes()} - fp_sum = sum(counter.values()) - for cc_key, cc_value in counter.items(): - dlog.info("{0:9s} : {1:6d} in {2:6d} {3:6.2f} %".format(cc_key, cc_value, fp_sum, cc_value/fp_sum*100)) + if use_clusters: + sys_accurate.append(subsys) + else: + sys_accurate = _add_system(sys_accurate, sys_name, subsys) + else: + raise RuntimeError('reach a place that should NOT be reached...') + if use_clusters: + counter = {"candidate": sys_candinate.get_nframes(), "accurate": sys_accurate.get_nframes(), "failed": sys_failed.get_nframes()} + fp_sum = sum(counter.values()) + for cc_key, cc_value in counter.items(): + dlog.info("{0:9s} : {1:6d} in {2:6d} {3:6.2f} %".format(cc_key, cc_value, fp_sum, cc_value/fp_sum*100)) + else: + all_names = list(all_names) + all_names.sort() + counter = {"candidate": 0, "accurate": 0, "failed": 0} + for kk in all_names: + sys_counter = {"candidate": 0, "accurate": 0, "failed": 0} + if kk in sys_candinate.keys(): + sys_counter['candidate'] += sys_candinate[kk].get_nframes() + if kk in sys_accurate.keys(): + sys_counter['accurate'] += sys_accurate[kk].get_nframes() + if kk in sys_failed.keys(): + sys_counter['failed'] += sys_failed[kk].get_nframes() + fp_sum = sum(sys_counter.values()) + for cc_key, cc_value in sys_counter.items(): + if fp_sum != 0: + dlog.info("sys{0:s} {1:9s} : {2:6d} in {3:6d} {4:6.2f} %".format(kk, cc_key, cc_value, fp_sum, cc_value/fp_sum*100)) + else: + dlog.info("sys{0:s} {1:9s} : {2:6d} in {3:6d} {4:6.2f} %".format(kk, cc_key, cc_value, fp_sum, 0*100)) + for ii in ['candidate', 'accurate', 'failed']: + counter[ii] += sys_counter[ii] # label the candidate system labels = [] - for key, system in sys_candinate.systems.items(): + if use_clusters: + items = sys_candinate.systems.items() + else: + items = sys_candinate.items() + for key, system in items: labels.extend([(key, j) for j in range(len(system))]) # candinate: pick up randomly iter_pick_number = jdata['iter_pick_number'] idx = np.arange(counter['candidate']) + assert(len(idx) == len(labels)) np.random.shuffle(idx) pick_idx = idx[:iter_pick_number] rest_idx = idx[iter_pick_number:] + dlog.info("total candidate {0:6d} picked {1:6d} ({2:6.2f} %) rest {3:6d} ({4:6.2f} % )".format\ + (counter['candidate'], len(pick_idx), float(len(pick_idx))/counter['candidate']*100., len(rest_idx), float(len(rest_idx))/counter['candidate']*100.)) # dump the picked candinate data - picked_systems = dpdata.MultiSystems() - for j in pick_idx: - sys_name, sys_id = labels[j] - picked_systems.append(sys_candinate[sys_name][sys_id]) - sys_data_path = os.path.join(work_path, picked_data_name) - - picked_systems.to_deepmd_raw(sys_data_path) - picked_systems.to_deepmd_npy(sys_data_path, set_size=iter_pick_number) + if use_clusters: + picked_systems = dpdata.MultiSystems() + for j in pick_idx: + sys_name, sys_id = labels[j] + picked_systems.append(sys_candinate[sys_name][sys_id]) + sys_data_path = os.path.join(work_path, picked_data_name) + picked_systems.to_deepmd_raw(sys_data_path) + picked_systems.to_deepmd_npy(sys_data_path, set_size=iter_pick_number) + else: + selc_systems = {} + for j in pick_idx: + sys_name, sys_id = labels[j] + selc_systems = _add_system(selc_systems, sys_name, sys_candinate[sys_name][sys_id]) + sys_data_path = os.path.join(work_path, picked_data_name) + _dump_system_dict(selc_systems, sys_data_path) # dump the rest data (not picked candinate data and failed data) - rest_systems = dpdata.MultiSystems() - for j in rest_idx: - sys_name, sys_id = labels[j] - rest_systems.append(sys_candinate[sys_name][sys_id]) - rest_systems += sys_failed - sys_data_path = os.path.join(work_path, rest_data_name) - rest_systems.to_deepmd_raw(sys_data_path) - rest_systems.to_deepmd_npy(sys_data_path, set_size=rest_idx.size) + if use_clusters: + rest_systems = dpdata.MultiSystems() + for j in rest_idx: + sys_name, sys_id = labels[j] + rest_systems.append(sys_candinate[sys_name][sys_id]) + rest_systems += sys_failed + sys_data_path = os.path.join(work_path, rest_data_name) + rest_systems.to_deepmd_raw(sys_data_path) + rest_systems.to_deepmd_npy(sys_data_path, set_size=rest_idx.size) + else: + selc_systems = {} + for j in rest_idx: + sys_name, sys_id = labels[j] + selc_systems = _add_system(selc_systems, sys_name, sys_candinate[sys_name][sys_id]) + for kk in sys_failed.keys(): + selc_systems = _add_system(selc_systems, kk, sys_failed[kk]) + sys_data_path = os.path.join(work_path, rest_data_name) + _dump_system_dict(selc_systems, sys_data_path) # dump the accurate data -- to another directory - sys_data_path = os.path.join(work_path, accurate_data_name) - sys_accurate.to_deepmd_raw(sys_data_path) - sys_accurate.to_deepmd_npy(sys_data_path, set_size=sys_accurate.get_nframes()) + if use_clusters: + sys_data_path = os.path.join(work_path, accurate_data_name) + sys_accurate.to_deepmd_raw(sys_data_path) + sys_accurate.to_deepmd_npy(sys_data_path, set_size=sys_accurate.get_nframes()) + else: + sys_data_path = os.path.join(work_path, accurate_data_name) + _dump_system_dict(sys_accurate, sys_data_path) -def make_fp(iter_index, jdata, mdata): +def make_fp_labeled(iter_index, jdata): + dlog.info("already labeled, skip make_fp and link data directly") + pick_data = jdata['pick_data'] + use_clusters = jdata.get('use_clusters', False) iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, fp_name) create_path(work_path) picked_data_path = os.path.join(iter_name, model_devi_name, picked_data_name) - if jdata.get("labeled", False): - dlog.info("already labeled, skip make_fp and link data directly") + if use_clusters: os.symlink(os.path.abspath(picked_data_path), os.path.abspath( os.path.join(work_path, "task.%03d" % 0))) os.symlink(os.path.abspath(picked_data_path), os.path.abspath( os.path.join(work_path, "data.%03d" % 0))) - return - systems = get_systems(picked_data_path, jdata) - fp_style = jdata['fp_style'] + else: + picked_data_path = os.path.abspath(picked_data_path) + sys_path = glob.glob(os.path.join(picked_data_path, sys_name_pattern)) + cwd = os.getcwd() + os.chdir(work_path) + for ii in sys_path: + sys_idx = os.path.basename(ii).split('.')[1] + data_dir = 'data.%s' % sys_idx + task_dir = 'task.%s' % sys_idx + r_fname = 'sys_path.%s' % sys_idx + os.symlink(os.path.relpath(ii), data_dir) + os.symlink(os.path.relpath(ii), task_dir) + with open(r_fname, 'w') as fp: + fp.write(ii) + os.chdir(cwd) + + +def make_fp_configs(iter_index, jdata): + pick_data = jdata['pick_data'] + use_clusters = jdata.get('use_clusters', False) + iter_name = make_iter_name(iter_index) + work_path = os.path.join(iter_name, fp_name) + create_path(work_path) + picked_data_path = os.path.join(iter_name, model_devi_name, picked_data_name) + if use_clusters: + systems = get_multi_system(picked_data_path, jdata) + jj = 0 + for system in systems: + for subsys in system: + task_name = "task.%03d.%06d" % (0, jj) + task_path = os.path.join(work_path, task_name) + create_path(task_path) + subsys.to('vasp/poscar', os.path.join(task_path, 'POSCAR')) + jj += 1 + else: + picked_data_path = os.path.abspath(picked_data_path) + sys_path = glob.glob(os.path.join(picked_data_path, sys_name_pattern)) + for ii in sys_path: + tmp_sys = dpdata.System(ii, fmt = 'deepmd/npy') + sys_idx = os.path.basename(ii).split('.')[1] + jj = 0 + for ss in tmp_sys: + task_name = "task.%s.%06d" % (sys_idx, jj) + task_path = os.path.join(work_path, task_name) + create_path(task_path) + ss.to('vasp/poscar', os.path.join(task_path, 'POSCAR')) + job = {} + with open(os.path.join(task_path, 'job.json'), 'w') as fp: + json.dump(job, fp, indent=4) + jj += 1 + + +def make_fp_gaussian(iter_index, jdata): + work_path = os.path.join(make_iter_name(iter_index), fp_name) + fp_task = glob.glob(os.path.join(work_path, 'task.*')) + cwd = os.getcwd() if 'user_fp_params' in jdata.keys() : fp_params = jdata['user_fp_params'] else: fp_params = jdata['fp_params'] - jj = 0 - for system in systems: - for subsys in system: - sys_data = subsys.data - task_name = "task.%03d.%06d" % (0, jj) - task_path = os.path.join(work_path, task_name) - create_path(task_path) - if fp_style == "gaussian" : - ret = make_gaussian_input(sys_data, fp_params) - with open(os.path.join(task_path, 'input'), 'w') as fp: - fp.write(ret) - else : - # TODO: support other formats - raise RuntimeError ("unsupported fp style") - jj += 1 + cwd = os.getcwd() + for ii in fp_tasks: + os.chdir(ii) + sys_data = dpdata.System('POSCAR').data + ret = make_gaussian_input(sys_data, fp_params) + with open('input', 'w') as fp: + fp.write(ret) + os.chdir(cwd) + + +def make_fp_vasp(iter_index, jdata): + # abs path for fp_incar if it exists + if 'fp_incar' in jdata: + jdata['fp_incar'] = os.path.abspath(jdata['fp_incar']) + # get nbands esti if it exists + if 'fp_nbands_esti_data' in jdata: + nbe = NBandsEsti(jdata['fp_nbands_esti_data']) + else: + nbe = None + # order is critical! + # 1, create potcar + sys_link_fp_vasp_pp(iter_index, jdata) + # 2, create incar + make_fp_vasp_incar(iter_index, jdata, nbands_esti = nbe) + # 3, create kpoints + make_fp_vasp_kp(iter_index, jdata) + # 4, copy cvasp + make_fp_vasp_cp_cvasp(iter_index,jdata) + + +def make_fp_calculation(iter_index, jdata): + fp_style = jdata['fp_style'] + if fp_style == 'vasp': + make_fp_vasp(iter_index, jdata) + elif fp_style == 'gaussian': + make_fp_gaussian(iter_index, jdata) + else : + raise RuntimeError('unsupported fp_style ' + fp_style) + + +def make_fp(iter_index, jdata, mdata): + labeled = jdata.get("labeled", False) + if labeled: + make_fp_labeled(iter_index, jdata) + else: + make_fp_configs(iter_index, jdata) + make_fp_calculation(iter_index, jdata) def run_iter(param_file, machine_file): From 647ada29fd023b99cadd2da5ad660e935ffa5b61 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 25 Mar 2020 09:19:43 +0800 Subject: [PATCH 154/201] add missing changes to run.py. fix bug in simplify --- dpgen/generator/run.py | 12 ++++++------ dpgen/simplify/simplify.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 0e982f7e5..864528a9e 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1279,7 +1279,7 @@ def make_vasp_incar_ele_temp(jdata, filename, ele_temp, nbands_esti = None): incar['NBANDS'] = nbands incar.write_file('INCAR') -def _make_fp_vasp_incar (iter_index, +def make_fp_vasp_incar (iter_index, jdata, nbands_esti = None) : iter_name = make_iter_name(iter_index) @@ -1316,7 +1316,7 @@ def _make_fp_pwmat_input (iter_index, os.system("sed -i '1,2c 4 1' etot.input") os.chdir(cwd) -def _copy_cvasp(iter_index,jdata): +def make_fp_vasp_cp_cvasp(iter_index,jdata): # Move cvasp interface to jdata if ('cvasp' in jdata) and (jdata['cvasp'] == True): pass @@ -1335,7 +1335,7 @@ def _copy_cvasp(iter_index,jdata): shutil.copyfile(cvasp_file, 'cvasp.py') os.chdir(cwd) -def _make_fp_vasp_kp (iter_index,jdata): +def make_fp_vasp_kp (iter_index,jdata): iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, fp_name) fp_aniso_kspacing = jdata.get('fp_aniso_kspacing') @@ -1490,11 +1490,11 @@ def make_fp_vasp (iter_index, # 1, create potcar sys_link_fp_vasp_pp(iter_index, jdata) # 2, create incar - _make_fp_vasp_incar(iter_index, jdata, nbands_esti = nbe) + make_fp_vasp_incar(iter_index, jdata, nbands_esti = nbe) # 3, create kpoints - _make_fp_vasp_kp(iter_index, jdata) + make_fp_vasp_kp(iter_index, jdata) # 4, copy cvasp - _copy_cvasp(iter_index,jdata) + make_fp_vasp_cp_cvasp(iter_index,jdata) def make_fp_pwscf(iter_index, diff --git a/dpgen/simplify/simplify.py b/dpgen/simplify/simplify.py index 89c052ff4..d4512f84d 100644 --- a/dpgen/simplify/simplify.py +++ b/dpgen/simplify/simplify.py @@ -344,6 +344,9 @@ def post_model_devi(iter_index, jdata, mdata): for ii in ['candidate', 'accurate', 'failed']: counter[ii] += sys_counter[ii] + if counter['candidate'] == 0 and counter['failed'] > 0: + raise RuntimeError('no candidate but still have failed cases, stop. You may want to refine the training or to increase the trust level hi') + # label the candidate system labels = [] if use_clusters: @@ -431,11 +434,8 @@ def make_fp_labeled(iter_index, jdata): sys_idx = os.path.basename(ii).split('.')[1] data_dir = 'data.%s' % sys_idx task_dir = 'task.%s' % sys_idx - r_fname = 'sys_path.%s' % sys_idx os.symlink(os.path.relpath(ii), data_dir) os.symlink(os.path.relpath(ii), task_dir) - with open(r_fname, 'w') as fp: - fp.write(ii) os.chdir(cwd) @@ -476,7 +476,7 @@ def make_fp_configs(iter_index, jdata): def make_fp_gaussian(iter_index, jdata): work_path = os.path.join(make_iter_name(iter_index), fp_name) - fp_task = glob.glob(os.path.join(work_path, 'task.*')) + fp_tasks = glob.glob(os.path.join(work_path, 'task.*')) cwd = os.getcwd() if 'user_fp_params' in jdata.keys() : fp_params = jdata['user_fp_params'] From 4e72dfc3a3e2ae19ead6d423ed432d229f679a43 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Wed, 25 Mar 2020 09:44:36 +0800 Subject: [PATCH 155/201] use private ip --- dpgen/dispatcher/ALI.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 8cf1020ba..1a10fa97b 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -277,7 +277,7 @@ def get_ip(self, instance_list): request.set_InstanceIds([instance_list[i]]) response = self.client.do_action_with_exception(request) response = json.loads(response) - ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + ip_list.append(response["Instances"]["Instance"][0]["VpcAttributes"]["PrivateIpAddress"]['IpAddress'][0]) else: iteration = len(instance_list) // 10 for i in range(iteration): @@ -285,13 +285,13 @@ def get_ip(self, instance_list): request.set_InstanceIds([instance_list[i*10+j]]) response = self.client.do_action_with_exception(request) response = json.loads(response) - ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + ip_list.append(response["Instances"]["Instance"][0]["VpcAttributes"]["PrivateIpAddress"]['IpAddress'][0]) if len(instance_list) - iteration * 10 != 0: for j in range(len(instance_list) - iteration * 10): request.set_InstanceIds([instance_list[iteration*10+j]]) response = self.client.do_action_with_exception(request) response = json.loads(response) - ip_list.append(response["Instances"]["Instance"][0]["PublicIpAddress"]['IpAddress'][0]) + ip_list.append(response["Instances"]["Instance"][0]["VpcAttributes"]["PrivateIpAddress"]['IpAddress'][0]) return ip_list def get_finished_job_num(self): From 2e579ce0241c58ca6c6cc80c8887a69d5169c9ba Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 25 Mar 2020 14:22:46 +0800 Subject: [PATCH 156/201] add init model to simplify --- dpgen/simplify/simplify.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/dpgen/simplify/simplify.py b/dpgen/simplify/simplify.py index d4512f84d..5d3a6d62a 100644 --- a/dpgen/simplify/simplify.py +++ b/dpgen/simplify/simplify.py @@ -24,7 +24,7 @@ from dpgen.util import sepline from dpgen.remote.decide_machine import decide_train_machine from dpgen.dispatcher.Dispatcher import Dispatcher, make_dispatcher -from dpgen.generator.run import make_train, run_train, post_train, run_fp, post_fp, fp_name, model_devi_name, train_name, sys_link_fp_vasp_pp, make_fp_vasp_incar, make_fp_vasp_kp, make_fp_vasp_cp_cvasp +from dpgen.generator.run import make_train, run_train, post_train, run_fp, post_fp, fp_name, model_devi_name, train_name, train_task_fmt, sys_link_fp_vasp_pp, make_fp_vasp_incar, make_fp_vasp_kp, make_fp_vasp_cp_cvasp # TODO: maybe the following functions can be moved to dpgen.util from dpgen.generator.lib.utils import log_iter, make_iter_name, create_path, record_iter from dpgen.remote.decide_machine import decide_train_machine, decide_fp_machine, decide_model_devi_machine @@ -80,6 +80,32 @@ def get_system_idx(path): return sys_idx_map +def init_model(iter_index, jdata, mdata): + training_init_model = jdata.get('training_init_model', False) + if not training_init_model: + return + iter0_models = [] + training_iter0_model = jdata.get('training_iter0_model_path', []) + if type(training_iter0_model) == str: + training_iter0_model = [training_iter0_model] + for ii in training_iter0_model: + model_is = glob.glob(ii) + model_is.sort() + iter0_models += [os.path.abspath(ii) for ii in model_is] + assert(jdata['numb_models'] == len(iter0_models)), "training_iter0_model should be provided, and the number of models should be equal to %d" % numb_models + work_path = os.path.join(make_iter_name(iter_index), train_name) + create_path(work_path) + cwd = os.getcwd() + for ii in range(len(iter0_models)): + train_path = os.path.join(work_path, train_task_fmt % ii) + create_path(train_path) + os.chdir(train_path) + ckpt_files = glob.glob(os.path.join(iter0_models[ii], 'model.ckpt*')) + for jj in ckpt_files: + os.symlink(jj, os.path.basename(jj)) + os.chdir(cwd) + + def init_pick(iter_index, jdata, mdata): """pick up init data from dataset randomly""" pick_data = jdata['pick_data'] @@ -103,7 +129,6 @@ def init_pick(iter_index, jdata, mdata): # random pick iter_name = make_iter_name(iter_index) - create_path(iter_name) work_path = os.path.join(iter_name, model_devi_name) create_path(work_path) idx = np.arange(len(labels)) @@ -603,6 +628,7 @@ def run_iter(param_file, machine_file): if ii == 0 and jj < 6: if jj == 0: log_iter("init_pick", ii, jj) + init_model(ii, jdata, mdata) init_pick(ii, jdata, mdata) dlog.info("first iter, skip step 1-5") elif jj == 0: From 79b62a00053bf0d44520835aa785b190647d2fa6 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 25 Mar 2020 15:23:12 +0800 Subject: [PATCH 157/201] fig bug in checking number of models --- dpgen/simplify/simplify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpgen/simplify/simplify.py b/dpgen/simplify/simplify.py index 5d3a6d62a..826fc87c5 100644 --- a/dpgen/simplify/simplify.py +++ b/dpgen/simplify/simplify.py @@ -92,7 +92,8 @@ def init_model(iter_index, jdata, mdata): model_is = glob.glob(ii) model_is.sort() iter0_models += [os.path.abspath(ii) for ii in model_is] - assert(jdata['numb_models'] == len(iter0_models)), "training_iter0_model should be provided, and the number of models should be equal to %d" % numb_models + numb_models = jdata['numb_models'] + assert(numb_models == len(iter0_models)), "training_iter0_model_path should be provided, and the number of models should be equal to %d" % numb_models work_path = os.path.join(make_iter_name(iter_index), train_name) create_path(work_path) cwd = os.getcwd() From 85f26fd2d13688fc59de5c1c814e3d2b7ffc2d76 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 25 Mar 2020 18:13:52 +0800 Subject: [PATCH 158/201] use compatible name format with run.py in simplify.py --- dpgen/simplify/simplify.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dpgen/simplify/simplify.py b/dpgen/simplify/simplify.py index 826fc87c5..3372968a7 100644 --- a/dpgen/simplify/simplify.py +++ b/dpgen/simplify/simplify.py @@ -24,7 +24,7 @@ from dpgen.util import sepline from dpgen.remote.decide_machine import decide_train_machine from dpgen.dispatcher.Dispatcher import Dispatcher, make_dispatcher -from dpgen.generator.run import make_train, run_train, post_train, run_fp, post_fp, fp_name, model_devi_name, train_name, train_task_fmt, sys_link_fp_vasp_pp, make_fp_vasp_incar, make_fp_vasp_kp, make_fp_vasp_cp_cvasp +from dpgen.generator.run import make_train, run_train, post_train, run_fp, post_fp, fp_name, model_devi_name, train_name, train_task_fmt, sys_link_fp_vasp_pp, make_fp_vasp_incar, make_fp_vasp_kp, make_fp_vasp_cp_cvasp, data_system_fmt, model_devi_task_fmt, fp_task_fmt # TODO: maybe the following functions can be moved to dpgen.util from dpgen.generator.lib.utils import log_iter, make_iter_name, create_path, record_iter from dpgen.remote.decide_machine import decide_train_machine, decide_fp_machine, decide_model_devi_machine @@ -35,7 +35,7 @@ rest_data_name = "data.rest" accurate_data_name = "data.accurate" detail_file_name_prefix = "details" -sys_name_fmt = 'sys.%03d' +sys_name_fmt = 'sys.' + data_system_fmt sys_name_pattern = 'sys.[0-9]*[0-9]' def expand_sys_str(root_dir): @@ -205,7 +205,7 @@ def make_model_devi(iter_index, jdata, mdata): return False if use_clusters: for jj, subsystem in enumerate(os.listdir(rest_data_path)): - task_name = "task.%03d.%06d" % (0, jj) + task_name = "task." + model_devi_task_fmt % (0, jj) task_path = os.path.join(work_path, task_name) create_path(task_path) os.symlink(os.path.abspath(os.path.join(rest_data_path, subsystem)), @@ -215,7 +215,7 @@ def make_model_devi(iter_index, jdata, mdata): sys_path = glob.glob(os.path.join(rest_data_path, sys_name_pattern)) cwd = os.getcwd() for ii in sys_path: - task_name = "task.%s.%06d" % (os.path.basename(ii).split('.')[1], 0) + task_name = "task." + model_devi_task_fmt % (int(os.path.basename(ii).split('.')[1]), 0) task_path = os.path.join(work_path, task_name) create_path(task_path) os.chdir(task_path) @@ -448,9 +448,9 @@ def make_fp_labeled(iter_index, jdata): picked_data_path = os.path.join(iter_name, model_devi_name, picked_data_name) if use_clusters: os.symlink(os.path.abspath(picked_data_path), os.path.abspath( - os.path.join(work_path, "task.%03d" % 0))) + os.path.join(work_path, "task." + data_system_fmt % 0))) os.symlink(os.path.abspath(picked_data_path), os.path.abspath( - os.path.join(work_path, "data.%03d" % 0))) + os.path.join(work_path, "data." + data_system_fmt % 0))) else: picked_data_path = os.path.abspath(picked_data_path) sys_path = glob.glob(os.path.join(picked_data_path, sys_name_pattern)) @@ -458,8 +458,8 @@ def make_fp_labeled(iter_index, jdata): os.chdir(work_path) for ii in sys_path: sys_idx = os.path.basename(ii).split('.')[1] - data_dir = 'data.%s' % sys_idx - task_dir = 'task.%s' % sys_idx + data_dir = 'data.' + data_system_fmt % int(sys_idx) + task_dir = 'task.' + data_system_fmt % int(sys_idx) os.symlink(os.path.relpath(ii), data_dir) os.symlink(os.path.relpath(ii), task_dir) os.chdir(cwd) @@ -477,7 +477,7 @@ def make_fp_configs(iter_index, jdata): jj = 0 for system in systems: for subsys in system: - task_name = "task.%03d.%06d" % (0, jj) + task_name = "task." + fp_task_fmt % (0, jj) task_path = os.path.join(work_path, task_name) create_path(task_path) subsys.to('vasp/poscar', os.path.join(task_path, 'POSCAR')) @@ -490,7 +490,7 @@ def make_fp_configs(iter_index, jdata): sys_idx = os.path.basename(ii).split('.')[1] jj = 0 for ss in tmp_sys: - task_name = "task.%s.%06d" % (sys_idx, jj) + task_name = "task." + fp_task_fmt % (int(sys_idx), jj) task_path = os.path.join(work_path, task_name) create_path(task_path) ss.to('vasp/poscar', os.path.join(task_path, 'POSCAR')) From 653394f8b52c556f724bed0e6bb6b20f71102c5f Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 27 Mar 2020 16:54:53 +0800 Subject: [PATCH 159/201] fix bug --- dpgen/dispatcher/ALI.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 1a10fa97b..c2f75deff 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -365,6 +365,7 @@ def resubmission(self): errlog) self.job_handlers[ii] = job_handler + #@profile(precision=6) def run_jobs(self, resources, command, @@ -417,7 +418,18 @@ def run_jobs(self, self.instance_list[ii] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: - dlog.info("ssh exception accured in %s" % self.ip_list[ii]) + if self.instance_list[ii] not in self.update_server_list(): + machine_exception_num += 1 + dlog.info("machine %s callback" % self.instance_list[ii]) + self.job_handlers[ii] = "exception" + self.ip_list[ii] = "exception" + self.instance_list[ii] = "exception" + self.dispatchers[ii][1] = "exception" + os.remove(self.job_handlers[ii]["job_record"].fname) + if self.adata["img_name"] == "vasp": + self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) + else: + dlog.info("ssh exception accured in %s" % self.ip_list[ii]) elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": @@ -457,18 +469,11 @@ def run_jobs(self, # status = ["unalloc", "working", "finished", "exception"] def check_server(self, profile): - #dlog.info("check server") - for ii in range(3): - try: - session = SSHSession(profile) - #dlog.info(True) - return True - except: - time.sleep(60) - #dlog.info(False) - pass - #dlog.info(False) - return False + try: + session = SSHSession(profile) + return True + except: + return False def check_dispatcher_finished(self): for ii in range(len(self.dispatchers)): From 3ca042669cbbb7840821df700fcbe09e983758d0 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 28 Mar 2020 11:34:14 +0800 Subject: [PATCH 160/201] fix bug --- dpgen/dispatcher/ALI.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index c2f75deff..40a0b2907 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -418,17 +418,18 @@ def run_jobs(self, self.instance_list[ii] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: - if self.instance_list[ii] not in self.update_server_list(): - machine_exception_num += 1 - dlog.info("machine %s callback" % self.instance_list[ii]) - self.job_handlers[ii] = "exception" - self.ip_list[ii] = "exception" - self.instance_list[ii] = "exception" - self.dispatchers[ii][1] = "exception" - os.remove(self.job_handlers[ii]["job_record"].fname) - if self.adata["img_name"] == "vasp": - self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) - else: + try: + if self.instance_list[ii] not in self.describe_apg_instances(): + machine_exception_num += 1 + dlog.info("machine %s callback" % self.instance_list[ii]) + os.remove(self.job_handlers[ii]["job_record"].fname) + self.job_handlers[ii] = "exception" + self.ip_list[ii] = "exception" + self.instance_list[ii] = "exception" + self.dispatchers[ii][1] = "exception" + if self.adata["img_name"] == "vasp": + self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) + except: dlog.info("ssh exception accured in %s" % self.ip_list[ii]) elif self.dispatchers[ii][1] == "finished": continue From 25d079cd8c4783d8b2531f53465e519a4d6fc542 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sat, 28 Mar 2020 11:39:34 +0800 Subject: [PATCH 161/201] fix bug --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 40a0b2907..fbae489d6 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -402,11 +402,11 @@ def run_jobs(self, if self.check_spot_callback(self.instance_list[ii]): machine_exception_num += 1 dlog.info("machine %s callback" % self.instance_list[ii]) + os.remove(self.job_handlers[ii]["job_record"].fname) self.job_handlers[ii] = "exception" self.ip_list[ii] = "exception" self.instance_list[ii] = "exception" self.dispatchers[ii][1] = "exception" - os.remove(self.job_handlers[ii]["job_record"].fname) if self.adata["img_name"] == "vasp": self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) if self.dispatchers[ii][1] == "working": From eb7c1c0b4d016cef45b92f4e2e9bb7bd6b1151da Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 29 Mar 2020 12:57:08 +0800 Subject: [PATCH 162/201] fix bug --- dpgen/dispatcher/ALI.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index fbae489d6..fcc24a22f 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -301,7 +301,7 @@ def get_finished_job_num(self): finished_num += 1 return finished_num - def resubmission(self): + def resubmission(self, machine_exception_num): if self.adata["img_name"] == "kit": new_ip_list = [] try: @@ -397,7 +397,7 @@ def run_jobs(self, machine_exception_num = 0 while True: if machine_exception_num > 0: - self.resubmission() + self.resubmission(machine_exception_num) for ii in range(self.nchunks): if self.check_spot_callback(self.instance_list[ii]): machine_exception_num += 1 From dc4dd1fb6f3cac0a0120109eeecf73d6f31d501d Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Sun, 29 Mar 2020 13:40:32 +0800 Subject: [PATCH 163/201] fix bug --- dpgen/dispatcher/ALI.py | 57 +++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index fcc24a22f..00903fffe 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -62,7 +62,7 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): def init(self, work_path, tasks, group_size): if self.check_restart(work_path, tasks, group_size): - pass + set_PasswordInherit else: self.create_ess() self.make_dispatchers() @@ -86,7 +86,12 @@ def delete_apg(self): request.set_accept_format('json') request.set_AutoProvisioningGroupId(self.apg_id) request.set_TerminateInstances(True) - response = self.client.do_action_with_exception(request) + try: + response = self.client.do_action_with_exception(request) + except ServerException as e: + dlog.info(e) + except ClientException as e: + dlog.info(e) def create_apg(self): request = CreateAutoProvisioningGroupRequest() @@ -104,11 +109,17 @@ def create_apg(self): request.set_SpotTargetCapacity(str(self.nchunks)) config = self.generate_config() request.set_LaunchTemplateConfigs(config) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - with open('apg_id.json', 'w') as fp: - json.dump({'apg_id': response["AutoProvisioningGroupId"]}, fp, indent=4) - return response["AutoProvisioningGroupId"] + try: + response = self.client.do_action_with_exception(request) + response = json.loads(response) + with open('apg_id.json', 'w') as fp: + json.dump({'apg_id': response["AutoProvisioningGroupId"]}, fp, indent=4) + return response["AutoProvisioningGroupId"] + except ServerException as e: + dlog.info(e) + except ClientException as e: + dlog.info(e) + def update_server_list(self): instance_list = self.describe_apg_instances() @@ -123,12 +134,19 @@ def describe_apg_instances(self): instance_list = [] for i in range(iteration + 1): request.set_PageNumber(i+1) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - for ins in response["Instances"]["Instance"]: - instance_list.append(ins["InstanceId"]) - #dlog.info(instance_list) - return instance_list + try: + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for ins in response["Instances"]["Instance"]: + instance_list.append(ins["InstanceId"]) + return instance_list + except ServerException as e: + dlog.info(e) + return "exception" + except ClientException as e: + dlog.info(e) + return "exception" + def generate_config(self): machine_config = self.adata["machine_type_price"] @@ -165,10 +183,15 @@ def create_template(self, image_id, sg_id, vpc_id): request.set_NetworkType("vpc") request.set_SpotStrategy("SpotWithPriceLimit") request.set_SpotPriceLimit(100) - response = self.client.do_action_with_exception(request) - response = json.loads(response) - return response["LaunchTemplateId"] - + try: + response = self.client.do_action_with_exception(request) + response = json.loads(response) + return response["LaunchTemplateId"] + except ServerException as e: + dlog.info(e) + except ClientException as e: + dlog.info(e) + def delete_template(self): request = DeleteLaunchTemplateRequest() request.set_accept_format('json') From 6b67519ac05160aef98db9611e17c957e90e3d39 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 30 Mar 2020 00:29:10 +0800 Subject: [PATCH 164/201] fix bug --- dpgen/dispatcher/ALI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 00903fffe..253030c73 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -62,7 +62,7 @@ def __init__(self, adata, mdata_resources, mdata_machine, nchunks): def init(self, work_path, tasks, group_size): if self.check_restart(work_path, tasks, group_size): - set_PasswordInherit + pass else: self.create_ess() self.make_dispatchers() From c225fc0d00b5a89d39fa23232bb962c3b914ad93 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 30 Mar 2020 10:52:09 +0800 Subject: [PATCH 165/201] with accurate thresthold, decay number of fp tasks to 0 if it is already accurate enough --- README.md | 2 ++ dpgen/generator/run.py | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3563b1418..fa1f1fb65 100644 --- a/README.md +++ b/README.md @@ -497,6 +497,8 @@ The bold notation of key (such aas **type_map**) means that it's a necessary key | **fp_style** | string | "vasp" | Software for First Principles. **Options** include “vasp”, “pwscf”, “siesta” and “gaussian” up to now. | | **fp_task_max** | Integer | 20 | Maximum of structures to be calculated in `02.fp` of each iteration. | | **fp_task_min** | Integer | 5 | Minimum of structures to calculate in `02.fp` of each iteration. | +| fp_accurate_threshold | Float | 0.9999 | If the accurate ratio is larger than this number, no fp calculation will be performed, i.e. fp_task_max = 0. | +| fp_accurate_soft_threshold | Float | 0.9999 | If the accurate ratio is between this number and `fp_accurate_threshold`, the fp_task_max linearly decays to zero. | | *fp_style == VASP* | **fp_pp_path** | String | "/sharedext4/.../ch4/" | Directory of psuedo-potential file to be used for 02.fp exists. | | **fp_pp_files** | List of string | ["POTCAR"] | Psuedo-potential file to be used for 02.fp. Note that the order of elements should correspond to the order in `type_map`. | diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 864528a9e..68e811adb 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -1140,7 +1140,22 @@ def _make_fp_vasp_inner (modd_path, with open(os.path.join(work_path,'rest_failed.shuffled.%s.out'%ss), 'w') as fp: for ii in fp_rest_failed: fp.write(" ".join([str(nn) for nn in ii]) + "\n") - numb_task = min(fp_task_max, len(fp_candidate)) + + # set number of tasks + accurate_ratio = float(counter['accurate']) / float(fp_sum) + fp_accurate_threshold = jdata.get('fp_accurate_threshold', 1) + fp_accurate_soft_threshold = jdata.get('fp_accurate_soft_threshold', fp_accurate_threshold) + if accurate_ratio < fp_accurate_soft_threshold : + this_fp_task_max = fp_task_max + elif accurate_ratio >= fp_accurate_soft_threshold and accurate_ratio < fp_accurate_threshold: + this_fp_task_max = int(fp_task_max * (accurate_ratio - fp_accurate_threshold) / (fp_accurate_soft_threshold - fp_accurate_threshold)) + else: + this_fp_task_max = 0 + numb_task = min(this_fp_task_max, len(fp_candidate)) + if (numb_task < fp_task_min): + numb_task = 0 + dlog.info("system {0:s} accurate_ratio: {1:8.4f} thresholds: {2:6.4f} and {3:6.4f} eff. task min and max {4:4d} {5:4d} number of fp tasks: {6:6d}".format(ss, accurate_ratio, fp_accurate_soft_threshold, fp_accurate_threshold, fp_task_min, this_fp_task_max, numb_task)) + # make fp tasks count_bad_box = 0 for cc in range(numb_task) : tt = fp_candidate[cc][0] From 8c44c7ff95eadf5249c6774784efe07ab859e181 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 30 Mar 2020 12:50:55 +0800 Subject: [PATCH 166/201] fix bug --- dpgen/dispatcher/ALI.py | 48 ++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 253030c73..32fd2cf60 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -120,7 +120,6 @@ def create_apg(self): except ClientException as e: dlog.info(e) - def update_server_list(self): instance_list = self.describe_apg_instances() return list(set(instance_list) - set(self.instance_list)) @@ -147,7 +146,6 @@ def describe_apg_instances(self): dlog.info(e) return "exception" - def generate_config(self): machine_config = self.adata["machine_type_price"] config = [] @@ -174,8 +172,6 @@ def create_template(self, image_id, sg_id, vpc_id): request.set_InstanceName(self.adata["instance_name"]) request.set_SecurityGroupId(sg_id) request.set_VpcId(vpc_id) - request.set_InternetMaxBandwidthIn(50) - request.set_InternetMaxBandwidthOut(50) request.set_SystemDiskCategory("cloud_efficiency") request.set_SystemDiskSize(40) request.set_IoOptimized("optimized") @@ -240,14 +236,17 @@ def check_spot_callback(self, instance_id): request = DescribeInstancesRequest() request.set_accept_format('json') request.set_InstanceIds([instance_id]) + status = False try: response = self.client.do_action_with_exception(request) response = json.loads(response) if len(response["Instances"]["Instance"]) == 1 and "Recycling" in response["Instances"]["Instance"][0]["OperationLocks"]["LockReason"]: - return True - return False + status = True + if instance_id not in self.describe_apg_instances(): + status = True except: - return False + pass + return status def check_restart(self, work_path, tasks, group_size): if os.path.exists('apg_id.json'): @@ -422,17 +421,18 @@ def run_jobs(self, if machine_exception_num > 0: self.resubmission(machine_exception_num) for ii in range(self.nchunks): - if self.check_spot_callback(self.instance_list[ii]): - machine_exception_num += 1 - dlog.info("machine %s callback" % self.instance_list[ii]) - os.remove(self.job_handlers[ii]["job_record"].fname) - self.job_handlers[ii] = "exception" - self.ip_list[ii] = "exception" - self.instance_list[ii] = "exception" - self.dispatchers[ii][1] = "exception" - if self.adata["img_name"] == "vasp": - self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) if self.dispatchers[ii][1] == "working": + if self.check_spot_callback(self.instance_list[ii]): + machine_exception_num += 1 + dlog.info("machine %s callback" % self.instance_list[ii]) + os.remove(self.job_handlers[ii]["job_record"].fname) + self.job_handlers[ii] = "exception" + self.ip_list[ii] = "exception" + self.instance_list[ii] = "exception" + self.dispatchers[ii][1] = "exception" + if self.adata["img_name"] == "vasp": + self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) + continue if self.check_server(self.dispatchers[ii][0].remote_profile): if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): self.delete(ii) @@ -441,19 +441,7 @@ def run_jobs(self, self.instance_list[ii] = "finished" self.change_apg_capasity(self.nchunks - self.get_finished_job_num()) else: - try: - if self.instance_list[ii] not in self.describe_apg_instances(): - machine_exception_num += 1 - dlog.info("machine %s callback" % self.instance_list[ii]) - os.remove(self.job_handlers[ii]["job_record"].fname) - self.job_handlers[ii] = "exception" - self.ip_list[ii] = "exception" - self.instance_list[ii] = "exception" - self.dispatchers[ii][1] = "exception" - if self.adata["img_name"] == "vasp": - self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) - except: - dlog.info("ssh exception accured in %s" % self.ip_list[ii]) + dlog.info("ssh exception accured in %s" % self.ip_list[ii]) elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": From b508f19dd6131f41fb9f4c8fe8f782b7913bbdb6 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 30 Mar 2020 12:56:29 +0800 Subject: [PATCH 167/201] fix bug --- dpgen/dispatcher/ALI.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 32fd2cf60..744c02011 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -133,18 +133,11 @@ def describe_apg_instances(self): instance_list = [] for i in range(iteration + 1): request.set_PageNumber(i+1) - try: - response = self.client.do_action_with_exception(request) - response = json.loads(response) - for ins in response["Instances"]["Instance"]: - instance_list.append(ins["InstanceId"]) - return instance_list - except ServerException as e: - dlog.info(e) - return "exception" - except ClientException as e: - dlog.info(e) - return "exception" + response = self.client.do_action_with_exception(request) + response = json.loads(response) + for ins in response["Instances"]["Instance"]: + instance_list.append(ins["InstanceId"]) + return instance_list def generate_config(self): machine_config = self.adata["machine_type_price"] From 112795538ca44fb938e8d0c33a155b83d264afea Mon Sep 17 00:00:00 2001 From: haidi Date: Mon, 30 Mar 2020 21:10:58 +0800 Subject: [PATCH 168/201] update autotest INCAR parameters --- dpgen/auto_test/lib/vasp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpgen/auto_test/lib/vasp.py b/dpgen/auto_test/lib/vasp.py index 2e234b3c1..aa13faafb 100644 --- a/dpgen/auto_test/lib/vasp.py +++ b/dpgen/auto_test/lib/vasp.py @@ -309,7 +309,8 @@ def make_vasp_relax_incar (ecut, ediff, ret += 'ISIF=%d\n' % isif ret += 'IBRION=2\n' ret += "\n" - ret += 'NSW=50\n' + ret += 'NSW=100\n' + ret += 'POTIM=0.3\n' ret += "\n" ret += 'LWAVE=F\n' ret += 'LCHARG=F\n' From f01eeae5822b954f0c8e7dd361e68c3f614a0701 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Wed, 1 Apr 2020 14:57:51 +0800 Subject: [PATCH 169/201] fix bug --- dpgen/dispatcher/ALI.py | 19 ++++++++++++------- dpgen/dispatcher/Dispatcher.py | 6 ++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 253030c73..6367479e5 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -73,8 +73,8 @@ def create_ess(self): self.template_id = self.create_template(img_id, sg_id, vpc_id) self.vsw_id = self.get_vsw_id(vpc_id) self.apg_id = self.create_apg() - dlog.info("begin to create ess") - time.sleep(90) + dlog.info("begin to create ess, please wait two minutes") + time.sleep(120) new_server_list = self.describe_apg_instances() new_ip_list = self.get_ip(new_server_list) for ii in range(len(new_server_list)): @@ -358,7 +358,7 @@ def resubmission(self, machine_exception_num): elif self.adata["img_name"] == "vasp": if machine_exception_num / self.nchunks > 0.05: self.change_apg_capasity(self.nchunks - self.get_finished_job_num() + machine_exception_num) - time.sleep(90) + time.sleep(120) new_ip_list = [] try: new_server_list = self.update_server_list() @@ -434,7 +434,7 @@ def run_jobs(self, self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) if self.dispatchers[ii][1] == "working": if self.check_server(self.dispatchers[ii][0].remote_profile): - if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure): + if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure, False): self.delete(ii) self.dispatchers[ii][1] = "finished" self.ip_list[ii] = "finished" @@ -495,15 +495,20 @@ def run_jobs(self, def check_server(self, profile): try: session = SSHSession(profile) + session.close() return True except: return False def check_dispatcher_finished(self): + count = 0 + flag = True for ii in range(len(self.dispatchers)): - if self.dispatchers[ii][1] == "unalloc" or self.dispatchers[ii][1] == "working" or self.dispatchers[ii][1] == "exception": - return False - return True + if self.dispatchers[ii][1] == "unalloc" or self.dispatchers[ii][1] == "working": flag = False + if self.dispatchers[ii][1] == "exception": count += 1 + if self.adata["img_name"] == "vasp" and count / self.nchunks > 0.05: flag = False + elif self.adata["img_name"] == "kit" and count > 0: flag = False + return flag def delete(self, ii): request = DeleteInstancesRequest() diff --git a/dpgen/dispatcher/Dispatcher.py b/dpgen/dispatcher/Dispatcher.py index 95b5a5dcb..444f3431e 100644 --- a/dpgen/dispatcher/Dispatcher.py +++ b/dpgen/dispatcher/Dispatcher.py @@ -186,7 +186,8 @@ def submit_jobs(self, def all_finished(self, job_handler, - mark_failure): + mark_failure, + clean=True): task_chunks = job_handler['task_chunks'] task_chunks_str = ['+'.join(ii) for ii in task_chunks] task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] @@ -222,7 +223,8 @@ def all_finished(self, rjob['context'].download(task_chunks[idx], backward_task_files, check_exists = True) else: rjob['context'].download(task_chunks[idx], backward_task_files) - rjob['context'].clean() + if clean: + rjob['context'].clean() job_record.record_finish(cur_hash) job_record.dump() job_record.dump() From 27da4cef92b8b51e5197dd828e8b20a4f659d242 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 2 Apr 2020 11:38:06 +0800 Subject: [PATCH 170/201] fix bugs in expand ess. Should wait 2 mins. --- dpgen/dispatcher/ALI.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 2ec96e489..66db2382a 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -441,6 +441,8 @@ def run_jobs(self, new_ip_list = [] try: new_server_list = self.update_server_list() + if new_server_list: + time.sleep(120) new_ip_list = self.get_ip(new_server_list) except: pass From d73e66cdcb491f46fd3853732788b7f89fbed323 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 2 Apr 2020 16:16:37 +0800 Subject: [PATCH 171/201] delete 2min waiting in every iteration. --- dpgen/dispatcher/ALI.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 66db2382a..1288a183d 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -441,31 +441,30 @@ def run_jobs(self, new_ip_list = [] try: new_server_list = self.update_server_list() - if new_server_list: - time.sleep(120) new_ip_list = self.get_ip(new_server_list) except: pass if new_ip_list: - self.ip_list[ii] = new_ip_list.pop() - self.instance_list[ii] = new_server_list.pop() profile = self.mdata_machine.copy() - profile["hostname"] = self.ip_list[ii] - profile["instance_id"] = self.instance_list[ii] - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] - dlog.info(self.ip_list[ii]) - job_handler = self.dispatchers[ii][0].submit_jobs(resources, - command, - work_path, - self.task_chunks[ii], - group_size, - forward_common_files, - forward_task_files, - backward_task_files, - forward_task_deference, - outlog, - errlog) - self.job_handlers[ii] = job_handler + profile["hostname"] = new_ip_list.pop() + profile["instance_id"] = new_server_list.pop() + if self.check_server(profile): + self.ip_list[ii] = profile["hostname"] + self.instance_list[ii] = profile["instance_id"] + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + dlog.info(self.ip_list[ii]) + job_handler = self.dispatchers[ii][0].submit_jobs(resources, + command, + work_path, + self.task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.job_handlers[ii] = job_handler if self.check_dispatcher_finished(): os.remove('apg_id.json') self.delete_template() From 40e5d94a6a9ee8628311c4ddb32496d851751885 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 2 Apr 2020 16:19:39 +0800 Subject: [PATCH 172/201] fix bug in make_dispatchers. If machine can't be connected, wait 2 minutes. --- dpgen/dispatcher/ALI.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 1288a183d..77929f882 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -505,7 +505,11 @@ def make_dispatchers(self): profile = self.mdata_machine.copy() profile['hostname'] = self.ip_list[ii] profile['instance_id'] = self.instance_list[ii] - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + if self.check_server(profile): + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + else: + time.sleep(120) + self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] def delete_machine(self): request = DeleteInstancesRequest() From 2025679c9082b467b78df528b0f24e4bbfd548c6 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 2 Apr 2020 17:27:16 +0800 Subject: [PATCH 173/201] add manual create/delete interface --- dpgen/dispatcher/ALI.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index 77929f882..fee9245fe 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -28,10 +28,10 @@ def manual_delete(stage): mdata_resources = mdata[stage][0]['resources'] mdata_machine = mdata[stage][0]['machine'] ali = ALI(adata, mdata_resources, mdata_machine, 0) - with open('machine_record.json', 'r') as fp2: - machine_record = json.load(fp2) - ali.instance_list = machine_record['instance_id'] - ali.delete_machine() + with open('apg_id.json', 'r') as fp2: + apg_id = json.load(fp2) + ali.apg_id = apg_id['apg_id'] + ali.delete_apg() def manual_create(stage, machine_number): with open('machine-ali.json') as fp: @@ -40,7 +40,7 @@ def manual_create(stage, machine_number): mdata_resources = mdata[stage][0]['resources'] mdata_machine = mdata[stage][0]['machine'] ali = ALI(adata, mdata_resources, mdata_machine, machine_number) - ali.alloc_machine() + ali.create_ess() print(ali.ip_list) class ALI(): From 02fb3707a776ea5af7ad54e89b525e74551f152f Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 2 Apr 2020 21:23:48 +0800 Subject: [PATCH 174/201] add dpgen init_bulk interface for ali --- dpgen/data/gen.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dpgen/data/gen.py b/dpgen/data/gen.py index 470710601..ce1573a61 100644 --- a/dpgen/data/gen.py +++ b/dpgen/data/gen.py @@ -556,7 +556,7 @@ def _vasp_check_fin (ii) : return False return True -def run_vasp_relax(jdata, mdata, dispatcher): +def run_vasp_relax(jdata, mdata): fp_command = mdata['fp_command'] fp_group_size = mdata['fp_group_size'] fp_resources = mdata['fp_resources'] @@ -581,7 +581,7 @@ def run_vasp_relax(jdata, mdata, dispatcher): # if not _vasp_check_fin(ii): # relax_run_tasks.append(ii) run_tasks = [os.path.basename(ii) for ii in relax_run_tasks] - + dispatcher = make_dispatcher(mdata['fp_machine'], mdata['fp_resources'], work_dir, run_tasks, fp_group_size) #dlog.info(run_tasks) dispatcher.run_jobs(fp_resources, [fp_command], @@ -592,7 +592,7 @@ def run_vasp_relax(jdata, mdata, dispatcher): forward_files, backward_files) -def run_vasp_md(jdata, mdata, dispatcher): +def run_vasp_md(jdata, mdata): fp_command = mdata['fp_command'] fp_group_size = mdata['fp_group_size'] fp_resources = mdata['fp_resources'] @@ -627,7 +627,7 @@ def run_vasp_md(jdata, mdata, dispatcher): run_tasks = [ii.replace(work_dir+"/", "") for ii in md_run_tasks] #dlog.info("md_work_dir", work_dir) #dlog.info("run_tasks",run_tasks) - + dispatcher = make_dispatcher(mdata['fp_machine'], mdata['fp_resources'], work_dir, run_tasks, fp_group_size) dispatcher.run_jobs(fp_resources, [fp_command], work_dir, @@ -655,7 +655,7 @@ def gen_init_bulk(args) : if args.MACHINE is not None: # Selecting a proper machine mdata = decide_fp_machine(mdata) - disp = make_dispatcher(mdata["fp_machine"]) + #disp = make_dispatcher(mdata["fp_machine"]) # Decide work path out_dir = out_dir_name(jdata) @@ -708,7 +708,7 @@ def gen_init_bulk(args) : place_element(jdata) if args.MACHINE is not None: make_vasp_relax(jdata, mdata) - run_vasp_relax(jdata, mdata, disp) + run_vasp_relax(jdata, mdata) else: make_vasp_relax(jdata, {"fp_resources":{}}) elif stage == 2 : @@ -719,7 +719,7 @@ def gen_init_bulk(args) : dlog.info("Current stage is 3, run a short md") make_vasp_md(jdata) if args.MACHINE is not None: - run_vasp_md(jdata, mdata, disp) + run_vasp_md(jdata, mdata) elif stage == 4 : dlog.info("Current stage is 4, collect data") coll_vasp_md(jdata) From 9005816549b49795e1ebcf882adc5c4277f940bf Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 6 Apr 2020 13:41:23 +0800 Subject: [PATCH 175/201] fix issue #247 add = to flags starting with -- --- dpgen/dispatcher/Slurm.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dpgen/dispatcher/Slurm.py b/dpgen/dispatcher/Slurm.py index f8bf91876..31b7130db 100644 --- a/dpgen/dispatcher/Slurm.py +++ b/dpgen/dispatcher/Slurm.py @@ -75,18 +75,18 @@ def sub_script_head(self, res): ret = '' ret += "#!/bin/bash -l\n" ret += "#SBATCH -N %d\n" % res['numb_node'] - ret += "#SBATCH --ntasks-per-node %d\n" % res['task_per_node'] + ret += "#SBATCH --ntasks-per-node=%d\n" % res['task_per_node'] if res['cpus_per_task'] > 0 : - ret += "#SBATCH --cpus-per-task %d\n" % res['cpus_per_task'] + ret += "#SBATCH --cpus-per-task=%d\n" % res['cpus_per_task'] ret += "#SBATCH -t %s\n" % res['time_limit'] if res['mem_limit'] > 0 : - ret += "#SBATCH --mem %dG \n" % res['mem_limit'] + ret += "#SBATCH --mem=%dG \n" % res['mem_limit'] if len(res['account']) > 0 : - ret += "#SBATCH --account %s \n" % res['account'] + ret += "#SBATCH --account=%s \n" % res['account'] if len(res['partition']) > 0 : - ret += "#SBATCH --partition %s \n" % res['partition'] + ret += "#SBATCH --partition=%s \n" % res['partition'] if len(res['qos']) > 0 : - ret += "#SBATCH --qos %s \n" % res['qos'] + ret += "#SBATCH --qos=%s \n" % res['qos'] if res['numb_gpu'] > 0 : ret += "#SBATCH --gres=gpu:%d\n" % res['numb_gpu'] for ii in res['constraint_list'] : @@ -99,7 +99,7 @@ def sub_script_head(self, res): temp_exclude += ii temp_exclude += "," temp_exclude = temp_exclude[:-1] - ret += '#SBATCH --exclude %s \n' % temp_exclude + ret += '#SBATCH --exclude=%s \n' % temp_exclude ret += "\n" for ii in res['module_unload_list'] : ret += "module unload %s\n" % ii From 9b7beefdad43e2c0b3d8c2d768e1757e3a3504b3 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Mon, 13 Apr 2020 09:46:25 +0800 Subject: [PATCH 176/201] stable version --- dpgen/dispatcher/ALI.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/dpgen/dispatcher/ALI.py b/dpgen/dispatcher/ALI.py index fee9245fe..f6e2c12ce 100644 --- a/dpgen/dispatcher/ALI.py +++ b/dpgen/dispatcher/ALI.py @@ -78,8 +78,12 @@ def create_ess(self): new_server_list = self.describe_apg_instances() new_ip_list = self.get_ip(new_server_list) for ii in range(len(new_server_list)): - self.instance_list[ii] = new_server_list[ii] - self.ip_list[ii] = new_ip_list[ii] + profile = self.mdata_machine.copy() + profile['hostname'] = new_ip_list[ii] + profile['instance_id'] = new_server_list[ii] + if self.check_server(profile): + self.instance_list[ii] = new_server_list[ii] + self.ip_list[ii] = new_ip_list[ii] def delete_apg(self): request = DeleteAutoProvisioningGroupRequest() @@ -411,6 +415,12 @@ def run_jobs(self, self.job_handlers[ii] = job_handler machine_exception_num = 0 while True: + new_ip_list = [] + try: + new_server_list = self.update_server_list() + new_ip_list = self.get_ip(new_server_list) + except: + pass if machine_exception_num > 0: self.resubmission(machine_exception_num) for ii in range(self.nchunks): @@ -424,7 +434,7 @@ def run_jobs(self, self.instance_list[ii] = "exception" self.dispatchers[ii][1] = "exception" if self.adata["img_name"] == "vasp": - self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - 1) + self.change_apg_capasity(self.nchunks - self.get_finished_job_num() - machine_exception_num) continue if self.check_server(self.dispatchers[ii][0].remote_profile): if self.dispatchers[ii][0].all_finished(self.job_handlers[ii], mark_failure, False): @@ -438,19 +448,13 @@ def run_jobs(self, elif self.dispatchers[ii][1] == "finished": continue elif self.dispatchers[ii][1] == "unalloc": - new_ip_list = [] - try: - new_server_list = self.update_server_list() - new_ip_list = self.get_ip(new_server_list) - except: - pass if new_ip_list: profile = self.mdata_machine.copy() - profile["hostname"] = new_ip_list.pop() - profile["instance_id"] = new_server_list.pop() + profile["hostname"] = new_ip_list[0] + profile["instance_id"] = new_server_list[0] if self.check_server(profile): - self.ip_list[ii] = profile["hostname"] - self.instance_list[ii] = profile["instance_id"] + self.ip_list[ii] = new_ip_list.pop(0) + self.instance_list[ii] = new_server_list.pop(0) self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] dlog.info(self.ip_list[ii]) job_handler = self.dispatchers[ii][0].submit_jobs(resources, @@ -508,8 +512,8 @@ def make_dispatchers(self): if self.check_server(profile): self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] else: - time.sleep(120) - self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] + continue + #self.dispatchers[ii] = [Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii), "working"] def delete_machine(self): request = DeleteInstancesRequest() From 8f6edb725dfa394ef24a152a8613a140e255ed21 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 13 Apr 2020 18:10:05 +0800 Subject: [PATCH 177/201] different activation func for different models --- dpgen/generator/run.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 68e811adb..e4a1ae624 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -201,6 +201,7 @@ def make_train (iter_index, training_reuse_start_lr = jdata.get('training_reuse_start_lr', 1e-4) training_reuse_start_pref_e = jdata.get('training_reuse_start_pref_e', 0.1) training_reuse_start_pref_f = jdata.get('training_reuse_start_pref_f', 100) + model_devi_activation_func = jdata.get('model_devi_activation_func', None) if iter_index > 0 and _check_empty_iter(iter_index-1, fp_task_min) : log_task('prev data is empty, copy prev model') @@ -324,15 +325,16 @@ def make_train (iter_index, jinput['loss']['start_pref_f'] = training_reuse_start_pref_f jinput['learning_rate']['start_lr'] = training_reuse_start_lr jinput['training']['stop_batch'] = training_reuse_stop_batch - # set random seed for each model, dump the input.json + for ii in range(numb_models) : task_path = os.path.join(work_path, train_task_fmt % ii) create_path(task_path) os.chdir(task_path) - for ii in init_data_sys : - if not os.path.isdir(ii) : - raise RuntimeError ("data sys %s does not exists, cwd is %s" % (ii, os.getcwd())) + for jj in init_data_sys : + if not os.path.isdir(jj) : + raise RuntimeError ("data sys %s does not exists, cwd is %s" % (jj, os.getcwd())) os.chdir(cwd) + # set random seed for each model if LooseVersion(mdata["deepmd_version"]) < LooseVersion('1'): # 0.x jinput['seed'] = random.randrange(sys.maxsize) % (2**32) @@ -341,6 +343,14 @@ def make_train (iter_index, jinput['model']['descriptor']['seed'] = random.randrange(sys.maxsize) % (2**32) jinput['model']['fitting_net']['seed'] = random.randrange(sys.maxsize) % (2**32) jinput['training']['seed'] = random.randrange(sys.maxsize) % (2**32) + # set model activation function + if model_devi_activation_func is not None: + if LooseVersion(mdata["deepmd_version"]) < LooseVersion('1'): + raise RuntimeError('model_devi_activation_func does not suppport deepmd version', mdata['deepmd_version']) + assert(type(model_devi_activation_func) is list and len(model_devi_activation_func) == numb_models) + jinput['model']['descriptor']['activation_function'] = model_devi_activation_func[ii] + jinput['model']['fitting_net']['activation_function'] = model_devi_activation_func[ii] + # dump the input.json with open(os.path.join(task_path, train_input_file), 'w') as outfile: json.dump(jinput, outfile, indent = 4) From c51e0ce13ba0f3bd0020e147e3251faab2894f34 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 13 Apr 2020 18:17:16 +0800 Subject: [PATCH 178/201] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fa1f1fb65..1983aa31c 100644 --- a/README.md +++ b/README.md @@ -483,6 +483,7 @@ The bold notation of key (such aas **type_map**) means that it's a necessary key | **model_devi_e_trust_hi** | Float | 1e10 | Upper bound of energies for the selection. | | **model_devi_clean_traj** | Boolean | true | Deciding whether to clean traj folders in MD since they are too large. | | **model_devi_nopbc** | Boolean | False | Assume open boundary condition in MD simulations. | +| model_devi_activation_func | List of String | ["tanh", "tanh", "tanh", "tanh"] | Set activation functions for models, length of the list should be the same as `numb_models` | | **model_devi_jobs** | [
{
"sys_idx": [0],
"temps":
[100],
"press":
[1],
"trj_freq":
10,
"nsteps":
1000,
"ensembles":
"nvt"
},
...
] | List of dict | Settings for exploration in `01.model_devi`. Each dict in the list corresponds to one iteration. The index of `model_devi_jobs` exactly accord with index of iterations | | **model_devi_jobs["sys_idx"]** | List of integer | [0] | Systems to be selected as the initial structure of MD and be explored. The index corresponds exactly to the `sys_configs`. | | **model_devi_jobs["temps"]** | List of integer | [50, 300] | Temperature (**K**) in MD From 770e613bd2bb0ea3d936baff21d11eff27958f31 Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 16 Apr 2020 11:57:34 +0800 Subject: [PATCH 179/201] add Dispatcher List, stable ALI.py --- dpgen/dispatcher/DispatcherList.py | 134 +++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 dpgen/dispatcher/DispatcherList.py diff --git a/dpgen/dispatcher/DispatcherList.py b/dpgen/dispatcher/DispatcherList.py new file mode 100644 index 000000000..a82cbdfb6 --- /dev/null +++ b/dpgen/dispatcher/DispatcherList.py @@ -0,0 +1,134 @@ +from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks, JobRecord + +class Entity() +class DispatcherList(): + def __init__(self, mdata_machine, mdata_resources, nchunks, cloud_resources=None): + self.mdata_machine = mdata_machine + self.mdata_resources = mdata_resources + self.nchunks = nchunks + self.cloud_resources = cloud_resources + self.dispatcher_list = list({"dispatcher": None, + "dispatcher_status": "unallocated", + "entity": Entity} for ii in range(nchunks)) + # Base + def init(self): + for dispatcher in self.dispatcher_list: + create() + + # Base + def run_jobs(self, + resources, + command, + work_path, + tasks, + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference = True, + mark_failure = False, + outlog = 'log', + errlog = 'err'): + task_chunks = _split_tasks(tasks, group_size) + while True: + if self.check_all_dispatchers_finished(): + break + ratio_failure = self.mdata_resources["ratio_failure"] + self.exception_handling(ratio_failure) + for ii in range(self.nchunks): + dispatcher_status = self.check_dispatcher_status(ii) + if dispatcher_status == "unsubmitted": + self.dispatcher_list[ii]["job_handler"] = self.dispatcher_list[ii]["dispatcher"].submit_jobs(resources, + command, + work_path, + task_chunks[ii], + group_size, + forward_common_files, + forward_task_files, + backward_task_files, + forward_task_deference, + outlog, + errlog) + self.dispatcher_list[ii]["dispatcher_status"] == "running" + elif dispatcher_status == "finished": + # to do + # no jobs in queue, delete current machine + # else allocate current machine to unalloc dispatcher + elif dispatcher_status == "running": + pass + elif dispatcher_status == "unallocated": + # to do: if we can create machine, then make dispatcher + # else pass + elif dispatcher_status == "terminated": + pass + + # Derivate + def check_restart(self): + '''use some methods to decide restart or not''' + + # Derivate + def create(self, num): + '''create machines + assignment the entities + make dispatchers + change status from unallocated to unsubmitted''' + + # Derivate + def delete(self): + '''delete one machine''' + + # Base + def check_all_dispatchers_finished(self, ratio_failure=0): + exception_num = 0 + for ii in range(self.nchunks): + if self.dispatcher_list[ii]["dispatcher_status"] == "terminated": + exception_num += 1 + if exception_num / self.nchunks > ratio_failure: return False + else return True + + # Base + def exception_handling(self, ratio_failure): + terminated_num = 0 + for ii in range(self.nchunks): + if self.dispatcher_list[ii]["dispatcher_status"] == "terminated": + terminated_num += 1 + if terminated_num / self.nchunks > ratio_failure: + self.resubmit() + + # Derivate + def resubmit(self): + '''create machines + make dispatcher + change status from terminated to unsubmitted''' + + # Base + def make_dispatcher(self, entity): + '''use entity to distinguish machine, for example if ip isn't None, means we can make_dispatcher + change status from unallocated to unsubmitted''' + if entity["ip"]: + profile = self.mdata_machine.copy() + profile['hostname'] = entity["ip"] + profile['instance_id'] = entity["instance_id"] + dispatcher = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + self.dispatcher_list[ii]["dispatcher_status"] = "unsubmitted" + + # Base + def check_dispatcher_status(self, ii): + '''catch running dispatcher exception + if no exception occured, check finished''' + if self.dispatcher_list[ii]["dispatcher_status"] == "running": + if not self.catch_dispatcher_exception(): + if self.dispatcher_list[ii]["dispatcher"].all_finished(): + self.dispatcher_list[ii]["dispatcher_status"] = "finished" + else: + self.dispatcher_list[ii]["dispatcher_status"] = "terminated" + return self.dispatcher_list[ii]["dispatcher_status"] + + # Derivate + def catch_dispatcher_exception(self): + + + + + + From 6edc1f1d176391aa3132b2c5138511c670d1fbaa Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Thu, 16 Apr 2020 18:19:25 +0800 Subject: [PATCH 180/201] add DispatcherList.py --- dpgen/dispatcher/DispatcherList.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dpgen/dispatcher/DispatcherList.py b/dpgen/dispatcher/DispatcherList.py index a82cbdfb6..e5591b6cb 100644 --- a/dpgen/dispatcher/DispatcherList.py +++ b/dpgen/dispatcher/DispatcherList.py @@ -1,6 +1,11 @@ from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks, JobRecord -class Entity() +class Entity(): + def __init__(self, ip, instance_id, job_handler): + self.ip = ip + self.instance_id = instance_id + self.job_handler = job_handler + class DispatcherList(): def __init__(self, mdata_machine, mdata_resources, nchunks, cloud_resources=None): self.mdata_machine = mdata_machine @@ -9,11 +14,11 @@ def __init__(self, mdata_machine, mdata_resources, nchunks, cloud_resources=None self.cloud_resources = cloud_resources self.dispatcher_list = list({"dispatcher": None, "dispatcher_status": "unallocated", - "entity": Entity} for ii in range(nchunks)) + "entity": None} for ii in range(nchunks)) # Base def init(self): for dispatcher in self.dispatcher_list: - create() + self.create() # Base def run_jobs(self, @@ -65,6 +70,7 @@ def run_jobs(self, # Derivate def check_restart(self): '''use some methods to decide restart or not''' + pass # Derivate def create(self, num): @@ -72,10 +78,12 @@ def create(self, num): assignment the entities make dispatchers change status from unallocated to unsubmitted''' + pass # Derivate def delete(self): '''delete one machine''' + pass # Base def check_all_dispatchers_finished(self, ratio_failure=0): @@ -100,6 +108,7 @@ def resubmit(self): '''create machines make dispatcher change status from terminated to unsubmitted''' + pass # Base def make_dispatcher(self, entity): @@ -126,6 +135,7 @@ def check_dispatcher_status(self, ii): # Derivate def catch_dispatcher_exception(self): + pass From 51c30fb0945d66d63f0ea5a47214156c2f3ece2b Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 17 Apr 2020 14:45:30 +0800 Subject: [PATCH 181/201] add DispatcherList --- dpgen/dispatcher/DispatcherList.py | 51 ++++++++++++++++++------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/dpgen/dispatcher/DispatcherList.py b/dpgen/dispatcher/DispatcherList.py index e5591b6cb..92576fd8b 100644 --- a/dpgen/dispatcher/DispatcherList.py +++ b/dpgen/dispatcher/DispatcherList.py @@ -1,11 +1,11 @@ from dpgen.dispatcher.Dispatcher import Dispatcher, _split_tasks, JobRecord class Entity(): - def __init__(self, ip, instance_id, job_handler): + def __init__(self, ip, instance_id, job_handler=None): self.ip = ip self.instance_id = instance_id self.job_handler = job_handler - + class DispatcherList(): def __init__(self, mdata_machine, mdata_resources, nchunks, cloud_resources=None): self.mdata_machine = mdata_machine @@ -17,8 +17,8 @@ def __init__(self, mdata_machine, mdata_resources, nchunks, cloud_resources=None "entity": None} for ii in range(nchunks)) # Base def init(self): - for dispatcher in self.dispatcher_list: - self.create() + for ii in range(self.nchunks): + self.create(ii) # Base def run_jobs(self, @@ -59,27 +59,35 @@ def run_jobs(self, # to do # no jobs in queue, delete current machine # else allocate current machine to unalloc dispatcher + pass elif dispatcher_status == "running": pass elif dispatcher_status == "unallocated": # to do: if we can create machine, then make dispatcher # else pass + pass elif dispatcher_status == "terminated": pass # Derivate - def check_restart(self): - '''use some methods to decide restart or not''' - pass - - # Derivate - def create(self, num): - '''create machines - assignment the entities - make dispatchers - change status from unallocated to unsubmitted''' - pass - + def create(self, ii): + '''if jr.json existed and job not finished, use jr.json to rebuild dispatcher + else create one machine, then make_dispatcher, change status from unallocated to unsubmitted''' + if not os.path.exists(os.path.join(os.path.abspath(work_path), "jr.%.06d.json" % ii)): + # to do: create machine, make dispatcher, change status from unallocated to unsubmitted + pass + else: + task_chunks = _split_tasks(tasks, group_size) + task_chunks_str = ['+'.join(ii) for ii in self.task_chunks] + task_hashes = [sha1(ii.encode('utf-8')).hexdigest() for ii in task_chunks_str] + job_record = JobRecord(work_path, task_chunks[ii], fname = "jr.%.06d.json" % ii) + cur_chunk = self.task_chunks[ii] + cur_hash = task_hashes[ii] + if not job_record.check_finished(cur_hash): + with open(os.path.join(work_path, fn)) as fp: + jr = json.load(fp) + self.dispatcher_list[ii]["entity"] = Entity(jr[cur_hash]['context'][3], jr[cur_hash]['context'][4]) + self.make_dispatcher(ii) # Derivate def delete(self): '''delete one machine''' @@ -111,14 +119,15 @@ def resubmit(self): pass # Base - def make_dispatcher(self, entity): + def make_dispatcher(self, ii): '''use entity to distinguish machine, for example if ip isn't None, means we can make_dispatcher change status from unallocated to unsubmitted''' - if entity["ip"]: + entity = self.dispatcher_list[ii]["entity"] + if entity.ip: profile = self.mdata_machine.copy() - profile['hostname'] = entity["ip"] - profile['instance_id'] = entity["instance_id"] - dispatcher = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) + profile['hostname'] = entity.ip + profile['instance_id'] = entity.instance_id + self.dispatcher_list[ii]["dispatcher"] = Dispatcher(profile, context_type='ssh', batch_type='shell', job_record='jr.%.06d.json' % ii) self.dispatcher_list[ii]["dispatcher_status"] = "unsubmitted" # Base From e9dd2d1cb18153873e46aed46f4cb5da522cdffa Mon Sep 17 00:00:00 2001 From: dingzhaohan Date: Fri, 17 Apr 2020 15:09:48 +0800 Subject: [PATCH 182/201] fix bug --- dpgen/dispatcher/DispatcherList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/DispatcherList.py b/dpgen/dispatcher/DispatcherList.py index 92576fd8b..a62bd2107 100644 --- a/dpgen/dispatcher/DispatcherList.py +++ b/dpgen/dispatcher/DispatcherList.py @@ -100,7 +100,7 @@ def check_all_dispatchers_finished(self, ratio_failure=0): if self.dispatcher_list[ii]["dispatcher_status"] == "terminated": exception_num += 1 if exception_num / self.nchunks > ratio_failure: return False - else return True + else: return True # Base def exception_handling(self, ratio_failure): From 67273375872fc1a18924422d606936d958ef6a3d Mon Sep 17 00:00:00 2001 From: haidi Date: Sun, 19 Apr 2020 01:10:32 +0800 Subject: [PATCH 183/201] update SSHContext.py --- dpgen/dispatcher/SSHContext.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index 14cfbb770..f032bf0ad 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -248,7 +248,10 @@ def _put_files(self, from_f = os.path.join(self.local_root, of) to_f = os.path.join(self.remote_root, of) sftp = self.ssh.open_sftp() - sftp.put(from_f, to_f) + try: + sftp.put(from_f, to_f) + except FileNotFoundError: + raise RuntimeError("from %s to %s Error!"%(from_f,to_f)) # remote extract self.block_checkcall('tar xf %s' % of) # clean up From 714a7427dde5bdae4ea246ce51c799d11485d53f Mon Sep 17 00:00:00 2001 From: AnguseZhang <529133328@qq.con> Date: Sun, 26 Apr 2020 18:26:53 +0800 Subject: [PATCH 184/201] Add 'checkpoint' to train backward_files --- dpgen/generator/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index e4a1ae624..92616e080 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -478,7 +478,7 @@ def run_train (iter_index, os.path.join('old', 'model.ckpt.data-00000-of-00001') ] backward_files = ['frozen_model.pb', 'lcurve.out', 'train.log'] - backward_files+= ['model.ckpt.meta', 'model.ckpt.index', 'model.ckpt.data-00000-of-00001'] + backward_files+= ['model.ckpt.meta', 'model.ckpt.index', 'model.ckpt.data-00000-of-00001', 'checkpoint'] init_data_sys_ = jdata['init_data_sys'] init_data_sys = [] for ii in init_data_sys_ : From 5bbcd1be79fdea2ed74f8791e6171290c43080e4 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 4 May 2020 15:10:02 +0800 Subject: [PATCH 185/201] fix bug of simplify when running with ali --- dpgen/simplify/simplify.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dpgen/simplify/simplify.py b/dpgen/simplify/simplify.py index 3372968a7..9856dc58a 100644 --- a/dpgen/simplify/simplify.py +++ b/dpgen/simplify/simplify.py @@ -225,7 +225,7 @@ def make_model_devi(iter_index, jdata, mdata): return True -def run_model_devi(iter_index, jdata, mdata, dispatcher): +def run_model_devi(iter_index, jdata, mdata): """submit dp test tasks""" iter_name = make_iter_name(iter_index) work_path = os.path.join(iter_name, model_devi_name) @@ -269,6 +269,7 @@ def run_model_devi(iter_index, jdata, mdata, dispatcher): forward_files = [rest_data_name] backward_files = sum([[pf+".e.out", pf+".f.out", pf+".v.out"] for pf in detail_file_names], []) + dispatcher = make_dispatcher(mdata['model_devi_machine'], mdata['model_devi_resources'], work_path, run_tasks, model_devi_group_size) dispatcher.run_jobs(mdata['model_devi_resources'], commands, work_path, @@ -638,7 +639,7 @@ def run_iter(param_file, machine_file): elif jj == 1: log_iter("run_train", ii, jj) mdata = decide_train_machine(mdata) - disp = make_dispatcher(mdata['train_machine']) + #disp = make_dispatcher(mdata['train_machine']) run_train(ii, jdata, mdata) elif jj == 2: log_iter("post_train", ii, jj) @@ -651,8 +652,8 @@ def run_iter(param_file, machine_file): elif jj == 4: log_iter("run_model_devi", ii, jj) mdata = decide_model_devi_machine(mdata) - disp = make_dispatcher(mdata['model_devi_machine']) - run_model_devi(ii, jdata, mdata, disp) + #disp = make_dispatcher(mdata['model_devi_machine']) + run_model_devi(ii, jdata, mdata) elif jj == 5: log_iter("post_model_devi", ii, jj) post_model_devi(ii, jdata, mdata) @@ -665,7 +666,7 @@ def run_iter(param_file, machine_file): dlog.info("already have labeled data, skip run_fp") else: mdata = decide_fp_machine(mdata) - disp = make_dispatcher(mdata['fp_machine']) + #disp = make_dispatcher(mdata['fp_machine']) run_fp(ii, jdata, mdata) elif jj == 8: log_iter("post_fp", ii, jj) From a53fb644c5ae4c1dc724473afeec4a6d6a469111 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Mon, 4 May 2020 15:10:27 +0800 Subject: [PATCH 186/201] remove local dir ignoring errors --- dpgen/dispatcher/LocalContext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/LocalContext.py b/dpgen/dispatcher/LocalContext.py index 457ae659b..373f112c7 100644 --- a/dpgen/dispatcher/LocalContext.py +++ b/dpgen/dispatcher/LocalContext.py @@ -122,7 +122,7 @@ def download(self, # both exists, replace! dlog.info('find existing %s, replacing by %s' % (lfile, rfile)) if os.path.isdir(lfile): - shutil.rmtree(lfile) + shutil.rmtree(lfile, ignore_errors=True) elif os.path.isfile(lfile) or os.path.islink(lfile): os.remove(lfile) shutil.move(rfile, lfile) @@ -160,7 +160,7 @@ def block_call(self, cmd) : return code, None, stdout, stderr def clean(self) : - shutil.rmtree(self.remote_root) + shutil.rmtree(self.remote_root, ignore_errors=True) def write_file(self, fname, write_str): with open(os.path.join(self.remote_root, fname), 'w') as fp : From e09326227919b0f2683bc4acad415ecc7ec17f42 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 8 May 2020 14:07:40 +0800 Subject: [PATCH 187/201] move slab to center of box along z direction --- dpgen/data/surf.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/dpgen/data/surf.py b/dpgen/data/surf.py index 95b8e9156..d8211239a 100644 --- a/dpgen/data/surf.py +++ b/dpgen/data/surf.py @@ -12,11 +12,12 @@ import time from dpgen import ROOT_PATH from dpgen.remote.decide_machine import decide_fp_machine -from pymatgen.core.surface import SlabGenerator,generate_all_slabs, Structure +from pymatgen.core.surface import SlabGenerator,generate_all_slabs from pymatgen.io.vasp import Poscar from dpgen.dispatcher.Dispatcher import Dispatcher, make_dispatcher #-----ASE------- from pymatgen.io.ase import AseAtomsAdaptor +from pymatgen import Structure from ase.io import read from ase.build import general_surface @@ -176,7 +177,7 @@ def poscar_scale (poscar_in, poscar_out, scale) : with open(poscar_out, 'w') as fout: fout.write("".join(lines)) -def poscar_elong (poscar_in, poscar_out, elong) : +def poscar_elong (poscar_in, poscar_out, elong, shift_center=True) : with open(poscar_in, 'r') as fin : lines = list(fin) if lines[7][0].upper() != 'C' : @@ -187,8 +188,18 @@ def poscar_elong (poscar_in, poscar_out, elong) : elong_ratio = elong / boxzl boxz = boxz * (1. + elong_ratio) lines[4] = '%.16e %.16e %.16e\n' % (boxz[0],boxz[1],boxz[2]) - with open(poscar_out, 'w') as fout: - fout.write("".join(lines)) + if shift_center: + poscar_str="".join(lines) + st=Structure.from_str(poscar_str,fmt='poscar') + cart_coords=st.cart_coords + z_mean=cart_coords[:,2].mean() + z_shift=st.lattice.c/2-z_mean + cart_coords[:,2]=cart_coords[:,2]+z_shift + nst=Structure(st.lattice,st.species,coords=cart_coords,coords_are_cartesian=True) + nst.to('poscar',poscar_out) + else: + with open(poscar_out, 'w') as fout: + fout.write("".join(lines)) def make_unit_cell (jdata) : From 1a0f49a0208bdb65163ac6fc3f38d0b6a4827930 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 8 May 2020 16:43:34 +0800 Subject: [PATCH 188/201] recover vasp.py --- dpgen/auto_test/lib/vasp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dpgen/auto_test/lib/vasp.py b/dpgen/auto_test/lib/vasp.py index aa13faafb..2e234b3c1 100644 --- a/dpgen/auto_test/lib/vasp.py +++ b/dpgen/auto_test/lib/vasp.py @@ -309,8 +309,7 @@ def make_vasp_relax_incar (ecut, ediff, ret += 'ISIF=%d\n' % isif ret += 'IBRION=2\n' ret += "\n" - ret += 'NSW=100\n' - ret += 'POTIM=0.3\n' + ret += 'NSW=50\n' ret += "\n" ret += 'LWAVE=F\n' ret += 'LCHARG=F\n' From 464aa0602ef3dcf5ec0e26f307fe93a335bedfec Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 15 May 2020 13:13:38 +0800 Subject: [PATCH 189/201] fix bug elong problem --- dpgen/data/surf.py | 20 ++++++++++++++------ tests/data/surf_poscar.json | 13 +++++-------- tests/data/test_gen_surf.py | 6 ++++-- tests/data/test_gen_surf_poscar.py | 12 +++++++----- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/dpgen/data/surf.py b/dpgen/data/surf.py index 95b8e9156..c73f4e211 100644 --- a/dpgen/data/surf.py +++ b/dpgen/data/surf.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import time import os,json,shutil,re,glob,argparse import numpy as np import subprocess as sp @@ -9,17 +10,18 @@ import dpgen.data.tools.sc as sc import dpgen.data.tools.bcc as bcc from dpgen import dlog -import time from dpgen import ROOT_PATH from dpgen.remote.decide_machine import decide_fp_machine -from pymatgen.core.surface import SlabGenerator,generate_all_slabs, Structure -from pymatgen.io.vasp import Poscar from dpgen.dispatcher.Dispatcher import Dispatcher, make_dispatcher -#-----ASE------- +#-----PMG--------- +from pymatgen.io.vasp import Poscar +from pymatgen import Structure,Element from pymatgen.io.ase import AseAtomsAdaptor +#-----ASE------- from ase.io import read from ase.build import general_surface + def create_path (path) : path += '/' if os.path.isdir(path) : @@ -215,6 +217,12 @@ def make_super_cell_pymatgen (jdata) : make_unit_cell(jdata) out_dir = jdata['out_dir'] path_uc = os.path.join(out_dir, global_dirname_02) + + elements=[Element(ii) for ii in jdata['elements']] + if 'vacuum_min' in jdata: + vacuum_min=jdata['vacuum_min'] + else: + vacuum_min=max([float(ii.atomic_radius) for ii in elements]) from_poscar= jdata.get('from_poscar',False) @@ -256,11 +264,11 @@ def make_super_cell_pymatgen (jdata) : os.chdir(path_cur_surf) #slabgen = SlabGenerator(ss, miller, z_min, 1e-3) if user_layer_numb: - slab=general_surface.surface(ss,indices=miller,vacuum=1e-3,layers=user_layer_numb) + slab=general_surface.surface(ss,indices=miller,vacuum=vacuum_min,layers=user_layer_numb) else: # build slab according to z_min value for layer_numb in range( 1,max_layer_numb+1): - slab=general_surface.surface(ss,indices=miller,vacuum=1e-3,layers=layer_numb) + slab=general_surface.surface(ss,indices=miller,vacuum=vacuum_min,layers=layer_numb) if slab.cell.lengths()[-1] >= z_min: break if layer_numb == max_layer_numb: diff --git a/tests/data/surf_poscar.json b/tests/data/surf_poscar.json index b5727ff83..3399b920f 100644 --- a/tests/data/surf_poscar.json +++ b/tests/data/surf_poscar.json @@ -12,14 +12,11 @@ 1 ], "layer_numb": 3, - "vacuum_max": 9, - "vacuum_resol": [ - 0.5, - 1 - ], - "mid_point": 4.0, - "head_ratio": 0.6, - "vacuum_numb": 20, + "vacuum_max": 5, + "vacuum_resol": [0.5,2], + "mid_point": 2.0, + "_head_ratio": 0.6, + "_vacuum_numb": 20, "millers": [ [ 1, diff --git a/tests/data/test_gen_surf.py b/tests/data/test_gen_surf.py index 5392ccb74..63e9ed7a4 100644 --- a/tests/data/test_gen_surf.py +++ b/tests/data/test_gen_surf.py @@ -1,6 +1,6 @@ import os,sys,json,glob,shutil import unittest -from pymatgen import Structure +from pymatgen import Structure,Element sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) __package__ = 'data' @@ -24,6 +24,7 @@ def setUp(self): make_vasp_relax(jdata) make_scale(jdata) pert_scaled(jdata) + self.jdata=jdata def tearDown(self): shutil.rmtree(self.root_dir) @@ -38,7 +39,8 @@ def test(self): surf=poscar.split('/')[-3] st1=Structure.from_file(surf+'.POSCAR') st2=Structure.from_file(poscar) - self.assertEqual(st1,st2) + vacuum_size=float(Element(self.jdata['elements'][0]).atomic_radius*2) + self.assertTrue(st1.lattice.c+vacuum_size-st2.lattice.c<0.01) for surf in self.surfs: elongs=glob.glob("surf.al.fcc.01x01x01/01.scale_pert/"+surf+"/sys-*/scale-1.000/el*") diff --git a/tests/data/test_gen_surf_poscar.py b/tests/data/test_gen_surf_poscar.py index 5c49b0578..1ed44eac3 100644 --- a/tests/data/test_gen_surf_poscar.py +++ b/tests/data/test_gen_surf_poscar.py @@ -1,6 +1,6 @@ import os,sys,json,glob,shutil import unittest -from pymatgen import Structure +from pymatgen import Structure,Element sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) __package__ = 'data' @@ -10,9 +10,8 @@ class TestGenSurfPOSCAR(unittest.TestCase): def setUp(self): self.surfs=["surf-100"] - self.elongs=["elong-0.500", "elong-1.000", "elong-1.500", "elong-2.000", "elong-2.500",\ - "elong-3.000", "elong-3.500", "elong-4.000", "elong-5.000", "elong-6.000",\ - "elong-7.000", "elong-8.000" ] + self.elongs=["elong-0.500", "elong-1.000", "elong-1.500", + "elong-2.000", "elong-4.000"] with open (param_file, 'r') as fp : jdata = json.load (fp) out_dir = out_dir_name(jdata) @@ -24,6 +23,7 @@ def setUp(self): make_vasp_relax(jdata) make_scale(jdata) pert_scaled(jdata) + self.jdata=jdata def tearDown(self): shutil.rmtree(self.root_dir) @@ -38,7 +38,9 @@ def test(self): surf=poscar.split('/')[-3] st1=Structure.from_file(surf+'.POSCAR') st2=Structure.from_file(poscar) - self.assertEqual(st1,st2) + vacuum_size=float(Element(self.jdata['elements'][0]).atomic_radius*2) + self.assertTrue(st1.lattice.c+vacuum_size-st2.lattice.c<0.01) + for surf in self.surfs: elongs=glob.glob("POSCAR.01x01x01/01.scale_pert/"+surf+"/sys-*/scale-1.000/el*") From 10cc8d0189218c7b0011510df053ece7f6cd9e77 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 15 May 2020 13:16:32 +0800 Subject: [PATCH 190/201] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1983aa31c..85d7e62cf 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,7 @@ The bold notation of key (such as **Elements**) means that it's a necessary key. | **layer_numb** | Integer | 3 | Number of equavilent layers of slab. | **z__min** | Float | 9.0 | Thickness of slab without vacuum (Angstrom). If the `layer_numb` and `z_min` are all setted, the `z_min` value will be ignored. | **vacuum_max** | Float | 9 | Maximal thickness of vacuum (Angstrom). +| **vacuum_min** | Float | 2xR_(atom) | Minimal thickness of vacuum (Angstrom). | **vacuum_resol** | List of float | [0.5, 1 ] | Interval of thichness of vacuum. If size of `vacuum_resol` is 1, the interval is fixed to its value. If size of `vacuum_resol` is 2, the interval is `vacuum_resol[0]` before `mid_point`, otherwise `vacuum_resol[1]` after `mid_point`. | **millers** | List of list of Integer | [[1,0,0]] | Miller indices. | relax_incar | String | "....../INCAR" | Path of INCAR for relaxation in VASP. **Necessary** if `stages` include 1. From c21ce83974e9e88499d013ae59e8ca3e70b30a34 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 15 May 2020 14:37:16 +0800 Subject: [PATCH 191/201] update README & fix bug SSHContext.py --- README.md | 2 +- dpgen/dispatcher/SSHContext.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85d7e62cf..9901f4fe2 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ The bold notation of key (such as **Elements**) means that it's a necessary key. | **layer_numb** | Integer | 3 | Number of equavilent layers of slab. | **z__min** | Float | 9.0 | Thickness of slab without vacuum (Angstrom). If the `layer_numb` and `z_min` are all setted, the `z_min` value will be ignored. | **vacuum_max** | Float | 9 | Maximal thickness of vacuum (Angstrom). -| **vacuum_min** | Float | 2xR_(atom) | Minimal thickness of vacuum (Angstrom). +| vacuum_min | Float | 2xR_(atom) | Minimal thickness of vacuum (Angstrom). | **vacuum_resol** | List of float | [0.5, 1 ] | Interval of thichness of vacuum. If size of `vacuum_resol` is 1, the interval is fixed to its value. If size of `vacuum_resol` is 2, the interval is `vacuum_resol[0]` before `mid_point`, otherwise `vacuum_resol[1]` after `mid_point`. | **millers** | List of list of Integer | [[1,0,0]] | Miller indices. | relax_incar | String | "....../INCAR" | Path of INCAR for relaxation in VASP. **Necessary** if `stages` include 1. diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index f032bf0ad..4347eb321 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -251,7 +251,7 @@ def _put_files(self, try: sftp.put(from_f, to_f) except FileNotFoundError: - raise RuntimeError("from %s to %s Error!"%(from_f,to_f)) + raise FileNotFoundError("from %s to %s Error!"%(from_f,to_f)) # remote extract self.block_checkcall('tar xf %s' % of) # clean up From c5db9e444d4031dd7359c8d72b9e0c90a5a1a546 Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 15 May 2020 14:38:29 +0800 Subject: [PATCH 192/201] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9901f4fe2..ebd140be4 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ The bold notation of key (such as **Elements**) means that it's a necessary key. | **layer_numb** | Integer | 3 | Number of equavilent layers of slab. | **z__min** | Float | 9.0 | Thickness of slab without vacuum (Angstrom). If the `layer_numb` and `z_min` are all setted, the `z_min` value will be ignored. | **vacuum_max** | Float | 9 | Maximal thickness of vacuum (Angstrom). -| vacuum_min | Float | 2xR_(atom) | Minimal thickness of vacuum (Angstrom). +| vacuum_min | Float | 2xR_(atom) | Minimal thickness of vacuum (Angstrom). Default value is 2 times atomic radius. | **vacuum_resol** | List of float | [0.5, 1 ] | Interval of thichness of vacuum. If size of `vacuum_resol` is 1, the interval is fixed to its value. If size of `vacuum_resol` is 2, the interval is `vacuum_resol[0]` before `mid_point`, otherwise `vacuum_resol[1]` after `mid_point`. | **millers** | List of list of Integer | [[1,0,0]] | Miller indices. | relax_incar | String | "....../INCAR" | Path of INCAR for relaxation in VASP. **Necessary** if `stages` include 1. From db780f7362a249aa0c7516113213654c49c31ddc Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 15 May 2020 14:44:16 +0800 Subject: [PATCH 193/201] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebd140be4..4a9104c74 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ The bold notation of key (such as **Elements**) means that it's a necessary key. | **layer_numb** | Integer | 3 | Number of equavilent layers of slab. | **z__min** | Float | 9.0 | Thickness of slab without vacuum (Angstrom). If the `layer_numb` and `z_min` are all setted, the `z_min` value will be ignored. | **vacuum_max** | Float | 9 | Maximal thickness of vacuum (Angstrom). -| vacuum_min | Float | 2xR_(atom) | Minimal thickness of vacuum (Angstrom). Default value is 2 times atomic radius. +| vacuum_min | Float | 3.0 | Minimal thickness of vacuum (Angstrom). Default value is 2 times atomic radius. | **vacuum_resol** | List of float | [0.5, 1 ] | Interval of thichness of vacuum. If size of `vacuum_resol` is 1, the interval is fixed to its value. If size of `vacuum_resol` is 2, the interval is `vacuum_resol[0]` before `mid_point`, otherwise `vacuum_resol[1]` after `mid_point`. | **millers** | List of list of Integer | [[1,0,0]] | Miller indices. | relax_incar | String | "....../INCAR" | Path of INCAR for relaxation in VASP. **Necessary** if `stages` include 1. From 442e86f59cd5662922cefd4db901ccd669ac9eea Mon Sep 17 00:00:00 2001 From: haidi Date: Fri, 15 May 2020 14:51:27 +0800 Subject: [PATCH 194/201] update README.md --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/README.md b/README.md index 4a9104c74..044ef0cf0 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,52 @@ Following is an example for `PARAM`, which generates data from a typical structu } ``` +Another example is `from_poscar` method. Here you need to specify the POSCAR file. + +``` +{ + "stages": [ + 1, + 2 + ], + "cell_type": "fcc", + "from_poscar": true, + "from_poscar_path": "POSCAR", + "super_cell": [ + 1, + 1, + 1 + ], + "layer_numb": 3, + "vacuum_max": 5, + "vacuum_resol": [0.5,2], + "mid_point": 2.0, + "millers": [ + [ + 1, + 0, + 0 + ] + ], + "elements": [ + "Al" + ], + "potcars": [ + "./POTCAR" + ], + "relax_incar" : "INCAR_metal_rlx_low", + "scale": [ + 1.0 + ], + "skip_relax": true, + "pert_numb": 5, + "pert_box": 0.03, + "pert_atom": 0.01, + "coll_ndata": 5000, + "_comment": "that's all" +} +``` + The following table gives explicit descriptions on keys in `PARAM`. The bold notation of key (such as **Elements**) means that it's a necessary key. From 77aa3766eea3aad3e52ba71e1659065da98d93fe Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Fri, 15 May 2020 03:22:00 -0400 Subject: [PATCH 195/201] restart training if interrupted --- dpgen/generator/run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 92616e080..581631d04 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -456,6 +456,7 @@ def run_train (iter_index, command = '%s train %s' % (train_command, train_input_file) if training_init_model: command += ' --init-model old/model.ckpt' + command = "{ if [ ! -f model.ckpt.index ]; then %s; else %s --restart model.ckpt; fi }" % (command, command) commands.append(command) command = '%s freeze' % train_command commands.append(command) From 370c74c738505f0c858fdea20052890e64ca4ed5 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 16 May 2020 05:07:38 -0400 Subject: [PATCH 196/201] fix: nve doesn't set the initial temperature --- dpgen/generator/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 92616e080..ca8d7aa30 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -564,7 +564,7 @@ def parse_cur_job(cur_job) : if 'npt' in ensemble : temps = _get_param_alias(cur_job, ['Ts','temps']) press = _get_param_alias(cur_job, ['Ps','press']) - elif 'nvt' == ensemble : + elif 'nvt' == ensemble or 'nve' == ensemble: temps = _get_param_alias(cur_job, ['Ts','temps']) nsteps = _get_param_alias(cur_job, ['nsteps']) trj_freq = _get_param_alias(cur_job, ['t_freq', 'trj_freq','traj_freq']) From adabffb35894f44b67d928a67d0b7ebc7e127113 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 16 May 2020 22:57:04 -0400 Subject: [PATCH 197/201] Update dpgen/generator/run.py --- dpgen/generator/run.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dpgen/generator/run.py b/dpgen/generator/run.py index 581631d04..caf9e736c 100644 --- a/dpgen/generator/run.py +++ b/dpgen/generator/run.py @@ -455,8 +455,9 @@ def run_train (iter_index, assert(train_command) command = '%s train %s' % (train_command, train_input_file) if training_init_model: - command += ' --init-model old/model.ckpt' - command = "{ if [ ! -f model.ckpt.index ]; then %s; else %s --restart model.ckpt; fi }" % (command, command) + command = "{ if [ ! -f model.ckpt.index ]; then %s --init-model old/model.ckpt; else %s --restart model.ckpt; fi }" % (command, command) + else: + command = "{ if [ ! -f model.ckpt.index ]; then %s; else %s --restart model.ckpt; fi }" % (command, command) commands.append(command) command = '%s freeze' % train_command commands.append(command) From 8ff33e6c2a0f22f5a19e01f7d1fa712027751027 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 25 May 2020 03:18:54 -0400 Subject: [PATCH 198/201] should be nj>task_max, not nj task_max def _make_squeue(self,mdata1, res): ret = '' From 0888c42d13ebef763b21a941d8a65aff591d3b46 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 25 May 2020 03:59:42 -0400 Subject: [PATCH 199/201] should be >= --- dpgen/dispatcher/Slurm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpgen/dispatcher/Slurm.py b/dpgen/dispatcher/Slurm.py index ad1709d2a..ff4acd063 100644 --- a/dpgen/dispatcher/Slurm.py +++ b/dpgen/dispatcher/Slurm.py @@ -185,7 +185,7 @@ def _check_sub_limit(self, task_max, **kwarg) : username = getpass.getuser() stdin, stdout, stderr = self.context.block_checkcall('squeue -u %s -h' % username) nj = len(stdout.readlines()) - return nj > task_max + return nj >= task_max def _make_squeue(self,mdata1, res): ret = '' From 0c32aaaafb0d1c0d29025afacb04cd863926b96c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 2 Jun 2020 07:02:36 -0400 Subject: [PATCH 200/201] retry slurm commands when failing In my experience, sometimes dpgen fails to execute slurm commands. In this commit, dpgen will sleep 60 s and retry for at most 3 times. --- dpgen/dispatcher/SSHContext.py | 10 +++++++++- dpgen/dispatcher/Slurm.py | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dpgen/dispatcher/SSHContext.py b/dpgen/dispatcher/SSHContext.py index 4347eb321..a6ac3c1d4 100644 --- a/dpgen/dispatcher/SSHContext.py +++ b/dpgen/dispatcher/SSHContext.py @@ -148,11 +148,19 @@ def download(self, os.chdir(cwd) def block_checkcall(self, - cmd) : + cmd, + retry=0) : self.ssh_session.ensure_alive() stdin, stdout, stderr = self.ssh.exec_command(('cd %s ;' % self.remote_root) + cmd) exit_status = stdout.channel.recv_exit_status() if exit_status != 0: + if retry<3: + # sleep 60 s + dlog.warning("Get error code %d in calling %s through ssh with job: %s . message: %s" % + (exit_status, cmd, self.job_uuid, stderr.read().decode('utf-8'))) + dlog.warning("Sleep 60 s and retry the command...") + time.sleep(60) + return self.block_checkcall(cmd, retry=retry+1) raise RuntimeError("Get error code %d in calling %s through ssh with job: %s . message: %s" % (exit_status, cmd, self.job_uuid, stderr.read().decode('utf-8'))) return stdin, stdout, stderr diff --git a/dpgen/dispatcher/Slurm.py b/dpgen/dispatcher/Slurm.py index c2595d6e2..9d409dbcc 100644 --- a/dpgen/dispatcher/Slurm.py +++ b/dpgen/dispatcher/Slurm.py @@ -149,7 +149,7 @@ def _get_job_id(self) : else: return "" - def _check_status_inner(self, job_id): + def _check_status_inner(self, job_id, retry=0): ret, stdin, stdout, stderr\ = self.context.block_call ('squeue -o "%.18i %.2t" -j ' + job_id) if (ret != 0) : @@ -160,6 +160,11 @@ def _check_status_inner(self, job_id): else : return JobStatus.terminated else : + # retry 3 times + if retry < 3: + # rest 60s + time.sleep(60) + return self._check_status_inner(job_id, retry=retry+1) raise RuntimeError\ ("status command squeue fails to execute\nerror message:%s\nreturn code %d\n" % (err_str, ret)) status_line = stdout.read().decode('utf-8').split ('\n')[-2] From 0b48c9c0e11073ab1e2818bc1d1db4e4202361f0 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Wed, 3 Jun 2020 14:17:48 +0800 Subject: [PATCH 201/201] fix bug in collect: set size should be equal to number of frames --- dpgen/collect/collect.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dpgen/collect/collect.py b/dpgen/collect/collect.py index 91d13b857..a8d5b4060 100644 --- a/dpgen/collect/collect.py +++ b/dpgen/collect/collect.py @@ -78,7 +78,8 @@ def collect_data(target_folder, param_file, output, # dump iter data for kk in coll_data.keys(): out_dir = 'sys.%s' % kk - coll_data[kk].to('deepmd/npy', os.path.join(output, out_dir)) + nframes = coll_data[kk].get_nframes() + coll_data[kk].to('deepmd/npy', os.path.join(output, out_dir), set_size = nframes) def gen_collect(args): collect_data(args.JOB_DIR, args.parameter, args.OUTPUT,