Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Метод конечных элементов для моделирования устройств и систем. Учебное пособие
.pdf
3.5. Рекомендации по реализации метода конечных элементов…
51
# точек, которые его составляют.
class Primitive:
def __init__(self):
self.nodes = []
def __str__(self):
s = '['
for n in self.nodes:
s += str(n) + '; '
s += ']'
return s
# Ребро – примитив из двух точек
class Edge(Primitive):
def __init__(self, n1, n2):
self.nodes = [int(n1)-1, int(n2)-1]
# Треугольник – примитив из трёх точек
class Triangle(Primitive):
def __init__(self, n1, n2, n3):
self.nodes = [int(n1)-1, int(n2)-1, int(n3)-1]
# Тетраэдр – примитив из четырёх точек
class Tetrahedron(Primitive):
def __init__(self, n1, n2, n3, n4):
self.nodes = [int(n1)-1, int(n2)-1, int(n3)-1, int(n4)-1]
# Данный класс позволяет считывать элементы из файла GMSH
class Mesh:
# Конструктор инициализирует поля пустыми значениями
def __init__(self):
self.nodes = []
self.edges = []
self.faces = []
self.tetrahedrons = []
self.vertices = []

3. Программная реализация напряженно-деформированных состояний…
52
# Метод заполняет поля для вершин, рёбер, граней,
# тетраэдров и узлов значениями из файла gmsh_file_name
def read_from_gmsh(self, gmsh_file_name):
with open(gmsh_file_name, 'r') as f:
self.__init__()
while f.readline() != SECTION_NODES:
continue
nodes_count = int(f.readline())
for i in range(nodes_count):
line = f.readline().split(' ')
self.nodes.append(Node(float(line[1]),
float(line[2]),
float(line[3])))
while f.readline() != SECTION_ELEMENTS:
continue
elements_count = int(f.readline())
for i in range(elements_count):
line = f.readline().split(' ')
if line[1] == TYPE_EDGE:
self.edges.append(Edge(line[5],
line[6]))
elif line[1] == TYPE_TRIANGLE:
self.faces.append(Triangle(line[5],
line[6],
line[7]))
elif line[1] == TYPE_TETRAHEDRON:
self.tetrahedrons.append(
Tetrahedron(line[5], line[6], line[7], line[8]))
elif line[1] == TYPE_VERTEX:
self.vertices.append(int(line[5]) - 1)
2. В следующем фрагменте определены классы, которые преобра-
зуют сырые данные, полученные из файла Gmsh, в данные о конкретных
геометрических объектах, которые готовы для использования в методе конечных элементов.
# Класс для определения и хранения координат параллелепипеда,

3.5. Рекомендации по реализации метода конечных элементов…
53
# в котором умещается вся решётка. Эти данные необходимы для
# поиска элементов, к которым могут быть применены
# граничные условия.
class BoundBox:
def __init__(self, min_x=float('inf'), min_y=float('inf'),
min_z=float('inf'), max_x=-float('inf'),
max_y=-float('inf'), max_z=-float('inf')):
self.min_x = min_x
self.min_y = min_y
self.min_z = min_z
self.max_x = max_x
self.max_y = max_y
self.max_z = max_z
def __str__(self):
precision = 4
float_mask = '%.' + str(precision) + 'f'
return ('(' + (float_mask % self.min_x) + ', '
+ (float_mask % self.min_y) + ', '
+ (float_mask % self.min_z) + ') -> ('
+ (float_mask % self.max_x) + ', '
+ (float_mask % self.max_y) + ', '
+ (float_mask % self.max_z) + ')')
# Класс для хранения информации об элементах, которые граничат
# с данным элементом по заданной грани.
class Neighbours:
def __init__(self):
self.elem_index = -1
self.face_index = -1
# Класс для построения и хранения всей решётки элементов
# целиком. Также он содержит различные служебные функции,
# относящиеся к конкретным элементам.
class GeometryBuilder:

3. Программная реализация напряженно-деформированных состояний…
54
def __init__(self):
self.elements = np.empty(1, dtype=TetrElement)
self.bound_box = BoundBox()
# Определение грани по её номеру в эаданном тетраэдре
def get_face(self, elem, face_index):
n_faces = len(elem.face_center)
assert face_index < n_faces
result = np.empty(n_faces - 1, dtype=Node)
for node in range(n_faces - 1):
result[node] = elem.nodes_coords[face_index]
face_index = (face_index + 1) % n_faces
return result
# Вычисление площади грани
def calculate_face_area(self, face):
area = 0.5 * abs(face[0].x * face[1].y * face[2].z +
face[0].y * face[1].z * face[2].x +
face[0].z * face[1].x * face[2].y face[0].z * face[1].y * face[2].x face[0].y * face[1].x * face[2].z face[0].x * face[1].z * face[2].y)
return area
# Вычисление координат центра элемента
def calculate_center(self, nodes):
cx, cy, cz = 0.0, 0.0, 0.0
for node in nodes:
cx += node.x
cy += node.y
cz += node.z
cx /= len(nodes)
cy /= len(nodes)
cz /= len(nodes)
return Node(cx, cy, cz)
# Вычисление координат нормали для заданной грани

3.5. Рекомендации по реализации метода конечных элементов…
55
def calculate_normal(self, face):
v1 = Vector3d(face[0], face[1])
v2 = Vector3d(face[0], face[2])
result = Vector3d()
result.x = v1.y * v2.z - v1.z * v2.y
result.y = v1.x * v2.z - v1.z * v2.x
result.z = v1.x * v2.y - v1.y * v2.x
result.normalize()
return result
# Расчёт ограничивающего параллелепипеда
def calculate_bound_box(self):
for it in self.elements:
nodes_count = len(it.nodes_coords)
for n_node in range(nodes_count):
self.merge(self.bound_box, it.nodes_coords[n_node])
def merge(self, box, point):
box.min_x = min(box.min_x, point.x)
box.min_y = min(box.min_y, point.y)
box.min_z = min(box.min_z, point.z)
box.max_x = max(box.max_x, point.x)
box.max_y = max(box.max_y, point.y)
box.max_z = max(box.max_z, point.z)
# Инициализация решётки
def init_geometry(self, nodes, tetrahedrons):
size = len(tetrahedrons)
if size > 0:
self.elements = []
for i in range(size):
te = TetrElement()
self.elements.append(te)
vertex_count = len(tetrahedrons[0].nodes)
for i in range(size):
for node in range(vertex_count):

3. Программная реализация напряженно-деформированных состояний…
56
node_idx = tetrahedrons[i].nodes[node]
self.elements[i].nodes_indices[node] =
node_idx
self.elements[i].nodes_coords[node] =
nodes[node_idx]
# Расчёт центров элементов
def calculate_centers(self):
for it in self.elements:
it.center_point =
self.calculate_center(it.nodes_coords)
# Определение ограничивающих поверхностей
def set_border_locations(self, map_borders):
for mit in map_borders.items():
elem = self.elements[mit[1].elem_index]
a_face = self.get_face(elem, mit[1].face_index)
bottom = True
top = True
back = True
left = True
right = True
for n_node in range(3):
bottom &= np.isclose(a_face[n_node].y,
self.bound_box.min_y)
top &= np.isclose(a_face[n_node].y,
self.bound_box.max_y)
back &= np.isclose(a_face[n_node].x,
self.bound_box.max_x)
left &= np.isclose(a_face[n_node].z,
self.bound_box.min_z)
right &= np.isclose(a_face[n_node].z,
self.bound_box.max_z)
if not(bottom or top or back
or left or right):
break

3.5. Рекомендации по реализации метода конечных элементов…
57
assert not(bottom and top)
if bottom:
elem.location = Location.bottom
elif top:
elem.location = Location.top
elif back:
elem.location = Location.back
elif left:
elem.location = Location.left
elif right:
elem.location = Location.right
else:
elem.location = Location.skew
# Расчёт граней
def calculate_faces(self):
for i,it in enumerate(self.elements):
nodes_count = len(it.face_center)
for n_face in range(nodes_count):
a_face = self.get_face(it, n_face)
it.areas[n_face] =
self.calculate_face_area(a_face)
it.face_center[n_face] =
self.calculate_center(a_face)
it.normals[n_face] =
self.calculate_normal(a_face)
# Нахождение соседних элементов для каждого элемента
def find_neighbours(self):
self.calculate_bound_box()
map_neighbours = {}
for n_index, it in enumerate(self.elements):
faces_count = len(it.face_center)
for n_face in range(faces_count):
key = str(it.face_center[n_face])
if (not key in map_neighbours

3. Программная реализация напряженно-деформированных состояний…
58
or map_neighbours[key].elem_index == -1):
nb = Neighbours()
nb.elem_index = n_index
nb.face_index = n_face
map_neighbours[key] = nb
else:
nb = map_neighbours[key]
it.neighbours[n_face] = nb.elem_index
self.elements[nb.elem_index].neighbours[
nb.face_index] = n_index
del map_neighbours[key]
self.set_border_locations(map_neighbours)
# Обновление координат нормалей
def update_normals(self):
for it in self.elements:
nb_count = len(it.neighbours)
for i in range(nb_count):
v = Vector3d()
nb_index = it.neighbours[i]
if nb_index != -1:
neighbour = self.elements[nb_index]
v.init_from_points(it.center_point, neighbour.center_point)
else:
v.init_from_points(it.center_point, it.face_center[i])
fval = (it.normals[i].x * v.x +
it.normals[i].y * v.y +
it.normals[i].z * v.z)
if fval <= 0:
it.normals[i].reverse()
# Построение решётки
def build(self, nodes, tetrahedrons):
self.init_geometry(nodes, tetrahedrons)
self.calculate_centers()
self.calculate_faces()

3.5. Рекомендации по реализации метода конечных элементов…
59
self.find_neighbours()
self.update_normals()
return self.elements
3. В следующем фрагменте приведено определение класса для
представления тетраэдральных элементов. В качестве полей тетраэдрального элемента выступают различные геометрические примитивы. В принципе для полного описания положения тетраэдрального элемента в пространстве достаточно знать координаты всех его четырёх вершин. Поскольку в основе вычисления различных характеристик тетраэдра лежат такие его параметры, как координаты центров граней, площади граней, координаты нормалей и координаты центра тяжести тетраэдра, целесообразно
завести для них специальные поля и вычислить заранее на этапе инициализации. Также целесообразно заранее инициализировать поля, которые индексируют соседние элементы, а также определяют, находится ли элемент
полностью внутри тела или выходит на поверхность. Если элемент выходит на внешнюю поверхность, то это означает, что к нему применимы граничные условия.
# Класс тетраэдральный элемент
class TetrElement:
# Конструктор класса
def __init__(self):
self.nodes_indices = [-1, -1, -1, -1]
self.nodes_coords = [Node(), Node(), Node(), Node()]
self.face_center = [Node(), Node(), Node(), Node()]
self.center_point = Node()
self.location = Location.inner
self.areas = [0.0, 0.0, 0.0, 0.0]
self.neighbours = [-1, -1, -1, -1]
self.normals = [Vector3d(), Vector3d(), Vector3d(), Vector3d()]
# Функция определения объёма тетраэдра
def get_volume(self):
matr = np.ones((4, 4), dtype=np.float)
for i, node in enumerate(self.nodes_coords):
matr[i, 1] = node.x
matr[i, 2] = node.y

3. Программная реализация напряженно-деформированных состояний…
60
matr[i, 3] = node.z
return np.linalg.det(matr) / 6
# Определение числа граней, выходящих
# на поверхность тела.
def get_surface_node_indices(self):
surface_edges = self.neighbours.count(-1)
if surface_edges > 1:
for i in range(4):
yield i
elif surface_edges == 1:
edge_idx = self.neighbours.index(-1)
for i in range(3):
yield (edge_idx + i) % 4
# Вычисление матрицы формы элемента
def get_shape_matrix(self):
x1 = self.nodes_coords[0].x
y1 = self.nodes_coords[0].y
z1 = self.nodes_coords[0].z
x2 = self.nodes_coords[1].x
y2 = self.nodes_coords[1].y
z2 = self.nodes_coords[1].z
x3 = self.nodes_coords[2].x
y3 = self.nodes_coords[2].y
z3 = self.nodes_coords[2].z
x4 = self.nodes_coords[3].x
y4 = self.nodes_coords[3].y
z4 = self.nodes_coords[3].z
v = self.get_volume()
mbeta1 = np.array([[1,y2,z2], [1,y3,z3], [1,y4,z4]])
mbeta2 = np.array([[1,y1,z1], [1,y3,z3], [1,y4,z4]])
mbeta3 = np.array([[1,y1,z1], [1,y2,z2], [1,y4,z4]])
mbeta4 = np.array([[1,y1,z1], [1,y2,z2], [1,y3,z3]])
mgamma1 = np.array([[1,x2,z2], [1,x3,z3], [1,x4,z4]])
mgamma2 = np.array([[1,x1,z1], [1,x3,z3], [1,x4,z4]])
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
