add a custom python script to check the completeness of tests inputs

This commit is contained in:
Axel Kohlmeyer
2020-08-03 00:12:51 -04:00
parent 75dcd9ae29
commit 896a08883b
2 changed files with 235 additions and 0 deletions

View File

@ -0,0 +1,218 @@
#!/usr/bin/env python3
from __future__ import print_function
from glob import glob
from argparse import ArgumentParser
import os, re, sys
parser = ArgumentParser(prog='check_tests.py',
description="Check force tests for completeness")
parser.add_argument("-v", "--verbose",
action='store_const',
const=True, default=False,
help="Enable verbose output")
parser.add_argument("-t", "--tests",
help="Path to LAMMPS test YAML format input files")
parser.add_argument("-s", "--src",
help="Path to LAMMPS sources")
args = parser.parse_args()
verbose = args.verbose
src = args.src
tests = args.tests
if not src:
src = os.path.join('.','..','..','src')
if not tests:
tests = os.path.join(src,'..','unittest','force-styles','tests')
try:
src = os.path.abspath(os.path.expanduser(src))
tests = os.path.abspath(os.path.expanduser(tests))
except:
parser.print_help()
sys.exit(1)
if not os.path.isdir(src):
sys.exit("LAMMPS source path %s does not exist" % src)
if not os.path.isdir(tests):
sys.exit("LAMMPS test inputs path %s does not exist" % tests)
headers = glob(os.path.join(src, '*', '*.h'))
headers += glob(os.path.join(src, '*.h'))
angle = {}
bond = {}
compute = {}
dihedral = {}
dump = {}
fix = {}
improper = {}
kspace = {}
pair = {}
total = 0
upper = re.compile("[A-Z]+")
gpu = re.compile("(.+)/gpu$")
intel = re.compile("(.+)/intel$")
kokkos = re.compile("(.+)/kk$")
kokkos_skip = re.compile("(.+)/kk/(host|device)$")
omp = re.compile("(.+)/omp$")
opt = re.compile("(.+)/opt$")
removed = re.compile("(.*)Deprecated$")
def register_style(list,style,info):
if style in list.keys():
list[style]['gpu'] += info['gpu']
list[style]['intel'] += info['intel']
list[style]['kokkos'] += info['kokkos']
list[style]['omp'] += info['omp']
list[style]['opt'] += info['opt']
list[style]['removed'] += info['removed']
else:
list[style] = info
def add_suffix(list,style):
suffix = ""
if list[style]['gpu']:
suffix += 'g'
if list[style]['intel']:
suffix += 'i'
if list[style]['kokkos']:
suffix += 'k'
if list[style]['omp']:
suffix += 'o'
if list[style]['opt']:
suffix += 't'
if suffix:
return style + ' (' + suffix + ')'
else:
return style
def check_style(file,dir,pattern,list,name,suffix=False,skip=()):
f = os.path.join(dir, file)
fp = open(f)
text = fp.read()
fp.close()
matches = re.findall(pattern,text,re.MULTILINE)
counter = 0
for c in list.keys():
# known undocumented aliases we need to skip
if c in skip: continue
s = c
if suffix: s = add_suffix(list,c)
if not s in matches:
if not list[c]['removed']:
print("%s style entry %s" % (name,s),
"is missing or incomplete in %s" % file)
counter += 1
return counter
print("Parsing style names from C++ tree in: ",src)
for h in headers:
if verbose: print("Checking ", h)
fp = open(h)
text = fp.read()
fp.close()
matches = re.findall("(.+)Style\((.+),(.+)\)",text,re.MULTILINE)
for m in matches:
# skip over internal styles w/o explicit documentation
style = m[1]
total += 1
if upper.match(style):
continue
# detect, process, and flag suffix styles:
info = { 'kokkos': 0, 'gpu': 0, 'intel': 0, \
'omp': 0, 'opt': 0, 'removed': 0 }
suffix = kokkos_skip.match(style)
if suffix:
continue
suffix = gpu.match(style)
if suffix:
style = suffix.groups()[0]
info['gpu'] = 1
suffix = intel.match(style)
if suffix:
style = suffix.groups()[0]
info['intel'] = 1
suffix = kokkos.match(style)
if suffix:
style = suffix.groups()[0]
info['kokkos'] = 1
suffix = omp.match(style)
if suffix:
style = suffix.groups()[0]
info['omp'] = 1
suffix = opt.match(style)
if suffix:
style = suffix.groups()[0]
info['opt'] = 1
deprecated = removed.match(m[2])
if deprecated:
info['removed'] = 1
# register style and suffix flags
if m[0] == 'Angle':
register_style(angle,style,info)
elif m[0] == 'Bond':
register_style(bond,style,info)
elif m[0] == 'Dihedral':
register_style(dihedral,style,info)
elif m[0] == 'Improper':
register_style(improper,style,info)
elif m[0] == 'KSpace':
register_style(kspace,style,info)
elif m[0] == 'Pair':
register_style(pair,style,info)
counter = 0
def check_tests(name,list,yaml,search,skip=()):
num = 0
yaml_files = glob(os.path.join(tests, yaml))
styles = []
missing = []
for y in yaml_files:
if verbose: print("Checking: ",y)
fp = open(y)
text = fp.read()
fp.close()
matches = re.findall(search,text,re.MULTILINE)
for m in matches:
styles.append(m)
for s in list.keys():
# known undocumented aliases we need to skip
if s in skip: continue
if not s in styles:
if not list[s]['removed']:
if verbose: print("No test for %s style %s" % (name,s))
num += 1
missing.append(s)
total = len(list)
print("\nTests for %s styles: %d of %d" % (name,total - num, total))
print("No tests for: ", missing)
return num
counter += check_tests('pair',pair,'*-pair-*.yaml',
'.*pair_style:\s*(\S+).*',skip=('meam','lj/sf'))
counter += check_tests('bond',bond,'bond-*.yaml',
'.*bond_style:\s*(\S+).*')
counter += check_tests('angle',angle,'angle-*.yaml',
'.*angle_style:\s*(\S+).*')
counter += check_tests('dihedral',dihedral,'dihedral-*.yaml',
'.*dihedral_style:\s*(\S+).*')
counter += check_tests('improper',improper,'improper-*.yaml',
'.*improper_style:\s*(\S+).*')
counter += check_tests('kspace',kspace,'kspace-*.yaml',
'.*kspace_style\s*(\S+).*')
total = len(pair)+len(bond)+len(angle)+len(dihedral)+len(improper)+len(kspace)
print("\nTotal tests missing: %d of %d" % (total - counter, total))