Skip to content

Commit de78bb7

Browse files
germa89akaszynski
andauthored
Feat/added block support to convert module (ansys#666)
* Fixing a character being "eaten" when the comment was made with /com,. * Adding 'WARNING' keyword to consider 'loglevel='warning'' * Added block commands conversion capabilities (ex CMBLOCK, NBLOCK, etc). Added function to convert strings. Detached main converting to '_convert' which is called when you want to convert strings or files. * Added test cases for 'convert_apdl_string' and the block commands (only nblock, eblock and cmblock, the most used) * Small change in how data is stored. * Fixing format. * Fixing doc. * Added reference to new function in documentation. * If the input of `convert_apdl_strings` is a string, the output is a string. Same if it is a list of strings the output is a list of strings * Removing conversion from /COM to #. * C*** to behave equal to /COM * Fixed passing the arguments between `convert_apdl_string` and the FileTranslator class. * Added switch to go into non_interactive if the output comamnd is issued. * Added conversor for NOPR, *DO and /output. Also I account for verify to be input in FINISH mode. Skip mapdl.get in non_interactive mode. Added function for run underscored_run * Added missig `log_apdl` argument passing. * Apply suggestions from code review - Part 1 Co-authored-by: Alex Kaszynski <[email protected]> * Apply suggestions from code review Co-authored-by: Alex Kaszynski <[email protected]> * Renamed function to ``convert_apdl_block`` * Fixing style. * Fixing wrong method order. Co-authored-by: Alex Kaszynski <[email protected]>
1 parent b66dcf2 commit de78bb7

File tree

6 files changed

+340
-23
lines changed

6 files changed

+340
-23
lines changed

ansys/mapdl/core/convert.py

Lines changed: 247 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
from warnings import warn
23

34
from ansys.mapdl.core import __version__
45
from ansys.mapdl.core.misc import is_float
@@ -61,21 +62,105 @@ def convert_script(
6162
>>> from ansys.mapdl.core import examples
6263
>>> clines = pymapdl.convert_script(examples.vmfiles['vm1'], 'vm1.py')
6364
"""
65+
with open(filename_in, 'r') as fid:
66+
apdl_strings = fid.readlines()
67+
68+
translator = _convert(apdl_strings=apdl_strings,
69+
loglevel=loglevel,
70+
auto_exit=auto_exit,
71+
line_ending=line_ending,
72+
exec_file=exec_file,
73+
macros_as_functions=macros_as_functions,
74+
use_function_names=use_function_names
75+
)
76+
77+
translator.save(filename_out)
78+
return translator.lines
79+
80+
81+
def convert_apdl_block(apdl_strings,
82+
loglevel="WARNING",
83+
auto_exit=True,
84+
line_ending=None,
85+
exec_file=None,
86+
macros_as_functions=True,
87+
use_function_names=True,
88+
):
89+
"""Converts an ANSYS input string to a python PyMAPDL string.
90+
91+
Parameters
92+
----------
93+
apdl_string : str
94+
APDL strings or list of strings to convert.
95+
96+
filename_out : str
97+
Filename of the python script to write a translation to.
98+
99+
loglevel : str, optional
100+
Logging level of the ansys object within the script.
101+
102+
auto_exit : bool, optional
103+
Adds a line to the end of the script to exit MAPDL. Default
104+
``True``.
105+
106+
line_ending : str, optional
107+
When None, automatically determined by OS being used.
108+
109+
macros_as_functions : bool, optional
110+
Attempt to convert MAPDL macros to python functions.
111+
112+
use_function_names : bool, optional
113+
Convert MAPDL functions to ansys.mapdl.core.Mapdl class
114+
methods. When ``True``, the MAPDL command "K" will be
115+
converted to ``mapdl.k``. When ``False``, it will be
116+
converted to ``mapdl.run('k')``.
117+
118+
Returns
119+
-------
120+
list
121+
List of lines translated.
122+
123+
"""
124+
125+
translator = _convert(apdl_strings,
126+
loglevel=loglevel,
127+
auto_exit=auto_exit,
128+
line_ending=line_ending,
129+
exec_file=exec_file,
130+
macros_as_functions=macros_as_functions,
131+
use_function_names=use_function_names)
132+
133+
if isinstance(apdl_strings, str):
134+
return translator.line_ending.join(translator.lines)
135+
return translator.lines
136+
137+
138+
def _convert(apdl_strings,
139+
loglevel="WARNING",
140+
auto_exit=True,
141+
line_ending=None,
142+
exec_file=None,
143+
macros_as_functions=True,
144+
use_function_names=True,
145+
):
146+
64147
translator = FileTranslator(
65148
loglevel,
66149
line_ending,
67150
exec_file=exec_file,
68151
macros_as_functions=macros_as_functions,
69152
use_function_names=use_function_names,
70153
)
71-
with open(filename_in) as file_in:
72-
for line in file_in.readlines():
73-
translator.translate_line(line)
154+
155+
if isinstance(apdl_strings, str):
156+
apdl_strings = apdl_strings.split(translator.line_ending)
157+
158+
for line in apdl_strings:
159+
translator.translate_line(line)
74160

75161
if auto_exit:
76162
translator.write_exit()
77-
translator.save(filename_out)
78-
return translator.lines
163+
return translator
79164

80165

81166
class FileTranslator:
@@ -107,7 +192,30 @@ def __init__(
107192
self.initialize_mapdl_object(loglevel, exec_file)
108193

109194
self._valid_commands = dir(Commands)
110-
self._non_interactive_commands = ["*CRE", "*VWR"]
195+
self._block_commands = {
196+
"NBLO": "NBLOCK",
197+
"EBLO": "EBLOCK",
198+
"BFBL": "BFBLOCK",
199+
"BFEB": "BFEBLOCK",
200+
"PREA": "PREAD",
201+
"SFEB": "SFEBLOCK"} #Way out: '-1' , 'END PREAD'
202+
203+
self._enum_block_commands = {
204+
"CMBL": "CMBLOCK",
205+
} # Commands where you need to count the number of lines.
206+
207+
_NON_INTERACTIVE_COMMANDS = {
208+
"*CRE" : "*CREATE",
209+
"*VWR" : "*VWRITE",
210+
"*VRE" : "*VREAD"
211+
}
212+
213+
self._non_interactive_commands = list(_NON_INTERACTIVE_COMMANDS) + list(self._block_commands) + list(self._enum_block_commands)
214+
215+
self._block_count = 0
216+
self._block_count_target = 0
217+
self._in_block = False
218+
self._block_current_cmd = None
111219

112220
def write_header(self):
113221
header = f'"""Script generated by ansys-mapdl-core version {__version__}"""'
@@ -152,6 +260,15 @@ def translate_line(self, line):
152260
line = line.strip()
153261
line = line.replace('"', "'")
154262

263+
if self._in_block:
264+
self._block_count += 1
265+
266+
if self._in_block and self._block_count >= self._block_count_target and self._block_count_target:
267+
self._in_block = False
268+
self.end_non_interactive()
269+
self._block_count = 0
270+
self._block_current_cmd = None
271+
155272
# check if line contains a comment
156273
if "!" in line:
157274
if "'!'" in line or '"!"' in line:
@@ -168,6 +285,34 @@ def translate_line(self, line):
168285

169286
if not line:
170287
return
288+
cmd_ = line.split(',')[0].upper()
289+
290+
if cmd_ == '*DO':
291+
self.start_non_interactive()
292+
self.store_run_command(line)
293+
return
294+
295+
if cmd_ == '*ENDDO':
296+
self.store_run_command(line)
297+
self.end_non_interactive()
298+
return
299+
300+
if self.output_to_file(line):
301+
self.start_non_interactive()
302+
self.store_run_command(line)
303+
return
304+
305+
if self.output_to_default(line):
306+
self.end_non_interactive()
307+
self.store_run_command(line)
308+
self.store_run_command('/GOPR')
309+
return
310+
311+
if cmd_ == '/VERIFY':
312+
self.store_run_command("FINISH")
313+
self.store_run_command(line)
314+
self.store_run_command("/PREP7")
315+
return
171316

172317
if line[:4].upper() == "*REP":
173318
if not self.non_interactive:
@@ -188,8 +333,17 @@ def translate_line(self, line):
188333
return self.store_command("title", ["".join(parameters).strip()])
189334

190335
if line[:4].upper() == "*GET":
191-
parameters = line.split(",")[1:]
192-
return self.store_command("get", parameters)
336+
if self.non_interactive: # gives error
337+
self.store_run_command(line)
338+
return
339+
else:
340+
parameters = line.split(",")[1:]
341+
return self.store_command("get", parameters)
342+
343+
if line[:4].upper() == "/NOP":
344+
self.comment = "It is not recommended to use '/NOPR' in a normal PyMAPDL session."
345+
self.store_under_scored_run_command(line)
346+
return
193347

194348
if line[:6].upper() == "/PREP7":
195349
return self.store_command("prep7", [])
@@ -200,11 +354,13 @@ def translate_line(self, line):
200354
self.store_empty_line()
201355
self.indent = self.indent[4:]
202356
self._infunction = False
203-
self.end_non_interactive()
357+
if not self._in_block:
358+
self.end_non_interactive()
204359
return
205360
else:
206361
self.store_run_command(line)
207-
self.end_non_interactive()
362+
if not self._in_block:
363+
self.end_non_interactive()
208364
return
209365

210366
# check for if statement
@@ -216,12 +372,14 @@ def translate_line(self, line):
216372
# check if line ends non-interactive
217373
if line[0] == "(":
218374
if not self.non_interactive:
219-
print(
220-
"Possible invalid line:\n%s\n" % line
221-
+ "This line requires a *VWRITE beforehand"
375+
warn(
376+
"\nPossible invalid line:\n%s\n" % line
377+
+ "This line requires a *VWRITE beforehand."
378+
+ "The previous line is: \n%s\n\n" % self.lines[-1]
222379
)
223380
self.store_run_command(line)
224-
self.end_non_interactive()
381+
if not self._in_block: # To escape cmds that require (XX) but they are not in block
382+
self.end_non_interactive()
225383
return
226384
elif line[:4] == "*USE" and self.macros_as_functions:
227385
items = line.split(",")
@@ -243,15 +401,36 @@ def translate_line(self, line):
243401
self.store_empty_line()
244402
return
245403

404+
if line == '-1' or line == 'END PREAD': # End of block commands
405+
self.store_run_command(line)
406+
self._in_block = False
407+
self.end_non_interactive()
408+
return
409+
246410
# check valid command
247411
if command not in self._valid_commands:
248-
if line[:4] == "*CRE": # creating a function
412+
if line[:4].upper() == "*CRE": # creating a function
249413
if self.macros_as_functions:
250414
self.start_function(items[1].strip())
251415
return
252416
else:
253417
self.start_non_interactive()
254-
elif line[:4] in self._non_interactive_commands:
418+
elif line[:4].upper() in self._non_interactive_commands:
419+
if line[:4].upper() in self._block_commands:
420+
self._in_block = True
421+
self._block_count = 0
422+
self._block_count_target = 0
423+
424+
elif line[:4].upper() in self._enum_block_commands:
425+
self._in_block = True
426+
self._block_count = 0
427+
if line[:4].upper() == 'CMBL': # In cmblock
428+
# CMBLOCK,Cname,Entity,NUMITEMS,,,,,KOPT
429+
numitems = int(line.split(',')[3])
430+
_block_count_target = numitems//8 + 1 if numitems%8 != 0 else numitems//8
431+
self._block_count_target = _block_count_target + 2 # because the cmd line and option line.
432+
433+
self._block_current_cmd = line[:4].upper()
255434
self.start_non_interactive()
256435
self.store_run_command(line)
257436
elif self.use_function_names:
@@ -274,10 +453,18 @@ def start_function(self, func_name):
274453
self.lines.append(line)
275454
self.indent = self.indent + " "
276455

277-
def store_run_command(self, command):
456+
def store_under_scored_run_command(self, command):
457+
self.store_run_command(command, run_underscored = True)
458+
459+
def store_run_command(self, command, run_underscored=False):
278460
"""Stores pyansys.ANSYS command that cannot be broken down
279461
into a function and parameters.
280462
"""
463+
if run_underscored:
464+
underscore = '_'
465+
else:
466+
underscore = ''
467+
281468
if self._infunction and "ARG" in command:
282469
args = []
283470
for i in range(1, 19):
@@ -288,22 +475,24 @@ def store_run_command(self, command):
288475
args.append(arg)
289476
c += 1
290477

291-
line = '%s%s.run("%s".format(%s))' % (
478+
line = '%s%s.%srun("%s".format(%s))' % (
292479
self.indent,
293480
self.obj_name,
481+
underscore,
294482
command,
295483
", ".join(args),
296484
)
297485

298486
elif self.comment:
299-
line = '%s%s.run("%s") # %s' % (
487+
line = '%s%s.%srun("%s") # %s' % (
300488
self.indent,
301489
self.obj_name,
490+
underscore,
302491
command,
303492
self.comment,
304493
)
305494
else:
306-
line = '%s%s.run("%s")' % (self.indent, self.obj_name, command)
495+
line = '%s%s.%srun("%s")' % (self.indent, self.obj_name, underscore, command)
307496
self.lines.append(line)
308497

309498
def store_comment(self):
@@ -355,3 +544,41 @@ def end_non_interactive(self):
355544
if self._non_interactive_level == 0:
356545
self.non_interactive = False
357546
self.indent = self.indent[4:]
547+
548+
def output_to_file(self, line):
549+
"""Return if an APDL line is redirecting to a file."""
550+
if line[:4].upper() == '/OUT':
551+
# We are redirecting the output to somewhere, probably a file.
552+
# Because of the problem with the ansys output, we need to execute
553+
# this in non_interactive mode.
554+
output_cmd = line.strip().upper().split(',')
555+
if len(output_cmd) > 1:
556+
opt1 = output_cmd[1].strip().upper()
557+
if opt1 != 'TERM':
558+
# A file is supplied.
559+
return True
560+
561+
if line[:4].upper() == '*CFO': # any([each[0:4] in '*CFOPEN' for each in dir(Commands)])
562+
# We might not need going into interactive mode for *CFOPEN/*CFCLOSE
563+
return True
564+
565+
return False
566+
567+
def output_to_default(self, line):
568+
if line[:4].upper() == '/OUT':
569+
# We are redirecting the output to somewhere, probably a file.
570+
# Because of the problem with the ansys output, we need to execute
571+
# this in non_interactive mode.
572+
output_cmd = line.strip().upper().split(',')
573+
if len(output_cmd) == 1:
574+
return True
575+
elif len(output_cmd) > 1:
576+
opt1 = output_cmd[1].strip().upper()
577+
if opt1 == 'TERM':
578+
# A file is supplied.
579+
return True
580+
if line[:4].upper() in '*CFCLOSE':
581+
# We might not need going into interactive mode for *CFOPEN/*CFCLOSE
582+
return True
583+
584+
return False

ansys/mapdl/core/logging.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
'DEBUG': DEBUG,
149149
'INFO': INFO,
150150
'WARN': WARN,
151+
'WARNING': WARN,
151152
'ERROR': ERROR,
152153
'CRITICAL': CRITICAL,
153154
}

0 commit comments

Comments
 (0)