Skip to content

Commit

Permalink
Merge pull request #353 from deepmodeling/devel
Browse files Browse the repository at this point in the history
merge recent development on devel to master
  • Loading branch information
amcadmus authored Feb 23, 2021
2 parents f8d70a4 + b73a49e commit 769ccf4
Show file tree
Hide file tree
Showing 89 changed files with 43,115 additions and 4,877 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Python package

on:
- push
- pull_request

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8]
PYMATGEN_VERSION: [2019.1.13, 2019.7.30]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install coverage codecov pymatgen==${{ matrix.PYMATGEN_VERSION }} .
- name: Test
run: coverage run --source=./dpgen -m unittest -v && coverage report
- run: codecov
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ dpgen.egg-info
.eggs
.coverage
dbconfig.json
.vscode/*
.vscode/*
.idea/*
19 changes: 0 additions & 19 deletions .travis.yml

This file was deleted.

48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,8 @@ The bold notation of key (such aas **type_map**) means that it's a necessary key
|**fp_params["mixingweight"]** | Float| 0.05 | Proportion a of output Density Matrix to be used for the input Density Matrix of next SCF cycle (linear mixing).
|**fp_params["NumberPulay"]** | Integer| 5 | Controls the Pulay convergence accelerator.
| *fp_style == cp2k*
| **fp_params** | Dict | |Parameters for cp2k calculation. find detail in manual.cp2k.org. only the kind section must be set before use. we assume that you have basic knowledge for cp2k input.
| **user_fp_params** | Dict | |Parameters for cp2k calculation. find detail in manual.cp2k.org. only the kind section must be set before use. we assume that you have basic knowledge for cp2k input.
| **external_input_path** | String | | Conflict with key:user_fp_params, use the template input provided by user, some rules should be followed, read the following text in detail.


#### Rules for cp2k input at dictionary form
Expand Down Expand Up @@ -609,9 +610,54 @@ Here are examples for setting:
}
}
}
```

#### Rules for use cp2k template input provided by user

See Full example template.inp and dpgen input parameter file in

`tests/generator/cp2k_make_fp_files/exinput/template.inp` and `tests/generator/param-mgo-cp2k-exinput.json`

Here is example for provide external input

```python
{
"_comment": " 02.fp ",
"fp_style": "cp2k",
"shuffle_poscar": false,
"fp_task_max": 100,
"fp_task_min": 10,
"fp_pp_path": ".",
"fp_pp_files": [],
"external_input_path": "./cp2k_make_fp_files/exinput/template.inp",
"_comment": " that's all
}
```

the following essential section should be provided in user template

```
&FORCE_EVAL
# add this line if you need to fit virial
STRESS_TENSOR ANALYTICAL
&PRINT
&FORCES ON
&END FORCES
# add this line if you need to fit virial
&STRESS_TENSOR ON
&END FORCES
&END PRINT
&SUBSYS
&CELL
ABC LEFT FOR DPGEN
&END CELL
&COORD
@include coord.xyz
&END COORD
&END SUBSYS
&END FORCE_EVAL
```

## Test: Auto-test for Deep Generator
### configure and param.json
Expand Down
2 changes: 1 addition & 1 deletion dpgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
SHORT_CMD="dpgen"
dlog = logging.getLogger(__name__)
dlog.setLevel(logging.INFO)
dlogf = logging.FileHandler(os.getcwd()+os.sep+SHORT_CMD+'.log')
dlogf = logging.FileHandler(os.getcwd()+os.sep+SHORT_CMD+'.log', delay=True)
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)
Expand Down
187 changes: 187 additions & 0 deletions dpgen/auto_test/EOS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import glob
import json
import os
import re

import numpy as np
from monty.serialization import loadfn, dumpfn

import dpgen.auto_test.lib.vasp as vasp
from dpgen import dlog
from dpgen.auto_test.Property import Property
from dpgen.auto_test.refine import make_refine
from dpgen.auto_test.reproduce import make_repro
from dpgen.auto_test.reproduce import post_repro


class EOS(Property):
def __init__(self,
parameter):
parameter['reproduce'] = parameter.get('reproduce', False)
self.reprod = parameter['reproduce']
if not self.reprod:
if not ('init_from_suffix' in parameter and 'output_suffix' in parameter):
self.vol_start = parameter['vol_start']
self.vol_end = parameter['vol_end']
self.vol_step = parameter['vol_step']
parameter['cal_type'] = parameter.get('cal_type', 'relaxation')
self.cal_type = parameter['cal_type']
default_cal_setting = {"relax_pos": True,
"relax_shape": True,
"relax_vol": False}
if 'cal_setting' not in parameter:
parameter['cal_setting'] = default_cal_setting
else:
if "relax_pos" not in parameter['cal_setting']:
parameter['cal_setting']['relax_pos'] = default_cal_setting['relax_pos']
if "relax_shape" not in parameter['cal_setting']:
parameter['cal_setting']['relax_shape'] = default_cal_setting['relax_shape']
if "relax_vol" not in parameter['cal_setting']:
parameter['cal_setting']['relax_vol'] = default_cal_setting['relax_vol']
self.cal_setting = parameter['cal_setting']
else:
parameter['cal_type'] = 'static'
self.cal_type = parameter['cal_type']
default_cal_setting = {"relax_pos": False,
"relax_shape": False,
"relax_vol": False}
if 'cal_setting' not in parameter:
parameter['cal_setting'] = default_cal_setting
else:
if "relax_pos" not in parameter['cal_setting']:
parameter['cal_setting']['relax_pos'] = default_cal_setting['relax_pos']
if "relax_shape" not in parameter['cal_setting']:
parameter['cal_setting']['relax_shape'] = default_cal_setting['relax_shape']
if "relax_vol" not in parameter['cal_setting']:
parameter['cal_setting']['relax_vol'] = default_cal_setting['relax_vol']
self.cal_setting = parameter['cal_setting']
parameter['init_from_suffix'] = parameter.get('init_from_suffix', '00')
self.init_from_suffix = parameter['init_from_suffix']
self.parameter = parameter

def make_confs(self,
path_to_work,
path_to_equi,
refine=False):
path_to_work = os.path.abspath(path_to_work)
if os.path.exists(path_to_work):
dlog.warning('%s already exists' % path_to_work)
else:
os.makedirs(path_to_work)
path_to_equi = os.path.abspath(path_to_equi)

if 'start_confs_path' in self.parameter and os.path.exists(self.parameter['start_confs_path']):
init_path_list = glob.glob(os.path.join(self.parameter['start_confs_path'], '*'))
struct_init_name_list = []
for ii in init_path_list:
struct_init_name_list.append(ii.split('/')[-1])
struct_output_name = path_to_work.split('/')[-2]
assert struct_output_name in struct_init_name_list
path_to_equi = os.path.abspath(os.path.join(self.parameter['start_confs_path'],
struct_output_name, 'relaxation', 'relax_task'))

cwd = os.getcwd()
task_list = []
if self.reprod:
print('eos reproduce starts')
if 'init_data_path' not in self.parameter:
raise RuntimeError("please provide the initial data path to reproduce")
init_data_path = os.path.abspath(self.parameter['init_data_path'])
task_list = make_repro(init_data_path, self.init_from_suffix,
path_to_work, self.parameter.get('reprod_last_frame', True))
os.chdir(cwd)

else:
if refine:
print('eos refine starts')
task_list = make_refine(self.parameter['init_from_suffix'],
self.parameter['output_suffix'],
path_to_work)
os.chdir(cwd)

init_from_path = re.sub(self.parameter['output_suffix'][::-1],
self.parameter['init_from_suffix'][::-1],
path_to_work[::-1], count=1)[::-1]
task_list_basename = list(map(os.path.basename, task_list))

for ii in task_list_basename:
init_from_task = os.path.join(init_from_path, ii)
output_task = os.path.join(path_to_work, ii)
os.chdir(output_task)
if os.path.isfile('eos.json'):
os.remove('eos.json')
if os.path.islink('eos.json'):
os.remove('eos.json')
os.symlink(os.path.relpath(os.path.join(init_from_task, 'eos.json')), 'eos.json')
os.chdir(cwd)

else:
print('gen eos from ' + str(self.vol_start) + ' to ' + str(self.vol_end) + ' by every ' + str(self.vol_step))
equi_contcar = os.path.join(path_to_equi, 'CONTCAR')
if not os.path.exists(equi_contcar):
raise RuntimeError("please do relaxation first")
vol_to_poscar = vasp.poscar_vol(equi_contcar) / vasp.poscar_natoms(equi_contcar)
self.parameter['scale2equi'] = []

task_num = 0
while self.vol_start + self.vol_step * task_num < self.vol_end:
# for vol in np.arange(int(self.vol_start * 100), int(self.vol_end * 100), int(self.vol_step * 100)):
# vol = vol / 100.0
vol = self.vol_start + task_num * self.vol_step
#task_num = int((vol - self.vol_start) / self.vol_step)
output_task = os.path.join(path_to_work, 'task.%06d' % task_num)
os.makedirs(output_task, exist_ok=True)
os.chdir(output_task)
for ii in ['INCAR', 'POTCAR', 'POSCAR.orig', 'POSCAR', 'conf.lmp', 'in.lammps']:
if os.path.exists(ii):
os.remove(ii)
task_list.append(output_task)
os.symlink(os.path.relpath(equi_contcar), 'POSCAR.orig')
# scale = (vol / vol_to_poscar) ** (1. / 3.)
scale = vol ** (1. / 3.)
eos_params = {'volume': vol * vol_to_poscar, 'scale': scale}
dumpfn(eos_params, 'eos.json', indent=4)
self.parameter['scale2equi'].append(scale) # 06/22
vasp.poscar_scale('POSCAR.orig', 'POSCAR', scale)
task_num += 1
os.chdir(cwd)
return task_list

def post_process(self, task_list):
pass

def task_type(self):
return self.parameter['type']

def task_param(self):
return self.parameter

def _compute_lower(self,
output_file,
all_tasks,
all_res):
output_file = os.path.abspath(output_file)
res_data = {}
ptr_data = "conf_dir: " + os.path.dirname(output_file) + "\n"
if not self.reprod:
ptr_data += ' VpA(A^3) EpA(eV)\n'
for ii in range(len(all_tasks)):
# vol = self.vol_start + ii * self.vol_step
vol = loadfn(os.path.join(all_tasks[ii], 'eos.json'))['volume']
task_result = loadfn(all_res[ii])
res_data[vol] = task_result['energies'][-1] / task_result['atom_numbs'][0]
ptr_data += '%7.3f %8.4f \n' % (vol, task_result['energies'][-1] / task_result['atom_numbs'][0])
# res_data[vol] = all_res[ii]['energy'] / len(all_res[ii]['force'])
# ptr_data += '%7.3f %8.4f \n' % (vol, all_res[ii]['energy'] / len(all_res[ii]['force']))

else:
if 'init_data_path' not in self.parameter:
raise RuntimeError("please provide the initial data path to reproduce")
init_data_path = os.path.abspath(self.parameter['init_data_path'])
res_data, ptr_data = post_repro(init_data_path, self.parameter['init_from_suffix'],
all_tasks, ptr_data, self.parameter.get('reprod_last_frame', True))

with open(output_file, 'w') as fp:
json.dump(res_data, fp, indent=4)

return res_data, ptr_data
Loading

0 comments on commit 769ccf4

Please sign in to comment.