get-quick-list.py script is feature complete
This commit is contained in:
@ -4,13 +4,30 @@ Find all example input files containing commands changed in this branch versus d
|
|||||||
Companion script to run_tests.py regression tester.
|
Companion script to run_tests.py regression tester.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, re, sys
|
import os, re, sys, subprocess
|
||||||
from glob import glob
|
from pathlib import Path
|
||||||
import subprocess
|
|
||||||
|
|
||||||
# infer top level lammps dir
|
if sys.version_info < (3,5):
|
||||||
|
raise BaseException("Must use at least Python 3.5")
|
||||||
|
|
||||||
|
# infer top level LAMMPS dir
|
||||||
LAMMPS_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
LAMMPS_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def changed_files_from_git(branch='develop'):
|
||||||
|
"""
|
||||||
|
Return list of changed file from git.
|
||||||
|
|
||||||
|
This function queries git to return the list of changed files on
|
||||||
|
the current branch relative to a given branch (default is 'develop').
|
||||||
|
|
||||||
|
param branch: branch to compare with
|
||||||
|
type branch: string
|
||||||
|
return: path names of files with changes relative to the repository root
|
||||||
|
rtype: list of strings
|
||||||
|
"""
|
||||||
|
|
||||||
# get list of changed files relative to the develop branch from git
|
# get list of changed files relative to the develop branch from git
|
||||||
output = None
|
output = None
|
||||||
try:
|
try:
|
||||||
@ -20,31 +37,55 @@ except:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# collect header files to check for styles
|
# collect header files to check for styles
|
||||||
|
# - skip files that don't end in '.h' or '.cpp'
|
||||||
|
# - skip paths that don't start with 'src/'
|
||||||
|
# - replace '.cpp' with '.h' w/o checking it exists
|
||||||
headers = []
|
headers = []
|
||||||
|
# output will have a letter 'A' or 'M' for added or modified files followed by pathname
|
||||||
|
# append iterms to list and return it
|
||||||
if output:
|
if output:
|
||||||
for changed in output.stdout.decode().split():
|
for changed in output.stdout.decode().split():
|
||||||
if (changed == 'A') or (changed == 'M'): continue
|
if (changed == 'A') or (changed == 'M'): continue
|
||||||
if not changed.startswith('src/'): continue
|
if not changed.startswith('src/'): continue
|
||||||
if changed.endswith('.h'): headers.append(changed)
|
if changed.endswith('.h'): headers.append(changed)
|
||||||
if changed.endswith('.cpp'): headers.append(changed.replace('.cpp','.h'))
|
if changed.endswith('.cpp'): headers.append(changed.replace('.cpp','.h'))
|
||||||
|
return headers
|
||||||
|
|
||||||
# now loop over header files, search for XxxxStyle() macros and append style name
|
# ----------------------------------------------------------------------
|
||||||
command = []
|
|
||||||
atom = []
|
|
||||||
compute = []
|
|
||||||
fix = []
|
|
||||||
pair = []
|
|
||||||
body = []
|
|
||||||
bond = []
|
|
||||||
angle = []
|
|
||||||
dihedral = []
|
|
||||||
improper = []
|
|
||||||
kspace = []
|
|
||||||
dump = []
|
|
||||||
region = []
|
|
||||||
integrate = []
|
|
||||||
minimize = []
|
|
||||||
|
|
||||||
|
def get_command_from_header(headers, topdir="."):
|
||||||
|
"""
|
||||||
|
Loop over list of header files and extract style names, if present.
|
||||||
|
|
||||||
|
LAMMPS commands have macros XxxxStyle() that connects a string with a class.
|
||||||
|
We search the header files for those macros, extract the string and append
|
||||||
|
it to a list in a dictionary of different types of styles. We skip over known
|
||||||
|
suffixes and deprecated commands.
|
||||||
|
|
||||||
|
param headers: header files to check for commands
|
||||||
|
type headers:
|
||||||
|
return: dictionary with lists of style names
|
||||||
|
rtype: dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
styles = {}
|
||||||
|
styles['command'] = []
|
||||||
|
styles['atom'] = []
|
||||||
|
styles['compute'] = []
|
||||||
|
styles['fix'] = []
|
||||||
|
styles['pair'] = []
|
||||||
|
styles['body'] = []
|
||||||
|
styles['bond'] = []
|
||||||
|
styles['angle'] = []
|
||||||
|
styles['dihedral'] = []
|
||||||
|
styles['improper'] = []
|
||||||
|
styles['kspace'] = []
|
||||||
|
styles['dump'] = []
|
||||||
|
styles['region'] = []
|
||||||
|
styles['integrate'] = []
|
||||||
|
styles['minimize'] = []
|
||||||
|
|
||||||
|
# some regex
|
||||||
style_pattern = re.compile(r"(.+)Style\((.+),(.+)\)")
|
style_pattern = re.compile(r"(.+)Style\((.+),(.+)\)")
|
||||||
upper = re.compile("[A-Z]+")
|
upper = re.compile("[A-Z]+")
|
||||||
gpu = re.compile("(.+)/gpu$")
|
gpu = re.compile("(.+)/gpu$")
|
||||||
@ -56,7 +97,9 @@ opt = re.compile("(.+)/opt$")
|
|||||||
removed = re.compile("(.*)Deprecated$")
|
removed = re.compile("(.*)Deprecated$")
|
||||||
|
|
||||||
for file in headers:
|
for file in headers:
|
||||||
with open(file) as f:
|
# don't fail if file is not present
|
||||||
|
try:
|
||||||
|
with open(os.path.join(topdir,file)) as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
matches = style_pattern.findall(line)
|
matches = style_pattern.findall(line)
|
||||||
for m in matches:
|
for m in matches:
|
||||||
@ -90,65 +133,146 @@ for file in headers:
|
|||||||
|
|
||||||
# register style and suffix flags
|
# register style and suffix flags
|
||||||
if m[0] == 'Angle':
|
if m[0] == 'Angle':
|
||||||
angle.append(style)
|
styles['angle'].append(style)
|
||||||
elif m[0] == 'Atom':
|
elif m[0] == 'Atom':
|
||||||
atom.append(style)
|
styles['atom'].append(style)
|
||||||
elif m[0] == 'Body':
|
elif m[0] == 'Body':
|
||||||
register_style(body,style,info)
|
styles['body'].append(style)
|
||||||
elif m[0] == 'Bond':
|
elif m[0] == 'Bond':
|
||||||
bond.applend(style)
|
styles['bond'].applend(style)
|
||||||
elif m[0] == 'Command':
|
elif m[0] == 'Command':
|
||||||
command.append(style)
|
styles['command'].append(style)
|
||||||
elif m[0] == 'Compute':
|
elif m[0] == 'Compute':
|
||||||
compute.append(style)
|
styles['compute'].append(style)
|
||||||
elif m[0] == 'Dihedral':
|
elif m[0] == 'Dihedral':
|
||||||
dihedral.append(style)
|
styles['dihedral'].append(style)
|
||||||
elif m[0] == 'Dump':
|
elif m[0] == 'Dump':
|
||||||
dump.append(style)
|
styles['dump'].append(style)
|
||||||
elif m[0] == 'Fix':
|
elif m[0] == 'Fix':
|
||||||
fix.append(style)
|
styles['fix'].append(style)
|
||||||
elif m[0] == 'Improper':
|
elif m[0] == 'Improper':
|
||||||
improper.append(style)
|
styles['improper'].append(style)
|
||||||
elif m[0] == 'Integrate':
|
elif m[0] == 'Integrate':
|
||||||
integrate.append(style)
|
styles['integrate'].append(style)
|
||||||
elif m[0] == 'KSpace':
|
elif m[0] == 'KSpace':
|
||||||
kspace.append(style)
|
styles['kspace'].append(style)
|
||||||
elif m[0] == 'Minimize':
|
elif m[0] == 'Minimize':
|
||||||
minimize.append(style)
|
styles['minimize'].append(style)
|
||||||
elif m[0] == 'Pair':
|
elif m[0] == 'Pair':
|
||||||
pair.append(style)
|
styles['pair'].append(style)
|
||||||
elif m[0] == 'Region':
|
elif m[0] == 'Region':
|
||||||
region.append(style)
|
styles['region'].append(style)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
# header file not found or not readable
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return styles
|
||||||
|
|
||||||
if len(command):
|
# ----------------------------------------------------------------------
|
||||||
print("Commands: ", '|'.join(command))
|
|
||||||
if len(atom):
|
def make_regex(styles):
|
||||||
print("Atom styles: ", '|'.join(atom))
|
"""Convert dictionary with styles into a regular expression to scan input files with
|
||||||
if len(compute):
|
|
||||||
print("Compute styles: ", '|'.join(compute))
|
This will construct a regular expression matching LAMMPS commands. Ignores continuation
|
||||||
if len(fix):
|
|
||||||
print("Fix styles: ", '|'.join(fix))
|
param styles: dictionary with style names
|
||||||
if len(pair):
|
type styles: dict
|
||||||
print("Pair styles: ", '|'.join(pair))
|
return: compiled regular expression
|
||||||
if len(body):
|
rtype: regex
|
||||||
print("Body styles: ", '|'.join(body))
|
"""
|
||||||
if len(bond):
|
|
||||||
print("Bond styles: ", '|'.join(bond))
|
restring = "^\\s*("
|
||||||
if len(angle):
|
if len(styles['command']):
|
||||||
print("Angle styles: ", '|'.join(angle))
|
restring += '(' + '|'.join(styles['command']) + ')|'
|
||||||
if len(dihedral):
|
if len(styles['atom']):
|
||||||
print("Dihedral styles: ", '|'.join(dihedral))
|
restring += '(atom_style\\s+(' + '|'.join(styles['atom']) + '))|'
|
||||||
if len(improper):
|
if len(styles['compute']):
|
||||||
print("Improper styles: ", '|'.join(improper))
|
restring += '(compute\\s+\\S+\\s+\\S+\\s+(' + '|'.join(styles['compute']) + '))|'
|
||||||
if len(kspace):
|
if len(styles['fix']):
|
||||||
print("Kspace styles: ", '|'.join(kspace))
|
restring += '(fix\\s+\\S+\\s+\\S+\\s+(' + '|'.join(styles['fix']) + '))|'
|
||||||
if len(dump):
|
if len(styles['pair']):
|
||||||
print("Dump styles: ", '|'.join(dump))
|
restring += '(pair_style\\s+(' + '|'.join(styles['pair']) + '))|'
|
||||||
if len(region):
|
if len(styles['body']):
|
||||||
print("Region styles: ", '|'.join(region))
|
restring += '(atom_style\\s+body\\s+(' + '|'.join(styles['body']) + '))|'
|
||||||
if len(integrate):
|
if len(styles['bond']):
|
||||||
print("Integrate styles: ", '|'.join(integrate))
|
restring += '(bond_style\\s+(' + '|'.join(styles['bond']) + '))|'
|
||||||
if len(minimize):
|
if len(styles['angle']):
|
||||||
print("Minimize styles: ", '|'.join(minimize))
|
restring += '(angle_style\\s+(' + '|'.join(styles['angle']) + '))|'
|
||||||
|
if len(styles['dihedral']):
|
||||||
|
restring += '(dihedral_style\\s+(' + '|'.join(styles['dihedral']) + '))|'
|
||||||
|
if len(styles['improper']):
|
||||||
|
restring += '(improper_style\\s+(' + '|'.join(styles['improper']) + '))|'
|
||||||
|
if len(styles['kspace']):
|
||||||
|
restring += '(kspace_style\\s+(' + '|'.join(styles['kspace']) + '))|'
|
||||||
|
if len(styles['dump']):
|
||||||
|
restring += '(dump\\s+\\S+\\s+\\S+\\s+(' + '|'.join(styles['dump']) + '))|'
|
||||||
|
if len(styles['region']):
|
||||||
|
restring += '(region\\s+(' + '|'.join(styles['region']) + '))|'
|
||||||
|
if len(styles['integrate']):
|
||||||
|
restring += '(run_style\\s+(' + '|'.join(styles['integrate']) + '))|'
|
||||||
|
if len(styles['minimize']):
|
||||||
|
restring += '(min_style\\s+(' + '|'.join(styles['minimize']) + '))|'
|
||||||
|
|
||||||
|
# replace last (pipe) character with closing parenthesis
|
||||||
|
length = len(restring)
|
||||||
|
restring = restring[:length-1] + ')'
|
||||||
|
# return compiled regex or None
|
||||||
|
if length > 5:
|
||||||
|
return re.compile(restring)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_examples_using_styles(regex, examples='examples'):
|
||||||
|
"""
|
||||||
|
Loop through LAMMPS examples tree and find all files staring with 'in.'
|
||||||
|
that have at least one line matching the regex.
|
||||||
|
|
||||||
|
param regex: pattern matching LAMMPS commands
|
||||||
|
type regex: compiled regex
|
||||||
|
param example: path where to start looking for examples recursively
|
||||||
|
type example: string
|
||||||
|
return: list of matching example inputs
|
||||||
|
rtype: list of strings
|
||||||
|
"""
|
||||||
|
|
||||||
|
inputs = []
|
||||||
|
for filename in Path(examples).rglob('in.*'):
|
||||||
|
with open(filename) as f:
|
||||||
|
for line in f:
|
||||||
|
matches = regex.match(line)
|
||||||
|
if matches:
|
||||||
|
inputs.append(filename)
|
||||||
|
break
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
headers = changed_files_from_git('develop')
|
||||||
|
styles = get_command_from_header(headers, LAMMPS_DIR)
|
||||||
|
inputs = get_examples_using_styles(make_regex(styles), os.path.join(LAMMPS_DIR,'examples'))
|
||||||
|
|
||||||
|
print("Suggested inputs for testing:")
|
||||||
|
for inp in inputs:
|
||||||
|
print(inp)
|
||||||
|
|
||||||
|
print("Found changes to the following styles:")
|
||||||
|
print("Commands: ", styles['command'])
|
||||||
|
print("Atom styles: ", styles['atom'])
|
||||||
|
print("Compute styles: ", styles['compute'])
|
||||||
|
print("Fix styles: ", styles['fix'])
|
||||||
|
print("Pair styles: ", styles['pair'])
|
||||||
|
print("Body styles: ", styles['body'])
|
||||||
|
print("Bond styles: ", styles['bond'])
|
||||||
|
print("Angle styles: ", styles['angle'])
|
||||||
|
print("Dihedral styles: ", styles['dihedral'])
|
||||||
|
print("Improper styles: ", styles['improper'])
|
||||||
|
print("Kspace styles: ", styles['kspace'])
|
||||||
|
print("Dump styles: ", styles['dump'])
|
||||||
|
print("Region styles: ", styles['region'])
|
||||||
|
print("Integrate styles: ", styles['integrate'])
|
||||||
|
print("Minimize styles: ", styles['minimize'])
|
||||||
|
|||||||
Reference in New Issue
Block a user