Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nexus: better guard for structure file and internal consistency #262

Merged
merged 1 commit into from
Jun 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions nexus/library/numerics.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,11 @@ def morse_reduced_mass(m1,m2=None):
m1 = ptable[m1].atomic_weight.me
#end if
if m2 is None:
m = m1
else:
if isinstance(m2,str):
m2 = ptable[m2].atomic_weight.me
#end if
m = 1./(1./m1+1./m2) # reduced mass
m2 = m1
elif isinstance(m2,str):
m2 = ptable[m2].atomic_weight.me
#end if
m = 1./(1./m1+1./m2) # reduced mass
return m
#end def morse_reduced_mass

Expand Down
63 changes: 54 additions & 9 deletions nexus/library/physical_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,18 +396,50 @@ def pseudize(self,**valency):
#end def pseudize


def check_folded_system(self):
def check_folded_system(self,exit=True,message=False):
msg = ''
sys_folded = self.folded_system!=None
struct_folded = self.structure.folded_structure!=None
if sys_folded!=struct_folded:
self.error('folding of physical system and structure is not consistent\n system folded: {0}\n structure folded: {1}'.format(sys_folded,struct_folded))
msg+='folding of physical system and structure is not consistent\nsystem folded: {0}\nstructure folded: {1}\n'.format(sys_folded,struct_folded)
#end if
if sys_folded and id(self.structure.folded_structure)!=id(self.folded_system.structure):
self.error('structure of folded system and folded structure are distinct\n this is not allowed and may be a developer error')
msg+='structure of folded system and folded structure are distinct\nthis is not allowed and may be a developer error'
#end if
success = len(msg)==0
if not success and exit:
self.error(msg)
#end if
if not message:
return success
else:
return success,msg
#end if
#end def check_folded_system


def check_consistent(self,tol=1e-8,exit=True,message=False):
fs,fm = self.check_folded_system(exit=False,message=True)
cs,cm = self.structure.check_consistent(tol,exit=False,message=True)
msg = ''
if not fs:
msg += fm+'\n'
#end if
if not cs:
msg += cm+'\n'
#end if
consistent = len(msg)==0
if not consistent and exit:
self.error(msg)
#end if
if not message:
return consistent
else:
return consistent,msg
#end if
#end def check_consistent


def change_units(self,units):
self.structure.change_units(units,folded=False)
if self.folded_system!=None:
Expand Down Expand Up @@ -623,14 +655,27 @@ def generate_physical_system(**kwargs):
if 'structure' in kwargs:
s = kwargs['structure']
is_str = isinstance(s,str)
if is_str and os.path.exists(s):# and '.' in os.path.split(s)[1]:
if 'elem' in kwargs:
kwargs['structure'] = read_structure(s,elem=kwargs['elem'])
if is_str:
if os.path.exists(s):
if 'elem' in kwargs:
kwargs['structure'] = read_structure(s,elem=kwargs['elem'])
else:
kwargs['structure'] = read_structure(s)
#end if
else:
kwargs['structure'] = read_structure(s)
slow = s.lower()
format = None
if '.' in slow:
format = slow.rsplit('.')[1]
elif 'poscar' in slow:
format = 'poscar'
#end if
is_path = '/' in s
is_file = format in set('xyz xsf poscar cif fhi-aims'.split())
if is_path or is_file:
PhysicalSystem.class_error('user provided structure file does not exist\nstructure file path: '+s,'generate_physical_system')
#end if
#end if
elif is_str and '/' in s:
PhysicalSystem.class_error('path provided for structure file does not exist: '+s,'generate_physical_system')
#end if
#end if

Expand Down
79 changes: 67 additions & 12 deletions nexus/library/pwscf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,57 @@
from debug import ci


unique_vdw_functionals = [
'optb86b-vdw',
'vdw-df3', # optB88+vdW
'vdw-df',
'vdw-df2',
'rev-vdw-df2',
'vdw-df-c09',
'vdw-df2-c09',
'rvv10',
]
repeat_vdw_functionals = [
'vdw-df4', # 'optB86b-vdW'
]
unique_functionals = [
'revpbe',
'pw86pbe',
'b86bpbe',
'pbe0',
'hse',
'gaup',
'pbesol',
'pbeq2d',
'optbk88',
'optb86b',
'pbe',
'wc',
'b3lyp',
'pbc',
'bp',
'pw91',
'hcth',
'olyp',
'tpss',
'oep',
'hf',
'blyp',
'lda',
'sogga',
'm06l',
'ev93',
]+unique_vdw_functionals
repeat_functionals = [
'q2d', # pbeq2d
'pz', # lda
]+repeat_vdw_functionals

vdw_functionals = set(unique_vdw_functionals+repeat_vdw_functionals)
allowed_functionals = set(unique_functionals+repeat_functionals)



class Pwscf(Simulation):
input_type = PwscfInput
analyzer_type = PwscfAnalyzer
Expand All @@ -41,11 +92,11 @@ class Pwscf(Simulation):

vdw_table = None

@staticmethod
def settings(vdw_table=None):
# van der Waals family of functional require the vdW table generated by
# generate_vdW_kernel_table.x: specify 'vdw_table' in settings
Pwscf.vdw_table = vdw_table
@staticmethod
def settings(vdw_table=None):
# van der Waals family of functional require the vdW table generated by
# generate_vdW_kernel_table.x: specify 'vdw_table' in settings
Pwscf.vdw_table = vdw_table
#end def settings

#def propagate_identifier(self):
Expand Down Expand Up @@ -75,16 +126,20 @@ def write_prep(self):
if not os.path.exists(outdir):
os.makedirs(outdir)
#end if
#copy over vdw_table for vdW-DF functional
#copy over vdw_table for vdW-DF functional
if self.path_exists('input/system/input_dft'):
if self.input.system.input_dft.lower().startswith('vdw-df'):
functional = self.input.system.input_dft.lower()
if '+' not in functional and functional not in allowed_functionals:
self.warn('functional "{0}" is unknown to pwscf'.format(functional))
#end if
if functional in vdw_functionals:
if self.vdw_table is None:
self.error('attempting to run vdW-DF functional, but vdw_table is missing\nplease provide path to table file via "vdw_table" parameter in settings')
self.error('attempting to run vdW functional "{0}", but vdw_table is missing\nplease provide path to table file via "vdw_table" parameter in settings'.format(functional))
#end if
cd_rel = os.path.relpath(self.vdw_table,self.locdir)
# copy instead of link to vdw_table to avoid file-lock from multiple pw.x instances
cp_cmd = 'cd '+self.locdir+';cp '+cd_rel+' .'
os.system(cp_cmd)
cd_rel = os.path.relpath(self.vdw_table,self.locdir)
# copy instead of link to vdw_table to avoid file-lock from multiple pw.x instances
cp_cmd = 'cd '+self.locdir+';cp '+cd_rel+' .'
os.system(cp_cmd)
#end if
#end if
#end def write_prep
Expand Down
5 changes: 5 additions & 0 deletions nexus/library/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,11 @@ def set(self,**kw):
#end if
if isinstance(self.system,PhysicalSystem):
self.system = self.system.copy()
consistent,msg = self.system.check_consistent(exit=False,message=True)
if not consistent:
locdir = os.path.join(nexus_core.local_directory,nexus_core.runs,self.path)
self.error('user provided physical system is not internally consistent\nsimulation identifier: {0}\nlocal directory: {1}\nmore details on the user error are given below\n\n{2}'.format(self.identifier,locdir,msg))
#end if
elif self.system!=None:
self.error('system must be a PhysicalSystem object\nyou provided an object of type: {0}'.format(self.system.__class__.__name__))
#end if
Expand Down
27 changes: 25 additions & 2 deletions nexus/library/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,26 @@ def __init__(self,axes=None,scale=1.,elem=None,pos=None,mag=None,
#end def __init__


def check_consistent(self,tol=1e-8,exit=True,message=False):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'message' flag doesn't seem necessary, and could be removed to simplify the function. The function can always return (consistent,msg). If the caller doesn't want the message, it can ignore it (a single underscore is a valid variable name, so it's often used to represent an unwanted return value, giving something like "consistent, _ = self.systemcheck_consistent(exit=False)"

Similarly for check_folded_system.

Copy link
Contributor Author

@jtkrogel jtkrogel Jun 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true, but it can help the UI. The "message" part is only really relevant to a message reporting system; most of the time someone writing a nexus user script will use s.check_consistent(), or "if s.check_consistent(exit=False): ...". There are a few user facing functions elsewhere in nexus that allow variable output lists depending on user request.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I didn't realize this was a user-facing function, but that makes sense.

msg = ''
if self.has_axes():
kaxes = 2*pi*inv(self.axes).T
abs_diff = abs(self.kaxes-kaxes).sum()
if abs_diff>tol:
msg += 'direct and reciprocal space axes are not consistent\naxes present:\n{0}\nkaxes present:\n{1}\nconsistent kaxes:\n{2}\nabsolute difference: {3}\n'.format(self.axes,self.kaxes,kaxes,abs_diff)
#end if
#end if
consistent = len(msg)==0
if not consistent and exit:
self.error(msg)
#end if
if not message:
return consistent
else:
return consistent,msg
#end if
#end def check_consistent


def set_axes(self,axes):
self.reset_axes(axes)
Expand Down Expand Up @@ -3181,8 +3201,11 @@ def add_kmesh(self,kgrid,kshift=None,unique=False):
#end def add_kmesh


def kpoints_unit(self):
return dot(self.kpoints,inv(self.kaxes))
def kpoints_unit(self,kpoints=None):
if kpoints is None:
kpoints = self.kpoints
#end if
return dot(kpoints,inv(self.kaxes))
#end def kpoints_unit


Expand Down