diff --git a/docs/source/conf.py b/docs/source/conf.py index 6878e5422..2ac1c34a8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -203,6 +203,7 @@ intersphinx_mapping = { "https://docs.python.org/": None, "numpy [stable]": ("https://numpy.org/doc/stable/", None), + "scipy [stable]": ("https://docs.scipy.org/doc/scipy/", None), "pynastran [latest]": ("https://pynastran-git.readthedocs.io/en/latest/", None), "mpi4py [stable]": ("https://mpi4py.readthedocs.io/en/stable/", None), "paropt": ("https://smdogroup.github.io/paropt/", None), diff --git a/docs/source/examples/Example-Transient_Battery.rst b/docs/source/examples/Example-Transient_Battery.rst index 841524882..2f325cbe6 100644 --- a/docs/source/examples/Example-Transient_Battery.rst +++ b/docs/source/examples/Example-Transient_Battery.rst @@ -189,13 +189,13 @@ index indicates the time-step of the analysis (000 through 050). These files can .. code-block:: console - $ f5tovtk Transient_000_0xx.f5 + $ f5tovtk Transient_000_*.f5 or .. code-block:: console - $ f5totec Transient_000_0xx.f5 + $ f5totec Transient_000_*.f5 The animation below shows what the transient heat transfer temperature solution looks like when visualized in Paraview. diff --git a/tacs/constraints/adjacency.py b/tacs/constraints/adjacency.py index ac0ffb315..97a3f8372 100644 --- a/tacs/constraints/adjacency.py +++ b/tacs/constraints/adjacency.py @@ -178,6 +178,29 @@ def addConstraint(self, conName, compIDs=None, lower=-1e20, upper=1e20, dvIndex= return success def _createConstraint(self, dvIndex, compIDs, lbound, ubound): + """ + Create a new constraint object for TACS. + + Parameters + ---------- + dvIndex : int + Index number of element DV to be used in constraint. + + compIDs: list[int] + List of compIDs to select. + + lbound: float or complex + lower bound for constraint. Defaults to 0.0. + + ubound: float or complex + upper bound for constraint. Defaults to 1e20. + + Returns + ------- + constraint : tacs.constraints.base.SparseLinearConstraint or None + Constraint object if successful, None otherwise. + + """ size = self.comm.size rank = self.comm.rank # Gather the dv mapping from each proc diff --git a/tacs/constraints/base.py b/tacs/constraints/base.py index 4ecda56c4..a26ea170f 100644 --- a/tacs/constraints/base.py +++ b/tacs/constraints/base.py @@ -21,6 +21,24 @@ class TACSConstraint(TACSSystem): def __init__( self, assembler, comm=None, options=None, outputViewer=None, meshLoader=None ): + """ + Parameters + ---------- + assembler : tacs.TACS.Assembler + Cython object responsible for creating and setting tacs objects used to solve problem + + comm : mpi4py.MPI.Intracomm + The comm object on which to create the pyTACS object. + + options : dict + Dictionary holding problem-specific option parameters (case-insensitive). + + outputViewer : tacs.TACS.TACSToFH5 + Cython object used to write out f5 files that can be converted and used for postprocessing. + + meshLoader : tacs.pymeshloader.pyMeshLoader + pyMeshLoader object used to create the assembler. + """ # Set attributes and options TACSSystem.__init__(self, assembler, comm, options, outputViewer, meshLoader) @@ -141,6 +159,11 @@ def getConstraintSizes(self, sizes, evalCons=None): def getConstraintKeys(self): """ Return a list of the current constraint key names + + Returns + ------- + conNames : list[str] + List containing user-defined names for constraint groups added so far. """ return list(self.constraintList.keys()) @@ -202,6 +225,19 @@ def evalConstraintsSens(self, funcsSens, evalCons=None): def _processEvalCons(self, evalCons, ignoreMissing=True): """ Internal method for processing user-provided evalCons + + Parameters + ---------- + evalCons : iterable object containing strings or None + The constraints the user wants returned + ignoreMissing : bool + Flag to supress checking for a valid constraint. + Defaults to True. + + Returns + ------- + conList : list[str] + List of constraints the user wants returned """ # Check if user specified which constraints to output # Otherwise, output them all diff --git a/tacs/constraints/dv.py b/tacs/constraints/dv.py index 8bc1379c6..eb42f3c22 100644 --- a/tacs/constraints/dv.py +++ b/tacs/constraints/dv.py @@ -131,6 +131,34 @@ def addConstraint( return success def _createConstraint(self, dvIndices, dvWeights, compIDs, lbound, ubound): + """ + Create a new constraint object for TACS. + + Parameters + ---------- + dvIndices : list[int] + Index numbers of element DVs to be used in constraint. + Defaults to 0. + + dvWeights : list[float or complex] + Linear scaling factors for each DV used in constraint definition. + If list, should match length of dvIndices. Defaults to 1's. + + compIDs: list[int] + List of compIDs to select. + + lbound: float or complex + lower bound for constraint. Defaults to 0.0. + + ubound: float or complex + upper bound for constraint. Defaults to 1e20. + + Returns + ------- + constraint : tacs.constraints.base.SparseLinearConstraint or None + Constraint object if successful, None otherwise. + + """ # Assemble constraint info conCount = 0 rows = [] diff --git a/tacs/constraints/volume.py b/tacs/constraints/volume.py index 48238ce00..9636b0dd0 100644 --- a/tacs/constraints/volume.py +++ b/tacs/constraints/volume.py @@ -189,6 +189,26 @@ def addConstraint(self, conName, compIDs=None, lower=0, upper=1e20): return success def _createConstraint(self, compIDs, lbound, ubound): + """ + Create a new constraint object for TACS. + + Parameters + ---------- + compIDs: list[int] + List of compIDs to select. + + lbound: float or complex + lower bound for constraint. Defaults to 0.0. + + ubound: float or complex + upper bound for constraint. Defaults to 1e20. + + Returns + ------- + constraint : ParallelVolumeConstraint or None + Constraint object if successful, None otherwise. + + """ # Check if elements in supplied compIDs are all shell elements or all solid elements elemIDs = self.meshLoader.getGlobalElementIDsForComps( compIDs, nastranOrdering=True @@ -651,7 +671,7 @@ def write_unstructured_zone( self.log.info("is_points = %s" % is_points) datapacking = "POINT" if is_points else "BLOCK" # Make sure to include title - msg += f" T={self.title}, n={nnodes:d}, e={nelements:d}, ZONETYPE={zone_type}, DATAPACKING={datapacking}\n" + msg += f' T="{self.title}", n={nnodes:d}, e={nelements:d}, ZONETYPE={zone_type}, DATAPACKING={datapacking}\n' tecplot_file.write(msg) self._write_xyz_results(tecplot_file, is_points, ivars) diff --git a/tacs/problems/base.py b/tacs/problems/base.py index 3b46a47bb..c92c8a790 100644 --- a/tacs/problems/base.py +++ b/tacs/problems/base.py @@ -22,6 +22,24 @@ class TACSProblem(TACSSystem): def __init__( self, assembler, comm=None, options=None, outputViewer=None, meshLoader=None ): + """ + Parameters + ---------- + assembler : tacs.TACS.Assembler + Cython object responsible for creating and setting tacs objects used to solve problem + + comm : mpi4py.MPI.Intracomm + The comm object on which to create the pyTACS object. + + options : dict + Dictionary holding problem-specific option parameters (case-insensitive). + + outputViewer : tacs.TACS.TACSToFH5 + Cython object used to write out f5 files that can be converted and used for postprocessing. + + meshLoader : tacs.pymeshloader.pyMeshLoader + pyMeshLoader object used to create the assembler. + """ # Set attributes and options TACSSystem.__init__(self, assembler, comm, options, outputViewer, meshLoader) @@ -81,6 +99,11 @@ def addFunction(self, funcName, funcHandle, compIDs=None, **kwargs): def getFunctionKeys(self): """ Return a list of the current function key names + + Returns + ------- + funcNames : list[str] + List containing user-defined names for functions added so far. """ return list(self.functionList.keys()) @@ -108,7 +131,7 @@ def _addLoadToComponents(self, FVec, compIDs, F, averageLoad=False): to determine this. F : numpy.ndarray 1d or 2d length (varsPerNodes) or (numCompIDs, varsPerNodes) - Vector(s) of 'force' to apply to each components. If only one force vector is provided, + Vector(s) of 'force' to apply to each component. If only one force vector is provided, force will be copied uniformly across all components. averageLoad : bool @@ -123,7 +146,7 @@ def _addLoadToComponents(self, FVec, compIDs, F, averageLoad=False): on the physics problem being solved and the dofs included in the model. - A couple of examples of force vector components for common problem are listed below: + A couple of examples of force vector components for common problems are listed below: In Heat Conduction with varsPerNode = 1 F = [Qdot] # heat rate @@ -208,14 +231,14 @@ def _addLoadToNodes(self, FVec, nodeIDs, F, nastranOrdering=False): or NASTRAN (grid IDs in bdf file) ordering Notes - ---------- + ----- The units of the entries of the 'force' vector F are not necessarily physical forces and their interpretation depends on the physics problem being solved and the dofs included in the model. - A couple of examples of force vector components for common problem are listed below: + A couple of examples of force vector components for common problems are listed below: In Heat Conduction with varsPerNode = 1 F = [Qdot] # heat rate @@ -254,7 +277,7 @@ def _addLoadToNodes(self, FVec, nodeIDs, F, nastranOrdering=False): "but length of vector provided was {}".format(vpn, len(F[0])) ) - # First find the cooresponding local node ID on each processor + # First find the corresponding local node ID on each processor localNodeIDs = self.meshLoader.getLocalNodeIDsFromGlobal( nodeIDs, nastranOrdering ) @@ -337,18 +360,18 @@ def _addTractionToComponents(self, auxElems, compIDs, tractions, faceIndex=0): Parameters ---------- - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. compIDs : list[int] or int The components with added loads. Use pyTACS.selectCompIDs method to determine this. - tractions : TACS AuxElements object - Array of traction vectors for each components + tractions : numpy.ndarray + Array of traction vectors for each component faceIndex : int - Indicates which face (side) of element to apply traction to. + Indicates which face (side) of the element to apply traction to. Note: not required for certain elements (i.e. shells) """ # Make sure compIDs is flat and unique @@ -386,7 +409,7 @@ def _addTractionToElements( Parameters ---------- - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. elemIDs : list[int] @@ -423,7 +446,7 @@ def _addTractionToElements( ) ) - # First find the coresponding local element ID on each processor + # First find the corresponding local element ID on each processor localElemIDs = self.meshLoader.getLocalElementIDsFromGlobal( elemIDs, nastranOrdering=nastranOrdering ) @@ -441,19 +464,19 @@ def _addTractionToElements( elemObj = self.meshLoader.getElementObjectForElemID( elemIDs[i], nastranOrdering=nastranOrdering ) - # Create appropriate traction object for this element type + # Create an appropriate traction object for this element type tracObj = elemObj.createElementTraction(faceIndex, tractions[i]) - # Traction not implemented for element + # Traction not implemented for this element if tracObj is None: self._TACSWarning( - "TACS element of type {} does not hav a traction implimentation. " + "TACS element of type {} does not hav a traction implementation. " "Skipping element in addTractionToElement procedure.".format( elemObj.getObjectName() ) ) # Traction implemented else: - # Add new traction to auxiliary element object + # Add new traction to the auxiliary element object auxElems.addElement(elemID, tracObj) # Reduce the element flag and make sure that every element was found on exactly 1 proc @@ -484,7 +507,7 @@ def _addPressureToComponents(self, auxElems, compIDs, pressures, faceIndex=0): Parameters ---------- - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. compIDs : list[int] or int @@ -492,10 +515,10 @@ def _addPressureToComponents(self, auxElems, compIDs, pressures, faceIndex=0): to determine this. pressures : Numpy array length 1 or compIDs - Array of pressure values for each components + Array of pressure values for each component faceIndex : int - Indicates which face (side) of element to apply pressure to. + Indicates which face (side) of the element to apply pressure to. Note: not required for certain elements (i.e. shells) """ # Make sure compIDs is flat and unique @@ -533,7 +556,7 @@ def _addPressureToElements( Parameters ---------- - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. elemIDs : list[int] @@ -570,7 +593,7 @@ def _addPressureToElements( ) ) - # First find the coresponding local element ID on each processor + # First find the corresponding local element ID on each processor localElemIDs = self.meshLoader.getLocalElementIDsFromGlobal( elemIDs, nastranOrdering=nastranOrdering ) @@ -592,7 +615,7 @@ def _addPressureToElements( # Pressure not implemented for element if pressObj is None: self._TACSWarning( - "TACS element of type {} does not hav a pressure implimentation. " + "TACS element of type {} does not hav a pressure implementation. " "Skipping element in addPressureToElement procedure.".format( elemObj.getObjectName() ) @@ -629,7 +652,7 @@ def _addInertialLoad(self, auxElems, inertiaVector): Parameters ---------- - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. inertiaVector : numpy.ndarray @@ -659,7 +682,7 @@ def _addCentrifugalLoad(self, auxElems, omegaVector, rotCenter, firstOrder=False Parameters ---------- - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. omegaVector : numpy.ndarray @@ -702,7 +725,7 @@ def _addLoadFromBDF(self, FVec, auxElems, loadID, setScale=1.0): FVec : tacs.TACS.Vec TACS BVec to add loads to. - auxElems : TACS AuxElements object + auxElems : tacs.TACS.AuxElements AuxElements object to add loads to. loadID : int diff --git a/tacs/problems/static.py b/tacs/problems/static.py index e9c11a91b..461f665ce 100644 --- a/tacs/problems/static.py +++ b/tacs/problems/static.py @@ -121,16 +121,16 @@ def __init__( name : str Name of this tacs problem - assembler : TACS.Assembler + assembler : tacs.TACS.Assembler Cython object responsible for creating and setting tacs objects used to solve problem comm : mpi4py.MPI.Intracomm The comm object on which to create the pyTACS object. - outputViewer : TACS.TACSToFH5 + outputViewer : tacs.TACS.TACSToFH5 Cython object used to write out f5 files that can be converted and used for postprocessing. - meshLoader : pymeshloader.pyMeshLoader + meshLoader : tacs.pymeshloader.pyMeshLoader pyMeshLoader object used to create the assembler. options : dict @@ -362,7 +362,7 @@ def addFunction(self, funcName, funcHandle, compIDs=None, **kwargs): The user-supplied name for the function. This will typically be a string that is meaningful to the user - funcHandle : TACS.Function + funcHandle : tacs.TACS.Function The function handle to use for creation. This must come from the functions module in tacs. @@ -427,7 +427,7 @@ def addLoadToComponents(self, compIDs, F, averageLoad=False): to determine this. F : numpy.ndarray 1d or 2d length (varsPerNodes) or (numCompIDs, varsPerNodes) - Vector(s) of 'force' to apply to each components. If only one force vector is provided, + Vector(s) of 'force' to apply to each component. If only one force vector is provided, force will be copied uniformly across all components. averageLoad : bool @@ -435,14 +435,14 @@ def addLoadToComponents(self, compIDs, F, averageLoad=False): or copied and applied individually to each component (False). Defaults to False. Notes - ---------- + ----- The units of the entries of the 'force' vector F are not necessarily physical forces and their interpretation depends on the physics problem being solved and the dofs included in the model. - A couple of examples of force vector components for common problem are listed below: + A couple of examples of force vector components for common problems are listed below: In Heat Conduction with varsPerNode = 1 F = [Qdot] # heat rate @@ -477,14 +477,14 @@ def addLoadToNodes(self, nodeIDs, F, nastranOrdering=False): or NASTRAN (grid IDs in bdf file) ordering Notes - ---------- + ----- The units of the entries of the 'force' vector F are not necessarily physical forces and their interpretation depends on the physics problem being solved and the dofs included in the model. - A couple of examples of force vector components for common problem are listed below: + A couple of examples of force vector components for common problems are listed below: In Heat Conduction with varsPerNode = 1 F = [Qdot] # heat rate @@ -535,7 +535,7 @@ def addTractionToComponents(self, compIDs, tractions, faceIndex=0): to determine this. tractions : Numpy array length 1 or compIDs - Array of traction vectors for each components + Array of traction vectors for each component faceIndex : int Indicates which face (side) of element to apply traction to. @@ -589,7 +589,7 @@ def addPressureToComponents(self, compIDs, pressures, faceIndex=0): to determine this. pressures : Numpy array length 1 or compIDs - Array of pressure values for each components + Array of pressure values for each component faceIndex : int Indicates which face (side) of element to apply pressure to. @@ -797,7 +797,7 @@ def solve(self, Fext=None): finalNormTime = time.time() # If timing was was requested print it, if the solution is nonlinear - # print this information automatically if prinititerations was requested. + # print this information automatically if print iterations was requested. if self.getOption("printTiming"): self._pp("+--------------------------------------------------+") self._pp("|") @@ -1107,7 +1107,7 @@ def addDVSens(self, evalFuncs, dvSensList, scale=1.0): evalFuncs : list[str] The functions the user wants returned - dvSensList : list[BVec] or list[numpy.ndarray] + dvSensList : list[tacs.TACS.Vec] or list[numpy.ndarray] List of sensitivity vectors to add partial sensitivity to scale : float @@ -1149,10 +1149,10 @@ def addAdjointResProducts(self, adjointlist, dvSensList, scale=-1.0): Parameters ---------- - adjointlist : list[BVec] or list[numpy.ndarray] + adjointlist : list[tacs.TACS.Vec] or list[numpy.ndarray] List of adjoint vectors for residual sensitivity product - dvSensList : list[BVec] or list[numpy.ndarray] + dvSensList : list[tacs.TACS.Vec] or list[numpy.ndarray] List of sensitivity vectors to add product to scale : float @@ -1205,7 +1205,7 @@ def addXptSens(self, evalFuncs, xptSensList, scale=1.0): evalFuncs : list[str] The functions the user wants returned - xptSensList : list[BVec] or list[numpy.ndarray] + xptSensList : list[tacs.TACS.Vec] or list[numpy.ndarray] List of sensitivity vectors to add partial sensitivity to scale : float @@ -1247,10 +1247,10 @@ def addAdjointResXptSensProducts(self, adjointlist, xptSensList, scale=-1.0): Parameters ---------- - adjointlist : list[BVec] or list[numpy.ndarray] + adjointlist : list[tacs.TACS.Vec] or list[numpy.ndarray] List of adjoint vectors for residual sensitivity product - xptSensList : list[BVec] or list[numpy.ndarray] + xptSensList : list[tacs.TACS.Vec] or list[numpy.ndarray] List of sensitivity vectors to add product to scale : float @@ -1302,10 +1302,10 @@ def getResidual(self, res, Fext=None): Parameters ---------- - res : TACS BVec or numpy array + res : tacs.TACS.Vec or numpy.ndarray If res is not None, place the residuals into this array. - Fext : TACS BVec or numpy array, optional + Fext : tacs.TACS.Vec or numpy.ndarray, optional Distributed array containing additional loads (ex. aerodynamic forces for aerostructural coupling) to applied to RHS of the static problem. @@ -1347,7 +1347,7 @@ def getJacobian(self): Returns ------- - tuple of 2 or 4 scipy.sparse.bsr_matrices + K : (scipy.sparse.bsr_matrix, scipy.sparse.bsr_matrix) or (scipy.sparse.bsr_matrix, scipy.sparse.bsr_matrix, scipy.sparse.bsr_matrix, scipy.sparse.bsr_matrix) A tuple of 2 scipy.sparse.bsr_matrices (A, B) if Jacobian is a TACSParallelMat, or 4 scipy.sparse.bsr_matrices (A, B, C, D) if Jacobian is a TACSSchurMat """ @@ -1364,10 +1364,10 @@ def addTransposeJacVecProduct(self, phi, prod, scale=1.0): Parameters ---------- - phi : TACS BVec or numpy array + phi : tacs.TACS.Vec or numpy.ndarray Input vector to product with the transpose Jacobian. - prod : TACS BVec or numpy array + prod : tacs.TACS.Vec or numpy.ndarray Output vector to add Jacobian product to. scale : float @@ -1421,9 +1421,9 @@ def solveAdjoint(self, rhs, phi): Parameters ---------- - rhs : TACS BVec or numpy array + rhs : tacs.TACS.Vec or numpy.ndarray right hand side vector for adjoint solve - phi : TACS BVec or numpy array + phi : tacs.TACS.Vec or numpy.ndarray BVec or numpy array into which the adjoint is saved """ diff --git a/tacs/problems/transient.py b/tacs/problems/transient.py index 5071eb604..cd0ee8b4d 100644 --- a/tacs/problems/transient.py +++ b/tacs/problems/transient.py @@ -364,7 +364,7 @@ def addLoadToComponents( to determine this. F : Numpy 1d or 2d array length (varsPerNodes) or (numCompIDs, varsPerNodes) - Vector(s) of 'force' to apply to each components. If only one force vector is provided, + Vector(s) of 'force' to apply to each component. If only one force vector is provided, force will be copied uniformly across all components. timeStage : int or None @@ -384,7 +384,7 @@ def addLoadToComponents( on the physics problem being solved and the dofs included in the model. - A couple of examples of force vector components for common problem are listed below: + A couple of examples of force vector components for common problems are listed below: In Heat Conduction with varsPerNode = 1 F = [Qdot] # heat rate @@ -446,7 +446,7 @@ def addLoadToNodes( on the physics problem being solved and the dofs included in the model. - A couple of examples of force vector components for common problem are listed below: + A couple of examples of force vector components for common problems are listed below: In Heat Conduction with varsPerNode = 1 F = [Qdot] # heat rate @@ -531,7 +531,7 @@ def addTractionToComponents( to determine this. tractions : numpy.ndarray length 1 or compIDs - Array of traction vectors for each components + Array of traction vectors for each component timeStage : int or None Time stage index to apply load to. Default is None, which is applicable only for @@ -630,7 +630,7 @@ def addPressureToComponents( to determine this. pressures : Numpy array length 1 or compIDs - Array of pressure values for each components + Array of pressure values for each component timeStage : int or None Time stage index to apply load to. Default is None, which is applicable only for @@ -1031,7 +1031,7 @@ def addFunction(self, funcName, funcHandle, compIDs=None, **kwargs): The user-supplied name for the function. This will typically be a string that is meaningful to the user - funcHandle : TACS.Function + funcHandle : tacs.TACS.Function The function handle to use for creation. This must come from the functions module in tacs. diff --git a/tacs/pymeshloader.py b/tacs/pymeshloader.py index cf036a0ab..c0823d81d 100644 --- a/tacs/pymeshloader.py +++ b/tacs/pymeshloader.py @@ -252,36 +252,66 @@ def _updateNastranToTACSDicts(self): def getBDFInfo(self): """ Return pynastran bdf object. + + Returns + ------- + bdfInfo : pyNastran.bdf.bdf.BDF + pyNastran bdf object. """ return self.bdfInfo def getNumComponents(self): """ Return number of components (properties) found in bdf. + + Returns + ------- + nComps : int + Number of components (properties) found in bdf file. """ return self.bdfInfo.nproperties def getNumBDFNodes(self): """ Return number of nodes found in bdf. + + Returns + ------- + nNodes : int + Number of nodes found in bdf file. """ return self.bdfInfo.nnodes def getNumOwnedNodes(self): """ Return number of nodes owned by this processor. + + Returns + ------- + nNodes : int + Number of nodes owned by this proc. """ return self.assembler.getNumOwnedNodes() def getNumBDFElements(self): """ Return number of elements found in bdf. + + Returns + ------- + nElems : int + Number of elements found in bdf file. """ return self.bdfInfo.nelements def getBDFNodes(self, nodeIDs, nastranOrdering=False): """ Return x,y,z location of specified node in bdf file. + + Returns + ------- + xyz : numpy.ndarray + Coordinates of specified nodes. """ # Convert to tacs numbering, if necessary if nastranOrdering: @@ -289,12 +319,37 @@ def getBDFNodes(self, nodeIDs, nastranOrdering=False): return self.bdfXpts[nodeIDs] def getElementComponents(self): + """ + Get a list specifying the component ID of each element. + + Returns + ------- + compIDList : list[int] + List containing componentID of each element found in the bdf file. + """ elements = self.bdfInfo.elements propertyIDList = [elements[eID].pid for eID in self.bdfInfo.element_ids] compIDList = self.idMap(propertyIDList, self.nastranToTACSCompIDDict) return compIDList def getConnectivityForComp(self, componentID, nastranOrdering=False): + """ + Get a nodal connectivities of each element belonging to the specified component. + + Parameters + ---------- + componentID : int + Component ID number. + + nastranOrdering : bool + Flag signaling whether nodeIDs should be returned in TACS (default) or NASTRAN (grid IDs in bdf file) ordering + Defaults to False. + + Returns + ------- + elemConns : list[list[int]] + List of nodal connectivities of each element belonging to this component. + """ # Find all of the element IDs belonging to this property group propertyID = list(self.bdfInfo.property_ids)[componentID] elementIDs = self.propertyIDToElementIDDict[propertyID] @@ -315,6 +370,16 @@ def getElementObjectNumsForComp(self, componentID): """ Return tacs element object number for each element type belonging to this component. + + Parameters + ---------- + componentID : int + Component ID number. + + Returns + ------- + objNums : list[int] + List holding element object nums for the specifed component. """ return self.elemObjectNumByComp[componentID][:] @@ -322,21 +387,46 @@ def getElementDescripts(self): """ Get nested list containing all element types owned by each component group example: [['CQUAD4', 'CTRIA3], ['CQUAD4'], ['CQUAD4', CQUAD9']] + + Returns + ------- + elemDescripts : list[list[str]] + Nested list holding all element types owned by each component group. """ return self.elemDescripts def getComponentDescripts(self): """ Get user-defined labels for each component read in from the BDF. + + Returns + ------- + compDescripts : list[str] + List holding description strings for each component. """ return self.compDescripts def getLocalNodeIDsFromGlobal(self, globalIDs, nastranOrdering=False): """ - given a list of node IDs in global (non-partitioned) ordering + Given a list of node IDs in global (non-partitioned) ordering returns the local (partitioned) node IDs on each processor. If a requested node is not included on this processor, an entry of -1 will be returned. + + Parameters + ---------- + globalIDs : int or list[int] + List of global node IDs. + + nastranOrdering : bool + Flag signaling whether globalIDs is in TACS (default) or NASTRAN (grid IDs in bdf file) ordering + Defaults to False. + + Returns + ------- + localIDs : list[int] + List of local node IDs for each entry in globalIDs. + If the node is not owned by this processor, its index is filled with a value of -1. """ # Convert to tacs node numbering, if necessary if nastranOrdering: @@ -366,10 +456,25 @@ def getLocalNodeIDsFromGlobal(self, globalIDs, nastranOrdering=False): def getLocalElementIDsFromGlobal(self, globalIDs, nastranOrdering=False): """ - given a list of element IDs in global (non-partitioned) ordering + Given a list of element IDs in global (non-partitioned) ordering returns the local (partitioned) element IDs on each processor. If a requested element is not included on this processor, an entry of -1 will be returned. + + Parameters + ---------- + globalIDs : int or list[int] + List of global element IDs. + + nastranOrdering : bool + Flag signaling whether globalIDs is in TACS (default) or NASTRAN (element IDs in bdf file) ordering + Defaults to False. + + Returns + ------- + localIDs : list[int] + List of local element IDs for each entry in globalIDs. + If the element is not owned by this processor, its index is filled with a value of -1. """ # Convert to tacs node numbering, if necessary if nastranOrdering: @@ -401,12 +506,12 @@ def getGlobalNodeIDsForComps(self, componentIDs, nastranOrdering=False): List of integers of the compIDs numbers. nastranOrdering : bool - Flag signaling whether nodeIDs are in TACS (default) or NASTRAN (grid IDs in bdf file) ordering + Flag signaling whether nodeIDs should be returned in TACS (default) or NASTRAN (grid IDs in bdf file) ordering Defaults to False. Returns ------- - nodeIDs : list + nodeIDs : list[int] List of unique nodeIDs that belong to the given list of compIDs """ # First determine the actual physical locations @@ -457,6 +562,20 @@ def getLocalNodeIDsForComps(self, componentIDs): def getGlobalElementIDsForComps(self, componentIDs, nastranOrdering=False): """ Returns a list of element IDs belonging to specified components + + Parameters + ---------- + componentIDs : list[int] + Component ID numbers. + + nastranOrdering : bool + Flag signaling whether elemIDs should be returned in TACS (default) or NASTRAN (grid IDs in bdf file) ordering + Defaults to False + + Returns + ------- + elemIDs : list[int] + List of global element IDs belonging to the specified components. """ # Make sure list is flat componentIDs = self._flatten(componentIDs) @@ -479,6 +598,16 @@ def getLocalElementIDsForComps(self, componentIDs): """ Get the local element numbers on each proc used by tacs corresponding to the component groups in componentIDs. + + Parameters + ---------- + componentIDs : list[int] + Component ID numbers. + + Returns + ------- + elemIDs : list[int] + List of local element IDs on this proc belonging to the specified components. """ if self.creator is None: raise self._TACSError( @@ -500,6 +629,11 @@ def getLocalElementIDsForComps(self, componentIDs): def getLocalMultiplierNodeIDs(self): """ Get the tacs indices of multiplier nodes used to hold lagrange multipliers on this processor. + + Returns + ------- + nodeIDs : list[int] + List of multiplier node ID's owned by this proc. """ return self.ownedMultiplierNodeIDs @@ -514,7 +648,7 @@ def getGlobalToLocalNodeIDDict(self): Returns ------- - globalToLocalNodeIDDict : dict + globalToLocalNodeIDDict : dict[int,int] Dictionary holding mapping from global to local node IDs for this proc """ globalToLocalNodeIDDict = {} @@ -535,6 +669,11 @@ def getGlobalToLocalElementIDDict(self): localElementID = globalToLocalElementIDDict[globalElementID] * assuming globalElementID is owned on this processor + + Returns + ------- + globalToLocalElementIDDict : dict[int,int] + Dictionary holding mapping from global to local element IDs for this proc """ # Do sorting on root proc if self.comm.rank == 0: @@ -566,6 +705,23 @@ def setElementObject(self, componentID, objectIndex, elemObject): self.elemObjects[pointer] = elemObject def getElementObjectForElemID(self, elemID, nastranOrdering=False): + """ + Return TACS element object corresponding to specified element ID. + + Parameters + ---------- + elemID : int + Element ID number + + nastranOrdering : bool + Flag signaling whether elemIDs are in TACS (default) or NASTRAN (grid IDs in bdf file) ordering + Defaults to False. + + Returns + ------- + elemObj : tacs.TACS.Element + TACS element object. + """ # Convert to tacs numbering, if necessary if nastranOrdering: elemID = self.idMap(elemID, self.nastranToTACSElemIDDict) @@ -577,6 +733,14 @@ def getElementObjectForElemID(self, elemID, nastranOrdering=False): def createTACSAssembler(self, varsPerNode, massDVs): """ Setup TACSCreator object responsible for creating TACSAssembler + + Parameters + ---------- + varsPerNode : int + Number of variables per node for the model. + + massDVs : dict + Dictionary holding dv info for point masses. """ self.creator = tacs.TACS.Creator(self.comm, varsPerNode) @@ -698,18 +862,24 @@ def createTACSAssembler(self, varsPerNode, massDVs): def _isDOFInString(self, constrained_dofs, dof): """ - Find if dof number (nastran numbering) occurs in constraint string. + Determine if dof number (nastran numbering) occurs in constraint string. Parameters ---------- constrained_dofs : string String containing list of dofs (ex. '123456') + dof : int or string nastran dof number to check for + + Returns + ------- + found : bool + Flag indicating if specified dof is in string. """ # Convert to string, if necessary if isinstance(dof, int): - dof = "%d" % (dof) + dof = "%d" % dof # pyNastran only supports 0,1,2,3,4,5,6 as valid dof components # For this reason, we'll treat 0 as if its 7, since it's traditionally never used in nastran if dof == "7": @@ -725,13 +895,21 @@ def _addTACSRBE2(self, rbeInfo, varsPerNode): """ Method to automatically set up RBE2 element from bdf file for user. User should *NOT* set these up in their elemCallBack function. + + Parameters + ---------- + rbeInfo : pyNastran.bdf.cards.elements.rigid.RBE2 + pyNastran object holding rbe info. + + varsPerNode : int + Number of variables per node for the model. """ indepNode = rbeInfo.independent_nodes depNodes = [] depConstrainedDOFs = [] dummyNodes = [] dofsAsString = rbeInfo.cm - dofsAsList = self.isDOFInString(dofsAsString, varsPerNode) + dofsAsList = self.dofStringToList(dofsAsString, varsPerNode) for node in rbeInfo.dependent_nodes: depNodes.append(node) depConstrainedDOFs.extend(dofsAsList) @@ -767,9 +945,17 @@ def _addTACSRBE3(self, rbeInfo, varsPerNode): """ Method to automatically set up RBE3 element from bdf file for user. User should *NOT* set these up in their elemCallBack function. + + Parameters + ---------- + rbeInfo : pyNastran.bdf.cards.elements.rigid.RBE3 + pyNastran object holding rbe info. + + varsPerNode : int + Number of variables per node for the model. """ depNode = rbeInfo.dependent_nodes - depConstrainedDOFs = self.isDOFInString(rbeInfo.refc, varsPerNode) + depConstrainedDOFs = self.dofStringToList(rbeInfo.refc, varsPerNode) # add dummy node for lagrange multipliers dummyNodeNum = max(self.bdfInfo.node_ids) + 1 # Next available node number @@ -790,7 +976,7 @@ def _addTACSRBE3(self, rbeInfo, varsPerNode): for depNodeGroup in rbeInfo.wt_cg_groups: wt = depNodeGroup[0] dofsAsString = depNodeGroup[1] - dofsAsList = self.isDOFInString(dofsAsString, varsPerNode) + dofsAsList = self.dofStringToList(dofsAsString, varsPerNode) for node in depNodeGroup[2]: indepNodes.append(node) indepWeights.append(wt) @@ -817,6 +1003,17 @@ def _addTACSMassElement(self, massInfo, varsPerNode, dvDict): """ Method to automatically set up TACS mass elements from bdf file for user. User should *NOT* set these up in their elemCallBack function. + + Parameters + ---------- + massInfo : pyNastran.bdf.cards.elements.mass.PointMassElement + pyNastran object holding rbe info. + + varsPerNode : int + Number of variables per node for the model. + + dvDict : dict + Dictionary holding dv info for point mass. """ if massInfo.type == "CONM2": m = massInfo.mass @@ -864,13 +1061,13 @@ def _addTACSMassElement(self, massInfo, varsPerNode, dvDict): def _unattachedNodeCheck(self): """ - Check for any nodes that aren't attached to element. + Check for any nodes that aren't attached to an element. Notify the user and throw an error if we find any. This must be checked before creating the TACS assembler or a SegFault may occur. """ numUnattached = 0 if self.comm.rank == 0: - # Flatten conectivity to single list + # Flatten connectivity to a single list flattenedConn = it.chain.from_iterable(self.elemConnectivity) # uniqueify and order all element-attached nodes attachedNodes = set(flattenedConn) @@ -894,12 +1091,25 @@ def _unattachedNodeCheck(self): f"Please make sure that all nodes are attached to at least one element." ) - def isDOFInString(self, dofString, numDOFs): + def dofStringToList(self, dofString, numDOFs): """ Converts a dof string to a boolean list. Examples: '123' -> [1, 1, 1, 0, 0, 0] '1346' -> [1, 0, 1, 1, 0, 1] + + Parameters + ---------- + dofString : string + String containing list of dofs (ex. '123456') + + numDOFs : int + Number of dofs in model + + Returns + ------- + dofList : list[int] + List of booleans indicating which dofs are present in input string. """ dofList = [] for dof in range(numDOFs): @@ -915,6 +1125,19 @@ def idMap(self, fromIDs, tacsIDDict): """ Translate fromIDs numbering from nastran numbering to tacs numbering. If node ID doesn't exist in nastranIDList, return -1 for entry + + Parameters + ---------- + fromIDs : int or list[int] + IDs in Nastran numbering + + tacsIDDict : dict[int, int] + ID mapping dict generated by `_updateNastranToTACSDicts` + + Returns + ------- + toIDs : int or list[int] + IDs in TACS numbering """ # Input is a list return a list if hasattr(fromIDs, "__iter__"): diff --git a/tacs/pytacs.py b/tacs/pytacs.py index a9ffa6ede..a1b698b30 100755 --- a/tacs/pytacs.py +++ b/tacs/pytacs.py @@ -687,7 +687,7 @@ def getCompNames(self, compIDs=None): def getGlobalNodeIDsForComps(self, compIDs, nastranOrdering=False): """ - return the global (non-partitioned) node IDs belonging to a given list of component IDs + Return the global (non-partitioned) node IDs belonging to a given list of component IDs Parameters ---------- @@ -696,7 +696,7 @@ def getGlobalNodeIDsForComps(self, compIDs, nastranOrdering=False): If None, returns nodeIDs for all components. Defaults to None. - nastranOrdering : False + nastranOrdering : bool Flag signaling whether nodeIDs are in TACS (default) or NASTRAN (grid IDs in bdf file) ordering Defaults to False. @@ -746,7 +746,7 @@ def initialize(self, elemCallBack=None): Parameters ---------- - elemCallBack : function + elemCallBack : callable The calling sequence for elemCallBack **must** be as follows:: @@ -1164,7 +1164,7 @@ def getOrigDesignVars(self): during assembler creation. Returns - ---------- + ------- x : numpy.ndarray The original design variable vector set in tacs. @@ -1177,7 +1177,7 @@ def getDesignVarRange(self): get the lower/upper bounds for the design variables. Returns - ---------- + ------- xlb : numpy.ndarray The design variable lower bound. xub : numpy.ndarray @@ -1200,7 +1200,7 @@ def createDesignVec(self, asBVec=False): Defaults to False. Returns - ---------- + ------- x : numpy.ndarray or tacs.TACS.Vec Distributed design variable vector """ @@ -1214,6 +1214,11 @@ def createDesignVec(self, asBVec=False): def getNumDesignVars(self): """ Return the number of design variables on this processor. + + Returns + ------- + ndvs : int + Number of design variables on this processor. """ return self.x0.getSize() @@ -1221,6 +1226,11 @@ def getNumDesignVars(self): def getTotalNumDesignVars(self): """ Return the number of design variables across all processors. + + Returns + ------- + ndvs : int + Total number of design variables across all processors. """ return self.dvNum @@ -1251,7 +1261,7 @@ def createNodeVec(self, asBVec=False): Defaults to False. Returns - ---------- + ------- xpts : numpy.ndarray or tacs.TACS.Vec Distributed node coordinate vector """ @@ -1266,13 +1276,23 @@ def createNodeVec(self, asBVec=False): def getNumOwnedNodes(self): """ Get the number of nodes owned by this processor. + + Returns + ------- + nNodes : int + Number of nodes owned by this proc. """ return self.assembler.getNumOwnedNodes() @postinitialize_method def getNumOwnedMultiplierNodes(self): """ - Get number of multiplier nodes owned by this processor. + Get the number of lagrange multiplier nodes owned by this processor. + + Returns + ------- + nMultNodes : int + Number of multiplier nodes owned by this proc. """ return len(self.meshLoader.getLocalMultiplierNodeIDs()) @@ -1280,6 +1300,11 @@ def getNumOwnedMultiplierNodes(self): def getLocalMultiplierNodeIDs(self): """ Get the tacs indices of multiplier nodes used to hold lagrange multipliers on this processor. + + Returns + ------- + nodeIDs : list[int] + List of multiplier node ID's owned by this proc. """ return self.meshLoader.getLocalMultiplierNodeIDs() @@ -1297,7 +1322,7 @@ def createVec(self, asBVec=False): Defaults to False. Returns - ---------- + ------- vars : numpy.ndarray or tacs.TACS.Vec Distributed state variable vector """ @@ -1311,13 +1336,23 @@ def createVec(self, asBVec=False): def getVarsPerNode(self): """ Get the number of variables per node for the model. + + Returns + ------- + vpn : int + Number of variables per node. """ return self.assembler.getVarsPerNode() @postinitialize_method def applyBCsToVec(self, vec): """ - Applies zeros to boundary condition dofs in input vector. + Applies zeros to boundary condition DOFs in input vector. + + Parameters + ---------- + vec : numpy.ndarray or tacs.TACS.Vec + Vector to apply boundary conditions to. """ # Check if input is a BVec or numpy array if isinstance(vec, tacs.TACS.Vec): @@ -1349,7 +1384,7 @@ def createStaticProblem(self, name, options=None): Defaults to None. Returns - ---------- + ------- problem : tacs.problems.StaticProblem StaticProblem object used for modeling and solving static cases. """ @@ -1383,7 +1418,7 @@ def createTransientProblem(self, name, tInit, tFinal, numSteps, options=None): Defaults to None. Returns - ---------- + ------- problem : tacs.problems.TransientProblem TransientProblem object used for modeling and solving transient cases. """ @@ -1424,7 +1459,7 @@ def createModalProblem(self, name, sigma, numEigs, options=None): Defaults to None. Returns - ---------- + ------- problem : tacs.problems.ModalProblem ModalProblem object used for performing modal eigenvalue analysis. """ @@ -1451,8 +1486,8 @@ def createTACSProbsFromBDF(self): skip setting loads in Python. Returns - ---------- - structProblems : dict[tacs.problems.TACSProblem] + ------- + structProblems : dict[int, tacs.problems.TACSProblem] Dictionary containing a predefined TACSProblem for every loadcase found in the BDF. The dictionary keys are the loadcase IDs from the BDF. @@ -1857,7 +1892,7 @@ def createAdjacencyConstraint(self, name, options=None): Defaults to None. Returns - ---------- + ------- constraint : tacs.constraints.AdjacencyConstraint AdjacencyConstraint object used for calculating constraints. """ @@ -1896,7 +1931,7 @@ def createDVConstraint(self, name, options=None): Defaults to None. Returns - ---------- + ------- constraint : tacs.constraints.DVConstraint DVConstraint object used for calculating constraints. """ @@ -1934,7 +1969,7 @@ def createVolumeConstraint(self, name, options=None): Defaults to None. Returns - ---------- + ------- constraint : tacs.constraints.VolumeConstraint VolumeConstraint object used for calculating constraints. """ diff --git a/tacs/system.py b/tacs/system.py index cb4b28997..970b4d61d 100644 --- a/tacs/system.py +++ b/tacs/system.py @@ -7,8 +7,9 @@ # ============================================================================= import numpy as np +import tacs.pymeshloader import tacs.TACS -from .utilities import BaseUI +from tacs.utilities import BaseUI class TACSSystem(BaseUI): @@ -19,6 +20,24 @@ class TACSSystem(BaseUI): def __init__( self, assembler, comm=None, options=None, outputViewer=None, meshLoader=None ): + """ + Parameters + ---------- + assembler : tacs.TACS.Assembler + Cython object responsible for creating and setting tacs objects used to solve problem + + comm : mpi4py.MPI.Intracomm + The comm object on which to create the pyTACS object. + + options : dict + Dictionary holding problem-specific option parameters (case-insensitive). + + outputViewer : tacs.TACS.TACSToFH5 + Cython object used to write out f5 files that can be converted and used for postprocessing. + + meshLoader : tacs.pymeshloader.pyMeshLoader + pyMeshLoader object used to create the assembler. + """ # TACS assembler object self.assembler = assembler # TACS F5 output writer @@ -145,6 +164,11 @@ def _arrayToDesignVec(self, dvArray): def getNumDesignVars(self): """ Return the number of design variables on this processor. + + Returns + ------- + ndvs : int + Number of design variables on this processor. """ return self.x.getSize() @@ -215,6 +239,11 @@ def _arrayToNodeVec(self, xptsArray): def getNumCoordinates(self): """ Return the number of mesh coordinates on this processor. + + Returns + ------- + ncoords : int + Number of mesh coordinates on this processor. """ return self.Xpts.getSize() @@ -223,12 +252,22 @@ def getNumCoordinates(self): def getVarsPerNode(self): """ Get the number of variables per node for the model. + + Returns + ------- + vpn : int + Number of variables per node. """ return self.assembler.getVarsPerNode() def getNumOwnedNodes(self): """ Get the number of nodes owned by this processor. + + Returns + ------- + nnodes : int + Number of nodes on this processor. """ return self.assembler.getNumOwnedNodes()