Skip to content

Commit

Permalink
Allowing brackets and spaces in parameters name (#1052)
Browse files Browse the repository at this point in the history
* Fixing array indexing issue.

* Improving unit tests

* Adding % and improving unit tests

* Fixing tests

* Improving comments

* Improving indexing detection during parameter name check.

Adding parameter name check to mapdl.dim

* Improving parameter retrieving

* Adding unit tests.
Moving unit tests from mapdl to parameters.

* Added documentation

Added context manager for full output (PARMS, _PARMS, PARMS_)

Added parameter name check when using parameters.__setitem__

* Adding parameters.__setitem__ to tests.

* fixing parameter name in context manager.

* Fixing unit tests

* Fixing codacity warnings.
  • Loading branch information
germa89 committed Apr 20, 2022
1 parent 9b17d94 commit 294ac31
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 191 deletions.
51 changes: 48 additions & 3 deletions src/ansys/mapdl/core/mapdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -2902,18 +2902,38 @@ def cwd(self, *args, **kwargs):

def _check_parameter_name(self, param_name):
"""Checks if a parameter name is allowed or not."""
param_name = param_name.strip()

match_valid_parameter_name = r"^[a-zA-Z_][a-zA-Z\d_]{0,31}$"
match_valid_parameter_name = r"^[a-zA-Z_][a-zA-Z\d_\(\),\s\%]{0,31}$"
# Using % is allowed, because of substitution, but it is very likely MAPDL will complain.
if not re.search(match_valid_parameter_name, param_name):
raise ValueError(
f"The parameter name `{param_name}` is an invalid parameter name."
"Only letters, numbers and `_` are permitted, up to 32 characters long."
"It cannot start with a number either."
)

# invalid parameter (using ARGXX or ARXX)
if "(" in param_name or ")" in param_name:
if param_name.count("(") != param_name.count(")"):
raise ValueError(
"The parameter name should have all the parenthesis in pairs (closed)."
)

if param_name[-1] != ")":
raise ValueError(
"If using parenthesis (indexing), you cannot use any character after the closing parenthesis."
)

# Check recursively the parameter name without parenthesis.
# This is the real parameter name, however it must already exists to not raise an error.
sub_param_name = re.findall(r"^(.*)\(", param_name)
if sub_param_name:
self._check_parameter_name(sub_param_name[0])
return # Following checks should not run against the parenthesis

# Using leading underscored parameters
match_reserved_leading_underscored_parameter_name = (
r"^_[a-zA-Z\d_]{1,31}[a-zA-Z\d]$"
r"^_[a-zA-Z\d_\(\),\s_]{1,31}[a-zA-Z\d\(\),\s]$"
)
# If it also ends in undescore, this won't be triggered.
if re.search(match_reserved_leading_underscored_parameter_name, param_name):
Expand All @@ -2922,6 +2942,7 @@ def _check_parameter_name(self, param_name):
"This convention is reserved for parameters used by the GUI and/or Mechanical APDL-provided macros."
)

# invalid parameter (using ARGXX or ARXX)
match_reserved_arg_parameter_name = r"^(AR|ARG)(\d{1,3})$"
if re.search(
match_reserved_arg_parameter_name, param_name
Expand Down Expand Up @@ -2968,3 +2989,27 @@ def mpwrite(
self.download(os.path.basename(fname_), progress_bar=progress_bar)

return output

@wraps(Commands.dim)
def dim(
self,
par="",
type_="",
imax="",
jmax="",
kmax="",
var1="",
var2="",
var3="",
csysid="",
**kwargs,
):
self._check_parameter_name(par) # parameter name check
if "(" in par or ")" in par:
raise ValueError(
"Parenthesis are not allowed as parameter name in 'mapdl.dim'."
)

return super().dim(
par, type_, imax, jmax, kmax, var1, var2, var3, csysid, **kwargs
)
122 changes: 120 additions & 2 deletions src/ansys/mapdl/core/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,72 @@
class Parameters:
"""Collection of MAPDL parameters obtainable from the :func:`ansys.mapdl.core.Mapdl.get` command.
Notes
-----
**Leading underscored parameters**
The parameters starting with underscore ('_') are reserved parameters
for MAPDL macros and routines, therefore its use is discouraged, and
in PyMAPDL you cannot set them by default.
If you need to set one of this parameters, you can use ``mapdl._run``
to avoid PyMAPDL parameter name checks. For example
>>> mapdl._run('_parameter=123')
'PARAMETER _PARAMETER = 123.00000000'
By default, this type of parameters cannot be seen when issuing
``mapdl.parameters``. However, you can change this by setting
``mapdl.parameters.show_leading_underscore_parameters`` equal to True.
For example:
>>> mapdl.parameters.show_leading_underscore_parameters=True
>>> mapdl.parameters
MAPDL Parameters
----------------
PORT : 50053.0
_RETURN : 0.0
_STATUS : 0.0
_UIQR : 17.0
**Trailing underscored parameters**
The parameters ending underscore are recommend for user routines
and macros.
You can set this type of parameters in PyMAPDL, but by default,
they cannot be seen in ``mapdl.parameters``, unless
``mapdl.parameters.show_trailing_underscore_parameters`` is set
to True.
>>> mapdl.parameters['param_'] = 1.0
>>> mapdl.parameters
MAPDL Parameters
----------------
>>> mapdl.parameters.show_trailing_underscore_parameters=True
>>> mapdl.parameters
MAPDL Parameters
----------------
PARAM_ : 1.0
**Parameters with leading and trailing underscore**
These are an especial type of parameters. They CANNOT be seen
in ``mapdl.parameters`` under any circumstances, and because
of it, it uses is not recommended.
You can still retrieve them using any of the normal methods
to retrieve parameters. But you shall know the parameter name.
For example:
>>> mapdl.parameters["_param_"] = 1.0
>>> mapdl.parameters
MAPDL Parameters
----------------
>>> print(mapdl.parameters['_param_'])
1.0
Examples
--------
Simply list all parameters except for MAPDL MATH parameters.
Expand Down Expand Up @@ -64,6 +130,9 @@ def __init__(self, mapdl):
if not isinstance(mapdl, _MapdlCore):
raise TypeError("Must be implemented from MAPDL class")
self._mapdl_weakref = weakref.ref(mapdl)
self.show_leading_underscore_parameters = False
self.show_trailing_underscore_parameters = False
self.full_parameters_output = self._full_parameter_output(self)

@property
def _mapdl(self):
Expand Down Expand Up @@ -262,7 +331,17 @@ def type(self) -> int:
@supress_logging
def _parm(self):
"""Current MAPDL parameters"""
return interp_star_status(self._mapdl.starstatus())
params = interp_star_status(self._mapdl.starstatus())

if self.show_leading_underscore_parameters:
_params = interp_star_status(self._mapdl.starstatus("_PRM"))
params.update(_params)

if self.show_trailing_underscore_parameters:
params_ = interp_star_status(self._mapdl.starstatus("PRM_"))
params.update(params_)

return params

def __repr__(self):
"""Return the current parameters in a pretty format"""
Expand All @@ -288,7 +367,14 @@ def __getitem__(self, key):
raise TypeError("Parameter name must be a string")
key = key.upper()

parameters = self._parm
# We are going to directly query the desired parameter.
# It is more efficient (less parsing) and
# you can obtain leading and trailing underscore parameters, which
# they don't appear in a normal ``*STATUS``

with self.full_parameters_output:
parameters = self._parm

if key not in parameters:
raise IndexError("%s not a valid parameter_name" % key)

Expand All @@ -304,6 +390,8 @@ def __getitem__(self, key):

def __setitem__(self, key, value):
"""Set a parameter"""
self._mapdl._check_parameter_name(key)

# parameters = self._parm # check parameter exists
if isinstance(value, (np.ndarray, list)):
self._set_parameter_array(key, value)
Expand Down Expand Up @@ -510,6 +598,36 @@ def _write_numpy_array(self, filename, arr):
if not self._mapdl._local:
self._mapdl.upload(filename, progress_bar=False)

class _full_parameter_output:
"""Change the show_** options to true to allow full parameter output."""

def __init__(self, parent):
self._parent = weakref.ref(parent)
self.show_leading_underscore_parameters = None
self.show_trailing_underscore_parameters = None

def __enter__(self):
"""Storing current state."""
self.show_leading_underscore_parameters = (
self._parent().show_leading_underscore_parameters
)
self.show_trailing_underscore_parameters = (
self._parent().show_trailing_underscore_parameters
)

# Getting full output.
self._parent().show_leading_underscore_parameters = True
self._parent().show_trailing_underscore_parameters = True

def __exit__(self, *args):
"""Coming back to previous state."""
self._parent().show_leading_underscore_parameters = (
self.show_leading_underscore_parameters
)
self._parent().show_trailing_underscore_parameters = (
self.show_trailing_underscore_parameters
)


def interp_star_status(status):
"""Interprets \*STATUS command output from MAPDL
Expand Down
Loading

0 comments on commit 294ac31

Please sign in to comment.