From f15a2024c9c1d8dd10c83255e03b5386d6e0c891 Mon Sep 17 00:00:00 2001 From: Joris Remmers Date: Tue, 31 Dec 2024 14:03:23 +0100 Subject: [PATCH] Adding documentation --- doc/installation.rst | 58 ++++-- pyfem/util/itemList.py | 85 ++++++-- pyfem/util/kinematics.py | 21 +- pyfem/util/logger.py | 74 +++---- pyfem/util/plotUtils.py | 47 +++-- pyfem/util/shapeFunctions.py | 364 ++++++++++++++++++++++++++++++++--- pyfem/util/utilFunctions.py | 43 +++-- 7 files changed, 561 insertions(+), 131 deletions(-) diff --git a/doc/installation.rst b/doc/installation.rst index f72cf12..c293f29 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -18,25 +18,55 @@ matplotlib. Installation guidelines are given for various operating systems. Linux ----- -The ''python'' program and the modules ''numpy'', ''scipy'' and ''matplotlib'' -are included in most common distributions of Linux and can be installed without any problems. In many -cases, different versions of ''python'' are offered. Please make sure that ''python'' version 3.6 or higher is -installed. In addition, the modules ''meshio'', ''pickle'' and ''h5py'' can be installed for additional functionality. +The Python compiler and the modules ``numpy``, ``scipy``, and ``matplotlib`` are included in +most common distributions of Linux and can be installed without any problems. In many cases, +different versions of ``python`` are offered. Please make sure that ``python`` version 3.6 or +higher is installed. In addition, the modules ``meshio``, ``pickle``, and ``h5py`` can be +installed for additional functionality. -Execute the file ''install.py'' in the root directory ''pyfem''. In a terminal, one can type: +Execute the file ``install.py`` in the root directory ``pyfem``. In a terminal, one can type: - python3 install.py +.. code-block:: bash -This script will check if the correct versions of Python and the various modules are available. In addition, -the total path to the executable is given. For your own convenience, you can add this to your ''.bashrc'' file: + ./install - alias pyfem="python /PyFEM.py" +This script will check if the correct versions of Python and the various modules are available. +If not, it will ask your permission to install the correct modules for you. -When using csh or tcsh add the following line to ''.cshrc'' or ''.tcshrc'': +The main executables are created. In commandline you can run PyFEM by typing - alias pyfem "python /PyFEM.py" +.. code-block:: bash + /pyfem.sh inputFile.pro -The main program ''pyfem'' can be run from the command prompt. For example, in order to run the -file ''StressWave20x20.pro'' in the directory ''examples/ch05'', simply type: +The Graphical User Interface of the code (currently under development) can be exectuted +by typing from any directory: - pyfem StressWave20x20.pro +.. code-block:: bash + /pyfem_gui.exe + +It is advised to create aliases. When using a bash shell, please +add the following lines to the file ``~/.bashrc``: + +.. code-block:: bash + alias pyfem='python3 /home/joris/Git/pyfem_github/PyFEM/PyFEM.py' + alias pyfem_gui = '/home/joris/Git/pyfem_github/PyFEM/pyfem_gui.x' + +You can then run PyFEM in commandline from any directory by typing: + +.. code-block:: bash + pyfem inputFile.pro + +You can start the gui by typing: + +.. code-block:: bash + pyfem_gui + +Windows +------- + +Under construction + +MacOS +----- + +Under construction diff --git a/pyfem/util/itemList.py b/pyfem/util/itemList.py index 799a785..224862e 100644 --- a/pyfem/util/itemList.py +++ b/pyfem/util/itemList.py @@ -30,33 +30,78 @@ class itemList ( dict ): - def add ( self, ID, item ): + """ + Class to construct a list of items that have a coninuous local number, and + a global ID. + """ + + def add ( self, ID: int, item ): + + """ + Adds an item with an ID to the list. This item will be stored in the list. - if ID in self: - raise RuntimeError( 'ID ' + str(ID) + ' already exists in ' + type(self).__name__ ) + Args: + ID (int): the ID of the item to be stored. + item: the value(s) of the item to be stored. + """ + + if ID in self: + raise RuntimeError( 'ID ' + str(ID) + ' already exists in ' + type(self).__name__ ) - self[ID] = item + self[ID] = item - def get ( self, IDs ): + def get ( self, IDs ): - if isinstance(IDs,int): - return self[IDs] - elif isinstance(IDs,list): - return [ self[ID] for ID in IDs ] + """ + Returns the index / indices of an ID or list of IDs of items in the list. + + Args: + IDs (list[int]|int,optional): the ID/IDs. If ommited, a list with all indces + will be returned. + Returns: + list[int]: a list with the indices. In the case of a single ID, this list has + length 1. + """ + + if isinstance(IDs,int): + return self[IDs] + elif isinstance(IDs,list): + return [ self[ID] for ID in IDs ] - raise RuntimeError('illegal argument for itemList.get') + raise RuntimeError('illegal argument for itemList.get') - def getIndices ( self, IDs = -1 ): + def getIndices ( self, IDs : list[int] | int = -1 ) -> list[int]: + + """ + Returns the index / indices of an ID or list of IDs of items in the list. - if IDs == -1: - return list(self.keys()) - elif isinstance(IDs,int): - return list(self.keys()).index( IDs ) - elif isinstance(IDs,list): - return [ list(self.keys()).index( ID ) for ID in IDs ] + Args: + IDs (list[int]|int,optional): the ID/IDs. If ommited, a list with all indces + will be returned. + Returns: + list[int]: a list with the indices. In the case of a single ID, this list has + length 1. + """ + + if IDs == -1: + return list(self.keys()) + elif isinstance(IDs,int): + return list(self.keys()).index( IDs ) + elif isinstance(IDs,list): + return [ list(self.keys()).index( ID ) for ID in IDs ] - raise RuntimeError('illegal argument for itemList.getIndices') + raise RuntimeError('illegal argument for itemList.getIndices') - def findID( self , index ): + def findID( self , index : int ) -> int: - return list(self.keys())[index] + """ + Returns the ID of an index in the list. + + Args: + index (int): the index of the item + + Returns: + int: the ID of the item + """ + + return list(self.keys())[index] diff --git a/pyfem/util/kinematics.py b/pyfem/util/kinematics.py index dfe62d7..03d41f1 100644 --- a/pyfem/util/kinematics.py +++ b/pyfem/util/kinematics.py @@ -32,10 +32,19 @@ class Kinematics: - def __init__( self , nDim , nStr ): + """ + Class that contains the kinematic state of a material + point, i.e. strain and deformation gradient. + + Args: + nDim (int): the number of spatial dimensions of the problem (2 or 3) + nStr (int): the number of strain components (2, 3 or 6) + """ + + def __init__( self , nDim: int , nStr: int ): - self.F = zeros( shape=( nDim , nDim ) ) - self.E = zeros( shape=( nDim , nDim ) ) - self.strain = zeros( nStr ) - self.dgdstrain = zeros( nStr ) - self.g = 0. + self.F = zeros( shape=( nDim , nDim ) ) + self.E = zeros( shape=( nDim , nDim ) ) + self.strain = zeros( nStr ) + self.dgdstrain = zeros( nStr ) + self.g = 0. diff --git a/pyfem/util/logger.py b/pyfem/util/logger.py index 6cfe679..d4cb6f3 100644 --- a/pyfem/util/logger.py +++ b/pyfem/util/logger.py @@ -30,48 +30,56 @@ import logging -#------------------------------------------------------------------------------- -# -#------------------------------------------------------------------------------- - -def setLogger( props ): +def setLogger( props : dict ): - level = "normal" + """ + Creates a logger for the current analysis with a given format and level. + + Args: + props(dict): A dictionary containing the input file of the problem. + Returns: + logger: an instance of the logger. + """ + + level = "normal" - if hasattr(props,"logger"): - level = props.logger.level + if hasattr(props,"logger"): + level = props.logger.level - if level not in ["normal","info","debug","critical","warning","silent"]: - raise NotImplementedError('Logger level should be "normal", "info", "debug", "critical", "silent" or "warning"') + if level not in ["normal","info","debug","critical","warning","silent"]: + raise NotImplementedError('Logger level should be "normal", "info", "debug", "critical", "silent" or "warning"') - logger = logging.getLogger() - handler = logging.StreamHandler() + logger = logging.getLogger() + handler = logging.StreamHandler() - if level == "debug": - formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') - logger .setLevel(logging.DEBUG) - elif level == "critical" or level == "silent": - formatter = logging.Formatter(' %(message)s') - logger .setLevel(logging.CRITICAL) - elif level == "warning": - formatter = logging.Formatter(' %(message)s') - logger .setLevel(logging.WARNING) - else: - formatter = logging.Formatter(' %(message)s') - logger .setLevel(logging.INFO) + if level == "debug": + formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') + logger .setLevel(logging.DEBUG) + elif level == "critical" or level == "silent": + formatter = logging.Formatter(' %(message)s') + logger .setLevel(logging.CRITICAL) + elif level == "warning": + formatter = logging.Formatter(' %(message)s') + logger .setLevel(logging.WARNING) + else: + formatter = logging.Formatter(' %(message)s') + logger .setLevel(logging.INFO) - handler.setFormatter(formatter) - logger .addHandler(handler) + handler.setFormatter(formatter) + logger .addHandler(handler) - return logger + return logger - -#------------------------------------------------------------------------------- -# -#------------------------------------------------------------------------------- - def getLogger(): - return logging.getLogger() + """ + Function that returns an instance of the active logger. + + Args: + None + Returnslogger: an instance of the active logger. + """ + + return logging.getLogger() diff --git a/pyfem/util/plotUtils.py b/pyfem/util/plotUtils.py index 16a6e46..1e9b7b9 100644 --- a/pyfem/util/plotUtils.py +++ b/pyfem/util/plotUtils.py @@ -5,7 +5,7 @@ # R. de Borst, M.A. Crisfield, J.J.C. Remmers and C.V. Verhoosel # # John Wiley and Sons, 2012, ISBN 978-0470666449 # # # -# Copyright (C) 2011-2024. The code is written in 2011-2012 by # +# Copyright (C) 2011-2025. The code is written in 2011-2012 by # # Joris J.C. Remmers, Clemens V. Verhoosel and Rene de Borst and since # # then augmented and maintained by Joris J.C. Remmers. # # All rights reserved. # @@ -28,39 +28,50 @@ # event caused by the use of the program. # ################################################################################ +import numpy as np -#------------------------------------------------------------------------------- -# -#------------------------------------------------------------------------------- +def plotCurve( output: np.ndarray ) -> None: + """ + Plots a curve based on the given output data points. -def plotCurve( output ): + Args: + output (List[Tuple[float, float]]): A list of (x, y) data points to plot. - from pylab import plot, show, xlabel, ylabel + Returns: + None + """ - plot( [x[0] for x in output], [x[1] for x in output], 'r-o' ) + from pylab import plot, show, xlabel, ylabel - show() - - -#- -# -#---------------- + plot( [x[0] for x in output], [x[1] for x in output], 'r-o' ) + show() + + +def plotTime(t: float) -> str: + + """ + Formats a given time duration into a human-readable string. -def plotTime( t ): + Args: + t (float): Time duration in seconds. + Returns: + str: A formatted string representing the time in seconds, minutes, or hours. + """ if t < 0.1: return f"{t:.1e} sec." elif t < 60.0: return f"{t:.3f} sec." elif t < 3600.0: - minutes = int(t // 60 ) + minutes = int(t // 60) seconds = t % 60 return f"{minutes} min. {seconds:.2f} sec." else: - hours = int(t // 3600 ) - minutes = int((t % 3600 ) // 60 ) + hours = int(t // 3600) + minutes = int((t % 3600) // 60) seconds = t % 60 - return f"{hours} hrs. {minutes} min. {seconds:.2f} sec." + return f"{hours} hrs. {minutes} min. {seconds:.2f} sec." + diff --git a/pyfem/util/shapeFunctions.py b/pyfem/util/shapeFunctions.py index a475dd0..97d440e 100644 --- a/pyfem/util/shapeFunctions.py +++ b/pyfem/util/shapeFunctions.py @@ -35,29 +35,58 @@ class shapeData: + ''' + Class that contains the shape function data for a single integration point. + ''' + pass #---------------------------------------------------------------------- class elemShapeData: + ''' + Class that contains the shape function data for an entire element. + This class is iterable. + ''' + def __init__( self ): self.sData = [] def __iter__( self ): + + ''' + Iteration over integration points + ''' return iter(self.sData) def __len__( self ): + + ''' + Function that returns the number of integration points in an element. + ''' return len(self.sData) #---------------------------------------------------------------------- -def getShapeLine2 ( xi ): +def getShapeLine2 ( xi : float ) -> shapeData: - #Check the dimensions of the physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 1D line element with 2 nodes (Line2). + + Args: + xi(float): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 1D coordinate (float) + ''' + if type(xi) != float: raise NotImplementedError('1D only') @@ -82,7 +111,19 @@ def getShapeLine2 ( xi ): def getShapeLine3 ( xi ): - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 1D line element with 3 nodes (Line3). + + Args: + xi(float): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 1D coordinate (float) + ''' + if type(xi) != float: raise NotImplementedError('1D only') @@ -107,9 +148,21 @@ def getShapeLine3 ( xi ): #---------------------------------------------------------------------- -def getShapeTria3 ( xi ): +def getShapeTria3 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 2D triangular element with 3 nodes (Tria3). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 2D coordinate (ndarray of length 2) + ''' + if len(xi) != 2: raise NotImplementedError('2D only') @@ -138,9 +191,21 @@ def getShapeTria3 ( xi ): #------------------------------------- -def getShapeQuad4 ( xi ): +def getShapeQuad4 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 2D quadrilateral element with 4 nodes (Quad4). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 2D coordinate (ndarray of length 2) + ''' + if len(xi) != 2: raise NotImplementedError('2D only') @@ -172,9 +237,21 @@ def getShapeQuad4 ( xi ): #------------------------------------- -def getShapeTria6 ( xi ): +def getShapeTria6 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 2D triangular element with 6 nodes (Tria4). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 2D coordinate (ndarray of length 2) + ''' + if len(xi) != 2: raise NotImplementedError('2D only') @@ -216,9 +293,21 @@ def getShapeTria6 ( xi ): #------------------------------------- -def getShapeQuad8 ( xi ): +def getShapeQuad8 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 2D quadrilateral element with 8 nodes (Quad8). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 2D coordinate (ndarray of length 2) + ''' + if len(xi) != 2: raise NotImplementedError('2D only') @@ -264,7 +353,19 @@ def getShapeQuad8 ( xi ): def getShapeQuad9 ( xi ): - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 2D quadrilateral element with 9 nodes (Quad9). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 2D coordinate (ndarray of length 2) + ''' + if len(xi) != 2: raise NotImplementedError('2D only') @@ -292,9 +393,21 @@ def getShapeQuad9 ( xi ): #---------------------------------------------------------------------- -def getShapeTetra4 ( xi ): +def getShapeTetra4 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 3D tetrahedral element with 4 nodes (Tetra4). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 3D coordinate (ndarray of length 3) + ''' + if len(xi) != 3: raise NotImplementedError('3D only') @@ -331,9 +444,21 @@ def getShapeTetra4 ( xi ): #---------------------------------------------------------------------- -def getShapePyramid5 ( xi ): +def getShapePyramid5 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 3D pyramid element with 5 nodes (Pyramid5). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 3D coordinate (ndarray of length 3) + ''' + if len(xi) != 3: raise NotImplementedError('3D only') @@ -374,9 +499,21 @@ def getShapePyramid5 ( xi ): #---------------------------------------------------------------------- -def getShapePrism6 ( xi ): +def getShapePrism6 ( xi : ndarray) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 3D prsimatic element with 6 nodes (Prism6). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 3D coordinate (ndarray of length 3) + ''' + if len(xi) != 3: raise NotImplementedError('3D only') @@ -405,9 +542,21 @@ def getShapePrism6 ( xi ): #---------------------------------------------------------------------- -def getShapePrism18 ( xi ): +def getShapePrism18 ( xi : ndarray ) -> shapeData: - #Check the dimension of physical space + ''' + Function that returns the shape function data in a single integration + point for a parent 3D prismatic element with 18 nodes (Prism18). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 3D coordinate (ndarray of length 3) + ''' + if len(xi) != 3: raise NotImplementedError('3D only') @@ -438,7 +587,20 @@ def getShapePrism18 ( xi ): # #------------------------------------------------------------------------------ -def getShapeHexa8 ( xi ): +def getShapeHexa8 ( xi : ndarray ) -> shapeData: + + ''' + Function that returns the shape function data in a single integration + point for a parent 3D hexahedron element with 8 nodes (Hexa8). + + Args: + xi(ndarray): Location of the integration point + Returns: + shapeData: The integration point shape data containin the parent + parameters, h, dhdxi and xi + Raises: + Error: when the input is not a 3D coordinate (ndarray of length 3) + ''' if len(xi) != 3: raise NotImplementedError('The isoparamatric coordinate should be 3D.') @@ -491,7 +653,26 @@ def getShapeHexa8 ( xi ): #---------------------------------------------------------------------- -def getElemType( elemCoords ): +def getElemType( elemCoords : ndarray ) -> str: + + ''' + Function that returns the element type based on the nodal + coordinates of the element. + + Args: + elemCoords(ndarray): Matrix (2D array) containing the nodal + coordinates of the element. The number of rows is the + number of nodes, the number of columns is the spatial + dimensions (1,2 or 3). + Returns: + str: elementType + - 1D elements: `Line2` and `Line3` + - 2D elements: `Tria3`, `Tria6`, `Quad4`, `Quad8` and `Quad9` + - 3D elements: `Tetra4`, `Pyramid5`, `Prism6`, `Hexa8` and `Prism18` + Raises: + Error: When the element type cannot be found, or when the spatial + dimensions (rank) is not 1,2 or 3. + ''' nNel = elemCoords.shape[0] rank = elemCoords.shape[1] @@ -536,8 +717,24 @@ def getElemType( elemCoords ): # #------------------------------------------------------------------------------ -def tria_scheme( order ): +def tria_scheme( order : int ): # -> tuple(list[list],list[float]): + ''' + Function that returns the integration scheme (coordinates in the parent + element and weights) for a 2D triangular element. + + Args: + order (int): the integration order. This number is either 1,3 or 7 + (representing the number of integration points). + Returns: + tuple(list[list],list[float]): A list of coordinates in the parent + element and a list of weights. + The length of the lists is idential to the + order. + Raises: + Error: when the order is not equal to 1, 3 or 7. + ''' + if order == 1: xi = [[1.0/3.0,1.0/3.0]] weight = [ 0.5 ] @@ -564,6 +761,8 @@ def tria_scheme( order ): w7 = 0.225 weight = [ w1,w1,w1,w4,w4,w4,w7 ] + else: + raise NotImplementedError('Order must be 1,3 or 7') return xi,weight @@ -571,8 +770,22 @@ def tria_scheme( order ): # #------------------------------------------------------------------------------ -def tetra_scheme( order ): +def tetra_scheme( order : int ): # -> tuple(list[list],list[float]): + ''' + Function that returns the integration scheme (coordinates in the parent + element and weights) for a 3D tetrahedral element. + + Args: + order (int): the integration order. This number is only 1, which + means a three point integration scheme. + Returns: + tuple(list[list],list[float]): A list of coordinates in the parent + element and a list of weights. The length of the lists is equal to 3. + Raises: + Error: when the order is not equal to 1. + ''' + if order == 1: third = 1./3. @@ -587,8 +800,22 @@ def tetra_scheme( order ): # #------------------------------------------------------------------------------ -def pyramid_scheme( order ): +def pyramid_scheme( order : int ): # -> tuple(list[list],list[float]): + ''' + Function that returns the integration scheme (coordinates in the parent + element and weights) for a 3D pyramid element. + + Args: + order (int): the integration order. This number is only 1, which + means a one point integration scheme. + Returns: + tuple(list[list],list[float]): A list of coordinates in the parent + element and a list of weights. The length of the lists is equal to 1. + Raises: + Error: when the order is not equal to 1. + ''' + if order == 1: xi = [[0.,0.,-0.5]] weight = [128.0/27.0]#[8.0/3.0] #[ 18.967 ] @@ -599,8 +826,29 @@ def pyramid_scheme( order ): #----------------------------------------------------------------------- -def getIntegrationPoints( elemType , order , scheme ): +def getIntegrationPoints( elemType : str , order : int , scheme : str ):# -> tuple(list[list],list[float]): + ''' + Function that returns the integration scheme (coordinates in the parent + element and weights) for any elemement type + + Args: + elemType (str): Indicating the type of element. + - 1D elements: `Line2` and `Line3` + - 2D elements: `Tria3`, `Tria6`, `Quad4`, `Quad8` and `Quad9` + - 3D elements: `Tetra4`, `Pyramid5`, `Prism6`, `Hexa8` and `Prism18` + order(int): the integration order. 0 represents the standard integration + for an element (e.g. Guass 2x2 for a Quad4 element); +1 indicates a + higher order integration (3x3 for a Quad4 element); -1 indicates a + lower order (1x1). + scheme(str): Integration scheme (is redundant). + Returns: + tuple(list[list],list[float]): A list of coordinates in the parent + element and a list of weights. The length of the lists is equal to 3. + Raises: + Error: when the element type is not known. + ''' + xi = [] weight = [] @@ -672,6 +920,8 @@ def getIntegrationPoints( elemType , order , scheme ): for j in range(stdOrder): xi. append( [float(ip0[i][0].real),float(ip0[i][1].real),float(ip1[j].real)] ) weight.append( w0[i]*w1[j] ) + else: + raise NotImplementedError('Element type not known.') return xi , weight @@ -679,7 +929,26 @@ def getIntegrationPoints( elemType , order , scheme ): # #------------------------------------------------------------------------------ -def calcWeightandDerivatives( elemCoords , sData , weight ): +def calcWeightandDerivatives( elemCoords : ndarray , sData : shapeData , weight : float ): + + ''' + Function that calculates the derivatives of shapefunctions and their weight in + the physical element. + + Args: + elemCoords(ndarray): Matrix (2D array) containing the nodal + coordinates of the element. The number of rows is the + number of nodes, the number of columns is the spatial + dimensions (1,2 or 3). + sData (shapeData): the current shape data in this integration point. This + contains the coordinate of the integration point xi and + the shape function h and its derivative dhdx. + weight(float): Integration weight. + Returns: + None: + + The physical derivative and weight are store in sData. + ''' jac = dot ( elemCoords.transpose() , sData.dhdxi ) @@ -707,8 +976,30 @@ def calcWeightandDerivatives( elemCoords , sData , weight ): # #------------------------------------------------------------------------------ -def getElemShapeData( elemCoords , order = 0 , method = 'Gauss' , elemType = 'Default' ): +def getElemShapeData( elemCoords : ndarray , order : int = 0 , + method : str = 'Gauss' , elemType : str = 'Default' ) -> elemShapeData: + ''' + Function to determine the element shape functions and integration point data for a given + element with nodal coordinates. + + Args: + elemCoords(ndarray): Matrix (2D array) containing the nodal + coordinates of the element. The number of rows is the + number of nodes, the number of columns is the spatial + dimensions (1,2 or 3). + order(int) : the order of integration. ) is default and indicates + a regular integration for such an element. + method(str) : the integration type. `Gauss` is default. + elemType(str) : the element type. If the default value is chosen, the + element type will be determined by means of the + dimensions of the elemCoords array. + Returns: + elemShapeData: + Raises: + Error: when the elementType is not known. + ''' + elemData = elemShapeData() if elemType == 'Default': @@ -734,8 +1025,23 @@ def getElemShapeData( elemCoords , order = 0 , method = 'Gauss' , elemType = 'De # #------------------------------------------------------------------------------ -def getShapeData( order = 0 , method = 'Gauss' , elemType = 'Default' ): +def getShapeData( order : int = 0 , method : str = 'Gauss' , elemType : str = 'Default' ) -> elemShapeData: + ''' + Function to determine the element shape functions and integration point data for a given + elementtype. + + Args: + order(int) : the order of integration. ) is default and indicates + a regular integration for such an element. + method(str) : the integration type. `Gauss` is default. + elemType(str) : the element type. + Returns: + elemShapeData: + Raises: + Error: when the elementType is not known. + ''' + shpData = elemShapeData() (intCrds,intWghts) = getIntegrationPoints( elemType , order , method ) diff --git a/pyfem/util/utilFunctions.py b/pyfem/util/utilFunctions.py index c878d35..871a191 100644 --- a/pyfem/util/utilFunctions.py +++ b/pyfem/util/utilFunctions.py @@ -28,19 +28,40 @@ # event caused by the use of the program. # ################################################################################ -def macauley( x ): - if x >= 0.: - return x - else: - return 0. +def macauley( x : float ) -> float: -#---------------------- + """ + Function that performs the macaulay operation to a variable. The macaulay + operation returns the exact same value when that value is positive and + returns zero when the values is negative. + + Args: + x (float): The input to the function + Returns: + float: The macauley value. + """ + + if x >= 0.: + return x + else: + return 0. -def sign( x ): - if x < 0.: - return -1.0 - else: - return 0.0 +def sign( x : float ) -> float: + + """ + Function that returns the sign of a value + + Args: + x (float): The input to the function + + Returns: + float: the sign (-1.0 or 1.0). + """ + + if x < 0.: + return -1.0 + else: + return 0.0