Skip to content

Commit db10946

Browse files
authored
Move controls to their own derived type. (#387)
[ci skip] Just add the linter needed to rename controls in s to s% ctrl
1 parent 396e076 commit db10946

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

linters/README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,9 @@ fix_inlists.py
9999
--------------
100100

101101
Fixes various controls in the test_suite inlists that should not be enabled by default.
102+
103+
104+
update_ctrls.py
105+
---------------
106+
107+
This handles the replacement of ``s%`` with ``s% ctrl%`` while being smart about only doing that to controls.

linters/update_ctrls.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
#!/usr/bin/env python
2+
3+
# This file is designed to handle moving controls from s% xx to s% ctrl %xx
4+
# While this replicates alot of check_defaults.py we want this to be standalone
5+
# and only depend on the python stdlib. This way users can easily use it to port their
6+
# run_star_extras.f90 files without needing to worry about python packaging.
7+
8+
# Usage: python update_ctrls.up file1.f90 file2.f90 ....
9+
10+
# Note this only works for s% (or s %) it does not work if you renamed the star_type varaible
11+
# to something other than s, for instance in the binary module.
12+
13+
import os
14+
import re
15+
from collections.abc import MutableSet
16+
import functools
17+
import operator
18+
import sys
19+
20+
21+
MESA_DIR = os.environ["MESA_DIR"]
22+
23+
ctrls_files = [ os.path.join("star_data","private","star_controls.inc"),
24+
os.path.join("star_data","private","star_controls_dev.inc")
25+
]
26+
27+
CRTL_NAME = 's% ctrl% '
28+
29+
# inspiration from https://stackoverflow.com/a/27531275
30+
class CaseInsensitiveSet(MutableSet):
31+
def __init__(self, iterable):
32+
self._values = {}
33+
self._fold = str.casefold
34+
for v in iterable:
35+
self.add(v)
36+
37+
def __repr__(self):
38+
return repr(self._values.values())
39+
40+
def __contains__(self, value):
41+
return self._fold(value) in self._values
42+
43+
def __iter__(self):
44+
return iter(self._values.values())
45+
46+
def __len__(self):
47+
return len(self._values)
48+
49+
def items(self):
50+
return self._values.items()
51+
52+
def add(self, value):
53+
if isinstance(value, CaseInsensitiveSet):
54+
for k,v in value.items():
55+
self._values[self._fold(k)] = v
56+
else:
57+
self._values[self._fold(value)] = value
58+
59+
60+
def discard(self, value):
61+
v = self._fold(value)
62+
if v in self._values:
63+
del self._values[v]
64+
65+
66+
def get_options(filename, regexp):
67+
"""Return a set of MESA option names"""
68+
r = re.compile(regexp)
69+
with open(os.path.join(MESA_DIR, filename)) as f:
70+
matches = r.finditer(f.read())
71+
return CaseInsensitiveSet(m.group(1) for m in matches)
72+
73+
74+
def get_columns(filename, regexp):
75+
"""Return a set of MESA column names"""
76+
r = re.compile(regexp)
77+
with open(os.path.join(MESA_DIR, filename)) as f:
78+
lines = f.readlines()
79+
matches = []
80+
for line in lines:
81+
m = r.match(line)
82+
if m is not None:
83+
matches.append(m.group(1))
84+
return CaseInsensitiveSet(matches)
85+
86+
def get_defaults(filename):
87+
# extract column names from defaults file
88+
89+
# these lines look like:
90+
# ! initial_mass = 1
91+
# ? ^^^^^^^^^
92+
# that is, they may or may not be commented out
93+
# and may or may not have a( )
94+
# and may or may not have space before a =
95+
96+
regexp = "^[ \t]*[ ]?(\w+)(\(.*\))*[ ^t]*="
97+
98+
return get_columns(filename, regexp)
99+
100+
def load_file(filename):
101+
with open(os.path.join(MESA_DIR, filename),"r") as f:
102+
lines = f.readlines()
103+
104+
return lines
105+
106+
107+
def get_inc(filename):
108+
# extract options from a inc file
109+
lines = load_file(filename)
110+
111+
# Remove line continutaion characters
112+
lines = [i.replace("&","").strip() for i in lines if i]
113+
114+
# Remove type defintion (i.e real(dp) :: x) leaves just x
115+
# as well as anything that starstwith a comment or has a comment embeded in it
116+
for idl,line in enumerate(lines):
117+
if "::" in line:
118+
lines[idl] = line.split("::")[1].strip()
119+
120+
lines = [i.split(",") for i in lines if i]
121+
122+
# Flatten list of lists
123+
lines = functools.reduce(operator.iconcat, lines, [])
124+
125+
# Remove array sizes from variables
126+
lines = [line.split("(")[0] for line in lines if line]
127+
128+
# Remove comments
129+
lines = [line.split("!")[0] for line in lines if line]
130+
131+
# Remove = x
132+
lines = [line.split("=")[0] for line in lines if line]
133+
134+
# Remove remaining empty strings
135+
lines = [line.strip() for line in lines if line]
136+
137+
return CaseInsensitiveSet(lines)
138+
139+
# Load controls names
140+
cinc = get_inc(ctrls_files[0])
141+
142+
for f in ctrls_files[1:]:
143+
cinc.add(get_inc(f))
144+
145+
146+
def update(filename):
147+
try:
148+
lines = load_file(filename)
149+
except (UnicodeDecodeError, IsADirectoryError):
150+
return
151+
152+
" s[0 or more space] % [0 or more space] [1 or more character or number or _]"
153+
# This wont match when s has been renamed
154+
regex_all = "(s[ \t]?[a-zA-Z0-9_]?%[ \t]?[a-zA-Z0-9_]*)"
155+
156+
" s [0 or more space] % [0 or more space] "
157+
regex_s = 's[ \ta-zA-Z0-9_]?%[ \t]?'
158+
159+
r_all = re.compile(regex_all)
160+
r_s = re.compile(regex_s)
161+
162+
for idl,line in enumerate(lines):
163+
# Split on s% something
164+
line_split = re.split(regex_all,line)
165+
for idm,match in enumerate(line_split):
166+
# Remove the s% so we can check if the variable is a control
167+
var = match.replace('s%','').strip()
168+
if var in cinc:
169+
# If it is a control then replace s% with CRTL_NAME
170+
line_split[idm] = re.sub(regex_s,CRTL_NAME,match)
171+
lines[idl] = ''.join(line_split)
172+
173+
with open(filename,'w') as f:
174+
f.writelines(lines)
175+
176+
if __name__ == "__main__":
177+
for i in sys.argv[1:]:
178+
update(i)
179+
180+
# Run over MESA_DIR
181+
182+
# python3 linters/update_ctrls.py star/test/src/*
183+
# python3 linters/update_ctrls.py star/work/src/*
184+
# python3 linters/update_ctrls.py star/job/*
185+
# python3 linters/update_ctrls.py star/p*/*.f90
186+
# python3 linters/update_ctrls.py star/test_suite/*/src/*.f90
187+
# python3 linters/update_ctrls.py star/other/*.f90
188+
# python3 linters/update_ctrls.py */test_suite/*/src/*.inc
189+
# python3 linters/update_ctrls.py */test_suite/*/src/*/*.inc
190+
191+
# python3 linters/update_ctrls.py binary/test/src/*
192+
# python3 linters/update_ctrls.py binary/work/src/*
193+
# python3 linters/update_ctrls.py binary/job/*
194+
# python3 linters/update_ctrls.py binary/p*/*.f90
195+
# python3 linters/update_ctrls.py binary/test_suite/*/src/*.f90
196+
# python3 linters/update_ctrls.py binary/other/*.f90
197+
198+
# python3 linters/update_ctrls.py astero/test/src/*
199+
# python3 linters/update_ctrls.py astero/work/src/*
200+
# python3 linters/update_ctrls.py astero/job/*
201+
# python3 linters/update_ctrls.py astero/p*/*.f90
202+
# python3 linters/update_ctrls.py astero/test_suite/*/src/*.f90
203+
# python3 linters/update_ctrls.py astero/other/*.f90

0 commit comments

Comments
 (0)