diff --git a/codecov.yml b/codecov.yml index a5f34c0a84..5185040ae4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,5 +1,4 @@ ignore: - - "src/ansys/mapdl/core/_commands" - "src/ansys/mapdl/core/jupyter.py" - "src/ansys/mapdl/core/mapdl_console.py" - "src/ansys/mapdl/core/mapdl_inprocess.py" diff --git a/doc/changelog.d/3501.added.md b/doc/changelog.d/3501.added.md new file mode 100644 index 0000000000..31292e041b --- /dev/null +++ b/doc/changelog.d/3501.added.md @@ -0,0 +1 @@ +test: check all commands are submitted \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 480246e92e..7ee7b6d705 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,7 +120,7 @@ pymapdl_convert_script = "ansys.mapdl.core.cli:old_pymapdl_convert_script_entry_ pymapdl = "ansys.mapdl.core.cli:main" [tool.pytest.ini_options] -addopts = "-ra -vvv --maxfail=10" +addopts = "-rxXsa -vvv --maxfail=10" junit_family = "legacy" filterwarnings = [ "ignore::FutureWarning", diff --git a/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py b/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py index 1a2e6a11a4..75915afe8e 100644 --- a/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py +++ b/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py @@ -336,7 +336,7 @@ def get( command = f"*GET,{par},{entity},{entnum},{item1},{it1num},{item2},{it2num}" return self.run(command, **kwargs) - def inquire(self, strarray="", func="", arg1="", arg2=""): + def inquire(self, strarray="", func="", arg1="", arg2="", **kwargs): """Returns system information. By default, with no arguments, it returns the working directory. @@ -489,7 +489,7 @@ def inquire(self, strarray="", func="", arg1="", arg2=""): >>> mapdl.inquire('', 'RSTFILE') 'file.rst' """ - return self.run(f"/INQUIRE,{strarray},{func},{arg1},{arg2}") + return self.run(f"/INQUIRE,{strarray},{func},{arg1},{arg2}", **kwargs) def parres(self, lab="", fname="", ext="", **kwargs): """Reads parameters from a file. diff --git a/src/ansys/mapdl/core/_commands/database/components.py b/src/ansys/mapdl/core/_commands/database/components.py index 9911aff565..b826947cec 100644 --- a/src/ansys/mapdl/core/_commands/database/components.py +++ b/src/ansys/mapdl/core/_commands/database/components.py @@ -495,185 +495,35 @@ def cmsel( def cmwrite( self, - option: str = "", fname: str = "", ext: str = "", - fnamei: str = "", - exti: str = "", fmat: str = "", **kwargs: Dict[Any, Any], - ) -> None: + ) -> str: """Writes node and element components and assemblies to a file. APDL Command: CMWRITE Parameters ---------- - option - Selects which data to write: + Fname + File name and directory path (248 characters maximum, including the + characters needed for the directory path). An unspecified directory + path defaults to the working directory; in this case, you can use + all 248 characters for the file name. + The file name defaults to ``Jobname``. + Ext + Filename extension (eight-character maximum). + The extension defaults to ``CM`` if ``fname`` is blank. - ALL - Write all appropriate geometry, material property, - load, and component data (default). Two files will - be produced. ``Fname.Ext`` will contain all data items - mentioned in "Notes", except the solid model - data. Fnamei.Exti will contain the solid model - geometry and solid model loads data in the form of - IGES commands. This option is not valid when - CDOPT,ANF is active. - - COMB - Write all data mentioned, but to a single file, - ``Fname.Ext``. Solid model geometry data will be - written in either IGES or ANF format as specified - in the CDOPT command, followed by the remainder of - the data in the form of ANSYS commands. More - information on these (IGES/ANF) file formats is - provided in "Notes". - - DB - Write all database information except the solid model - and solid model loads to ``Fname.Ext`` in the form of - ANSYS commands. This option is not valid when - CDOPT,ANF is active. - - SOLID - Write only the solid model geometry and solid - model load data. This output will be in IGES or - ANF format, as specified in the CDOPT - command. More information on these (IGES/ANF) file - formats is provided in "Notes". - - GEOM - Write only element and nodal geometry data. Neither - solid model geometry nor element attribute data - will be written. One file, ``Fname.Ext``, will be - produced. Use CDREAD,DB to read in a file written - with this option. Element types [ET] compatible - with the connectivity of the elements on the file - must first be defined before reading the file in - with CDREAD,DB. - - CM - Write only node and element component and geometry - data to ``Fname.Ext``. - - MAT - Write only material property data (both linear and - nonlinear) to ``Fname.Ext`` . - - LOAD - Write only loads for current load step to - ``Fname.Ext``. - - SECT - Write only section data to ``Fname.Ext``. Pretension - sections are not included. - - - fname - File name and directory path (248 characters maximum, - including the characters needed for the directory path). - An unspecified directory path defaults to the working - directory; in this case, you can use all 248 characters - for the file name. - - ext - Filename extension (eight-character maximum). The - extension defaults to CDB if Fname is blank. - - fnamei - Name of the IGES file and its directory path (248 - characters maximum, including directory). If you do not - specify a directory path, it will default to your working - directory and you can use all 248 characters for the file - name. - - The file name defaults to Fname. Used only if - Option = ALL or SOLID. Previous data on this file, if any, - is overwritten. - - Exti - Filename extension (eight-character maximum). The - extension defaults to IGES in all cases, except when - CDOPT,ANF is active and CDWRITE, Option = SOLID. In this - case Exti = ANF. - - fmat - Format of the output file (defaults to BLOCKED). - - BLOCKED - Blocked format. This format allows faster - reading of the output file. The time savings is - most significant when BLOCKED is used to read - .cdb files associated with very large models. - - UNBLOCKED - Unblocked format. + Fmat + Format of the output file (defaults to ``BLOCKED``). + + * ``BLOCKED``: Blocked format. This format allows faster + reading of the file. + + * ``UNBLOCKED``: Unblocked format. - Notes - ----- - Load data includes the current load step only. Loads applied - to the solid model (if any) are automatically transferred to - the finite element model when this command is issued. ``CDWRITE`` - writes out solid model loads for meshed models only. If the - model is not meshed, the solid model loads cannot be - saved. Component data include component definitions, but not - assembly definitions. Appropriate ``NUMOFF`` commands are included - at the beginning of the file; this is to avoid overlap of an - existing database when the file is read in. - - Solution control commands are typically not written to the - file unless you specifically change a default solution - setting. - - ``CDWRITE`` does not support the ``GSBDATA`` and ``GSGDATA`` commands, and - these commands are not written to the file. - - The data may be reread (on a different machine, for example) - with the ``CDREAD`` command. Caution: When the file is read in, - the ``NUMOFF,MAT`` command may cause a mismatch between material - definitions and material numbers referenced by certain loads - and element real constants. See ``NUMOFF`` for details. Also, be - aware that the files created by the ``CDWRITE`` command explicitly - set the active coordinate system to Cartesian (CSYS,0). - - You should generally use the blocked format (Fmat = BLOCKED) - when writing out model data with ``CDWRITE``. This is a compressed - data format that greatly reduces the time required to read - large models through the CDREAD command. The blocked and - unblocked formats are described in Chapter 3 of the Guide to - Interfacing with ANSYS. - - If you use ``CDWRITE`` in any of the derived products (ANSYS - Mechanical Pro, ANSYS Mechanical Premium), then before reading - the file, you must edit the Jobname.cdb file to remove - commands that are not available in the respective component - product. - - The ``CDWRITE`` command writes PART information for any ANSYS - LS-DYNA input file to the Jobname.cdb file via the EDPREAD - command. (EDPREAD is not a documented command; it is written - only when the ``CDWRITE`` command is issued.) The PART information - can be automatically read in via the CDREAD command; however, - if more than one Jobname.cdb file is read, the PART list from - the last Jobname.cdb file overwrites the existing PART list of - the total model. This behavior affects all PART-related - commands contained in the Jobname.cdb file. You can join - models, but not PART-related inputs, which you must modify - using the newly-created PART numbers. In limited cases, an - update of the PART list (EDWRITE,PUPDATE) is possible; doing - so requires that no used combination of MAT/TYPE/REAL appears - more than once in the list. - - The ``CDWRITE`` command does not support (for beam meshing) any - line operation that relies on solid model associativity. For - example, meshing the areas adjacent to the meshed line, - plotting the line that contains the orientation nodes, or - clearing the mesh from the line that contains orientation - nodes may not work as expected. For more information about - beam meshing, see Meshing Your Solid Model in the Modeling and - Meshing Guide. """ - command = f"CDWRITE,{option},{fname},{ext},,{fnamei},{exti},{fmat}" + command = f"CMWRITE,{fname},{ext},,,{fmat}" return self.run(command, **kwargs) diff --git a/src/ansys/mapdl/core/_commands/parse.py b/src/ansys/mapdl/core/_commands/parse.py index 87f4ab43b7..a1a4820dcc 100644 --- a/src/ansys/mapdl/core/_commands/parse.py +++ b/src/ansys/mapdl/core/_commands/parse.py @@ -39,14 +39,14 @@ NUM_PATTERN = re.compile(NUMERIC_CONST_PATTERN, re.VERBOSE) -def parse_kdist(msg): +def parse_kdist(msg: Optional[str] = None) -> Optional[int]: """Parse the keypoint value from a keypoint message""" finds = re.findall(NUM_PATTERN, msg)[-4:] if len(finds) == 4: return [float(val) for val in finds] -def parse_et(msg: Optional[str]) -> Optional[int]: +def parse_et(msg: Optional[str] = None) -> Optional[int]: """Parse local element type number definition message and return element type number. """ @@ -56,7 +56,7 @@ def parse_et(msg: Optional[str]) -> Optional[int]: return int(res.group(2)) -def parse_e(msg: Optional[str]) -> Optional[int]: +def parse_e(msg: Optional[str] = None) -> Optional[int]: """Parse create element message and return element number.""" if msg: res = re.search(r"(ELEMENT\s*)([0-9]+)", msg) @@ -64,7 +64,19 @@ def parse_e(msg: Optional[str]) -> Optional[int]: return int(res.group(2)) -def parse_kpoint(msg): +def parse_k(msg: Optional[str] = None) -> Optional[int]: + """Parse output from ``K`` command""" + if msg: + if not re.search(r"KEYPOINT NUMBER", msg): + res = re.search(r"(KEYPOINT\s*)([0-9]+)", msg) + else: + res = re.search(r"(KEYPOINT NUMBER =\s*)([0-9]+)", msg) + + if res: + return int(res.group(2)) + + +def parse_kpoint(msg: Optional[str] = None) -> Optional[int]: """Parse create keypoint message and return keypoint number.""" if msg: res = re.search(r"kpoint=\s+(\d+)\s+", msg) @@ -72,7 +84,7 @@ def parse_kpoint(msg): return int(res.group(1)) -def parse_output_areas(msg): +def parse_output_areas(msg: Optional[str] = None) -> Optional[int]: """Parse create area message and return area number.""" if msg: res = re.search(r"(OUTPUT AREAS =\s*)([0-9]+)", msg) @@ -83,7 +95,7 @@ def parse_output_areas(msg): return int(res.group(2)) -def parse_a(msg): +def parse_a(msg: Optional[str] = None) -> Optional[int]: """Parse create area message and return area number.""" if msg: res = re.search(r"(AREA NUMBER =\s*)([0-9]+)", msg) @@ -91,7 +103,7 @@ def parse_a(msg): return int(res.group(2)) -def parse_line_no(msg): +def parse_line_no(msg: Optional[str] = None) -> Optional[int]: """Parse create line message and return line number.""" if msg: res = re.search(r"LINE NO[.]=\s+(\d+)", msg) @@ -99,14 +111,14 @@ def parse_line_no(msg): return int(res.group(1)) -def parse_line_nos(msg): +def parse_line_nos(msg: Optional[str] = None) -> Optional[int]: if msg: matches = re.findall(r"LINE NO[.]=\s*(\d*)", msg) if matches: return [int(match) for match in matches] -def parse_v(msg): +def parse_v(msg: Optional[str] = None) -> Optional[int]: """Parse volume message and return volume number""" if msg: res = re.search(r"(VOLUME NUMBER =\s*)([0-9]+)", msg) @@ -114,7 +126,7 @@ def parse_v(msg): return int(res.group(2)) -def parse_output_volume_area(msg): +def parse_output_volume_area(msg: Optional[str] = None) -> Optional[int]: """Parse create area message and return area or volume number""" if msg: res = re.search(r"OUTPUT (AREA|VOLUME|AREAS) =\s*([0-9]+)", msg) @@ -122,8 +134,32 @@ def parse_output_volume_area(msg): return int(res.group(2)) -def parse_ndist(msg): +def parse_n(msg: Optional[str] = None) -> Optional[int]: + """Parse output of ``N``""" + if msg: + res = re.search(r"(NODE\s*)([0-9]+)", msg) + if res is not None: + return int(res.group(2)) + + +def parse_ndist(msg: Optional[str] = None) -> Optional[int]: """Parse the node value from a node message""" finds = re.findall(NUM_PATTERN, msg)[-4:] if len(finds) == 4: return [float(val) for val in finds] + + +def parse_kl(msg: Optional[str] = None) -> Optional[int]: + """Parse the output of ``KL``.""" + if msg: + res = re.search(r"KEYPOINT\s+(\d+)\s+", msg) + if res is not None: + return int(res.group(1)) + + +def parse_knode(msg: Optional[str] = None) -> Optional[int]: + """Parse the output of ``KNODE``.""" + if msg: + res = re.search(r"KEYPOINT NUMBER =\s+(\d+)", msg) + if res is not None: + return int(res.group(1)) diff --git a/src/ansys/mapdl/core/_commands/post1_/special.py b/src/ansys/mapdl/core/_commands/post1_/special.py index 391c02a9cb..798bc5751e 100644 --- a/src/ansys/mapdl/core/_commands/post1_/special.py +++ b/src/ansys/mapdl/core/_commands/post1_/special.py @@ -1911,7 +1911,7 @@ def prcamp( Distributed ANSYS Restriction: This command is not supported in Distributed ANSYS. """ - command = f"PRCAMP,{option},{slope},{unit},{freqb},{cname},{stabval},{keyallfreq},{keynegfreq}" + command = f"PRCAMP,{option},{slope},{unit},{freqb},{cname},{stabval},{keyallfreq},{keynegfreq},{keywhirl}" return self.run(command, **kwargs) def prfar( diff --git a/src/ansys/mapdl/core/_commands/preproc/element_type.py b/src/ansys/mapdl/core/_commands/preproc/element_type.py index 9e1c97b535..bfe21c2d46 100644 --- a/src/ansys/mapdl/core/_commands/preproc/element_type.py +++ b/src/ansys/mapdl/core/_commands/preproc/element_type.py @@ -25,7 +25,7 @@ """ from typing import Optional, Union -from ansys.mapdl.core._commands.parse import parse_et +from ansys.mapdl.core._commands import parse from ansys.mapdl.core.mapdl_types import MapdlInt @@ -272,9 +272,9 @@ def et( """ command = ( - f"ET,{itype},{ename},{kop1},{kop2},{kop3},{kop4}," f"{kop5},{kop6},{inopr}" + f"ET,{itype},{ename},{kop1},{kop2},{kop3},{kop4},{kop5},{kop6},{inopr}" ) - return parse_et(self.run(command, **kwargs)) + return parse.parse_et(self.run(command, **kwargs)) def etchg(self, cnv: str = "", **kwargs) -> Optional[str]: """Changes element types to their corresponding types. diff --git a/src/ansys/mapdl/core/_commands/preproc/elements.py b/src/ansys/mapdl/core/_commands/preproc/elements.py index ac5358060e..56c8742608 100644 --- a/src/ansys/mapdl/core/_commands/preproc/elements.py +++ b/src/ansys/mapdl/core/_commands/preproc/elements.py @@ -22,7 +22,7 @@ from typing import Optional, Union -from ansys.mapdl.core._commands.parse import parse_e +from ansys.mapdl.core._commands import parse from ansys.mapdl.core.mapdl_types import MapdlFloat, MapdlInt @@ -141,7 +141,7 @@ def e( """ command = f"E,{i},{j},{k},{l},{m},{n},{o},{p}" - return parse_e(self.run(command, **kwargs)) + return parse.parse_e(self.run(command, **kwargs)) def ecpchg(self, **kwargs): """Optimizes degree-of-freedom usage in a coupled acoustic model. diff --git a/src/ansys/mapdl/core/_commands/preproc/keypoints.py b/src/ansys/mapdl/core/_commands/preproc/keypoints.py index a68e7192d3..37ce6b2a7a 100644 --- a/src/ansys/mapdl/core/_commands/preproc/keypoints.py +++ b/src/ansys/mapdl/core/_commands/preproc/keypoints.py @@ -20,8 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import re - from ansys.mapdl.core._commands import parse @@ -70,16 +68,7 @@ def k(self, npt="", x="", y="", z="", **kwargs) -> int: """ command = f"K,{npt},{x},{y},{z}" - msg = self.run(command, **kwargs) - - if msg: - if not re.search(r"KEYPOINT NUMBER", msg): - res = re.search(r"(KEYPOINT\s*)([0-9]+)", msg) - else: - res = re.search(r"(KEYPOINT NUMBER =\s*)([0-9]+)", msg) - - if res: - return int(res.group(2)) + return parse.parse_k(self.run(command, **kwargs)) def kbetw(self, kp1="", kp2="", kpnew="", type_="", value="", **kwargs) -> int: """Creates a keypoint between two existing keypoints. @@ -418,11 +407,8 @@ def kl(self, nl1="", ratio="", nk1="", **kwargs) -> int: 1 """ - msg = self.run(f"KL,{nl1},{ratio},{nk1}", **kwargs) - if msg: - res = re.search(r"KEYPOINT\s+(\d+)\s+", msg) - if res is not None: - return int(res.group(1)) + cmd = f"KL,{nl1},{ratio},{nk1}" + return parse.parse_kl(self.run(cmd, **kwargs)) def klist(self, np1="", np2="", ninc="", lab="", **kwargs): """Lists the defined keypoints or hard points. @@ -565,7 +551,7 @@ def kmove( command = f"KMOVE,{npt},{kc1},{x1},{y1},{z1},{kc2},{x2},{y2},{z2}" return self.run(command, **kwargs) - def knode(self, npt="", node="", **kwargs) -> int: + def knode(self, npt="", node="", **kwargs) -> str: """Defines a keypoint at an existing node location. APDL Command: KNODE @@ -595,11 +581,8 @@ def knode(self, npt="", node="", **kwargs) -> int: 1 """ - msg = self.run(f"KNODE,{npt},{node}", **kwargs) - if msg: - res = re.search(r"KEYPOINT NUMBER =\s+(\d+)", msg) - if res is not None: - return int(res.group(1)) + cmd = f"KNODE,{npt},{node}" + return parse.parse_knode(self.run(cmd, **kwargs)) def kplot(self, np1="", np2="", ninc="", lab="", **kwargs): """Displays the selected keypoints. diff --git a/src/ansys/mapdl/core/_commands/preproc/material_data_tables.py b/src/ansys/mapdl/core/_commands/preproc/material_data_tables.py index a5769de079..a0c1c4dc33 100644 --- a/src/ansys/mapdl/core/_commands/preproc/material_data_tables.py +++ b/src/ansys/mapdl/core/_commands/preproc/material_data_tables.py @@ -420,12 +420,7 @@ def tbdele(self, lab="", mat1="", mat2="", inc="", tbopt="", **kwargs): This command is also valid in the solution processor (:meth:`mapdl.slashsolu() `), but is not intended for changing material behaviors between load steps. """ - command = "TBDELE,%s,%s,%s,%s" % ( - str(lab), - str(mat1), - str(mat2), - str(inc), - ) + command = f"TBDELE,{lab},{mat1},{mat2},{inc},{tbopt}" return self.run(command, **kwargs) def tbeo(self, par="", value="", **kwargs): diff --git a/src/ansys/mapdl/core/_commands/preproc/nodes.py b/src/ansys/mapdl/core/_commands/preproc/nodes.py index f0407732f3..48f2a43f78 100644 --- a/src/ansys/mapdl/core/_commands/preproc/nodes.py +++ b/src/ansys/mapdl/core/_commands/preproc/nodes.py @@ -20,8 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import re - from ansys.mapdl.core._commands import parse @@ -249,11 +247,7 @@ def n(self, node="", x="", y="", z="", thxy="", thyz="", thzx="", **kwargs) -> i """ command = f"N,{node},{x},{y},{z},{thxy},{thyz},{thzx}" - msg = self.run(command, **kwargs) - if msg: - res = re.search(r"(NODE\s*)([0-9]+)", msg) - if res is not None: - return int(res.group(2)) + return parse.parse_n(self.run(command, **kwargs)) def naxis(self, action="", val="", **kwargs): """Generates nodes for general axisymmetric element sections. diff --git a/src/ansys/mapdl/core/_commands/preproc/special_purpose.py b/src/ansys/mapdl/core/_commands/preproc/special_purpose.py index 4679846207..6f36494d18 100644 --- a/src/ansys/mapdl/core/_commands/preproc/special_purpose.py +++ b/src/ansys/mapdl/core/_commands/preproc/special_purpose.py @@ -205,7 +205,7 @@ def cint( command = f"CINT,{action},{par1},{par2},{par3},{par4},{par5},{par6},{par7}" return self.run(command, **kwargs) - def cycexpand(self, wn="", option="", value1="", value2=""): + def cycexpand(self, wn="", option="", value1="", value2="", **kwargs): """Graphically expands displacements, stresses and strains of a cyclically symmetric model. diff --git a/src/ansys/mapdl/core/_commands/session/run_controls.py b/src/ansys/mapdl/core/_commands/session/run_controls.py index 6981a401e9..29fea7ec2d 100644 --- a/src/ansys/mapdl/core/_commands/session/run_controls.py +++ b/src/ansys/mapdl/core/_commands/session/run_controls.py @@ -664,7 +664,7 @@ def sys(self, string="", **kwargs): This command is valid in any processor. """ - command = "/SYS,%s" % (str(string)) + command = f"/SYS,{string}" return self.run(command, **kwargs) def unpause(self, **kwargs): diff --git a/src/ansys/mapdl/core/_commands/solution/analysis_options.py b/src/ansys/mapdl/core/_commands/solution/analysis_options.py index eab0a0d6cc..e5b87da1e8 100644 --- a/src/ansys/mapdl/core/_commands/solution/analysis_options.py +++ b/src/ansys/mapdl/core/_commands/solution/analysis_options.py @@ -1167,7 +1167,7 @@ def ddoption(self, decomp="", nprocpersol="", numsolforlp="", **kwargs): domains by expanding the smaller domains from the reduced graph back to the original mesh. """ - command = f"DDOPTION,{decomp}" + command = f"DDOPTION,{decomp},{nprocpersol},{numsolforlp}" return self.run(command, **kwargs) def dmpext( diff --git a/src/ansys/mapdl/core/mapdl_extended.py b/src/ansys/mapdl/core/mapdl_extended.py index abdd55c027..3b5d1f56f7 100644 --- a/src/ansys/mapdl/core/mapdl_extended.py +++ b/src/ansys/mapdl/core/mapdl_extended.py @@ -1380,7 +1380,7 @@ def cmplot(self, label: str = "", entity: str = "", keyword: str = "", **kwargs) return output @wraps(_MapdlCore.inquire) - def inquire(self, strarray="", func="", arg1="", arg2=""): + def inquire(self, strarray="", func="", arg1="", arg2="", **kwargs): """Wraps original INQUIRE function""" func_options = [ "LOGIN", @@ -1422,7 +1422,9 @@ def inquire(self, strarray="", func="", arg1="", arg2=""): f"The arguments (strarray='{strarray}', func='{func}') are not valid." ) - response = self.run(f"/INQUIRE,{strarray},{func},{arg1},{arg2}", mute=False) + response = self.run( + f"/INQUIRE,{strarray},{func},{arg1},{arg2}", mute=False, **kwargs + ) if func.upper() in [ "ENV", "TITLE", @@ -1461,7 +1463,7 @@ def lgwrite(self, fname="", ext="", kedit="", remove_grpc_extra=True, **kwargs): fname_ = self._get_file_name(fname=file_, ext=ext_) # generate the log and download if necessary - output = super().lgwrite(fname=fname_, kedit=kedit, **kwargs) + output = super().lgwrite(fname=fname_, ext="", kedit=kedit, **kwargs) # Let's download the file to the location self._download(fname_, fname) diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py index 28210b8f40..ee74462dd3 100644 --- a/src/ansys/mapdl/core/mapdl_grpc.py +++ b/src/ansys/mapdl/core/mapdl_grpc.py @@ -1356,7 +1356,7 @@ def list_files(self, refresh_cache: bool = True) -> List[str]: return files @supress_logging - def sys(self, cmd): + def sys(self, cmd, **kwargs): """Pass a command string to the operating system. APDL Command: /SYS @@ -1389,7 +1389,7 @@ def sys(self, cmd): """ # always redirect system output to a temporary file tmp_file = f"__tmp_sys_out_{random_string()}__" - super().sys(f"{cmd} > {tmp_file}") + super().sys(f"{cmd} > {tmp_file}", **kwargs) if self._local: # no need to download when local with open(os.path.join(self.directory, tmp_file)) as fobj: obj = fobj.read() diff --git a/tests/test_commands.py b/tests/test_commands.py index 2aa4ec8b19..51573fb4a0 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -21,6 +21,7 @@ # SOFTWARE. import inspect +from unittest.mock import patch import numpy as np import pytest @@ -1045,3 +1046,87 @@ def test_flist(self, mapdl): assert not flist_result.empty assert flist_result.compare(df_f).empty + + +class Test_MAPDL_commands: + SKIP = [ + "aplot", + "cfopen", + "cmatrix", + "create", + "end", + "eplot", + "geometry", + "input", + "kplot", + "lgwrite", + "lplot", + "lsread", + "mwrite", + "nplot", + "sys", + "vplot", + "vwrite", + ] + + @staticmethod + def fake_wrap(*args, **kwags): + return args[0] + + MAPDL_cmds = [each for each in dir(Commands) if not each.startswith("_")] + + @pytest.mark.parametrize("cmd", MAPDL_cmds) + @patch("ansys.mapdl.core.mapdl_grpc.MapdlGrpc._send_command", fake_wrap) + # Skip post processing the plot in PLESOL commands like. + @patch("ansys.mapdl.core.mapdl_core.PLOT_COMMANDS", []) + # skip retrieving value + @patch("ansys.mapdl.core.mapdl_grpc.MapdlGrpc.scalar_param", fake_wrap) + # Skip output the entity id after geometry manipulation + @patch("ansys.mapdl.core._commands.parse.parse_a", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_e", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_et", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_k", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_knode", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_kdist", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_kl", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_kpoint", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_line_no", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_line_nos", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_n", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_ndist", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_output_areas", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_output_volume_area", fake_wrap) + @patch("ansys.mapdl.core._commands.parse.parse_v", fake_wrap) + def test_command(self, mapdl, cmd): + func = getattr(mapdl, cmd) + + # Avoid wraps + while hasattr(func, "__wrapped__"): + func = func.__wrapped__ + + if cmd in self.SKIP: + pytest.skip("This function is overwritten in a subclass.") + + parm = inspect.signature(func).parameters + assert "kwargs" in parm, "'kwargs' argument is missing in function signature." + + args = [f"arg{i}" for i in range(len(parm) - 1)] # 3 = self, cmd, kwargs + + if list(parm)[0].lower() == "self": + args = args[:-1] + post = func(mapdl, *args) + else: + post = func(*args) + + for arg in args: + assert arg in post + + # assert ",".join(args) in post.replace(",,", ",").replace(" ", "") + cmd_ = cmd.upper() + if cmd_.startswith("SLASH"): + cmd_ = cmd_.replace("SLASH_", "/").replace("SLASH", "/") + + if cmd_.startswith("STAR"): + cmd_ = cmd_.replace("STAR", "*") + + assert cmd_ in post.upper()