diff --git a/doc/source/user_guide/launcher.rst b/doc/source/user_guide/launcher.rst index d7b2028a79..cfc3c61941 100644 --- a/doc/source/user_guide/launcher.rst +++ b/doc/source/user_guide/launcher.rst @@ -6,7 +6,7 @@ the location of MAPDL must be provided for non-standard installations. When running for the first time, ``ansys-mapdl-core`` requests the location of the MAPDL executable if it cannot automatically find it. You can test your installation of PyMAPDL and set it up by running -the :func:`launch_mapdl() ` function: +the :func:`launch_mapdl() ` function: .. code:: python @@ -81,4 +81,4 @@ keyword argument: API reference ~~~~~~~~~~~~~ For more information on controlling how MAPDL launches locally, see the -description of the :func:`launch_mapdl() ` function. +description of the :func:`launch_mapdl() ` function. diff --git a/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py b/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py index f459e70486..0504307d11 100644 --- a/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py +++ b/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py @@ -518,13 +518,15 @@ def parres(self, lab="", fname="", ext="", **kwargs): >>> mapdl.parres('parm.PARM') """ - if ext: - fname = fname + "." + ext - elif not fname: - fname = "." + "PARM" + if not fname: + fname = self.jobname - if "Grpc" in self.__class__.__name__: # grpc mode - if self._local: + fname = self._get_file_name( + fname=fname, ext=ext, default_extension="parm" + ) # Although documentation says `PARM` + + if self._mode == "grpc": # grpc mode + if self.is_local: # It must be a file! if os.path.isfile(fname): # And it exist! diff --git a/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py b/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py index 7201c54824..0ac25dfa89 100644 --- a/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py +++ b/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py @@ -9,8 +9,11 @@ def hemiopt(self, hres="", **kwargs): hres Hemicube resolution. Increase value to increase the accuracy of the view factor calculation. Defaults to 10. + + tolerance + Tolerance value that controls whether or not facets are subdivided in view factor calculations to increase view factor accuracy. TOLERANCE is closely related to the spacing between facets. Defaults to 1e-6. """ - command = f"HEMIOPT,{hres}" + command = f"HEMIOPT,{hres},{tolerance}" return self.run(command, **kwargs) def radopt( diff --git a/src/ansys/mapdl/core/_commands/post1_/special.py b/src/ansys/mapdl/core/_commands/post1_/special.py index 74659aed3b..25e8831ea6 100644 --- a/src/ansys/mapdl/core/_commands/post1_/special.py +++ b/src/ansys/mapdl/core/_commands/post1_/special.py @@ -1748,6 +1748,7 @@ def prcamp( stabval="", keyallfreq="", keynegfreq="", + keywhirl="", **kwargs, ): """Prints Campbell diagram data for applications involving rotating @@ -1768,19 +1769,18 @@ def prcamp( slope The slope of the line to be printed. This value must be positive. - SLOPE > 0 - The line represents the number of excitations per revolution of the rotor. For - example, SLOPE = 1 represents one excitation per - revolution, usually resulting from unbalance. + SLOPE > 0 - The line represents the number of excitations per + revolution of the rotor. For example, SLOPE = 1 represents one + excitation per revolution, usually resulting from unbalance. - SLOPE = 0 - The line represents the stability threshold for stability values or logarithmic - decrements printout (STABVAL = 1 or 2) + SLOPE = 0 - The line represents the stability threshold for stability + values or logarithmic decrements printout (STABVAL = 1 or 2) unit Specifies the unit of measurement for rotational angular velocities: - RDS - Rotational angular velocities in radians per second (rad/s). This value is the - default. + RDS - Rotational angular velocities in radians per second (rad/s). This value is the default. RPM - Rotational angular velocities in revolutions per minute (RPMs). @@ -1794,8 +1794,8 @@ def prcamp( stabval Flag to print the stability values: - 0 (OFF or NO) - Print the frequencies (the imaginary parts of the eigenvalues in Hz). This - value is the default. + 0 (OFF or NO) - Print the frequencies (the imaginary parts of the + eigenvalues in Hz). This value is the default. 1 (ON or YES) - Print the stability values (the real parts of the eigenvalues in Hz). @@ -1804,9 +1804,8 @@ def prcamp( keyallfreq Key to specify if all frequencies above FREQB are printed out: - 0 (OFF or NO) - A maximum of 10 frequencies are printed out. They correspond to the frequencies - displayed via the PLCAMP command. This value is the - default. + 0 (OFF or NO) - A maximum of 10 frequencies are printed out. They + correspond to the frequencies displayed via the PLCAMP command. This value is the default. 1 (ON or YES) - All frequencies are printed out. @@ -1819,6 +1818,12 @@ def prcamp( 1 (ON or YES) - Negative and positive frequencies are printed out. + keywhirl + Flag to print the whirl and instability keys for each load step: + + 0 (OFF or NO) - Print the whirl for the last load step. This value is the default. + 1 (ON or YES) - Print the whirl and instability keys for each load step. + Notes ----- The following items are required when generating a Campbell diagram: diff --git a/src/ansys/mapdl/core/_commands/post26_/special.py b/src/ansys/mapdl/core/_commands/post26_/special.py index 37e75e9d3d..3a899bf393 100644 --- a/src/ansys/mapdl/core/_commands/post26_/special.py +++ b/src/ansys/mapdl/core/_commands/post26_/special.py @@ -330,7 +330,7 @@ def rpsd( See POST26 - Response Power Spectral Density in the Mechanical APDL Theory Reference for more information on these equations. """ - command = f"RPSD,{ir},{ia},{ib},{itype},{datum},{name},{signif}" + command = f"RPSD,{ir},{ia},{ib},{itype},{datum},{name},,{signif}" return self.run(command, **kwargs) def smooth( diff --git a/src/ansys/mapdl/core/_commands/preproc/special_purpose.py b/src/ansys/mapdl/core/_commands/preproc/special_purpose.py index 23c962db2d..7f80d5b4b4 100644 --- a/src/ansys/mapdl/core/_commands/preproc/special_purpose.py +++ b/src/ansys/mapdl/core/_commands/preproc/special_purpose.py @@ -969,7 +969,9 @@ def sstate( command = f"SSTATE,{action},{cm_name},{val1},{val2},{val3},{val4},{val5},{val6},{val7},{val8},{val9}" return self.run(command, **kwargs) - def xfdata(self, enrichmentid="", elemnum="", nodenum="", phi="", **kwargs): + def xfdata( + self, enrichmentid="", lsm="", elemnum="", nodenum="", phi="", psi="", **kwargs + ): """Defines a crack in the model by specifying nodal level set values APDL Command: XFDATA @@ -993,6 +995,10 @@ def xfdata(self, enrichmentid="", elemnum="", nodenum="", phi="", **kwargs): phi Signed normal distance of the node from the crack. + psi + Signed normal distance of the node from the crack tip (or crack front). + Used only in the singularity- based XFEM method. + Notes ----- Issue the XFDATA command multiple times as needed to specify nodal @@ -1000,10 +1006,19 @@ def xfdata(self, enrichmentid="", elemnum="", nodenum="", phi="", **kwargs): This command is valid in PREP7 (/PREP7) only. """ - command = f"XFDATA,{enrichmentid},{elemnum},{nodenum},{phi}" + command = f"XFDATA,{enrichmentid},{lsm},{elemnum},{nodenum},{phi},{psi}" return self.run(command, **kwargs) - def xfenrich(self, enrichmentid="", compname="", matid="", **kwargs): + def xfenrich( + self, + enrichmentid="", + compname="", + matid="", + method="", + radius="", + snaptoler="", + **kwargs, + ): """Defines parameters associated with crack propagation using XFEM APDL Command: XFENRICH @@ -1025,6 +1040,19 @@ def xfenrich(self, enrichmentid="", compname="", matid="", **kwargs): the initial crack. If 0 or not specified, the initial crack is assumed to be free of cohesive zone behavior. + method + PHAN -- Use phantom-node-based XFEM (default). + SING -- Use singularity-based XFEM. + + radius + Radius defining the region around the crack tip encompassing the + set of elements to be influenced by the crack-tip singularity effects. + Default = 0.0. Used only in singularity-based XFEM. + + snaptoler + Snap tolerance to snap the crack tip to the closest crack face along + the extension direction. Default = 1.0E-6. Used only in singularity-based XFEM. + Notes ----- If MatID is specified, the cohesive zone behavior is described by the @@ -1032,7 +1060,7 @@ def xfenrich(self, enrichmentid="", compname="", matid="", **kwargs): This command is valid in PREP7 (/PREP7) only. """ - command = f"XFENRICH,{enrichmentid},{compname},{matid}" + command = f"XFENRICH,{enrichmentid},{compname},{matid}, {method}, {radius}, {snaptoler}" return self.run(command, **kwargs) def xflist(self, enrichmentid="", **kwargs): diff --git a/src/ansys/mapdl/core/_commands/solution/analysis_options.py b/src/ansys/mapdl/core/_commands/solution/analysis_options.py index 585c9b42ea..deed5ba9e5 100644 --- a/src/ansys/mapdl/core/_commands/solution/analysis_options.py +++ b/src/ansys/mapdl/core/_commands/solution/analysis_options.py @@ -1105,25 +1105,27 @@ def cutcontrol(self, lab="", value="", option="", **kwargs): command = f"CUTCONTROL,{lab},{value},{option}" return self.run(command, **kwargs) - def ddoption(self, decomp="", **kwargs): + def ddoption(self, decomp="", nprocpersol="", numsolforlp="", **kwargs): """Sets domain decomposer option for Distributed ANSYS. APDL Command: DDOPTION Parameters ---------- - decomp + Decomp Controls which domain decomposition algorithm to use. - AUTO - Use the default domain decomposition algorithm when splitting the model into - domains for Distributed ANSYS (default). + * AUTO - Automatically selects the optimal domain decomposition method (default). + * MESH - Decompose the FEA mesh. + * FREQ - Decompose the frequency domain for harmonic analyses. + * CYCHI -Decompose the harmonic indices for cyclic symmetry modal analyses. - GREEDY - Use the "greedy" domain decomposition algorithm. + nprocpersol + Number of processes to be used for mesh-based decomposition in conjunction with each frequency solution (`Decomp = FREQ`) or harmonic index solution (`Decomp = CYCHI`). Defaults to 1. This field + is ignored when `Decomp = MESH`. - METIS - Use the METIS graph partitioning domain decomposition algorithm. + numsolforlp + Number of frequency or harmonic index solutions in a subsequent linear perturbation harmonic or linear perturbation cyclic modal analysis. This field is ignored when `Decomp = MESH` Notes ----- @@ -2016,7 +2018,7 @@ def essolv( command = f"ESSOLV,{electit},{strutit},{dimn},{morphopt},{mcomp},{xcomp},{electol},{strutol},{mxloop},,{ruseky},{restky},{eiscomp}" return self.run(command, **kwargs) - def expass(self, key="", **kwargs): + def expass(self, key="", keystat="", **kwargs): """Specifies an expansion pass of an analysis. APDL Command: EXPASS @@ -2025,10 +2027,13 @@ def expass(self, key="", **kwargs): ---------- key Expansion pass key: + * OFF - No expansion pass will be performed (default). + * ON - An expansion pass will be performed. - OFF - No expansion pass will be performed (default). - - ON - An expansion pass will be performed. + keystat + Static correction vectors key: + * ON - Include static correction vectors in the expanded displacements (default). + * OFF - Do not include static correction vectors in the expanded displacements. Notes ----- @@ -2040,7 +2045,7 @@ def expass(self, key="", **kwargs): This command is also valid in PREP7. """ - command = f"EXPASS,{key}" + command = f"EXPASS,{key},,,{keystat}" return self.run(command, **kwargs) def gauge(self, opt="", freq="", **kwargs): diff --git a/src/ansys/mapdl/core/_commands/solution/spectrum_options.py b/src/ansys/mapdl/core/_commands/solution/spectrum_options.py index ff65b77274..55ecbf445b 100644 --- a/src/ansys/mapdl/core/_commands/solution/spectrum_options.py +++ b/src/ansys/mapdl/core/_commands/solution/spectrum_options.py @@ -669,7 +669,7 @@ def psdfrq( command = f"PSDFRQ,{tblno1},{tblno2},{freq1},{freq2},{freq3},{freq4},{freq5},{freq6},{freq7}" return self.run(command, **kwargs) - def psdgraph(self, tblno1="", tblno2="", **kwargs): + def psdgraph(self, tblno1="", tblno2="", displaykey="", **kwargs): """Displays input PSD curves APDL Command: PSDGRAPH @@ -683,6 +683,14 @@ def psdgraph(self, tblno1="", tblno2="", **kwargs): Second PSD table number to display. TBLNO2 is used only in conjunction with the COVAL or the QDVAL commands. + displaykey + Key to display the points markers and numbering: + + 0 - Display points markers and numbering (default). + 1 - Display points numbering only. + 2 - Display points markers only. + 3 - No points markers or numbering. + Notes ----- The input PSD tables are displayed in log-log format as dotted lines. @@ -699,7 +707,7 @@ def psdgraph(self, tblno1="", tblno2="", **kwargs): This command is valid in any processor. """ - command = f"PSDGRAPH,{tblno1},{tblno2}" + command = f"PSDGRAPH,{tblno1},{tblno2},{displaykey}" return self.run(command, **kwargs) def psdres(self, lab="", relkey="", **kwargs): diff --git a/src/ansys/mapdl/core/convert.py b/src/ansys/mapdl/core/convert.py index 0c8530a468..1385256e41 100644 --- a/src/ansys/mapdl/core/convert.py +++ b/src/ansys/mapdl/core/convert.py @@ -15,58 +15,97 @@ "max-line-length": 100, } +LOGLEVEL_DEFAULT = "WARNING" +AUTO_EXIT_DEFAULT = True +LINE_ENDING_DEFAULT = None +EXEC_FILE_DEFAULT = None +MACROS_AS_FUNCTIONS_DEFAULT = True +USE_FUNCTION_NAMES_DEFAULT = True +SHOW_LOG_DEFAULT = False +ADD_IMPORTS_DEFAULT = True +COMMENT_SOLVE_DEFAULT = False +CLEANUP_OUTPUT_DEFAULT = True +HEADER_DEFAULT = True +PRINT_COM_DEFAULT = True +ONLY_COMMANDS_DEFAULT = False +USE_VTK_DEFAULT = None +CLEAR_AT_START_DEFAULT = False +CHECK_PARAMETER_NAMES_DEFAULT = True + + # This commands have "--" as one or some arguments -COMMANDS_WITH_EMPTY_ARGS = [ - "/CMA", # "/CMAP, - "/NER", # "/NERR, - "/PBF", # "/PBF, - "/PMO", # "/PMORE, - "ANTY", # ANTYPE, - "ASBL", # ASBL, - "ATAN", # ATAN, - "BCSO", # BCSOPTION, - "CLOG", # CLOG, - "CONJ", # CONJUG, - "DERI", # DERIV, - "DSPO", # DSPOPTION, - "ENER", # ENERSOL, - "ENSY", # ENSYM, - "ESYM", # ESYM, - "EXP", # EXP, - "EXPA", # EXPAND, - "FCLI", # FCLIST, - "FILE", # FILEAUX2, - "FLUR", # FLUREAD, - "GMAT", # GMATRIX, - "IMAG", # IMAGIN, - "INT1", # INT1, - "LARG", # LARGE, - "LATT", # LATT, - "MAP", # MAP, - "MORP", # MORPH, - "MPCO", # MPCOPY, - "NLOG", # NLOG, - "PLMA", # PLMAP, - "PRED", # PRED, - "PROD", # PROD, - "QRDO", # QRDOPT, - "QUOT", # QUOT, - "RACE", # RACE, - "REAL", # REALVAR, - "REME", # REMESH, - "SESY", # SESYMM, - "SETF", # SETFGAP, - "SETR", # SETRAN, - "SMAL", # SMALL, - "SNOP", # SNOPTION, - "SURE", # SURESU, - "THOP", # THOPT, - "TINT", # TINTP, -] +COMMANDS_WITH_EMPTY_ARGS = { + "/CMA": (), # "/CMAP, + "/NER": (), # "/NERR, + "/PBF": (), # "/PBF, + "/PMO": (), # "/PMORE, + "ADD": (), # "ADD" + "ANTY": (), # ANTYPE, + "ASBL": (), # ASBL, + "ATAN": (), # ATAN, + "BCSO": (), # BCSOPTION, + "CORI": (), # CORIOLIS + "CDRE": (), # CDREAD + "CLOG": (), # CLOG, + "CONJ": (), # CONJUG, + "DERI": (), # DERIV, + "DSPO": (), # DSPOPTION, + "ENER": (), # ENERSOL, + "ENSY": (), # ENSYM, + "ESYM": (), # ESYM, + "EXP": (), # EXP, + "EXPA": (), # EXPAND, + "FCLI": (), # FCLIST, + "FILE": (), # FILEAUX2, + "FLUR": (), # FLUREAD, + "GMAT": (), # GMATRIX, + "IMAG": (), # IMAGIN, + "INT1": (), # INT1, + "LARG": (), # LARGE, + "LATT": (), # LATT, + "MAP": (), # MAP, + "MORP": (), # MORPH, + "MPCO": (), # MPCOPY, + "NLOG": (), # NLOG, + "PLMA": (), # PLMAP, + "PRED": (), # PRED, + "PROD": (), # PROD, + "QRDO": (), # QRDOPT, + "QUOT": (), # QUOT, + "RACE": (), # RACE, + "RDEC": (), # RDEC + "REAL": (), # REALVAR, + "REME": (), # REMESH, + "RPSD": (), # RPSD + "SECR": (), # SECREAD + "SECW": (), # SECWRITE + "SESY": (), # SESYMM, + "SETF": (), # SETFGAP, + "SETR": (), # SETRAN, + "SMAL": (), # SMALL, + "SNOP": (), # SNOPTION, + "SQRT": (), # SQRT + "SURE": (), # SURESU, + "THOP": (), # THOPT, + "TINT": (), # TINTP, + "XFDA": (), # XFDATA +} -COMMANDS_TO_NOT_BE_CONVERTED = [] -COMMANDS_TO_NOT_BE_CONVERTED.extend(COMMANDS_WITH_EMPTY_ARGS) +COMMANDS_TO_NOT_BE_CONVERTED = [ + "CMPL", # CMPLOT default behaviour does not match the `mapdl.cmplot`'s at the moemnt + "MODE", # Until we merge #2431 + "/LIN", # Until we merge 2432 + "/LAR", # Until we merge 2432 + "/TYP", # Until we merge 2432 + "/DSC", # Until we merge 2432 + # CDREAD # commented above +] + +FORCED_MAPPING = { + # Forced mapping between MAPDL and PyMAPDL + "SECT": "sectype", # Because it is shadowed by `sectinqr` +} def convert_script( @@ -85,6 +124,9 @@ def convert_script( header=True, print_com=True, only_commands=False, + use_vtk=None, + clear_at_start=False, + check_parameter_names=True, ): """Converts an ANSYS input file to a python PyMAPDL script. @@ -156,6 +198,13 @@ def convert_script( and exit commands are NOT included (``auto_exit=False``). Overrides ``header``, ``add_imports`` and ``auto_exit``. + use_vtk : bool, optional + It sets the `mapdl.use_vtk` argument equals True or False depending on + this value. + + clear_at_start : bool, optional + Add a `mapdl.clear()` after the Mapdl object initialization. + Returns ------- list @@ -211,6 +260,9 @@ def convert_script( header=header, print_com=print_com, only_commands=only_commands, + use_vtk=use_vtk, + clear_at_start=clear_at_start, + check_parameter_names=check_parameter_names, ) translator.save(filename_out) @@ -232,6 +284,9 @@ def convert_apdl_block( header=True, print_com=True, only_commands=False, + use_vtk=None, + clear_at_start=False, + check_parameter_names=False, ): """Converts an ANSYS input string to a python PyMAPDL string. @@ -299,6 +354,17 @@ def convert_apdl_block( and exit commands are NOT included (``auto_exit=False``). Overrides ``header``, ``add_imports`` and ``auto_exit``. + use_vtk : bool, optional + It sets the `mapdl.use_vtk` argument equals True or False depending on + this value. Defaults to `None` which is Mapdl class default. + + clear_at_start : bool, optional + Add a `mapdl.clear()` after the Mapdl object initialization. Defaults to + `False`. + + check_parameter_names : bool, optional + Set MAPDL object to avoid parameter name checks (do not raise leading underscored parameter exceptions). Defaults to `False`. + Returns ------- list @@ -335,6 +401,9 @@ def convert_apdl_block( header=header, print_com=print_com, only_commands=only_commands, + use_vtk=use_vtk, + clear_at_start=clear_at_start, + check_parameter_names=check_parameter_names, ) if isinstance(apdl_strings, str): @@ -357,6 +426,9 @@ def _convert( header=True, print_com=True, only_commands=False, + use_vtk=None, + clear_at_start=False, + check_parameter_names=True, ): if only_commands: auto_exit = False @@ -375,6 +447,9 @@ def _convert( cleanup_output=cleanup_output, header=header, print_com=print_com, + use_vtk=use_vtk, + clear_at_start=clear_at_start, + check_parameter_names=check_parameter_names, ) if isinstance(apdl_strings, str): @@ -437,6 +512,9 @@ def __init__( cleanup_output=True, header=True, print_com=True, + use_vtk=None, + clear_at_start=False, + check_parameter_names=False, ): self._non_interactive_level = 0 self.lines = Lines(mute=not show_log) @@ -454,12 +532,17 @@ def __init__( self.cleanup_output = cleanup_output self._header = header self.print_com = print_com + self.verification_example = False + self.use_vtk = use_vtk + self.clear_at_start = clear_at_start + self.check_parameter_names = check_parameter_names + self.macros_names = [] self.write_header() if self._add_imports: self.initialize_mapdl_object(loglevel, exec_file) - self._valid_commands = dir(Commands) + self._valid_commands = self._get_valid_pymapdl_methods_short() self._block_commands = { "NBLO": "NBLOCK", "EBLO": "EBLOCK", @@ -474,7 +557,6 @@ def __init__( } # Commands where you need to count the number of lines. _NON_INTERACTIVE_COMMANDS = { - "*CRE": "*CREATE", "*VWR": "*VWRITE", "*VRE": "*VREAD", } @@ -553,17 +635,26 @@ def initialize_mapdl_object(self, loglevel, exec_file): core_module = "ansys.mapdl.core" # shouldn't change self.lines.append(f"from {core_module} import launch_mapdl") + mapdl_arguments = [f'loglevel="{loglevel}"'] + if exec_file: - exec_file_parameter = f'exec_file="{exec_file}", ' - else: - exec_file_parameter = "" + mapdl_arguments.append(f'exec_file="{exec_file}"') if self.print_com: - line = f'{self.obj_name} = launch_mapdl({exec_file_parameter}loglevel="{loglevel}", print_com=True)\n' - else: - line = f'{self.obj_name} = launch_mapdl({exec_file_parameter}loglevel="{loglevel}")\n' + mapdl_arguments.append("print_com=True") + + if self.use_vtk is not None: + mapdl_arguments.append(f"use_vtk={bool(self.use_vtk)}") + + if self.check_parameter_names is not None and not self.check_parameter_names: + mapdl_arguments.append("check_parameter_names=False") + + line = f'{self.obj_name} = launch_mapdl({", ".join(mapdl_arguments)})' self.lines.append(line) + if self.clear_at_start: + self.lines.append(f"{self.obj_name}.clear() # Clearing session") + @property def line_ending(self): return self._line_ending @@ -611,6 +702,8 @@ def translate_line(self, line): self.comment = self.comment.lstrip() if not line: + # Keeping empty lines + self.lines.append("") return # Cleaning ending empty arguments. @@ -623,6 +716,8 @@ def translate_line(self, line): line = ",".join(line_[ind:][::-1]) + line_with_trailing_commas = line + # remove trailing comma line = line[:-1] if line[-1] == "," else line line_upper = line.upper() @@ -630,7 +725,7 @@ def translate_line(self, line): cmd_caps = line.split(",")[0].upper() cmd_caps_short = cmd_caps[:4] - items = line.split(",") + items = self._get_items(line.strip()) if cmd_caps_short in ["SOLV", "LSSO"] and self._comment_solve: self.store_command( @@ -640,11 +735,19 @@ def translate_line(self, line): self.store_command("com", [line]) return + if cmd_caps_short == "/VER": + self.verification_example = True + if cmd_caps_short == "/COM": # It is a comment self.store_command("com", [line[5:]]) return + if cmd_caps_short == "C***": + # It is an old style comment + self.store_command("com", [line[5:]]) + return + if cmd_caps == "*DO": self.start_non_interactive() self.store_run_command(line) @@ -655,9 +758,16 @@ def translate_line(self, line): self.end_non_interactive() return + if "/EXI" in cmd_caps.upper() and self.non_interactive: + self.store_command("com", [f"Skipping: {line}"]) + return + if self.output_to_file(line): - self.start_non_interactive() - self.store_run_command(line) + if self.verification_example and "SCRATCH" in line.upper(): + self.store_command("com", [f"Skipping: {line}"]) + else: + self.start_non_interactive() + self.store_run_command(line) return if self.output_to_default(line): @@ -669,7 +779,6 @@ def translate_line(self, line): if cmd_caps == "/VERIFY": self.store_run_command("FINISH") self.store_run_command(line) - self.store_run_command("/PREP7") return if cmd_caps_short == "*REP": @@ -677,7 +786,7 @@ def translate_line(self, line): prev_cmd = self.lines.pop(-1) self.start_non_interactive() new_prev_cmd = ( - " " + prev_cmd + self.indent + prev_cmd ) # Since we are writing in self.lines we need to add the indentation by ourselves. self.lines.append(new_prev_cmd) self.store_run_command( @@ -686,13 +795,27 @@ def translate_line(self, line): self.end_non_interactive() return + # Skipping conversion if command has empty arguments and there is ",," in the call + if ( + cmd_caps_short in COMMANDS_WITH_EMPTY_ARGS + and ",," in line_with_trailing_commas.replace(" ", "") + ): + self.store_run_command(line.strip()) + return + + # Skipping commands to not be converted if cmd_caps_short in COMMANDS_TO_NOT_BE_CONVERTED: - self.store_run_command(line) + self.store_run_command(line.strip()) return if cmd_caps_short == "/TIT": # /TITLE parameters = line.split(",")[1:] - return self.store_command("title", ["".join(parameters).strip()]) + return self.store_command("title", [",".join(parameters).strip()]) + + if cmd_caps_short == "/AXL": # /AXLAB + parameters = line.split(",")[1:] + parameters_ = [parameters[0], ",".join(parameters[1:])] + return self.store_command("axlab", parameters_) if cmd_caps_short == "*GET": if self.non_interactive: # gives error @@ -710,12 +833,29 @@ def translate_line(self, line): return if cmd_caps_short == "*CRE": # creating a function + self.macros_names.append(items[1]) if self.macros_as_functions: self.start_function(items[1].strip()) return else: self.start_non_interactive() + ## Treating functions + if items[0] in self.macros_names and self.macros_as_functions: + # We are calling the function/macro created before. + func_name = items[0].strip() + + args = self._parse_arguments(items[1:]) + self.store_python_command(f"{func_name}({args})") + return + + if cmd_caps_short == "*USE" and self.macros_as_functions: + func_name = items[1].strip() + if func_name in self._functions: + args = self._parse_arguments(items[2:]) + self.store_python_command(f"{func_name}({args})") + return + if cmd_caps == "/PREP7": return self.store_command("prep7", []) @@ -749,17 +889,7 @@ def translate_line(self, line): + "The previous line is: \n%s\n\n" % self.lines[-1] ) self.store_run_command(line) - if ( - not self._in_block - ): # To escape cmds that require (XX) but they are not in block - self.end_non_interactive() return - elif cmd_caps_short == "*USE" and self.macros_as_functions: - func_name = items[1].strip() - if func_name in self._functions: - args = ", ".join(items[2:]) - self.lines.append(f"{func_name}({args})") - return # check if a line is setting a variable if "=" in items[0]: # line sets a variable: @@ -781,52 +911,65 @@ def translate_line(self, line): # check valid command if ( self._pymapdl_command(command) not in self._valid_commands - or cmd_caps_short in self._non_interactive_commands + and cmd_caps_short in self._non_interactive_commands ): - if cmd_caps_short in self._non_interactive_commands: - if cmd_caps_short in self._block_commands: - self._in_block = True - self._block_count = 0 - self._block_count_target = 0 - - elif cmd_caps_short in self._enum_block_commands: - self._in_block = True - self._block_count = 0 - if cmd_caps_short == "CMBL": # In cmblock - # CMBLOCK,Cname,Entity,NUMITEMS,,,,,KOPT - numitems = int(line.split(",")[3]) - _block_count_target = ( - numitems // 8 + 1 if numitems % 8 != 0 else numitems // 8 - ) - self._block_count_target = ( - _block_count_target + 2 - ) # because the cmd_caps_short line and option line. - - self._block_current_cmd = cmd_caps_short - self.start_non_interactive() + if cmd_caps_short in self._block_commands: + self._in_block = True + self._block_count = 0 + self._block_count_target = 0 + + elif cmd_caps_short in self._enum_block_commands: + self._in_block = True + self._block_count = 0 + if cmd_caps_short == "CMBL": # In cmblock + # CMBLOCK,Cname,Entity,NUMITEMS,,,,,KOPT + numitems = int(line.split(",")[3]) + _block_count_target = ( + numitems // 8 + 1 if numitems % 8 != 0 else numitems // 8 + ) + self._block_count_target = ( + _block_count_target + 2 + ) # because the cmd_caps_short line and option line. + + self._block_current_cmd = cmd_caps_short + self.start_non_interactive() + self.store_run_command(line.strip()) + + elif self.use_function_names: + # Takign into account the leading characters + if command.upper() in FORCED_MAPPING: + # Checking exceptions/forced mapping + command = FORCED_MAPPING[command.upper()] - if self._in_block and cmd_caps_short not in self._non_interactive_commands: - self.store_run_command(original_line) else: - self.store_run_command(line) + # Looking for a suitable candidate. + if command[0] == "/": + slash_command = f"slash{command[1:4]}" + if slash_command in self._valid_commands: + command = slash_command + else: + command = command[1:] + elif command[0] == "*": + star_command = f"star{command[1:4]}" + if star_command in self._valid_commands: + command = star_command + else: + command = command[1:] + + # Some commands are abbreviated (only 4 letters) + from ansys.mapdl.core import Mapdl + + if command not in dir(Mapdl): + command = self.find_match(command) + + # Storing + if command: + self.store_command(command, parameters) + else: # find_match can return None + self.store_run_command(line.strip()) - elif self.use_function_names: - if command[0] == "/": - slash_command = f"slash{command[1:]}" - if slash_command in dir(Commands): - command = slash_command - else: - command = command[1:] - elif command[0] == "*": - star_command = f"star{command[1:]}" - if star_command in dir(Commands): - command = star_command - else: - command = command[1:] - - self.store_command(command, parameters) else: - self.store_run_command(line) + self.store_run_command(line.strip()) def _pymapdl_command(self, command): if command[0] in ["/", "*"]: @@ -839,14 +982,16 @@ def start_function(self, func_name): self.store_empty_line() self._infunction = True spacing = " " * (len(func_name) + 5) - line = "def %s(%s," % ( + line = self.indent + "def %s(%s," % ( func_name, ", ".join(["ARG%d=''" % i for i in range(1, 7)]), ) + line += "\n" line += "%s%s," % ( spacing, ", ".join(["ARG%d=''" % i for i in range(7, 13)]), ) + line += "\n" line += "%s%s):" % ( spacing, ", ".join(["ARG%d=''" % i for i in range(13, 19)]), @@ -866,22 +1011,24 @@ def store_run_command(self, command, run_underscored=False): else: underscore = "" + # Removing trailing/leading spaces + command = command.strip() + if self._infunction and "ARG" in command: args = [] for i in range(1, 19): arg = "ARG%d" % i c = 0 if arg in command: - command = command.replace(arg, "{%d:s}" % c) + command = command.replace(arg, "{" + f"{arg}" + "}") args.append(arg) c += 1 - line = '%s%s.%srun("%s".format(%s))' % ( + line = '%s%s.%srun(f"%s")' % ( self.indent, self.obj_name, underscore, command, - ", ".join(args), ) elif self.comment: @@ -910,12 +1057,15 @@ def store_empty_line(self): """Stores an empty line""" self.lines.append("") - def store_command(self, function, parameters): - """Stores a valid pyansys function with parameters""" + def store_python_command(self, command): + line = f"{self.indent}{command}" + self.lines.append(line) + + def _parse_arguments(self, parameters): parsed_parameters = [] for parameter in parameters: parameter = parameter.strip() - if is_float(parameter): + if is_float(parameter) and parameter.upper() != "INF": parsed_parameters.append(parameter) elif "ARG" in parameter and self._infunction: parsed_parameters.append("%s" % parameter) @@ -927,7 +1077,12 @@ def store_command(self, function, parameters): parameter = parameter[1:-1] parsed_parameters.append(f'"{parameter}"') - parameter_str = ", ".join(parsed_parameters) + return ", ".join(parsed_parameters) + + def store_command(self, function, parameters): + """Stores a valid pyansys function with parameters""" + parameter_str = self._parse_arguments(parameters) + if self.comment: line = "%s%s.%s(%s) # %s" % ( self.indent, @@ -957,9 +1112,9 @@ def start_non_interactive(self): def end_non_interactive(self): self._non_interactive_level -= 1 - if self._non_interactive_level == 0: - self.non_interactive = False + if self._non_interactive_level <= 0: self.indent = self.indent[4:] + self.non_interactive = False def output_to_file(self, line): """Return if an APDL line is redirecting to a file.""" @@ -1001,6 +1156,52 @@ def output_to_default(self, line): return False + def _get_items(self, line_): + """Parse the line items (comma separated elements) but ignoring the ones inside parenthesis, or brackets""" + + parenthesis_count = 0 + + items = [] + begining_substring = 0 + + for ind, each_char in enumerate(line_): + if each_char in ["(", "[", "{"]: + parenthesis_count += 1 + + if each_char == "," and parenthesis_count == 0: + items.append(line_[begining_substring:ind]) + begining_substring = ind + 1 + + if ind == len(line_) - 1: # reaching ending of line + items.append(line_[begining_substring : ind + 1]) + + if each_char in [")", "]", "}"]: + parenthesis_count -= 1 + + return items + + def _get_valid_pymapdl_methods_short(self): + pymethods = dir(Commands) + + reduced_list = [] + for each_method in pymethods: + if not re.match(r"^[\*~/A-Za-z]\w*$", each_method): + continue + if each_method.startswith("slash"): + reduced_list.append(each_method[:8]) + elif each_method.startswith("star"): + reduced_list.append(each_method[:7]) + else: + reduced_list.append(each_method[:4]) + return reduced_list + + def find_match(self, cmd): + pymethods = sorted(dir(Commands)) + + for each in pymethods: + if each.startswith(cmd): + return each + import click @@ -1011,60 +1212,89 @@ def output_to_default(self, line): @click.option("--filename_out", default=None, help="Name of the output Python script.") @click.option( "--loglevel", - default="WARNING", + default=LOGLEVEL_DEFAULT, help="Logging level of the ansys object within the script.", ) @click.option( "--auto_exit", - default=True, + default=AUTO_EXIT_DEFAULT, help="Adds a line to the end of the script to exit MAPDL. Default ``True``", ) -@click.option("--line_ending", default=None, help="When None, automatically is ``\n.``") +@click.option( + "--line_ending", + default=LINE_ENDING_DEFAULT, + help="When None, automatically is ``\n.``", +) @click.option( "--exec_file", - default=None, + default=EXEC_FILE_DEFAULT, help="Specify the location of the ANSYS executable and include it in the converter output ``launch_mapdl`` call.", ) @click.option( "--macros_as_functions", - default=True, + default=MACROS_AS_FUNCTIONS_DEFAULT, help="Attempt to convert MAPDL macros to python functions.", ) @click.option( "--use_function_names", - default=True, + default=USE_FUNCTION_NAMES_DEFAULT, help="Convert MAPDL functions to ansys.mapdl.core.Mapdl class methods. When ``True``, the MAPDL command ``K`` will be converted to ``mapdl.k``. When ``False``, it will be converted to ``mapdl.run('k')``.", ) @click.option( "--show_log", - default=False, + default=SHOW_LOG_DEFAULT, help="Print the converted commands using a logger (from ``logging`` Python module).", ) @click.option( "--add_imports", - default=True, + default=ADD_IMPORTS_DEFAULT, help='If ``True``, add the lines ``from ansys.mapdl.core import launch_mapdl`` and ``mapdl = launch_mapdl(loglevel="WARNING")`` to the beginning of the output file. This option is useful if you are planning to use the output script from another mapdl session. See examples section. This option overrides ``auto_exit``.', ) @click.option( "--comment_solve", - default=False, + default=COMMENT_SOLVE_DEFAULT, help='If ``True``, it will pythonically comment the lines that contain ``"SOLVE"`` or ``"/EOF"``.', ) @click.option( "--cleanup_output", - default=True, + default=CLEANUP_OUTPUT_DEFAULT, help="If ``True`` the output is formatted using ``autopep8`` before writing the file or returning the string. This requires ``autopep8`` to be installed.", ) @click.option( "--header", - default=True, + default=HEADER_DEFAULT, help="If ``True``, the default header is written in the first line of the output. If a string is provided, this string will be used as header.", ) @click.option( "--print_com", - default=True, + default=PRINT_COM_DEFAULT, help="Print command ``/COM`` arguments to python console. Defaults to ``True``.", ) +@click.option( + "--only_commands", + default=ONLY_COMMANDS_DEFAULT, + help="""converts only the commands, meaning that header + (``header=False``), imports (``add_imports=False``), + and exit commands are NOT included (``auto_exit=False``). + Overrides ``header``, ``add_imports`` and ``auto_exit``.""", +) +@click.option( + "--use_vtk", + default=USE_VTK_DEFAULT, + help="""It sets the `mapdl.use_vtk` argument equals True or False depending on + this value.""", +) +@click.option( + "--clear_at_start", + default=CLEAR_AT_START_DEFAULT, + help="""Add a `mapdl.clear()` after the Mapdl object initialization. Defaults to + `False`.""", +) +@click.option( + "--check_parameter_names", + default=CHECK_PARAMETER_NAMES_DEFAULT, + help="""Set MAPDL object to avoid parameter name checks (do not raise leading underscored parameter exceptions). Defaults to `True`.""", +) def cli( filename_in, o, @@ -1081,6 +1311,10 @@ def cli( cleanup_output, header, print_com, + only_commands, + use_vtk, + clear_at_start, + check_parameter_names, ): """PyMAPDL CLI tool for converting MAPDL scripts to PyMAPDL scripts. @@ -1113,6 +1347,30 @@ def cli( if o: filename_out = o + # Parsing commands: + loglevel = _parse_cli_command(loglevel, LOGLEVEL_DEFAULT) + auto_exit = _parse_cli_command(auto_exit, AUTO_EXIT_DEFAULT) + line_ending = _parse_cli_command(line_ending, LINE_ENDING_DEFAULT) + exec_file = _parse_cli_command(exec_file, EXEC_FILE_DEFAULT) + macros_as_functions = _parse_cli_command( + macros_as_functions, MACROS_AS_FUNCTIONS_DEFAULT + ) + use_function_names = _parse_cli_command( + use_function_names, USE_FUNCTION_NAMES_DEFAULT + ) + show_log = _parse_cli_command(show_log, SHOW_LOG_DEFAULT) + add_imports = _parse_cli_command(add_imports, ADD_IMPORTS_DEFAULT) + comment_solve = _parse_cli_command(comment_solve, COMMENT_SOLVE_DEFAULT) + cleanup_output = _parse_cli_command(cleanup_output, CLEANUP_OUTPUT_DEFAULT) + header = _parse_cli_command(header, HEADER_DEFAULT) + print_com = _parse_cli_command(print_com, PRINT_COM_DEFAULT) + only_commands = _parse_cli_command(only_commands, ONLY_COMMANDS_DEFAULT) + use_vtk = _parse_cli_command(use_vtk, USE_VTK_DEFAULT) + clear_at_start = _parse_cli_command(clear_at_start, CLEAR_AT_START_DEFAULT) + check_parameter_names = _parse_cli_command( + check_parameter_names, CHECK_PARAMETER_NAMES_DEFAULT + ) + convert_script( filename_in, filename_out, @@ -1128,6 +1386,10 @@ def cli( cleanup_output, header, print_com, + only_commands, + use_vtk, + clear_at_start, + check_parameter_names, ) if filename_out: @@ -1136,3 +1398,17 @@ def cli( print( f"File {filename_in} successfully converted to {os.path.splitext(filename_in)[0] + '.py'}." ) + + +def _parse_cli_command(command, default=None): + if isinstance(command, bool): + return command + if command is None: + return + + if command.upper() == "TRUE": + return True + elif command.upper() == "FALSE": + return False + else: + return default diff --git a/src/ansys/mapdl/core/examples/verif/vm132.dat b/src/ansys/mapdl/core/examples/verif/vm132.dat index 85cc37dbcb..98c7483d6c 100755 --- a/src/ansys/mapdl/core/examples/verif/vm132.dat +++ b/src/ansys/mapdl/core/examples/verif/vm132.dat @@ -22,7 +22,7 @@ D,ALL,ALL ! FIX ALL DOFS FINISH /SOLU NSUBST,100,100,100 -RATE,ON,ON +RATE,ON OUTPR,BASIC,10 ! PRINT BASIC SOLUTION FOR EVERY 10TH SUBSTEP OUTRES,ESOL,1 ! STORE ELEMENT SOLUTION FOR EVERY SUBSTEP /OUT,SCRATCH diff --git a/src/ansys/mapdl/core/examples/verif/vm139.dat b/src/ansys/mapdl/core/examples/verif/vm139.dat index 863ef1c87c..cf126a7f0b 100755 --- a/src/ansys/mapdl/core/examples/verif/vm139.dat +++ b/src/ansys/mapdl/core/examples/verif/vm139.dat @@ -25,7 +25,7 @@ D,1,UX,,,,,UY,UZ D,11,UX,,,,,UZ D,ALL,ROTZ NSEL,S,LOC,X,22.5 -DSYM,SYMM,X,0,22.5 ! SYMMETRY B.C.'S AT CENTERLINE +DSYM,SYMM,X,0 ! SYMMETRY B.C.'S AT CENTERLINE NSEL,ALL FINISH /SOLU @@ -100,7 +100,7 @@ NSEL,R,LOC,Y,0 D,ALL,UY ALLSEL,ALL NSEL,S,LOC,X,22.5 -DSYM,SYMM,X,0,22.5 ! SYMMETRY B.C.'S AT CENTERLINE +DSYM,SYMM,X,0 ! SYMMETRY B.C.'S AT CENTERLINE NSEL,ALL FINISH /SOLU diff --git a/src/ansys/mapdl/core/examples/verif/vm147.dat b/src/ansys/mapdl/core/examples/verif/vm147.dat index 0b34f0f978..29676c2de1 100755 --- a/src/ansys/mapdl/core/examples/verif/vm147.dat +++ b/src/ansys/mapdl/core/examples/verif/vm147.dat @@ -26,7 +26,7 @@ VTYPE,1 ! NON-HIDDEN (FAST) METHOD GEOM,1,50 ! 2-D AXISYMMETRIC GEOM WITH 50 FACETS MPRINT,1 STEF,5.6696E-8 ! STEFAN-BOLTZMANN CONSTANT IN MKS UNITS -WRITE,CONE,SUB ! WRITE RADIATION SUBSTRUCTURE MATRIX +WRITE,CONE ! WRITE RADIATION SUBSTRUCTURE MATRIX FINISH /CLEAR,NOSTART ! CLEAR DATABASE; DO NOT READ START.ANS FILE /PREP7 diff --git a/src/ansys/mapdl/core/examples/verif/vm221.dat b/src/ansys/mapdl/core/examples/verif/vm221.dat index 523bbcbb5e..07f4b5ac29 100755 --- a/src/ansys/mapdl/core/examples/verif/vm221.dat +++ b/src/ansys/mapdl/core/examples/verif/vm221.dat @@ -24,7 +24,7 @@ C6=70E3 !MPA, [MARTENSITE MODULUS] C7=0 !M = 0, SYMMETRICAL BEHAVIOR TB,SMA,1,,7,MEFF -TBDATA,1,C1,C2,C3,C4,C5,C6,C7 +TBDATA,1,C1,C2,C3,C4,C5,C6 BLOCK,0.00,5.00,0.00,5.00,0.00,5.00 ESIZE,5 diff --git a/src/ansys/mapdl/core/examples/verif/vm227.dat b/src/ansys/mapdl/core/examples/verif/vm227.dat index eacfe91d18..7f8fec704c 100755 --- a/src/ansys/mapdl/core/examples/verif/vm227.dat +++ b/src/ansys/mapdl/core/examples/verif/vm227.dat @@ -28,7 +28,7 @@ SFL,8,RDSF,1, ,1, FINISH /AUX12 STEF,0.119E-10 !SET STEFAN-BOLTZMAN CONSTANT FOR MODEL -hemiopt,,,,,,,,,,,,,,,,,,,0 +hemiopt TOFFST,100 !SET TEMPERATURE OFFSET RADOPT,,0.1,2,1000,0.1,0.1 !SET RADIOSITY OPTIONS SPCTEMP,1,0.E+00 !SET TEMPERATURE FOR RADIATION TO SPACE diff --git a/src/ansys/mapdl/core/examples/verif/vm273.dat b/src/ansys/mapdl/core/examples/verif/vm273.dat index a4f5ed1dcf..f0145eed29 100755 --- a/src/ansys/mapdl/core/examples/verif/vm273.dat +++ b/src/ansys/mapdl/core/examples/verif/vm273.dat @@ -27,7 +27,7 @@ C6=70E3 !MPA, [MARTENSITE MODULUS] C7=0 ! M = 0, SYMMETRICAL BEHAVIOR TB,SMA,1,,7,MEFF -TBDATA,1,C1,C2,C3,C4,C5,C6,C7 +TBDATA,1,C1,C2,C3,C4,C5,C6 BLOCK,0.00,10.00,0.00,10.00,0.00,10.00 ESIZE,10 diff --git a/src/ansys/mapdl/core/examples/verif/vm285.dat b/src/ansys/mapdl/core/examples/verif/vm285.dat index 8c06bd0da1..c33f584b02 100644 --- a/src/ansys/mapdl/core/examples/verif/vm285.dat +++ b/src/ansys/mapdl/core/examples/verif/vm285.dat @@ -46,7 +46,7 @@ A,1,2,3,4,7 A,7,4,5,6 AGLUE,ALL -KSCON,2,A/20,0,12,,10 ! CRACK TIP ELEMENTS +KSCON,2,A/20,0,12,, ! CRACK TIP ELEMENTS ESIZE,0.3 AMESH,1 diff --git a/src/ansys/mapdl/core/examples/verif/vm287.dat b/src/ansys/mapdl/core/examples/verif/vm287.dat index 8fec708652..a3aec45a2d 100644 --- a/src/ansys/mapdl/core/examples/verif/vm287.dat +++ b/src/ansys/mapdl/core/examples/verif/vm287.dat @@ -70,7 +70,7 @@ ALLSEL XFENRICH,ENRICH1,TESTCMP,1 FLST,3,2,8 FITEM,3,0.0,5.0,0 -FITEM,3,5.0,5.0,0,0 +FITEM,3,5.0,5.0,0 XFDATA,ENRICH1,P51X ALLSEL /OUT diff --git a/src/ansys/mapdl/core/examples/verif/vm309.dat b/src/ansys/mapdl/core/examples/verif/vm309.dat index 0eae3c8044..ae5776c976 100644 --- a/src/ansys/mapdl/core/examples/verif/vm309.dat +++ b/src/ansys/mapdl/core/examples/verif/vm309.dat @@ -95,7 +95,7 @@ FINISH OUTRES,ALL,ALL ANTYPE,1 BUCOPT,LANB,3 -SUBOPT,0,0,0,0,0,ALL +SUBOPT,0,0 MXPAND,3,,,YES SOLVE FINISH diff --git a/src/ansys/mapdl/core/examples/verif/vm51.dat b/src/ansys/mapdl/core/examples/verif/vm51.dat index cc02967b5e..69cbeafcaa 100755 --- a/src/ansys/mapdl/core/examples/verif/vm51.dat +++ b/src/ansys/mapdl/core/examples/verif/vm51.dat @@ -1,5 +1,4 @@ /COM,ANSYS MEDIA REL. 2023R2 (05/12/2023) REF. VERIF. MANUAL: REL. 2023R2 -/OUT,SCRATCH /VERIFY,VM51 JPGPRF,500,100,1 ! MACRO TO SET PREFS FOR JPEG PLOTS /SHOW,JPEG diff --git a/src/ansys/mapdl/core/launcher.py b/src/ansys/mapdl/core/launcher.py index a11834df37..d68872e387 100644 --- a/src/ansys/mapdl/core/launcher.py +++ b/src/ansys/mapdl/core/launcher.py @@ -32,6 +32,7 @@ VersionError, ) from ansys.mapdl.core.licensing import ALLOWABLE_LICENSES, LicenseChecker +from ansys.mapdl.core.mapdl import _ALLOWED_START_PARM from ansys.mapdl.core.mapdl_grpc import MAX_MESSAGE_LENGTH, MapdlGrpc from ansys.mapdl.core.misc import ( check_valid_ip, @@ -1355,6 +1356,15 @@ def launch_mapdl( # Extract arguments: force_intel = kwargs.pop("force_intel", False) broadcast = kwargs.pop("log_broadcast", False) + use_vtk = kwargs.pop("use_vtk", None) + + # Transferring MAPDL arguments to start_parameters: + start_parm = {} + + kwargs_keys = list(kwargs.keys()) + for each_par in kwargs_keys: + if each_par in _ALLOWED_START_PARM: + start_parm[each_par] = kwargs.pop(each_par) # Raising error if using non-allowed arguments if kwargs: @@ -1449,6 +1459,7 @@ def launch_mapdl( cleanup_on_exit=False, loglevel=loglevel, set_no_abort=set_no_abort, + **start_parm, ) GALLERY_INSTANCE[0] = {"ip": mapdl._ip, "port": mapdl._port} return mapdl @@ -1461,6 +1472,8 @@ def launch_mapdl( cleanup_on_exit=False, loglevel=loglevel, set_no_abort=set_no_abort, + use_vtk=use_vtk, + **start_parm, ) if clear_on_connect: mapdl.clear() @@ -1474,6 +1487,8 @@ def launch_mapdl( cleanup_on_exit=False, loglevel=loglevel, set_no_abort=set_no_abort, + use_vtk=use_vtk, + **start_parm, ) if clear_on_connect: mapdl.clear() @@ -1489,6 +1504,8 @@ def launch_mapdl( loglevel=loglevel, set_no_abort=set_no_abort, log_apdl=log_apdl, + use_vtk=use_vtk, + **start_parm, ) if clear_on_connect: mapdl.clear() @@ -1556,14 +1573,16 @@ def launch_mapdl( additional_switches = _check_license_argument(license_type, additional_switches) LOG.debug(f"Using additional switches {additional_switches}.") - start_parm = { - "exec_file": exec_file, - "run_location": run_location, - "additional_switches": additional_switches, - "jobname": jobname, - "nproc": nproc, - "print_com": print_com, - } + start_parm.update( + { + "exec_file": exec_file, + "run_location": run_location, + "additional_switches": additional_switches, + "jobname": jobname, + "nproc": nproc, + "print_com": print_com, + } + ) if mode in ["console", "corba"]: start_parm["start_timeout"] = start_timeout @@ -1585,7 +1604,9 @@ def launch_mapdl( if mode == "console": from ansys.mapdl.core.mapdl_console import MapdlConsole - mapdl = MapdlConsole(loglevel=loglevel, log_apdl=log_apdl, **start_parm) + mapdl = MapdlConsole( + loglevel=loglevel, log_apdl=log_apdl, use_vtk=use_vtk, **start_parm + ) elif mode == "corba": try: # pending deprecation to ansys-mapdl-corba @@ -1601,6 +1622,7 @@ def launch_mapdl( log_apdl=log_apdl, log_broadcast=broadcast, verbose=verbose_mapdl, + use_vtk=use_vtk, **start_parm, ) elif mode == "grpc": @@ -1621,6 +1643,7 @@ def launch_mapdl( remove_temp_dir_on_exit=remove_temp_dir_on_exit, log_apdl=log_apdl, process=process, + use_vtk=use_vtk, **start_parm, ) if run_location is None: diff --git a/src/ansys/mapdl/core/mapdl.py b/src/ansys/mapdl/core/mapdl.py index 1ba922e538..a34959f80e 100644 --- a/src/ansys/mapdl/core/mapdl.py +++ b/src/ansys/mapdl/core/mapdl.py @@ -193,7 +193,9 @@ def setup_logger(loglevel="INFO", log_file=True, mapdl_instance=None): "process", "ram", "run_location", - "start_timeout" "timeout", + "start_timeout", + "timeout", + "check_parameter_names", ] @@ -267,6 +269,7 @@ def __init__( self._jobname: str = start_parm.get("jobname", "file") self._path: Union[str, pathlib.Path] = start_parm.get("run_location", None) self._print_com: bool = print_com # print the command /COM input. + self.check_parameter_names = start_parm.get("check_parameter_names", True) # Setting up loggers self._log: logging.Logger = logger.add_instance_logger( @@ -366,6 +369,9 @@ def check_status(self): @property def file_type_for_plots(self): """Returns the current file type for plotting.""" + if not self._file_type_for_plots: + self._run("/show, PNG") + self._file_type_for_plots = "PNG" return self._file_type_for_plots @file_type_for_plots.setter @@ -475,6 +481,10 @@ def wrap_xsel_function_output(method): @wraps(func) def inner_wrapper(*args, **kwargs): # in interactive mode (item='p'), the output is not suppressed + if self._store_commands: + # In non-interactive mode, we do not need to check anything. + return + is_interactive_arg = ( True if len(args) >= 2 @@ -2019,27 +2029,31 @@ def __enter__(self) -> None: "``vtk=True``" ) - if not self._parent()._png_mode: - self._parent().show("PNG", mute=True) - self._parent().gfile(self._pixel_res, mute=True) + if not self._parent()._store_commands: + if not self._parent()._png_mode: + self._parent().show("PNG", mute=True) + self._parent().gfile(self._pixel_res, mute=True) - self.previous_device = self._parent().file_type_for_plots + self.previous_device = self._parent().file_type_for_plots - if self._parent().file_type_for_plots not in ["PNG", "TIFF", "PNG", "VRML"]: - self._parent().show(self._parent().default_file_type_for_plots) + if self._parent().file_type_for_plots not in [ + "PNG", + "TIFF", + "PNG", + "VRML", + ]: + self._parent().show(self._parent().default_file_type_for_plots) def __exit__(self, *args) -> None: self._parent()._log.debug("Exiting in 'WithInterativePlotting' mode") self._parent().show("close", mute=True) - if not self._parent()._png_mode: - self._parent().show("PNG", mute=True) - self._parent().gfile(self._pixel_res, mute=True) - self._parent().file_type_for_plots = self.previous_device + if not self._parent()._store_commands: + if not self._parent()._png_mode: + self._parent().show("PNG", mute=True) + self._parent().gfile(self._pixel_res, mute=True) - def __exit__(self, *args) -> None: - self._parent()._log.debug("Exiting in 'WithInterativePlotting' mode") - self._parent().show("close", mute=True) + self._parent().file_type_for_plots = self.previous_device @property def _has_matplotlib(self): @@ -2053,7 +2067,8 @@ def _has_matplotlib(self): @property def _png_mode(self): """Returns True when MAPDL is set to write plots as png to file.""" - return "PNG" in self.show(mute=False) + with self.force_output: + return "PNG" in self.show(mute=False) def set_log_level(self, loglevel: DEBUG_LEVELS) -> None: """Sets log level @@ -2786,6 +2801,10 @@ def get( with self.force_output: response = self.run(command, **kwargs) + if self._store_commands: + # Return early in non_interactive + return + value = response.split("=")[-1].strip() if item3: self._log.info( @@ -3247,7 +3266,7 @@ def run( command = "/CLE,NOSTART" # Tracking output device - if command[:4].upper() == "/SHO": + if command[:4].upper() == "/SHO" and "," in command: self._file_type_for_plots = command.split(",")[1].upper() # Invalid commands silently ignored. @@ -3290,8 +3309,17 @@ def run( # Edge case. `\title, 'par=1234' ` self._check_parameter_name(param_name) + short_cmd = parse_to_short_cmd(command) text = self._run(command, verbose=verbose, mute=mute) + if ( + "Display device has not yet been specified with the /SHOW command" in text + and short_cmd in PLOT_COMMANDS + ): + # Reissuing the command to make sure we get output. + self.show(self.default_file_type_for_plots) + text = self._run(command, verbose=verbose, mute=mute) + if command[:4].upper() == "/CLE" and self.is_grpc: # We have reset the database, so we need to create a new session id self._create_session() @@ -3311,8 +3339,6 @@ def run( self._raise_errors(text) # special returns for certain geometry commands - short_cmd = parse_to_short_cmd(command) - if short_cmd in PLOT_COMMANDS: self._log.debug("It is a plot command.") plot_path = self._get_plot_name(text) @@ -3981,6 +4007,9 @@ def get_nodal_constrains(self, label=None): def _check_parameter_name(self, param_name): """Checks if a parameter name is allowed or not.""" + if not self.check_parameter_names: + return + param_name = param_name.strip() match_valid_parameter_name = r"^[a-zA-Z_][a-zA-Z\d_\(\),\s\%]{0,31}$" diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py index b13e3e5ed0..c9c119d2de 100644 --- a/src/ansys/mapdl/core/mapdl_grpc.py +++ b/src/ansys/mapdl/core/mapdl_grpc.py @@ -873,6 +873,7 @@ def _run_at_connect(self): self.show(self._file_type_for_plots) self.version # Caching version + self.file_type_for_plots # Setting /show,png and caching it. def _reset_cache(self): """Reset cached items.""" @@ -1811,11 +1812,6 @@ def input( if not self._apdl_log.closed: self._apdl_log.write(tmp_dat) - # Escaping early if inside non_interactive context - if self._store_commands: - self._stored_commands.append(tmp_dat.splitlines()[1]) - return None - if self._local: local_path = self.directory tmp_name_path = os.path.join(local_path, tmp_name) @@ -1824,6 +1820,11 @@ def input( else: self._upload_raw(tmp_dat.encode(), tmp_name) + # Escaping early if inside non_interactive context + if self._store_commands: + self._stored_commands.append(tmp_dat.splitlines()[1]) + return None + request = pb_types.InputFileRequest(filename=tmp_name) # even though we don't care about the output, we still @@ -1896,9 +1897,13 @@ def _get_file_path(self, fname: str, progress_bar: bool = False) -> str: if os.path.isfile(fname): # And it exists filename = os.path.join(os.getcwd(), fname) - elif fname in self.list_files(): + elif not self._store_commands and fname in self.list_files(): # It exists in the Mapdl working directory filename = os.path.join(self.directory, fname) + elif self._store_commands: + # Assuming that in non_interactive we have uploaded the file + # manually. + filename = os.path.join(self.directory, fname) else: # Finally raise FileNotFoundError(f"Unable to locate filename '{fname}'") @@ -1909,10 +1914,15 @@ def _get_file_path(self, fname: str, progress_bar: bool = False) -> str: self.upload(ffullpath, progress_bar=progress_bar) filename = fname - elif fname in self.list_files(): + elif not self._store_commands and fname in self.list_files(): # It exists in the Mapdl working directory filename = fname + elif self._store_commands: + # Assuming that in non_interactive, the file exists already in + # the Mapdl working directory + filename = fname + else: raise FileNotFoundError(f"Unable to locate filename '{fname}'") @@ -3034,7 +3044,8 @@ def vget( ) -> NDArray[np.float64]: """Wraps VGET""" super().vget(par=par, ir=ir, tstrt=tstrt, kcplx=kcplx, **kwargs) - return self.parameters[par] + if not self._store_commands: + return self.parameters[par] def get_variable( self, diff --git a/src/ansys/mapdl/core/misc.py b/src/ansys/mapdl/core/misc.py index 4b526712e7..29aa153e7f 100644 --- a/src/ansys/mapdl/core/misc.py +++ b/src/ansys/mapdl/core/misc.py @@ -1249,13 +1249,13 @@ def wrapper(self, *args, **kwargs): else: return original_sel_func( self, - type_=type_, - item=item, - comp=comp, - vmin=vmin, - vmax=vmax, - vinc=vinc, - kabs=kabs, + type_, + item, + comp, + vmin, + vmax, + vinc, + kabs, # ksel, esel, nsel uses kabs, but lsel, asel, vsel uses kswp **kwargs, ) diff --git a/src/ansys/mapdl/core/post.py b/src/ansys/mapdl/core/post.py index 702a6c676a..1a4c95f295 100644 --- a/src/ansys/mapdl/core/post.py +++ b/src/ansys/mapdl/core/post.py @@ -3,6 +3,7 @@ import numpy as np +from ansys.mapdl.core.errors import MapdlRuntimeError from ansys.mapdl.core.misc import supress_logging from ansys.mapdl.core.plotting import general_plotter diff --git a/tests/test_convert.py b/tests/test_convert.py index bfaceae501..6f088cdeda 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -43,34 +43,34 @@ pynblock = """with mapdl.non_interactive: mapdl.run("nblock,3,,326253") mapdl.run("(1i9,3e20.9e3)") - mapdl.run(" 1 3.352881632E-03 1.110639271E-02 5.172433282E-03") - mapdl.run(" 2 3.485685736E-03 1.110981270E-02 4.999255638E-03") - mapdl.run(" 3 3.615164748E-03 1.111323677E-02 4.823719994E-03") - mapdl.run(" 4 3.673859471E-03 1.111439119E-02 4.740300611E-03") - mapdl.run(" 5 3.709417144E-03 1.111407057E-02 4.691582629E-03") + mapdl.run("1 3.352881632E-03 1.110639271E-02 5.172433282E-03") + mapdl.run("2 3.485685736E-03 1.110981270E-02 4.999255638E-03") + mapdl.run("3 3.615164748E-03 1.111323677E-02 4.823719994E-03") + mapdl.run("4 3.673859471E-03 1.111439119E-02 4.740300611E-03") + mapdl.run("5 3.709417144E-03 1.111407057E-02 4.691582629E-03") mapdl.run("-1")""" pyeblock = """with mapdl.non_interactive: mapdl.run("eblock,19,solid,,6240") mapdl.run("(19i9)") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38161 186586 186589 192999 193065 191265 191262 193063 193064") - mapdl.run(" 194712 194731 213866 194716 210305 210306 213993 210310 194715 194730 213865 213995") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38162 186586 193065 192999 186589 186781 193066 192935 186784") - mapdl.run(" 194716 213866 194731 194712 195560 213737 195572 195557 194714 213997 213736 194729") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38163 186781 193066 192935 186784 186976 193067 192871 186979") - mapdl.run(" 195560 213737 195572 195557 196210 213608 196222 196207 195559 213998 213607 195571") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38164 186976 193067 192871 186979 187171 193068 192807 187174") - mapdl.run(" 196210 213608 196222 196207 196860 213479 196872 196857 196209 213999 213478 196221")""" + mapdl.run("1 1 1 1 0 0 0 0 20 0 38161 186586 186589 192999 193065 191265 191262 193063 193064") + mapdl.run("194712 194731 213866 194716 210305 210306 213993 210310 194715 194730 213865 213995") + mapdl.run("1 1 1 1 0 0 0 0 20 0 38162 186586 193065 192999 186589 186781 193066 192935 186784") + mapdl.run("194716 213866 194731 194712 195560 213737 195572 195557 194714 213997 213736 194729") + mapdl.run("1 1 1 1 0 0 0 0 20 0 38163 186781 193066 192935 186784 186976 193067 192871 186979") + mapdl.run("195560 213737 195572 195557 196210 213608 196222 196207 195559 213998 213607 195571") + mapdl.run("1 1 1 1 0 0 0 0 20 0 38164 186976 193067 192871 186979 187171 193068 192807 187174") + mapdl.run("196210 213608 196222 196207 196860 213479 196872 196857 196209 213999 213478 196221")""" pycmblock = """with mapdl.non_interactive: mapdl.run("CMBLOCK,PRESSURE_AREAS,NODE, 48") mapdl.run("(8i10)") - mapdl.run(" 1688 1689 1690 1691 1700 1701 1702 1703") - mapdl.run(" 1704 1705 1706 1707 1708 1709 1710 1711") - mapdl.run(" 1712 1721 1723 1731 1736 1754 1755 1756") - mapdl.run(" 1757 1758 1759 1760 1761 1762 1763 1764") - mapdl.run(" 1765 1766 1767 1768 1769 1802 1803 1804") - mapdl.run(" 1805 1806 1807 1808 1809 1831 1832 1833")""" + mapdl.run("1688 1689 1690 1691 1700 1701 1702 1703") + mapdl.run("1704 1705 1706 1707 1708 1709 1710 1711") + mapdl.run("1712 1721 1723 1731 1736 1754 1755 1756") + mapdl.run("1757 1758 1759 1760 1761 1762 1763 1764") + mapdl.run("1765 1766 1767 1768 1769 1802 1803 1804") + mapdl.run("1805 1806 1807 1808 1809 1831 1832 1833")""" block_commands = ["nblock", "eblock", "cmblock"] @@ -142,6 +142,25 @@ *END *USE,SLV""" +DO_CONVERSION = """with mapdl.non_interactive: + mapdl.run("*DO,I,1,81") # REPEAT MACRO EXECUTION""" + +GOLDEN_TESTS = { + "/DELETE,TABLE_1": 'mapdl.slashdelete("TABLE_1")', + "PROD,4,3, , ,FORCE , , ,-1.0,1,1,": 'mapdl.run("PROD,4,3, , ,FORCE , , ,-1.0,1,1")', + "ALLSEL,ALL": 'mapdl.allsel("ALL")', + "/EXIT,NOSAVE": 'mapdl.exit("NOSAVE")', + "": "", + "*DO,I,1,81 ! REPEAT MACRO EXECUTION": DO_CONVERSION, + " *USE,LOAD ! EXECUTE MACRO": 'mapdl.use("LOAD") # EXECUTE MACRO', + "*ENDDO": 'mapdl.run("*ENDDO")', + "SECT,1,SHELL": 'mapdl.sectype(1, "SHELL")', + "SECD,.00005,1 ! PLATE THICKNESS": "mapdl.secdata(.00005, 1) # PLATE THICKNESS", + "/show, asdf": 'mapdl.show("asdf")', + "*STAT,UXFEA2 ": 'mapdl.starstatus("UXFEA2")', + "/AXLAB,X,NORMALIZED TIME,TAU=ALPHA**2*D*t": 'mapdl.axlab("X", "NORMALIZED TIME,TAU=ALPHA**2*D*t")', +} + def test_convert_no_use_function_names(tmpdir): vm_file = examples.vmfiles["vm1"] @@ -318,9 +337,16 @@ def test_header(): assert '"""My header"""' in convert_apdl_block("/com", header="My header") -def test_com(): - converted_output = convert_apdl_block( +@pytest.mark.parametrize( + "cmd", + [ "/com, this is a comment !inline comment!", + "C***, this is a comment !inline comment!", + ], +) +def test_com(cmd): + converted_output = convert_apdl_block( + cmd, header=False, add_imports=False, ) @@ -360,13 +386,45 @@ def test_repeat(): ) -@pytest.mark.parametrize("cmd", COMMANDS_TO_NOT_BE_CONVERTED) -def test_commands_to_not_be_converted(cmd): - assert f'mapdl.run("{cmd}")' in convert_apdl_block( +@pytest.mark.parametrize( + "cmd", + [ + "/PMORE", # "/PMORE, + "ANTYPE", # ANTYPE, + "ASBL", # ASBL, + "ATAN", # ATAN, + ], +) +def test_empty_arguments_2(cmd): + # Checking trailing commas does not avoid conversion + assert f'mapdl.run("{cmd}")' not in convert_apdl_block( + cmd + ",,", header=False, add_imports=False + ) + + # Checking empty arguments avoid conversion + assert f'mapdl.run("{cmd.upper()},,OTHER_ARG")' in convert_apdl_block( + cmd + ",,OTHER_ARG", header=False, add_imports=False + ) + + # Checking default conversion + assert f'mapdl.run("{cmd}")' not in convert_apdl_block( cmd, header=False, add_imports=False ) +def test_commands_with_empty_arguments(): + cmd = """ANTYPE,STATIC ! STATIC ANALYSIS +ANTYPE,STATIC,,NON_EMPTY_ARGUMENT + +ANTYPE,STATIC,,,""" + pycmd = """mapdl.antype("STATIC") # STATIC ANALYSIS +mapdl.run("ANTYPE,STATIC,,NON_EMPTY_ARGUMENT") + +mapdl.antype("STATIC")""" + + assert pycmd in convert_apdl_block(cmd, header=False, add_imports=False) + + @pytest.mark.parametrize("ending", ["\n", "\r\n"]) def test_detect_line_ending(ending): assert ending in convert_apdl_block( @@ -387,7 +445,7 @@ def test_no_macro_as_functions(): APDL_MACRO, macros_as_functions=False, add_imports=False, header=False ) assert "with mapdl.non_interactive" in output - assert ' mapdl.run("*CREATE,SLV")' in output + assert ' mapdl.create("SLV")' in output assert ' mapdl.run("*END")' in output @@ -427,6 +485,54 @@ def test_only_commands(): assert "mapdl.exit" not in output +@pytest.mark.parametrize("vtk", [None, True, False]) +def test_use_vtk(vtk): + output = convert_apdl_block( + "/view,1,1,1", + only_commands=False, + add_imports=True, + use_vtk=vtk, + ) + assert "mapdl.view(1, 1, 1)" in output + assert "launch_mapdl" in output + if vtk is None: + assert "use_vtk" not in output + else: + assert f"use_vtk={vtk}" in output + + +@pytest.mark.parametrize("check_parameter_names", [None, True, False]) +def test_check_parameter_names(check_parameter_names): + output = convert_apdl_block( + "/view,1,1,1", + only_commands=False, + add_imports=True, + check_parameter_names=check_parameter_names, + ) + assert "mapdl.view(1, 1, 1)" in output + assert "launch_mapdl" in output + if check_parameter_names is not None and not check_parameter_names: + assert "check_parameter_names=False" in output + else: + assert f"check_parameter_names" not in output + + +@pytest.mark.parametrize("clear_at_start", [None, True, False]) +def test_clear_at_start(clear_at_start): + output = convert_apdl_block( + "/view,1,1,1", + only_commands=False, + add_imports=True, + clear_at_start=clear_at_start, + ) + assert "mapdl.view(1, 1, 1)" in output + assert "launch_mapdl" in output + if clear_at_start: + assert "mapdl.clear()" in output + else: + assert "mapdl.clear()" not in output + + @pytest.mark.parametrize( "parameters", [ @@ -452,6 +558,30 @@ def test_convert_star_slash(parameters): assert convert_apdl_block(parameters[0], only_commands=True) == parameters[1] +def test_parsing_items(): + cmd = "VGET,VALUE7(2,2),2,7E-2" + conv_cmd = convert_apdl_block(cmd, only_commands=True) + assert 'mapdl.vget("VALUE7(2,2)", 2, 7E-2)' in conv_cmd + + +def test_macros_call(): + cmd = """ +*create,myfunc +/prep7 +*end +myfunc +""" + conv_cmd = convert_apdl_block(cmd, only_commands=True) + assert "mapdl.prep7()" in conv_cmd + assert "def myfunc(" in conv_cmd + assert "myfunc()" in conv_cmd + + +@pytest.mark.parametrize("mapdl_cmd", GOLDEN_TESTS.keys()) +def test_golden(mapdl_cmd): + assert GOLDEN_TESTS[mapdl_cmd] == convert_apdl_block(mapdl_cmd, only_commands=True) + + ## CLI testing @@ -510,3 +640,32 @@ def test_converter_cli(tmpdir, run_cli): assert "mapdl.prep7()" in newcontent assert "mapdl.exit()" not in newcontent assert "launch_mapdl" not in newcontent + + +def test_exit_in_non_interactive(): + cmd = """ +*do,i,1,10 +/exit +*enddo""" + output = convert_apdl_block(cmd, only_commands=True) + + assert 'mapdl.run("*do,i,1,10")' in output + assert 'mapdl.com("Skipping: /exit")' in output + assert 'mapdl.run("*enddo")' in output + assert "mapdl.exit" not in output + + +@pytest.mark.parametrize("cmd", COMMANDS_TO_NOT_BE_CONVERTED) +def test_commands_to_not_be_converted(cmd): + assert f'mapdl.run("{cmd}")' in convert_apdl_block(cmd, only_commands=True) + + +def test_vwrite(): + cmd = """*VWRITE ! WRITE OUTPUT IN TABULAR FORMAT +(///T14,'MODE',T24,'COEFF',T34,'ISYM',/)""" + + pycmd = """with mapdl.non_interactive: + mapdl.run("*VWRITE") # WRITE OUTPUT IN TABULAR FORMAT + mapdl.run("(///T14,'MODE',T24,'COEFF',T34,'ISYM',/)")""" + + assert pycmd in convert_apdl_block(cmd, only_commands=True) diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index 3291616f53..283fc70487 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -1995,3 +1995,12 @@ def test_non_valid_kwarg(mapdl): with pytest.warns(UserWarning): mapdl.cdwrite(options="DB", fname="test1", ext="cdb") + + +def test_check_parameter_names(mapdl): + with pytest.raises(ValueError): + mapdl.parameters["_dummy"] = 1 + + mapdl.check_parameter_names = False + mapdl.parameters["_dummy"] = 1 + mapdl.check_parameter_names = True # returning to default