KratosShapeOptimizationApplication 10.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- KratosMultiphysics/.libs/KratosShapeOptimizationApplication.so +0 -0
- KratosMultiphysics/.libs/libKratosShapeOptimizationCore.dylib +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/TemplateMainKratosShapeOptimization.py +31 -0
- KratosMultiphysics/ShapeOptimizationApplication/__init__.py +19 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_base.py +31 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_bead_optimization.py +412 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_factory.py +62 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_gradient_projection.py +317 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_penalized_projection.py +225 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_relaxed_gradient_projection.py +580 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_shape_fraction_optimization.py +622 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_steepest_descent.py +255 -0
- KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_trust_region.py +657 -0
- KratosMultiphysics/ShapeOptimizationApplication/analyzers/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_base.py +30 -0
- KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_empty.py +21 -0
- KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_factory.py +310 -0
- KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_internal.py +155 -0
- KratosMultiphysics/ShapeOptimizationApplication/communicator_factory.py +259 -0
- KratosMultiphysics/ShapeOptimizationApplication/custom_ios/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/custom_ios/wrl_io.py +82 -0
- KratosMultiphysics/ShapeOptimizationApplication/custom_ios/wrl_reader.py +119 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/data_logger_factory.py +199 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_base.py +28 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_gid.py +112 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_unv.py +73 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_vtk.py +96 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/sensitivity_heatmap_logger.py +91 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_base.py +128 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_bead_optimization.py +79 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_gradient_projection.py +80 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_penalized_projection.py +79 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_relaxed_gradient_projection.py +108 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_shape_fraction_optimization.py +92 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_steepest_descent.py +64 -0
- KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_trust_region.py +100 -0
- KratosMultiphysics/ShapeOptimizationApplication/mapper_factory.py +100 -0
- KratosMultiphysics/ShapeOptimizationApplication/mapping/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/mapping/in_plane_vertex_morphing_mapper.py +140 -0
- KratosMultiphysics/ShapeOptimizationApplication/mapping/sliding_vertex_morphing_mapper.py +378 -0
- KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/mesh_controller_base.py +22 -0
- KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/mesh_controller_basic_updating.py +35 -0
- KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/mesh_controller_with_solver.py +237 -0
- KratosMultiphysics/ShapeOptimizationApplication/model_part_controller_factory.py +197 -0
- KratosMultiphysics/ShapeOptimizationApplication/optimizer_factory.py +235 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/airfoil_2d_responses.py +189 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/face_angle.py +140 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/mesh_based_packaging.py +70 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/packaging_response_base.py +156 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/plane_based_packaging.py +61 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/response_function_factory.py +40 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/surface_normal_shape_change.py +125 -0
- KratosMultiphysics/ShapeOptimizationApplication/response_functions/total_volume.py +56 -0
- KratosMultiphysics/ShapeOptimizationApplication/utilities/__init__.py +0 -0
- KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_math.py +443 -0
- KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_sensitivity_heatmap.py +235 -0
- KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_timer.py +47 -0
- KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_variable_utilities.py +54 -0
- kratosshapeoptimizationapplication-10.4.4.dist-info/METADATA +31 -0
- kratosshapeoptimizationapplication-10.4.4.dist-info/RECORD +69 -0
- kratosshapeoptimizationapplication-10.4.4.dist-info/WHEEL +6 -0
- kratosshapeoptimizationapplication.dylibs/libKratosCore.dylib +4 -0
- kratosshapeoptimizationapplication.dylibs/libgcc_s.1.1.dylib +0 -0
- kratosshapeoptimizationapplication.dylibs/libgomp.1.dylib +0 -0
- kratosshapeoptimizationapplication.dylibs/libstdc++.6.dylib +0 -0
|
File without changes
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import KratosMultiphysics
|
|
2
|
+
from KratosMultiphysics import Parameters, Logger
|
|
3
|
+
import KratosMultiphysics.ShapeOptimizationApplication as KSO
|
|
4
|
+
from KratosMultiphysics.response_functions.response_function_interface import ResponseFunctionInterface
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
def DotProduct(A,B):
|
|
8
|
+
result = 0
|
|
9
|
+
for i,j in zip(A,B):
|
|
10
|
+
result += i*j
|
|
11
|
+
return result
|
|
12
|
+
|
|
13
|
+
class AngleOfAttackResponseFunction(ResponseFunctionInterface):
|
|
14
|
+
def __init__(self, response_id, response_settings, model):
|
|
15
|
+
self.model = model
|
|
16
|
+
if not response_settings.Has("trailing_edge_model_part") or not response_settings.Has("leading_edge_model_part"):
|
|
17
|
+
raise(Exception("Please define a model part with the trailing edge node and a model part with the leading edge node"))
|
|
18
|
+
|
|
19
|
+
self.trailing_edge_model_part_name = response_settings["trailing_edge_model_part"].GetString()
|
|
20
|
+
self.leading_edge_sub_model_part_name = response_settings["leading_edge_model_part"].GetString()
|
|
21
|
+
self.reference_direction= response_settings["reference_direction"].GetVector() if response_settings.Has("reference_direction") else [1.0,0.0,0.0]
|
|
22
|
+
|
|
23
|
+
def Initialize(self):
|
|
24
|
+
self.te_model_part = self.model[self.trailing_edge_model_part_name]
|
|
25
|
+
self.le_model_part = self.model[self.leading_edge_sub_model_part_name]
|
|
26
|
+
self.main_model_part = self.te_model_part.GetRootModelPart()
|
|
27
|
+
|
|
28
|
+
for node in self.te_model_part.Nodes:
|
|
29
|
+
self.te_node = node
|
|
30
|
+
break
|
|
31
|
+
for node in self.le_model_part.Nodes:
|
|
32
|
+
self.le_node = node
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
def CalculateValue(self):
|
|
36
|
+
self.aoa = self._CalculateAOA(self.te_node.X,self.te_node.Y, self.le_node.X, self.le_node.Y)
|
|
37
|
+
|
|
38
|
+
def CalculateGradient(self):
|
|
39
|
+
x_diff = self.le_node.X - self.te_node.X
|
|
40
|
+
y_diff = self.le_node.Y - self.te_node.Y
|
|
41
|
+
|
|
42
|
+
cshape_sensitivity_0 = x_diff
|
|
43
|
+
cshape_sensitivity_1 = y_diff
|
|
44
|
+
cshape_sensitivity_2 = cshape_sensitivity_0**2 + cshape_sensitivity_1**2
|
|
45
|
+
cshape_sensitivity_3 = 1/cshape_sensitivity_2
|
|
46
|
+
cshape_sensitivity_4 = cshape_sensitivity_0*self.reference_direction[0] + cshape_sensitivity_1*self.reference_direction[1]
|
|
47
|
+
cshape_sensitivity_5 = cshape_sensitivity_3*cshape_sensitivity_4
|
|
48
|
+
cshape_sensitivity_6 = self.reference_direction[0]**2 + self.reference_direction[1]**2
|
|
49
|
+
cshape_sensitivity_7 = 1/(math.sqrt(cshape_sensitivity_2)*math.sqrt(cshape_sensitivity_6)*math.sqrt(-cshape_sensitivity_3*cshape_sensitivity_4**2/cshape_sensitivity_6 + 1))
|
|
50
|
+
|
|
51
|
+
self.te_x_gradient = -cshape_sensitivity_7*(-cshape_sensitivity_0*cshape_sensitivity_5 + self.reference_direction[0])
|
|
52
|
+
self.te_y_gradient = -cshape_sensitivity_7*(-cshape_sensitivity_1*cshape_sensitivity_5 + self.reference_direction[1])
|
|
53
|
+
self.le_x_gradient = cshape_sensitivity_7*(-cshape_sensitivity_0*cshape_sensitivity_5 + self.reference_direction[0])
|
|
54
|
+
self.le_y_gradient = cshape_sensitivity_7*(-cshape_sensitivity_1*cshape_sensitivity_5 + self.reference_direction[1])
|
|
55
|
+
|
|
56
|
+
def GetValue(self):
|
|
57
|
+
return self.aoa
|
|
58
|
+
|
|
59
|
+
def GetNodalGradient(self, variable):
|
|
60
|
+
zero_vector = KratosMultiphysics.Vector(3, 0.0)
|
|
61
|
+
gradient ={node.Id : zero_vector for node in self.main_model_part.Nodes}
|
|
62
|
+
|
|
63
|
+
shape_gradient = KratosMultiphysics.Vector(3, 0.0)
|
|
64
|
+
shape_gradient[0] = self.te_x_gradient
|
|
65
|
+
shape_gradient[1] = self.te_y_gradient
|
|
66
|
+
gradient[self.te_node.Id] = shape_gradient
|
|
67
|
+
|
|
68
|
+
shape_gradient = KratosMultiphysics.Vector(3, 0.0)
|
|
69
|
+
shape_gradient[0] = self.le_x_gradient
|
|
70
|
+
shape_gradient[1] = self.le_y_gradient
|
|
71
|
+
gradient[self.le_node.Id] = shape_gradient
|
|
72
|
+
|
|
73
|
+
return gradient
|
|
74
|
+
|
|
75
|
+
def _CalculateAOA(self, te_x, te_y, le_x, le_y):
|
|
76
|
+
ref_dir_norm = KratosMultiphysics.Vector(self.reference_direction).norm_2()
|
|
77
|
+
chord_vector = KratosMultiphysics.Vector(3, 0.0)
|
|
78
|
+
chord_vector[0] = te_x-le_x
|
|
79
|
+
chord_vector[1] = te_y-le_y
|
|
80
|
+
chord_norm = chord_vector.norm_2()
|
|
81
|
+
aoa = math.acos(DotProduct(self.reference_direction, chord_vector)/(ref_dir_norm*chord_norm))
|
|
82
|
+
|
|
83
|
+
return aoa
|
|
84
|
+
|
|
85
|
+
class ChordLengthResponseFunction(ResponseFunctionInterface):
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def __init__(self, response_id, response_settings, model):
|
|
89
|
+
self.model = model
|
|
90
|
+
|
|
91
|
+
if not response_settings.Has("trailing_edge_model_part") or not response_settings.Has("leading_edge_model_part"):
|
|
92
|
+
raise(Exception("Please define a model part with the trailing edge node and a model part with the leading edge node"))
|
|
93
|
+
|
|
94
|
+
self.trailing_edge_model_part_name = response_settings["trailing_edge_model_part"].GetString()
|
|
95
|
+
self.leading_edge_sub_model_part_name = response_settings["leading_edge_model_part"].GetString()
|
|
96
|
+
|
|
97
|
+
def Initialize(self):
|
|
98
|
+
self.te_model_part = self.model[self.trailing_edge_model_part_name]
|
|
99
|
+
self.le_model_part = self.model[self.leading_edge_sub_model_part_name]
|
|
100
|
+
self.main_model_part = self.te_model_part.GetRootModelPart()
|
|
101
|
+
|
|
102
|
+
for node in self.te_model_part.Nodes:
|
|
103
|
+
self.te_node = node
|
|
104
|
+
break
|
|
105
|
+
for node in self.le_model_part.Nodes:
|
|
106
|
+
self.le_node = node
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
def _ComputeChord(self, te_x, te_y, le_x, le_y):
|
|
110
|
+
chord = math.sqrt((te_x-le_x)**2+(te_y-le_y)**2)
|
|
111
|
+
|
|
112
|
+
return chord
|
|
113
|
+
|
|
114
|
+
def CalculateValue(self):
|
|
115
|
+
self.chord = self._ComputeChord(self.te_node.X,self.te_node.Y, self.le_node.X,self.le_node.Y)
|
|
116
|
+
|
|
117
|
+
def CalculateGradient(self):
|
|
118
|
+
x_diff = self.le_node.X - self.te_node.X
|
|
119
|
+
y_diff = self.le_node.Y - self.te_node.Y
|
|
120
|
+
|
|
121
|
+
self.te_x_gradient = -x_diff/math.sqrt(x_diff**2 + y_diff**2)
|
|
122
|
+
self.te_y_gradient = -y_diff/math.sqrt(x_diff**2 + y_diff**2)
|
|
123
|
+
self.le_x_gradient = x_diff/math.sqrt(x_diff**2 + y_diff**2)
|
|
124
|
+
self.le_y_gradient = y_diff/math.sqrt(x_diff**2 + y_diff**2)
|
|
125
|
+
|
|
126
|
+
def GetValue(self):
|
|
127
|
+
return self.chord
|
|
128
|
+
|
|
129
|
+
def GetNodalGradient(self, variable):
|
|
130
|
+
zero_vector = KratosMultiphysics.Vector(3, 0.0)
|
|
131
|
+
gradient ={node.Id : zero_vector for node in self.main_model_part.Nodes}
|
|
132
|
+
|
|
133
|
+
shape_gradient = KratosMultiphysics.Vector(3, 0.0)
|
|
134
|
+
shape_gradient[0] = self.te_x_gradient
|
|
135
|
+
shape_gradient[1] = self.te_y_gradient
|
|
136
|
+
gradient[self.te_node.Id] = shape_gradient
|
|
137
|
+
|
|
138
|
+
shape_gradient = KratosMultiphysics.Vector(3, 0.0)
|
|
139
|
+
shape_gradient[0] = self.le_x_gradient
|
|
140
|
+
shape_gradient[1] = self.le_y_gradient
|
|
141
|
+
gradient[self.le_node.Id] = shape_gradient
|
|
142
|
+
|
|
143
|
+
return gradient
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class PerimeterResponseFunction(ResponseFunctionInterface):
|
|
148
|
+
|
|
149
|
+
def __init__(self, response_id, response_settings, model):
|
|
150
|
+
self.model = model
|
|
151
|
+
self.model_part_name = response_settings["model_part_name"].GetString()
|
|
152
|
+
self.step_size = response_settings["step_size"].GetDouble() if response_settings.Has("step_size") else 1e-6
|
|
153
|
+
|
|
154
|
+
def Initialize(self):
|
|
155
|
+
self.main_model_part = self.model[self.model_part_name]
|
|
156
|
+
|
|
157
|
+
def _ComputePerimeter(self, model_part):
|
|
158
|
+
return KSO.GeometryUtilities(model_part).CalculateLength(model_part.Conditions)
|
|
159
|
+
|
|
160
|
+
def CalculateValue(self):
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
def CalculateGradient(self):
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
def GetValue(self):
|
|
167
|
+
|
|
168
|
+
return self._ComputePerimeter(self.main_model_part)
|
|
169
|
+
|
|
170
|
+
def GetNodalGradient(self, variable):
|
|
171
|
+
gradient = {}
|
|
172
|
+
initial_perimeter = self._ComputePerimeter(self.main_model_part)
|
|
173
|
+
|
|
174
|
+
for node in self.main_model_part.Nodes:
|
|
175
|
+
shape_gradient = KratosMultiphysics.Vector(3, 0.0)
|
|
176
|
+
|
|
177
|
+
node.X += self.step_size
|
|
178
|
+
current_perimeter = self._ComputePerimeter(self.main_model_part)
|
|
179
|
+
shape_gradient[0] = (current_perimeter-initial_perimeter)/self.step_size
|
|
180
|
+
node.X -= self.step_size
|
|
181
|
+
|
|
182
|
+
node.Y += self.step_size
|
|
183
|
+
current_perimeter = self._ComputePerimeter(self.main_model_part)
|
|
184
|
+
shape_gradient[1] = (current_perimeter-initial_perimeter)/self.step_size
|
|
185
|
+
node.Y -= self.step_size
|
|
186
|
+
|
|
187
|
+
gradient[node.Id] = shape_gradient
|
|
188
|
+
|
|
189
|
+
return gradient
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# importing the Kratos Library
|
|
2
|
+
import KratosMultiphysics as KM
|
|
3
|
+
from KratosMultiphysics import Parameters, Logger
|
|
4
|
+
from KratosMultiphysics.response_functions.response_function_interface import ResponseFunctionInterface
|
|
5
|
+
import KratosMultiphysics.ShapeOptimizationApplication as KSO
|
|
6
|
+
|
|
7
|
+
import time as timer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _AddConditionsFromParent(parent, child):
|
|
11
|
+
node_ids = set([node.Id for node in child.Nodes])
|
|
12
|
+
conditions = []
|
|
13
|
+
for condition in parent.Conditions:
|
|
14
|
+
all_nodes_found = True
|
|
15
|
+
for node in condition.GetNodes():
|
|
16
|
+
if node.Id not in node_ids:
|
|
17
|
+
all_nodes_found = False
|
|
18
|
+
break
|
|
19
|
+
if all_nodes_found:
|
|
20
|
+
conditions.append(condition.Id)
|
|
21
|
+
child.AddConditions(conditions)
|
|
22
|
+
|
|
23
|
+
# ==============================================================================
|
|
24
|
+
class FaceAngleResponseFunction(ResponseFunctionInterface):
|
|
25
|
+
"""
|
|
26
|
+
Face angle response function.
|
|
27
|
+
It aggregates the deviation of the face angles of all surface conditions using sqrt(sum(g_i)),
|
|
28
|
+
where g_i are the condition wise violations - feasible conditions do not contribute
|
|
29
|
+
|
|
30
|
+
It requires surface conditions in the modelpart, since they are used to compute the face orientation.
|
|
31
|
+
Ideally the design surface model part is used.
|
|
32
|
+
|
|
33
|
+
Attributes
|
|
34
|
+
----------
|
|
35
|
+
model_part : Model part object of the response function
|
|
36
|
+
response_function_utility: Cpp utilities object doing the actual computation of response value and gradient.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, identifier, response_settings, model):
|
|
40
|
+
self.identifier = identifier
|
|
41
|
+
|
|
42
|
+
response_settings.ValidateAndAssignDefaults(self.GetDefaultParameters())
|
|
43
|
+
|
|
44
|
+
self.response_settings = response_settings
|
|
45
|
+
self.model = model
|
|
46
|
+
self.model_part_needs_to_be_imported = False
|
|
47
|
+
|
|
48
|
+
self.value = None
|
|
49
|
+
|
|
50
|
+
self._model_part_name = response_settings["model_part_name"].GetString()
|
|
51
|
+
input_type = response_settings["model_import_settings"]["input_type"].GetString()
|
|
52
|
+
if input_type == "mdpa":
|
|
53
|
+
self.model_part = self.model.CreateModelPart(self._model_part_name, 2)
|
|
54
|
+
domain_size = response_settings["domain_size"].GetInt()
|
|
55
|
+
if domain_size not in [3]:
|
|
56
|
+
raise Exception("FaceAngleResponseFunction: Invalid 'domain_size': {}".format(domain_size))
|
|
57
|
+
self.model_part.ProcessInfo.SetValue(KM.DOMAIN_SIZE, domain_size)
|
|
58
|
+
self.model_part_needs_to_be_imported = True
|
|
59
|
+
elif input_type == "use_input_model_part":
|
|
60
|
+
self.model_part = None # will be retrieved in Initialize()
|
|
61
|
+
else:
|
|
62
|
+
raise Exception("Other model part input options are not yet implemented.")
|
|
63
|
+
|
|
64
|
+
self.response_function_utility = None # will be created in Initialize()
|
|
65
|
+
|
|
66
|
+
self.model.GetModelPart(self._model_part_name.split(".")[0]).AddNodalSolutionStepVariable(KM.SHAPE_SENSITIVITY)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def GetDefaultParameters(cls):
|
|
70
|
+
this_defaults = KM.Parameters("""{
|
|
71
|
+
"response_type" : "UNKNOWN_TYPE",
|
|
72
|
+
"model_part_name" : "UNKNOWN_NAME",
|
|
73
|
+
"only" : "",
|
|
74
|
+
"domain_size" : 3,
|
|
75
|
+
"model_import_settings" : {
|
|
76
|
+
"input_type" : "use_input_model_part",
|
|
77
|
+
"input_filename" : "UNKNOWN_NAME"
|
|
78
|
+
},
|
|
79
|
+
"main_direction": [0.0, 0.0, 1.0],
|
|
80
|
+
"min_angle": 0.0,
|
|
81
|
+
"gradient_mode": "finite_differencing",
|
|
82
|
+
"step_size": 1e-6,
|
|
83
|
+
"consider_only_initially_feasible": false
|
|
84
|
+
}""")
|
|
85
|
+
return this_defaults
|
|
86
|
+
|
|
87
|
+
def Initialize(self):
|
|
88
|
+
if self.response_settings["model_import_settings"]["input_type"].GetString() == "mdpa":
|
|
89
|
+
file_name = self.response_settings["model_import_settings"]["input_filename"].GetString()
|
|
90
|
+
model_part_io = KM.ModelPartIO(file_name)
|
|
91
|
+
model_part_io.ReadModelPart(self.model_part)
|
|
92
|
+
else:
|
|
93
|
+
self.model_part = self.model.GetModelPart(self._model_part_name)
|
|
94
|
+
|
|
95
|
+
only = self.response_settings["only"].GetString()
|
|
96
|
+
if only != "":
|
|
97
|
+
only_part = self.model.GetModelPart(only)
|
|
98
|
+
if only_part.NumberOfConditions() == 0:
|
|
99
|
+
_AddConditionsFromParent(self.model_part, only_part)
|
|
100
|
+
Logger.PrintWarning("FaceAngleResponse", "Automatically added {} conditions to model_part '{}'.".format(only_part.NumberOfConditions(), only_part.Name))
|
|
101
|
+
else:
|
|
102
|
+
only_part = self.model_part
|
|
103
|
+
|
|
104
|
+
if only_part.NumberOfConditions() == 0:
|
|
105
|
+
raise RuntimeError("The model_part '{}' does not have any surface conditions!".format(only_part.Name))
|
|
106
|
+
|
|
107
|
+
self.response_function_utility = KSO.FaceAngleResponseFunctionUtility(only_part, self.response_settings)
|
|
108
|
+
|
|
109
|
+
self.response_function_utility.Initialize()
|
|
110
|
+
|
|
111
|
+
def UpdateDesign(self, updated_model_part, variable):
|
|
112
|
+
self.value = None
|
|
113
|
+
|
|
114
|
+
def CalculateValue(self):
|
|
115
|
+
Logger.PrintInfo("FaceAngleResponse", "Starting calculation of response value:", self.identifier)
|
|
116
|
+
|
|
117
|
+
startTime = timer.time()
|
|
118
|
+
self.value = self.response_function_utility.CalculateValue()
|
|
119
|
+
Logger.PrintInfo("FaceAngleResponse", "Time needed for calculating the response value = ",round(timer.time() - startTime,2),"s")
|
|
120
|
+
|
|
121
|
+
def CalculateGradient(self):
|
|
122
|
+
Logger.PrintInfo("FaceAngleResponse", "Starting gradient calculation for response", self.identifier)
|
|
123
|
+
|
|
124
|
+
for node in self.model_part.Nodes:
|
|
125
|
+
node.SetSolutionStepValue(KM.SHAPE_SENSITIVITY, [0.0, 0.0, 0.0])
|
|
126
|
+
|
|
127
|
+
startTime = timer.time()
|
|
128
|
+
self.response_function_utility.CalculateGradient()
|
|
129
|
+
Logger.PrintInfo("FaceAngleResponse", "Time needed for calculating gradients",round(timer.time() - startTime,2),"s")
|
|
130
|
+
|
|
131
|
+
def GetValue(self):
|
|
132
|
+
return self.value
|
|
133
|
+
|
|
134
|
+
def GetNodalGradient(self, variable):
|
|
135
|
+
if variable != KM.SHAPE_SENSITIVITY:
|
|
136
|
+
raise RuntimeError("GetNodalGradient: No gradient for {}!".format(variable.Name))
|
|
137
|
+
gradient = {}
|
|
138
|
+
for node in self.model_part.Nodes:
|
|
139
|
+
gradient[node.Id] = node.GetSolutionStepValue(variable)
|
|
140
|
+
return gradient
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# KratosShapeOptimizationApplication
|
|
3
|
+
#
|
|
4
|
+
# License: BSD License
|
|
5
|
+
# license: ShapeOptimizationApplication/license.txt
|
|
6
|
+
#
|
|
7
|
+
# Main authors: Geiser Armin, https://github.com/armingeiser
|
|
8
|
+
#
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
import KratosMultiphysics as KM
|
|
12
|
+
import KratosMultiphysics.ShapeOptimizationApplication as KSO
|
|
13
|
+
from .packaging_response_base import PackagingResponseBase
|
|
14
|
+
from ..custom_ios.wrl_io import WrlIO
|
|
15
|
+
|
|
16
|
+
class MeshBasedPackaging(PackagingResponseBase):
|
|
17
|
+
"""
|
|
18
|
+
A class for mesh packaging response function. The mesh should contain surface conditions only.
|
|
19
|
+
By default the normals of the conditions indicate the feasible side of the mesh (see setting 'feasible_in_normal_direction')
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, identifier, response_settings, model):
|
|
23
|
+
super().__init__(identifier, response_settings, model)
|
|
24
|
+
|
|
25
|
+
self.packaging_model_part = None
|
|
26
|
+
self.packaging_model_part_needs_to_be_imported = False
|
|
27
|
+
|
|
28
|
+
packaging_model_part_name = response_settings["packaging_model_part_name"].GetString()
|
|
29
|
+
self.packaging_input_type = response_settings["packaging_model_import_settings"]["input_type"].GetString()
|
|
30
|
+
if self.packaging_input_type in ["mdpa", "vrml", "wrl"]:
|
|
31
|
+
self.packaging_model_part = self.model.CreateModelPart(packaging_model_part_name)
|
|
32
|
+
domain_size = response_settings["packaging_domain_size"].GetInt()
|
|
33
|
+
if domain_size not in [2, 3]:
|
|
34
|
+
raise Exception("PlaneBasedPackaging: Invalid 'domain_size': {}".format(domain_size))
|
|
35
|
+
self.packaging_model_part.ProcessInfo.SetValue(KM.DOMAIN_SIZE, domain_size)
|
|
36
|
+
elif self.packaging_input_type == "use_input_model_part":
|
|
37
|
+
self.packaging_model_part = self.model.GetModelPart(packaging_model_part_name)
|
|
38
|
+
else:
|
|
39
|
+
raise Exception("Other model part input options are not yet implemented.")
|
|
40
|
+
|
|
41
|
+
self.packaging_model_part.AddNodalSolutionStepVariable(KM.NORMAL)
|
|
42
|
+
self.packaging_model_part.AddNodalSolutionStepVariable(KSO.NORMALIZED_SURFACE_NORMAL)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def GetDefaultParameters(cls):
|
|
46
|
+
this_defaults = KM.Parameters("""{
|
|
47
|
+
"packaging_model_part_name" : "UNKNOWN_NAME",
|
|
48
|
+
"packaging_domain_size" : 3,
|
|
49
|
+
"packaging_model_import_settings" : {
|
|
50
|
+
"input_type" : "mdpa",
|
|
51
|
+
"input_filename" : "UNKNOWN_NAME"
|
|
52
|
+
}
|
|
53
|
+
}""")
|
|
54
|
+
this_defaults.AddMissingParameters(super().GetDefaultParameters())
|
|
55
|
+
return this_defaults
|
|
56
|
+
|
|
57
|
+
def Initialize(self):
|
|
58
|
+
super().Initialize()
|
|
59
|
+
|
|
60
|
+
if self.packaging_input_type == "mdpa":
|
|
61
|
+
model_part_io = KM.ModelPartIO(self.response_settings["packaging_model_import_settings"]["input_filename"].GetString())
|
|
62
|
+
model_part_io.ReadModelPart(self.packaging_model_part)
|
|
63
|
+
elif self.packaging_input_type in ["vrml", "wrl"]:
|
|
64
|
+
model_part_io = WrlIO(self.response_settings["packaging_model_import_settings"]["input_filename"].GetString())
|
|
65
|
+
model_part_io.ReadModelPart(self.packaging_model_part)
|
|
66
|
+
|
|
67
|
+
def _CalculateDistances(self):
|
|
68
|
+
geometry_tools = KSO.GeometryUtilities(self.model_part)
|
|
69
|
+
|
|
70
|
+
self.signed_distances, self.directions = geometry_tools.ComputeDistancesToBoundingModelPart(self.packaging_model_part)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# KratosShapeOptimizationApplication
|
|
3
|
+
#
|
|
4
|
+
# License: BSD License
|
|
5
|
+
# license: ShapeOptimizationApplication/license.txt
|
|
6
|
+
#
|
|
7
|
+
# Main authors: Geiser Armin, https://github.com/armingeiser
|
|
8
|
+
#
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
import time as timer
|
|
12
|
+
import KratosMultiphysics as KM
|
|
13
|
+
from KratosMultiphysics import Logger
|
|
14
|
+
from KratosMultiphysics.response_functions.response_function_interface import ResponseFunctionInterface
|
|
15
|
+
|
|
16
|
+
class PackagingResponseBase(ResponseFunctionInterface):
|
|
17
|
+
"""
|
|
18
|
+
A base class for packaging response functions that agglomerate the nodal violations
|
|
19
|
+
into a single response function.
|
|
20
|
+
The agglomeration happens by summing up the square of each nodal violation.
|
|
21
|
+
Nodes that are feasible do NOT contribute to the response value/gradient.
|
|
22
|
+
This is why a prediction of the violation using the gradients is not possible,
|
|
23
|
+
only correction of violations (e.g. from the last step) will happen.
|
|
24
|
+
|
|
25
|
+
Derived classes need to implement the calculation of the nodal violations
|
|
26
|
+
|
|
27
|
+
Important settings:
|
|
28
|
+
feasible_in_normal_direction : boolean flag that indicates if the normal of bounding instance
|
|
29
|
+
points to the feasible side. True by default
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, identifier, response_settings, model):
|
|
33
|
+
self.identifier = identifier
|
|
34
|
+
|
|
35
|
+
response_settings.ValidateAndAssignDefaults(self.GetDefaultParameters())
|
|
36
|
+
|
|
37
|
+
self.response_settings = response_settings
|
|
38
|
+
self.model = model
|
|
39
|
+
|
|
40
|
+
self._model_part_name = response_settings["model_part_name"].GetString()
|
|
41
|
+
input_type = response_settings["model_import_settings"]["input_type"].GetString()
|
|
42
|
+
if input_type == "mdpa":
|
|
43
|
+
self.model_part = self.model.CreateModelPart(self._model_part_name)
|
|
44
|
+
domain_size = response_settings["domain_size"].GetInt()
|
|
45
|
+
if domain_size not in [2, 3]:
|
|
46
|
+
raise Exception("PackagingResponseBase: Invalid 'domain_size': {}".format(domain_size))
|
|
47
|
+
self.model_part.ProcessInfo.SetValue(KM.DOMAIN_SIZE, domain_size)
|
|
48
|
+
elif input_type == "use_input_model_part":
|
|
49
|
+
self.model_part = None # will be retrieved in Initialize()
|
|
50
|
+
else:
|
|
51
|
+
raise Exception("PackagingResponseBase: '{}' model part input type not implemented.".format(input_type))
|
|
52
|
+
|
|
53
|
+
self.signed_distances = None
|
|
54
|
+
self.directions = None
|
|
55
|
+
|
|
56
|
+
self.value = None
|
|
57
|
+
|
|
58
|
+
self.gradient = {}
|
|
59
|
+
|
|
60
|
+
self.feasible_in_normal_direction = self.response_settings["feasible_in_normal_direction"].GetBool()
|
|
61
|
+
self.exponent = 2
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def GetDefaultParameters(cls):
|
|
65
|
+
this_defaults = KM.Parameters("""{
|
|
66
|
+
"response_type" : "UNKNOWN_TYPE",
|
|
67
|
+
"model_part_name" : "UNKNOWN_NAME",
|
|
68
|
+
"domain_size" : 3,
|
|
69
|
+
"model_import_settings" : {
|
|
70
|
+
"input_type" : "use_input_model_part",
|
|
71
|
+
"input_filename" : "UNKNOWN_NAME"
|
|
72
|
+
},
|
|
73
|
+
"feasible_in_normal_direction" : true
|
|
74
|
+
}""")
|
|
75
|
+
return this_defaults
|
|
76
|
+
|
|
77
|
+
def Initialize(self):
|
|
78
|
+
if self.response_settings["model_import_settings"]["input_type"].GetString() == "mdpa":
|
|
79
|
+
file_name = self.response_settings["model_import_settings"]["input_filename"].GetString()
|
|
80
|
+
model_part_io = KM.ModelPartIO(file_name)
|
|
81
|
+
model_part_io.ReadModelPart(self.model_part)
|
|
82
|
+
else:
|
|
83
|
+
self.model_part = self.model.GetModelPart(self._model_part_name)
|
|
84
|
+
|
|
85
|
+
def InitializeSolutionStep(self):
|
|
86
|
+
self.value = None
|
|
87
|
+
self.signed_distances = None
|
|
88
|
+
self.directions = None
|
|
89
|
+
self.gradient = {}
|
|
90
|
+
|
|
91
|
+
def CalculateValue(self):
|
|
92
|
+
Logger.PrintInfo("\n> Starting primal analysis for response", self.identifier)
|
|
93
|
+
|
|
94
|
+
startTime = timer.time()
|
|
95
|
+
|
|
96
|
+
if not self.directions or not self.signed_distances:
|
|
97
|
+
self._CalculateDistances()
|
|
98
|
+
|
|
99
|
+
value = 0.0
|
|
100
|
+
for i in range(len(self.signed_distances)):
|
|
101
|
+
value += self._CalculateNodalValue(self.signed_distances[i])
|
|
102
|
+
|
|
103
|
+
self.value = value
|
|
104
|
+
|
|
105
|
+
Logger.PrintInfo("> Time needed for calculating the response value = ", round(timer.time() - startTime,2), "s")
|
|
106
|
+
|
|
107
|
+
def CalculateGradient(self):
|
|
108
|
+
Logger.PrintInfo("\n> Starting gradient calculation for response", self.identifier)
|
|
109
|
+
|
|
110
|
+
startTime = timer.time()
|
|
111
|
+
|
|
112
|
+
if not self.directions or not self.signed_distances:
|
|
113
|
+
self._CalculateDistances()
|
|
114
|
+
|
|
115
|
+
for i, node in enumerate(self.model_part.Nodes):
|
|
116
|
+
gradient = self._CalculateNodalGradient(self.signed_distances[i], self.directions[i*3:i*3+3])
|
|
117
|
+
self.gradient[node.Id] = gradient
|
|
118
|
+
|
|
119
|
+
Logger.PrintInfo("> Time needed for calculating gradients = ", round(timer.time() - startTime,2), "s")
|
|
120
|
+
|
|
121
|
+
def GetValue(self):
|
|
122
|
+
return self.value
|
|
123
|
+
|
|
124
|
+
def GetNodalGradient(self, variable):
|
|
125
|
+
if not self.gradient:
|
|
126
|
+
raise RuntimeError("Gradient was not calculated")
|
|
127
|
+
if variable != KM.SHAPE_SENSITIVITY:
|
|
128
|
+
raise RuntimeError("GetNodalGradient: No gradient for {}!".format(variable.Name))
|
|
129
|
+
return self.gradient
|
|
130
|
+
|
|
131
|
+
def _CalculateDistances(self):
|
|
132
|
+
raise NotImplementedError("_CalculateDistances needs to be implemented by the derived class!")
|
|
133
|
+
|
|
134
|
+
def _CalculateNodalValue(self, signed_distance):
|
|
135
|
+
if not self._HasContribution(signed_distance):
|
|
136
|
+
return 0.0
|
|
137
|
+
return pow(signed_distance, self.exponent)
|
|
138
|
+
|
|
139
|
+
def _CalculateNodalGradient(self, signed_distance, direction):
|
|
140
|
+
if not self._HasContribution(signed_distance):
|
|
141
|
+
return [0.0, 0.0, 0.0]
|
|
142
|
+
|
|
143
|
+
factor = self.exponent * pow(signed_distance, self.exponent-1)
|
|
144
|
+
gradient = [
|
|
145
|
+
direction[0] * factor,
|
|
146
|
+
direction[1] * factor,
|
|
147
|
+
direction[2] * factor
|
|
148
|
+
]
|
|
149
|
+
return gradient
|
|
150
|
+
|
|
151
|
+
def _HasContribution(self, signed_distance):
|
|
152
|
+
if not self.feasible_in_normal_direction and signed_distance > 0:
|
|
153
|
+
return True
|
|
154
|
+
elif self.feasible_in_normal_direction and signed_distance < 0:
|
|
155
|
+
return True
|
|
156
|
+
return False
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# KratosShapeOptimizationApplication
|
|
3
|
+
#
|
|
4
|
+
# License: BSD License
|
|
5
|
+
# license: ShapeOptimizationApplication/license.txt
|
|
6
|
+
#
|
|
7
|
+
# Main authors: Geiser Armin, https://github.com/armingeiser
|
|
8
|
+
#
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
import KratosMultiphysics as KM
|
|
12
|
+
from .packaging_response_base import PackagingResponseBase
|
|
13
|
+
from KratosMultiphysics.ShapeOptimizationApplication.utilities import custom_math as cm
|
|
14
|
+
|
|
15
|
+
class PlaneBasedPackaging(PackagingResponseBase):
|
|
16
|
+
"""
|
|
17
|
+
A class that defines the response function for plane-based packaging. The plane is defined by an origin point and a normal vector.
|
|
18
|
+
By default the normal of the plane indicates the feasible side of the plane (see setting 'feasible_in_normal_direction')
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, identifier, response_settings, model):
|
|
22
|
+
|
|
23
|
+
super().__init__(identifier, response_settings, model)
|
|
24
|
+
|
|
25
|
+
self.plane_origin = []
|
|
26
|
+
for i in range(0,3):
|
|
27
|
+
self.plane_origin.append(self.response_settings["plane_origin"][i].GetDouble())
|
|
28
|
+
|
|
29
|
+
self.plane_normal = []
|
|
30
|
+
for i in range(0,3):
|
|
31
|
+
self.plane_normal.append(self.response_settings["plane_normal"][i].GetDouble())
|
|
32
|
+
|
|
33
|
+
self.plane_normal = cm.ScalarVectorProduct(1.0/cm.Norm2(self.plane_normal), self.plane_normal)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def GetDefaultParameters(cls):
|
|
37
|
+
this_defaults = KM.Parameters("""{
|
|
38
|
+
"plane_origin" : [0.0, 0.0, 0.0],
|
|
39
|
+
"plane_normal" : [0.0, 0.0, 1.0]
|
|
40
|
+
}""")
|
|
41
|
+
this_defaults.AddMissingParameters(super().GetDefaultParameters())
|
|
42
|
+
return this_defaults
|
|
43
|
+
|
|
44
|
+
def _DistanceVectorToPlane(self, node):
|
|
45
|
+
plane_to_point = [
|
|
46
|
+
node.X - self.plane_origin[0],
|
|
47
|
+
node.Y - self.plane_origin[1],
|
|
48
|
+
node.Z - self.plane_origin[2]
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
distance = cm.Dot(plane_to_point, self.plane_normal)
|
|
52
|
+
|
|
53
|
+
return distance, self.plane_normal
|
|
54
|
+
|
|
55
|
+
def _CalculateDistances(self):
|
|
56
|
+
self.signed_distances = []
|
|
57
|
+
self.directions = []
|
|
58
|
+
for node in self.model_part.Nodes:
|
|
59
|
+
distance, direction = self._DistanceVectorToPlane(node)
|
|
60
|
+
self.signed_distances.append(distance)
|
|
61
|
+
self.directions.extend(direction)
|
KratosMultiphysics/ShapeOptimizationApplication/response_functions/response_function_factory.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# KratosShapeOptimizationApplication
|
|
3
|
+
#
|
|
4
|
+
# License: BSD License
|
|
5
|
+
# license: ShapeOptimizationApplication/license.txt
|
|
6
|
+
#
|
|
7
|
+
# Main authors: Geiser Armin, https://github.com/armingeiser
|
|
8
|
+
#
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
from . import plane_based_packaging
|
|
12
|
+
from . import mesh_based_packaging
|
|
13
|
+
from . import surface_normal_shape_change
|
|
14
|
+
from . import face_angle
|
|
15
|
+
from . import airfoil_2d_responses
|
|
16
|
+
from . import total_volume
|
|
17
|
+
|
|
18
|
+
def CreateResponseFunction(response_id, response_settings, model):
|
|
19
|
+
response_type = response_settings["response_type"].GetString()
|
|
20
|
+
|
|
21
|
+
if response_type == "plane_based_packaging":
|
|
22
|
+
return plane_based_packaging.PlaneBasedPackaging(response_id, response_settings, model)
|
|
23
|
+
elif response_type == "mesh_based_packaging":
|
|
24
|
+
return mesh_based_packaging.MeshBasedPackaging(response_id, response_settings, model)
|
|
25
|
+
elif response_type == "surface_normal_shape_change":
|
|
26
|
+
return surface_normal_shape_change.SurfaceNormalShapeChange(response_id, response_settings, model)
|
|
27
|
+
elif response_type == "face_angle":
|
|
28
|
+
return face_angle.FaceAngleResponseFunction(response_id, response_settings, model)
|
|
29
|
+
elif response_type == "airfoil_angle_of_attack":
|
|
30
|
+
return airfoil_2d_responses.AngleOfAttackResponseFunction(response_id, response_settings, model)
|
|
31
|
+
elif response_type == "airfoil_chord_length":
|
|
32
|
+
return airfoil_2d_responses.ChordLengthResponseFunction(response_id, response_settings, model)
|
|
33
|
+
elif response_type == "airfoil_perimeter":
|
|
34
|
+
return airfoil_2d_responses.PerimeterResponseFunction(response_id, response_settings, model)
|
|
35
|
+
elif response_type == "total_volume":
|
|
36
|
+
return total_volume.TotalVolume(response_id, response_settings, model)
|
|
37
|
+
else:
|
|
38
|
+
raise NameError("The type of the following response function is not specified: "+ response_id +
|
|
39
|
+
".\nAvailable types are: 'plane_based_packaging', 'mesh_based_packaging', 'face_angle', " +
|
|
40
|
+
"'airfoil_angle_of_attack', 'airfoil_chord_length', 'airfoil_perimeter.")
|