Skip to content

Commit 38d964c

Browse files
robertodrarnfinn
authored andcommitted
Fix uppercasing of input (#95)
* Write to temporary file uppercased input, fix #91 The input file is uppercased, to ensure a consistent case is used throughout. However, the original input file as provided by the user, was being overwritten by its uppercased counterpart. This annoying bug was noted by @ilfreddy. The current commit fixes #91 by writing the uppercased text to a temporary file. The temporary file is removed as soon as the parsing has been done. * Update CHANGELOG.md [ci skip] * Fix Dangerfile undefined variable * minor (changelog)
1 parent 40cc52d commit 38d964c

File tree

4 files changed

+70
-48
lines changed

4 files changed

+70
-48
lines changed

.style.yapf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[style]
2+
COLUMN_LIMIT = 119
3+
INDENT_WIDTH = 4
4+
USE_TABS = False

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
### Changed
1717

1818
- Use [`#pragma once`](https://en.wikipedia.org/wiki/Pragma_once) instead of
19-
`#ifndef, #define, #endif` to guard against multiple inclusion of header
20-
files.
19+
`#ifndef, #define, #endif` to guard against multiple inclusion of header files.
20+
- The uppercased contents of the `.pcm` input file are written to a temporary
21+
file, instead of overwriting the user provided file. The temporary file is
22+
removed after it has been parsed. Fixes #91 as noted by @ilfreddy.
2123

2224
## [Version 1.1.10] - 2017-03-27
2325

Dangerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ end
6666
# but didn't write any docs?
6767
# ------------------------------------------------------------------------------
6868
doc_changes_recommended = git.insertions > 15
69-
if has_code_changes && !has_doc_changes && doc_changes_recommended && not_declared_trivial
69+
if has_code_changes && !has_doc_changes && doc_changes_recommended && !declared_trivial
7070
warn("Consider adding supporting documentation to this change. Documentation sources can be found in the `doc` directory.")
7171
end
7272

tools/pcmsolver.py.in

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,21 @@ routine names my_perfect_routine
4545
keyword names MYPERFECTKEYWORD
4646
"""
4747

48-
isAngstrom = False
49-
CODATAyear = 2010
50-
5148
import sys
49+
import tempfile
5250
import os
53-
sys.path.append(os.path.dirname(__file__))
51+
from copy import deepcopy
52+
import getkw
53+
import re
54+
import subprocess
55+
from codata import CODATAdict
5456

57+
sys.path.append(os.path.dirname(__file__))
5558
try:
5659
import docopt
57-
except:
60+
except ImportError:
5861
sys.path.append('cmake/lib/docopt')
5962
import docopt
60-
from copy import deepcopy
61-
import getkw
62-
import re
63-
import subprocess
6463

6564
options = """
6665
Usage:
@@ -73,8 +72,8 @@ Options:
7372
-h --help Show this screen.
7473
"""
7574

76-
import getkw
77-
from codata import CODATA, CODATAdict
75+
isAngstrom = False
76+
CODATAyear = 2010
7877

7978
allowedSolvents = {'Water' : ('WATER', 'H2O'),
8079
'Propylene Carbonate' : ('PROPYLENE CARBONATE', 'C4H6O3'),
@@ -110,12 +109,12 @@ def iequal(a, b):
110109
def convert_to_upper_case(filename):
111110
"""
112111
Reads contents of filename and converts them to uppercase.
113-
The case-converted contents are written back to filename.
112+
The case-converted contents are written back to a temporary file.
114113
"""
115114
contents = ''
116-
final = ''
115+
final = ''
117116
with open(filename, 'r') as inputFile:
118-
contents = inputFile.readlines()
117+
contents = inputFile.readlines()
119118
# In case the "restart" field is present the case conversion
120119
# has to be done a bit more carefully. The filename argument to
121120
# "restart" should not be touched, whereas the rest of the file
@@ -135,9 +134,14 @@ def convert_to_upper_case(filename):
135134
final = re.sub('FALSE', 'False', final)
136135
final = re.sub('TRUE', 'True', final)
137136

138-
# Write to file
139-
with open(filename, 'wt') as outputFile:
137+
# Write to temporary file in current working directory
138+
# The temporary file has the name of the input file, joined with a unique id
139+
temp, path = tempfile.mkstemp(prefix=filename + '.', dir=os.getcwd())
140+
with open(path, 'wt') as outputFile:
140141
outputFile.write(final)
142+
os.close(temp)
143+
return path
144+
141145

142146
def main():
143147
try:
@@ -164,15 +168,15 @@ def parse_pcm_input(inputFile):
164168
# Set up valid keywords.
165169
valid_keywords = setup_keywords()
166170

167-
# Convert to uppercase
168-
convert_to_upper_case(inputFile)
171+
# Convert to uppercase and get the path to the temporary file
172+
uppercased = convert_to_upper_case(inputFile)
169173

170174
# Set up a GetKw object and let it parse our input:
171175
# here is where the magic happens.
172-
input = getkw.GetkwParser()
173-
inkw = input.parseFile(inputFile)
176+
inkw = getkw.GetkwParser().parseFile(uppercased)
177+
# Remove temporary file
178+
os.remove(uppercased)
174179
inkw.sanitize(valid_keywords)
175-
topsect = inkw.get_topsect()
176180
inkw.run_callbacks(valid_keywords)
177181

178182
# The parsed, machine-readable file is now saved.
@@ -186,6 +190,7 @@ def parse_pcm_input(inputFile):
186190

187191
return parsedFile
188192

193+
189194
def execute(parsedFile):
190195
executable = os.path.join(os.path.dirname(__file__), "run_pcm" + "@CMAKE_EXECUTABLE_SUFFIX@")
191196
if (executable):
@@ -205,12 +210,13 @@ def execute(parsedFile):
205210
print('No executable available for standalone run')
206211
sys.exit(1)
207212

213+
208214
def setup_keywords():
209215
"""
210216
Sets up sections, keywords and respective callback functions.
211217
"""
212218
# Top-level section
213-
top = getkw.Section('toplevel', callback = verify_top)
219+
top = getkw.Section('toplevel', callback=verify_top)
214220
top.set_status(True)
215221
# Define units of measure
216222
# Valid values: AU (atomic units) or ANGSTROM
@@ -222,7 +228,7 @@ def setup_keywords():
222228
top.add_kw('CODATA', 'INT', 2010)
223229

224230
# Cavity section
225-
cavity = getkw.Section('CAVITY', callback = verify_cavity)
231+
cavity = getkw.Section('CAVITY', callback=verify_cavity)
226232
# Type of the cavity
227233
# Valid values: GEPOL, WAVELET, TSLESS and RESTART
228234
cavity.add_kw('TYPE', 'STR')
@@ -290,11 +296,11 @@ def setup_keywords():
290296
# List of spheres
291297
# Valid for: GePol, TsLess and Wavelet in MODE=EXPLICIT
292298
# Valid values: array of doubles in format [x, y, z, R]
293-
cavity.add_kw('SPHERES', 'DBL_ARRAY', callback = verify_spheres)
299+
cavity.add_kw('SPHERES', 'DBL_ARRAY', callback=verify_spheres)
294300
top.add_sect(cavity)
295301

296302
# Medium section
297-
medium = getkw.Section('MEDIUM', callback = verify_medium)
303+
medium = getkw.Section('MEDIUM', callback=verify_medium)
298304
# Type of solver
299305
# Valid values: IEFPCM, CPCM, WAVELET or LINEAR
300306
medium.add_kw('SOLVERTYPE', 'STR', 'IEFPCM')
@@ -356,7 +362,7 @@ def setup_keywords():
356362
top.add_sect(medium)
357363

358364
# Green's function section
359-
green = getkw.Section('GREEN', callback = verify_green)
365+
green = getkw.Section('GREEN', callback=verify_green)
360366
# Green's function type
361367
# Valid values: VACUUM, UNIFORMDIELECTRIC, SPHERICALDIFFUSE, METALSPHERE, GREENSFUNCTIONSUM
362368
# Default: VACUUM
@@ -450,7 +456,7 @@ def setup_keywords():
450456
# List of geometry and classical point charges
451457
# Valid values: array of doubles in format [x, y, z, Q]
452458
# Notes: charges are always in atomic units
453-
molecule.add_kw('GEOMETRY', 'DBL_ARRAY', callback = verify_geometry)
459+
molecule.add_kw('GEOMETRY', 'DBL_ARRAY', callback=verify_geometry)
454460
# Calculate the molecular electrostatic potential (MEP) at the cavity for the given molecule
455461
# Valid values: boolean
456462
# Default: True
@@ -460,7 +466,7 @@ def setup_keywords():
460466
# ChargeDistribution section
461467
# Set a classical charge distribution, inside or outside the cavity
462468
# No additional spheres will be generated.
463-
charge_distribution = getkw.Section('CHARGEDISTRIBUTION', callback = verify_charge_distribution)
469+
charge_distribution = getkw.Section('CHARGEDISTRIBUTION', callback=verify_charge_distribution)
464470
# Monopoles
465471
# Valid values: array of doubles in format [x, y, z, Q]
466472
# Notes: charges are always in atomic units
@@ -473,6 +479,7 @@ def setup_keywords():
473479

474480
return top
475481

482+
476483
def verify_top(section):
477484
global isAngstrom, CODATAyear
478485
allowed_units = ('AU', 'ANGSTROM')
@@ -489,6 +496,7 @@ def verify_top(section):
489496
print(('CODATA set requested {} is not among the allowed sets: {}'.format(CODATAyear, allowed_codata)))
490497
sys.exit(1)
491498

499+
492500
def verify_cavity(section):
493501
allowed = ('GEPOL', 'WAVELET', 'TSLESS', 'RESTART')
494502
type = section.get('TYPE')
@@ -505,7 +513,6 @@ def verify_cavity(section):
505513
if (section['MINDISTANCE'].is_set()):
506514
convert_length_scalar(section['MINDISTANCE'])
507515

508-
509516
if (type.get() == 'GEPOL'):
510517
area = section.get('AREA')
511518
a = area.get()
@@ -514,7 +521,7 @@ def verify_cavity(section):
514521
sys.exit(1)
515522
minRadius = section.get('MINRADIUS')
516523
mr = minRadius.get()
517-
if ( mr < 0.4 ):
524+
if (mr < 0.4):
518525
print(('Requested minimal radius for added spheres too small: {}. Minimal value is: 0.4 au'.format(mr)))
519526
sys.exit(1)
520527
elif (type.get() == 'WAVELET'):
@@ -532,16 +539,16 @@ def verify_cavity(section):
532539
if (a < 0.01):
533540
print(('Requested area value too small: {}. Minimal value is: 0.01 au^2'.format(a)))
534541
sys.exit(1)
535-
minDistance = section.get('MINDISTANCE')
536-
md = minDistance.get()
542+
# minDistance = section.get('MINDISTANCE')
543+
# md = minDistance.get()
537544
# Insert a sanity check on minimal distance!
538-
#if ( md < 0.4 ):
545+
# if ( md < 0.4 ):
539546
# print("Requested minimal distance between sampling points too small: {}. Minimal value is: 0.4 au".format(md))
540547
# sys.exit(1)
541-
derOrder = section.get('DERORDER')
542-
do = derOrder.get()
548+
# derOrder = section.get('DERORDER')
549+
# do = derOrder.get()
543550
# Insert a check on derivative order
544-
#if ( do > 4 ):
551+
# if ( do > 4 ):
545552
# print("Invalid derivative order requested: {}. Derivative order has to be within [1, 5]".format(md))
546553
# sys.exit(1)
547554
elif (type.get() == 'RESTART'):
@@ -564,10 +571,10 @@ def verify_cavity(section):
564571
print(('Cavity creation mode requested {} is not among the allowed modes: {}'.format(mode.get(), allowed_modes)))
565572
sys.exit(1)
566573

567-
atoms=section.get('ATOMS')
568-
at=atoms.get()
574+
atoms = section.get('ATOMS')
575+
at = atoms.get()
569576
radii = section.get('RADII')
570-
convert_length_array(radii);
577+
convert_length_array(radii)
571578
r = radii.get()
572579

573580
if (mode.get() == 'ATOMS'):
@@ -580,6 +587,7 @@ def verify_cavity(section):
580587
print('Incoherent input for Atoms keyword. Too many spheres on the same atom(s).')
581588
sys.exit(1)
582589

590+
583591
def verify_medium(section):
584592
solvent = section.get('SOLVENT')
585593
explicitSolvent = solvent.get() in allowedSolvents['Explicit']
@@ -598,7 +606,6 @@ def verify_medium(section):
598606
solventFound = False
599607
for i, v in allowedSolvents.items():
600608
if (solvent.get() in v):
601-
solventName = i
602609
# Set name to the first value in the value pair
603610
# C++ will look for this name!
604611
solvent.set(v[0])
@@ -660,17 +667,18 @@ def verify_medium(section):
660667
print('A posteriori compression parameter has to be in ]0.0, 1.0[')
661668
sys.exit(1)
662669

670+
663671
def verify_green(section):
664-
allowed = ('VACUUM', 'UNIFORMDIELECTRIC', 'SPHERICALDIFFUSE', 'ALTERNATESPHERICALDIFFUSE', 'METALSPHERE', 'GREENSFUNCTIONSUM')
672+
allowed = ('VACUUM', 'UNIFORMDIELECTRIC', 'SPHERICALDIFFUSE', 'ALTERNATESPHERICALDIFFUSE', 'METALSPHERE', 'GREENSFUNCTIONSUM')
665673
allowed_der = ('NUMERICAL', 'DERIVATIVE', 'GRADIENT', 'HESSIAN')
666674
allowed_profiles = ('TANH', 'ERF')
667675

668676
green1 = section.fetch_sect('GREEN<ONE>')
669677
green2 = section.fetch_sect('GREEN<TWO>')
670-
eps = section.get('EPS')
678+
eps = section.get('EPS')
671679
epsdyn = section.get('EPSDYN')
672680
epsimg = section.get('EPSIMG')
673-
epsre = section.get('EPSRE')
681+
epsre = section.get('EPSRE')
674682

675683
convert_length_array(section.get('SPHEREPOSITION'))
676684
position = section.get('SPHEREPOSITION')
@@ -722,6 +730,7 @@ def verify_green(section):
722730
print(('Allowed profiles are: {}'.format(allowed_profiles)))
723731
sys.exit(1)
724732

733+
725734
def check_array(name, array, offset):
726735
dim = len(array)
727736
if (dim % offset != 0):
@@ -736,36 +745,43 @@ def check_array(name, array, offset):
736745
array[j+2] /= CODATAdict[CODATAyear].ToAngstrom
737746
j += offset
738747

748+
739749
def verify_geometry(keyword):
740750
data = keyword.get()
741751
check_array('GEOMETRY', data, 4)
742752

753+
743754
def verify_charge_distribution(section):
744755
mono = section.get('MONOPOLES').get()
745756
check_array('MONOPOLES', mono, 4)
746757
dipole = section.get('DIPOLES').get()
747758
check_array('DIPOLES', dipole, 6)
748759

760+
749761
def verify_spheres(keyword):
750-
length=len(keyword.get())
762+
length = len(keyword.get())
751763
if (length % 4 != 0):
752764
print('Empty or incoherent Spheres list.')
753765
sys.exit(1)
754766
convert_length_array(keyword)
755767

768+
756769
def convert_length_array(keyword):
757-
length=len(keyword.get())
770+
length = len(keyword.get())
758771
if (isAngstrom):
759772
for i in range(length):
760773
keyword[i] /= CODATAdict[CODATAyear].ToAngstrom
761774

775+
762776
def convert_length_scalar(keyword):
763777
if (isAngstrom):
764778
keyword[0] /= CODATAdict[CODATAyear].ToAngstrom
765779

780+
766781
def convert_area_scalar(keyword):
767782
if (isAngstrom):
768783
keyword[0] /= (CODATAdict[CODATAyear].ToAngstrom * CODATAdict[CODATAyear].ToAngstrom)
769784

785+
770786
if __name__ == '__main__':
771787
main()

0 commit comments

Comments
 (0)