[docs]class Node:
"""Class for a node to be used in finite element analyses.
A node object is defined by its position in 3D cartesian space and has six associated degrees
of freedom.
:cvar coords: Cartesian coordinates of the node
:vartype coords: list[float, float, float]
:cvar dofs: List of degrees of freedom for the node *(x, y, z, rx, ry, rz)*
:vartype dofs: list[:class:`~feastruct.fea.node.DoF`]
:cvar nfs: Node freedom signature
:vartype nfs: list[bool]
"""
[docs] def __init__(self, coords):
"""Inits the Node class.
:param coords: Cartesian coordinates of the node *([x], [x, y] or [x, y, z])*
:type coords: list[float]
"""
if len(coords) == 1:
self.coords = [coords[0], 0, 0]
elif len(coords) == 2:
self.coords = [coords[0], coords[1], 0]
elif len(coords) == 3:
self.coords = coords
else:
# TODO: throw an error
return
# create six dofs
self.dofs = []
for i in range(6):
self.dofs.append(DoF(node=self, node_dof_num=i))
# set the node freedom signature
self.nfs = [False, False, False, False, False, False]
# get x-coordinate
@property
def x(self):
return self.coords[0]
# get y-coordinate
@property
def y(self):
return self.coords[1]
# get z-coordinate
@property
def z(self):
return self.coords[2]
[docs] def move_node(self, vec):
"""Moves the current node by the vector vec *([dx], [dx, dy] or [dx, dy, dz])*.
:param vec: Vector to move the current node by
:type vec: list[float]
"""
if len(vec) == 1:
self.coords[0] += vec[0]
elif len(vec) == 2:
self.coords[0] += vec[0]
self.coords[1] += vec[1]
elif len(vec) == 3:
self.coords += vec
else:
# TODO: throw an error
return
[docs] def copy_node(self, vec):
"""Copies and returns a new node shifted by the vector vec
*([dx], [dx, dy] or [dx, dy, dz])*.
:param vec: Vector to move the current node by
:type vec: list[float]
:returns: New node object
:rtype: :class:`~feastruct.fea.node.Node`
"""
new_coords = self.coords
if len(vec) == 1:
new_coords[0] += vec[0]
elif len(vec) == 2:
new_coords[0] += vec[0]
new_coords[1] += vec[1]
elif len(vec) == 3:
new_coords += vec
else:
# TODO: throw an error
return
return Node(coords=new_coords)
[docs] def get_dofs(self, freedom_signature):
"""Returns the degree of freedom objects relating to the freedom_signature list.
:param freedom_signature: Degree of freedom indices to return
:type freedom_signature: list[bool]
:returns: List containing degree of freedom objects
:rtype: list[:class:`~feastruct.fea.node.DoF`]
"""
# initialise dof_list
dof_list = []
# loop through freedom_signature list
for (i, freedom) in enumerate(freedom_signature):
# if the freedom is True
if freedom:
dof_list.append(self.dofs[i])
return dof_list
[docs] def get_displacements(self, analysis_case):
"""Returns the displacement vector for the current node and analysis case. If the degree
of freedom is not used in the current analysis case, a *None* value is assigned.
Degree of freedoms are ordered as follows: *(x, y, z, rx, ry, rz)*
:param analysis_case: Analysis case relating to the displacement
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
:returns: Displacement vector of length *(1 x 6)*
:rtype: float
"""
# assign displacement vector
disp_vector = []
# loop throught the degrees of freedom
for dof in self.dofs:
try:
disp = dof.get_displacement(analysis_case=analysis_case)
except Exception:
disp = None
disp_vector.append(disp)
return disp_vector
[docs]class DoF:
"""Class for a degree of freedom to be used in finite element analyses.
A degree of freedom relates to a specific node and has a specific degree of freedom number used
in the finite element analysis. Displacement results are stored within the degree of freedom.
:cvar node: Parent node
:vartype node: :class:`~feastruct.fea.node.Node`
:cvar int node_dof_num: Node degree of freedom number
:cvar int global_dof_num: Global degree of freedom number
:cvar displacements: A list of Displacement objects for the dof
:vartype displacements: list[:class:`~feastruct.fea.node.Displacement`]
:cvar buckling_results: A list of BucklingModes objects for the dof
:vartype buckling_results: list[:class:`~feastruct.fea.node.BucklingMode`]
:cvar frequency_results: A list of FrequencyModes objects for the dof
:vartype frequency_results: list[:class:`~feastruct.fea.node.FrequencyMode`]
"""
[docs] def __init__(self, node, node_dof_num):
"""Inits the DoF class.
:param node: Parent node
:type node: :class:`~feastruct.fea.node.Node`
"""
self.node = node
self.node_dof_num = node_dof_num
self.global_dof_num = None
self.displacements = []
self.buckling_results = []
self.frequency_results = []
[docs] def get_displacement(self, analysis_case):
"""Returns the displacement value relating to an :class:`~feastruct.fea.cases.AnalysisCase`.
:param analysis_case: Analysis case relating to the displacement
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
:returns: Displacement value
:rtype: float
:raises Exception: If a displacement corresponding to analysis_case cannot be found
"""
# loop through all the displacements
for displacement in self.displacements:
# if the analysis_case is found
if analysis_case == displacement.analysis_case:
# return the displacement
return displacement.disp
# if nothing is found raise an exception
str = 'Displacement corresponding to dof {0}'.format(self)
str += ' could not be found for analysis case {0}'.format(analysis_case)
raise Exception(str)
[docs] def get_buckling_mode(self, analysis_case, buckling_mode):
"""Returns the value of the eigenvalue and eigenvector for a given buckling_mode related to
an :class:`~feastruct.fea.cases.AnalysisCase`.
:param analysis_case: Analysis case relating to the buckling results
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
:param int buckling_mode: Buckling mode number
:returns: Eigenvalue and eigenvector corresponding to an analysis_case and buckling_mode
:rtype: tuple(float, float)
:raises Exception: If buckling results corresponding to an analysis_case cannot be found,
or if the buckling mode does not exist
"""
# loop through all the buckling results
for buckling_result in self.buckling_results:
# if the analysis case is found
if analysis_case == buckling_result.analysis_case:
# loop through all the modes
for (i, mode) in enumerate(buckling_result.buckling_modes):
# if the mode is found
if mode == buckling_mode:
# return the eigenvalue and eigevector
return (buckling_result.w[i], buckling_result.v[i])
# if the mode is not found
else:
str = 'Buckling result for dof {0} corresponding to buckling mode {1}'.format(
self, buckling_mode)
str += ' cannot be found for analysis case {0}.'.format(analysis_case)
str += ' Analysis case located, but buckling mode not found.'
raise Exception(str)
# if the analysis case is not found
str = 'Buckling result for dof {0} could not be found for analysis case {1}'.format(
self, analysis_case)
raise Exception(str)
[docs] def get_frequency_mode(self, analysis_case, frequency_mode):
"""Returns the value of the eigenvalue and eigenvector for a given frequency_mode related
to an :class:`~feastruct.fea.cases.AnalysisCase`.
:param analysis_case: Analysis case relating to the frequency results
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
:param int frequency_mode: Frequency mode number
:returns: Eigenvalue and eigenvector corresponding to an analysis_case and frequency_mode
:rtype: tuple(float, float)
:raises Exception: If frequency results corresponding to an analysis_case cannot be found,
or if the frequency mode does not exist
"""
# loop through all the frequency results
for frequency_result in self.frequency_results:
# if the analysis case is found
if analysis_case == frequency_result.analysis_case:
# loop through all the modes
for (i, mode) in enumerate(frequency_result.frequency_modes):
# if the mode is found
if mode == frequency_mode:
# return the eigenvalue and eigevector
return (frequency_result.w[i], frequency_result.v[i])
# if the mode is not found
else:
str = 'Frequency result for dof {0} corresponding to frequency mode'.format(
self)
str += ' {1} cannot be found for analysis case {0}.'.format(
analysis_case, frequency_mode)
str += ' Analysis case located, but frequency mode not found.'
raise Exception(str)
# if the analysis case is not found
str = 'Frequency result for dof {0} could not be found for analysis case {1}'.format(
self, analysis_case)
raise Exception(str)
[docs] def save_displacement(self, disp, analysis_case):
"""Saves a displacement result to the DoF.
:param float disp: Value of the displacement
:param analysis_case: Analysis case relating to the displacement
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
# check to see if there is already a displacement for the current analysis_case
for displacement in self.displacements:
if displacement.analysis_case == analysis_case:
displacement.disp = disp
return
self.displacements.append(Displacement(disp=disp, analysis_case=analysis_case))
[docs] def save_buckling_modes(self, buckling_modes, w, v, analysis_case):
"""Saves buckling results to the DoF.
:param buckling_modes: Buckling mode numbers
:type buckling_modes: list[int]
:param w: Eigenvalues corresponding to the given modes
:type w: list[float]
:param v: Value of the eigevectors at the DoF corresponding to the given modes
:type v: list[float]
:param analysis_case: Analysis case relating to the buckling analysis
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
# create buckling mode result
new_result = BucklingModes(
buckling_modes=buckling_modes, w=w, v=v, analysis_case=analysis_case
)
# loop through all the buckling results
for buckling_result in self.buckling_results:
# if the analysis case already exists, overwrite the results
if buckling_result.analysis_case == analysis_case:
buckling_result = new_result
return
# otherwise add a new result
self.buckling_results.append(new_result)
[docs] def save_frequency_modes(self, frequency_modes, w, v, analysis_case):
"""Saves frequency results to the DoF.
:param frequency_modes: Frequency mode numbers
:type frequency_modes: list[int]
:param w: Eigenvalues corresponding to the given modes
:type w: list[float]
:param v: Value of the eigevectors at the DoF corresponding to the given modes
:type v: list[float]
:param analysis_case: Analysis case relating to the frequency analysis
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
# create frequency mode result
new_result = FrequencyModes(
frequency_modes=frequency_modes, w=w, v=v, analysis_case=analysis_case
)
# loop through all the frequency results
for frequency_result in self.frequency_results:
# if the analysis case already exists, overwrite the results
if frequency_result.analysis_case == analysis_case:
frequency_result = new_result
return
# otherwise add a new result
self.frequency_results.append(new_result)
[docs]class Displacement:
"""Class for storing a displacement at a degree of freedom for a specific analysis case.
:cvar float disp: Displacement at a degree of freedom
:cvar analysis_case: Analysis case relating to the displacement
:vartype analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
[docs] def __init__(self, disp, analysis_case):
"""Inits the Displacement class.
:param float disp: Displacement at a degree of freedom
:param analysis_case: Analysis case relating to the displacement
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
self.disp = disp
self.analysis_case = analysis_case
[docs]class BucklingModes:
"""Class for storing buckling modes at a degree of freedom for a specific analysis case.
:cvar buckling_modes: Buckling mode numbers
:vartype buckling_modes: list[int]
:cvar w: Eigenvalues corresponding to the given modes
:vartype w: list[float]
:cvar float v: Value of the eigevectors at the DoF corresponding to the given modes
:vartype v: list[float]
:cvar analysis_case: Analysis case relating to the buckling analysis
:vartype analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
[docs] def __init__(self, buckling_modes, w, v, analysis_case):
"""Inits the Displacement class.
:param buckling_modes: Buckling mode numbers
:type buckling_modes: list[int]
:param w: Eigenvalues corresponding to the given modes
:type w: list[float]
:param v: Value of the eigevectors at the DoF corresponding to the given modes
:type v: list[float]
:param analysis_case: Analysis case relating to the buckling analysis
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
self.buckling_modes = buckling_modes
self.w = w
self.v = v
self.analysis_case = analysis_case
[docs]class FrequencyModes:
"""Class for storing frequency modes at a degree of freedom for a specific analysis case.
:cvar frequency_modes: Frequency mode numbers
:vartype frequency_modes: list[int]
:cvar w: Eigenvalues corresponding to the given modes
:vartype w: list[float]
:cvar float v: Value of the eigevectors at the DoF corresponding to the given modes
:vartype v: list[float]
:cvar analysis_case: Analysis case relating to the frequency analysis
:vartype analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
[docs] def __init__(self, frequency_modes, w, v, analysis_case):
"""Inits the Displacement class.
:param frequency_modes: Frequency mode numbers
:type frequency_modes: list[int]
:param w: Eigenvalues corresponding to the given modes
:type w: list[float]
:param v: Value of the eigevectors at the DoF corresponding to the given modes
:type v: list[float]
:param analysis_case: Analysis case relating to the frequency analysis
:type analysis_case: :class:`~feastruct.fea.cases.AnalysisCase`
"""
self.frequency_modes = frequency_modes
self.w = w
self.v = v
self.analysis_case = analysis_case