-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathcreate_matrix.py
201 lines (151 loc) · 7.24 KB
/
create_matrix.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
__copyright__ = "Copyright (C) 2013 Kristoffer Carlsson"
__license__ = """
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import logging
import numpy as np
from phon.mesh_objects.element import Element
from phon.mesh_tools.create_cohesive_elements import create_cohesive_elements
logger = logging.getLogger(__name__)
def create_matrix(mesh, thickness, mesh_dimension):
"""
This method creates elements that are similar to the cohesive
elements except that they have a finite width. The cohesive
elements are made thicker by pulling them apart in the normal
direction of the cohesive element.
:param thickness: Thickness of the generated elements
:type thickness: Float
:param mesh: The mesh
:type mesh: :class:`Mesh`
:param mesh_dimension: The dimension of the mesh
:type mesh_dimension: Int
"""
logger.warning("Method does currently only move the nodes in the "
"interface between the grains. This means that excessive thickness "
"will lead to bad mesh quality.")
corner_sets = ["x0y0z0", "x0y0z1", "x0y1z0", "x0y1z1",
"x1y0z0", "x1y0z1", "x1y1z0", "x1y1z1"]
edge_sets = ["x0y1", "x0z1", "x0y0", "x0z0",
"x1y0", "x1z1", "x1y1", "x1z0",
"y0z1", "y0z0", "y1z0", "y1z1"]
face_sets = ["x0", "x1", "y0", "y1", "z0", "z1"]
# Loop over every cohesive element set:
create_cohesive_elements(mesh, mesh_dimension)
normal_vec = {}
# Pre calculate the normals
for element_set_name in mesh.element_sets.keys():
if element_set_name.startswith("coh_face"):
element_set = mesh.element_sets[element_set_name]
for element_id in element_set.ids:
normal_vec[element_id] = _calculate_normal(
mesh, mesh.elements[element_id], mesh_dimension)
for element_set_name in mesh.element_sets.keys():
if "coh_face" in element_set_name:
node_already_moved = []
element_set = mesh.element_sets[element_set_name]
# Loop over elements in face set
for element_id in element_set.ids:
element = mesh.elements[element_id]
# Loop over the nodes in the element
for i, node_id in enumerate(element.vertices):
if node_id in node_already_moved:
continue
node_already_moved.append(node_id)
node = mesh.nodes[node_id]
r = find_displacement_vector(mesh, node_id, corner_sets,
edge_sets, face_sets, normal_vec[element_id], thickness)
node.c += r
# if node.x > 1.0 or node.x < 0.0:
# node.x -= r[0]
# if abs(node.y) > 1.0 or node.y < 0.0:
# node.y -= r[1]
# if abs(node.z) > 1.0 or node.z < 0.0:
# node.z -= r[2]
def find_displacement_vector(mesh, node_id, corner_sets,
edge_sets, face_sets, normal_vec, thickness):
# For now ignore projection stuff
# TODO: Fix
return normal_vec * thickness / 2.0
for node_set_name in corner_sets:
if node_id in mesh.node_sets[node_set_name].ids:
return [0, 0, 0]
for node_set_name in edge_sets:
if node_id in mesh.node_sets[node_set_name].ids:
return project_on_line(node_set_name, normal_vec, thickness)
for node_set_name in face_sets:
if node_id in mesh.node_sets[node_set_name].ids:
return project_on_plane(node_set_name, normal_vec, thickness)
def project_on_line(node_set_name, normal_gb, thickness):
line_dirs = {"x0y1": np.array([0, 0, 1]),
"x0z1": np.array([0, 1, 0]),
"x0y0": np.array([0, 0, 1]),
"x0z0": np.array([0, 1, 0]),
"x1y0": np.array([0, 0, 1]),
"x1z1": np.array([0, 1, 0]),
"x1y1": np.array([0, 0, 1]),
"x1z0": np.array([0, 1, 0]),
"y0z1": np.array([1, 0, 0]),
"y0z0": np.array([1, 0, 0]),
"y1z0": np.array([1, 0, 0]),
"y1z1": np.array([1, 0, 0])}
line_dir = line_dirs[node_set_name]
length_line = thickness / 2.0 / np.dot(line_dir, normal_gb)
return line_dir * length_line
def project_on_plane(node_set_name, normal_gb, thickness):
normal_planes = {"z0": np.array([0, 0, -1]),
"z1": np.array([0, 0, 1]),
"x0": np.array([-1, 0, 0]),
"x1": np.array([1, 0, 0]),
"y0": np.array([0, -1, 0]),
"y1": np.array([0, 1, 0])}
normal_plane = normal_planes[node_set_name]
np_dot_ng = np.dot(normal_plane, normal_gb)
projection = normal_gb - np_dot_ng * normal_plane
projection_normed = projection / np.linalg.norm(projection)
length_plane = thickness / 2.0 / np.dot(projection, normal_gb)
return projection_normed * length_plane
def _calculate_normal(mesh, element, mesh_dimension):
"""
Calculates the normal to a cohesive element normalized
to a length of 1.
:param element: The cohesive element
:type element: :class:`Element`
:return: Array with the normal in the form [x,y,z]
:rtype: Array with numbers
:param mesh_dimension: The dimension of the mesh
:type mesh_dimension: Int
"""
if mesh_dimension == 2:
node_1 = mesh.nodes[element.vertices[0]]
node_2 = mesh.nodes[element.vertices[1]]
crs = np.array([node_2.c[1] - node_1.c[1], node_1.c[0] - node_2.c[0], 0.0])
if mesh_dimension == 3:
node_1 = mesh.nodes[element.vertices[0]]
node_2 = mesh.nodes[element.vertices[1]]
node_3 = mesh.nodes[element.vertices[2]]
crs = np.cross(node_2.c - node_1.c, node_3.c - node_1.c)
return crs / np.linalg.norm(crs)
class UnsupportedDimensionError(Exception):
"""
Exception to raise when wrong dimension given to create_element_sides
"""
def __init__(self, status):
Exception.__init__(self, status)
self.status = status
def __str__(self):
"""Return a string representation of the :exc:`UnsupportedDimensionError`."""
return str(self.status)