@@ -404,6 +404,7 @@ def PlotCyclicNodalResult(self, rnum, phase=0, comp='norm', as_abs=False, label=
404404 """
405405 Plots a nodal result from a cyclic analysis.
406406
407+
407408 Parameters
408409 ----------
409410 rnum : interger
@@ -428,11 +429,13 @@ def PlotCyclicNodalResult(self, rnum, phase=0, comp='norm', as_abs=False, label=
428429 default. When disabled, plots the maximum response of a single
429430 sector of the cyclic solution in the component of interest.
430431
432+
431433 Returns
432434 -------
433435 cpos : list
434436 Camera position from vtk render window.
435-
437+
438+
436439 Notes
437440 -----
438441 None
@@ -642,17 +645,20 @@ def PlotNodalResult(self, rnum, comp='norm', as_abs=False, label=''):
642645
643646 def GetTimeValues (self ):
644647 """
645- SUMMARY
646648 Returns table of time values for results. For a modal analysis, this
647649 corresponds to the frequencies of each mode.
648650
649651
650- INPUTS
652+ Parameters
653+ ----------
651654 None
652655
653656
654- OUTPUTS
655- tvalues (np.float64 array)
657+ Returns
658+ -------
659+ tvalues : np.float64 array
660+ Table of time values for results. For a modal analysis, this
661+ corresponds to the frequencies of each mode.
656662
657663 """
658664
@@ -677,6 +683,7 @@ def GetNodalResult(self, rnum, sort=True):
677683 """
678684 Returns the nodal result for a result number
679685
686+
680687 Parameters
681688 ----------
682689 rnum : interger
@@ -687,11 +694,13 @@ def GetNodalResult(self, rnum, sort=True):
687694 node numbering (self.nnum) (default). If left unsorted, results
688695 correspond to the nodal equivalence array self.resultheader['neqv']
689696
697+
690698 Returns
691699 -------
692700 result : numpy.float array
693701 Result is (nnod x numdof), or number of nodes by degrees of freedom
694-
702+
703+
695704 Notes
696705 -----
697706 None
@@ -832,7 +841,6 @@ def StoreGeometry(self):
832841 NODES - node numbers defining the element
833842 """
834843
835-
836844 # allocate memory for this (a maximum of 21 points per element)
837845 etype = np .empty (nelm , np .int32 )
838846
@@ -905,7 +913,7 @@ def NodalStress(self, rnum):
905913 None
906914
907915 """
908- #%% debug cell
916+
909917 # Get the header information from the header dictionary
910918 endian = self .resultheader ['endian' ]
911919 rpointers = self .resultheader ['rpointers' ]
@@ -939,6 +947,7 @@ def NodalStress(self, rnum):
939947 ele_ind_table += element_rst_ptr
940948
941949 # Each element header contains 25 records for the individual results
950+ # get the location of the nodal stress
942951 table_index = e_table .index ('ptrENS' )
943952
944953 # check number of records to read (differs with each version)
@@ -948,57 +957,43 @@ def NodalStress(self, rnum):
948957 nnode_elem = nodstr [etype [0 ]]
949958 f .seek ((ele_ind_table [0 ] + ptrENS - 2 )* 4 )
950959 nitem = np .fromfile (f , endian + 'i' , 1 )[0 ]/ nnode_elem
951-
952-
953- nstresses = self .edge_idx .size
954- stresses = np .empty ((nstresses , 6 ), np .float32 )
960+ f .close ()
955961
956- #%% debug cell 2
957- c = 0
958- for i in range (len (ele_ind_table )):
959- # Element nodal stresses, ptrENS, is the third item in the table
960- f .seek ((ele_ind_table [i ] + table_index )* 4 )
961- ptrENS = np .fromfile (f , endian + 'i' , 1 )[0 ]
962-
963- # read the stresses evaluated at the intergration points or nodes
964- nnode_elem = nodstr [etype [i ]]
965-
966- f .seek ((ele_ind_table [i ] + ptrENS )* 4 )
967- stress = np .fromfile (f , endian + 'f' , nnode_elem * nitem ).reshape ((- 1 , nitem ))#[:, sidx]
968-
969- # store stresses
970- stresses [c :c + nnode_elem ] = stress [:, :6 ]
971- c += nnode_elem
972-
973- # close file
974- f .close ()
975-
976-
977- # Average the stresses for each element at each node
978- # enode = self.edge_node_num_idx
979- # s_node = np.empty((enode.size, 6), np.float32)
980- # for i in range(6):
981- # s_node[:, i] = np.bincount(self.edge_idx, weights=stresses[:, i])[enode]
982- # ntimes = np.bincount(self.edge_idx)[enode]
983- # s_node /= ntimes.reshape((-1, 1))
962+ # number of nodes
963+ nnod = self .resultheader ['nnod' ]
984964
965+ # different behavior depending on version of ANSYS
966+ # v15 seems to use floating point while < v14.5 uses double and stores
967+ # principle values
968+ if nitem == 6 : # single precision >= v14.5
969+ ele_data_arr = np .zeros ((nnod , 6 ), np .float32 )
970+ _rstHelper .LoadStress (self .filename , table_index , ele_ind_table ,
971+ nodstr , etype , nitem , ele_data_arr ,
972+ self .edge_idx )
973+ elif nitem == 22 : # double precision < v14.5
974+ nitem = 11
975+ ele_data_arr = np .zeros ((nnod , 6 ), np .float64 )
976+ _rstHelper .LoadStressDouble (self .filename , table_index ,
977+ ele_ind_table , nodstr , etype , nitem ,
978+ ele_data_arr , self .edge_idx )
979+
980+ elif nitem == 11 : # single precision < v14.5
981+ ele_data_arr = np .zeros ((nnod , 6 ), np .float32 )
982+ _rstHelper .LoadStress (self .filename , table_index , ele_ind_table ,
983+ nodstr , etype , nitem , ele_data_arr ,
984+ self .edge_idx )
985985
986+ else :
987+ raise Exception ('Invalid nitem. Unable to load nodal stresses' )
986988
987- # grabe element results from binary
989+ # Average based on the edges of elements
988990 enode = self .edge_node_num_idx
989991 ntimes = np .bincount (self .edge_idx )[enode ]
990-
991- nnod = self .resultheader ['nnod' ]
992- ele_data_arr = np .zeros ((nnod , 6 ), np .float32 )
993- _rstHelper .LoadStress (self .filename , table_index ,
994- ele_ind_table , nodstr , etype ,
995- nitem , ele_data_arr , self .edge_idx )
996-
997992 s_node = ele_data_arr [enode ]
998993 s_node /= ntimes .reshape ((- 1 , 1 ))
999994
1000995 return s_node
1001-
996+
1002997
1003998 def PlotNodalStress (self , rnum , stype ):
1004999 """
@@ -1010,26 +1005,25 @@ def PlotNodalStress(self, rnum, stype):
10101005 across elements, stresses will vary based on the element they are
10111006 evaluated from.
10121007
1008+
10131009 Parameters
10141010 ----------
10151011 rnum : interger
10161012 Result set using zero based indexing.
10171013 stype : string
10181014 Stress type from the following list: [Sx Sy Sz Sxy Syz Sxz]
10191015
1016+
10201017 Returns
10211018 -------
10221019 None
10231020
1024- Notes
1025- -----
1026- None
1027-
10281021 """
10291022
10301023 stress_types = ['Sx' , 'Sy' , 'Sz' , 'Sxy' , 'Syz' , 'Sxz' , 'Seqv' ]
10311024 if stype not in stress_types :
1032- raise Exception ("Stress type not in \n ['Sx', 'Sy', 'Sz', 'Sxy', 'Syz', 'Sxz']" )
1025+ raise Exception ('Stress type not in \n ' + \
1026+ "['Sx', 'Sy', 'Sz', 'Sxy', 'Syz', 'Sxz']" )
10331027
10341028 sidx = ['Sx' , 'Sy' , 'Sz' , 'Sxy' , 'Syz' , 'Sxz' ].index (stype )
10351029
@@ -1052,6 +1046,53 @@ def PlotNodalStress(self, rnum, stype):
10521046 del plobj
10531047
10541048 return cpos
1049+
1050+
1051+ def SaveAsVTK (self , filename , binary = True ):
1052+ """
1053+ Writes all appends all results to an unstructured grid and writes it to
1054+ disk.
1055+
1056+ The file extension will select the type of writer to use. *.vtk will
1057+ use the legacy writer, while *.vtu will select the VTK XML writer.
1058+
1059+
1060+ Parameters
1061+ ----------
1062+ filename : str
1063+ Filename of grid to be written. The file extension will select the
1064+ type of writer to use. *.vtk will use the legacy writer, while
1065+ *.vtu will select the VTK XML writer.
1066+ binary : bool, optional
1067+ Writes as a binary file by default. Set to False to write ASCII
1068+
1069+
1070+ Notes
1071+ -----
1072+ Binary files write much faster than ASCII, but binary files written on
1073+ one system may not be readable on other systems. Binary can only be
1074+ selected for the legacy writer.
1075+
1076+
1077+ """
1078+
1079+ # Copy grid as to not write results to original object
1080+ grid = self .uGrid .Copy ()
1081+
1082+ for i in range (self .nsets ):
1083+ # Nodal Results
1084+ val = self .GetNodalResult (i )
1085+ grid .AddPointScalars (val , 'NodalResult{:03d}' .format (i ))
1086+
1087+ # Nodal Stress values are only valid
1088+ stress = self .NodalStress (i )
1089+ val = np .zeros ((grid .GetNumberOfPoints (), stress .shape [1 ]))
1090+ val [self .edge_node_num_idx ] = stress
1091+ grid .AddPointScalars (val , 'NodalStress{:03d}' .format (i ))
1092+
1093+ # Write to file and clean up
1094+ grid .WriteGrid (filename )
1095+ del grid
10551096
10561097
10571098def GetResultInfo (filename ):
@@ -1205,4 +1246,39 @@ def delete_row_csc(mat, i):
12051246 mat .indptr [i :- 1 ] = mat .indptr [i + 1 :]
12061247 mat .indptr [i :] -= n
12071248 mat .indptr = mat .indptr [:- 1 ]
1208- mat ._shape = (mat ._shape [0 ]- 1 , mat ._shape [1 ])
1249+ mat ._shape = (mat ._shape [0 ]- 1 , mat ._shape [1 ])
1250+
1251+
1252+ # =============================================================================
1253+ # load stress debug using numpy
1254+ # =============================================================================
1255+ # #%% numpy debug
1256+ # f = open(self.filename)
1257+ # nstresses = self.edge_idx.size
1258+ # stresses = np.empty((nstresses, 6), np.float32)
1259+ # c = 0
1260+ # for i in range(len(ele_ind_table)):
1261+ # # Element nodal stresses, ptrENS, is the third item in the table
1262+ # f.seek((ele_ind_table[i] + table_index)*4)
1263+ # ptrENS = np.fromfile(f, endian + 'i', 1)[0]
1264+ #
1265+ # # read the stresses evaluated at the intergration points or nodes
1266+ # nnode_elem = nodstr[etype[i]]
1267+ #
1268+ # f.seek((ele_ind_table[i] + ptrENS)*4)
1269+ # stress = np.fromfile(f, endian + 'f', nnode_elem*nitem).reshape((-1, nitem))#[:, sidx]
1270+ #
1271+ # # store stresses
1272+ # stresses[c:c + nnode_elem] = stress[:, :6]
1273+ # c += nnode_elem
1274+ #
1275+ # # close file
1276+ # f.close()
1277+ #
1278+ # # Average the stresses for each element at each node
1279+ # enode = self.edge_node_num_idx
1280+ # s_node = np.empty((enode.size, 6), np.float32)
1281+ # for i in range(6):
1282+ # s_node[:, i] = np.bincount(self.edge_idx, weights=stresses[:, i])[enode]
1283+ # ntimes = np.bincount(self.edge_idx)[enode]
1284+ # s_node /= ntimes.reshape((-1, 1))
0 commit comments