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.
Files changed (69) hide show
  1. KratosMultiphysics/.libs/KratosShapeOptimizationApplication.so +0 -0
  2. KratosMultiphysics/.libs/libKratosShapeOptimizationCore.dylib +0 -0
  3. KratosMultiphysics/ShapeOptimizationApplication/TemplateMainKratosShapeOptimization.py +31 -0
  4. KratosMultiphysics/ShapeOptimizationApplication/__init__.py +19 -0
  5. KratosMultiphysics/ShapeOptimizationApplication/algorithms/__init__.py +0 -0
  6. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_base.py +31 -0
  7. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_bead_optimization.py +412 -0
  8. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_factory.py +62 -0
  9. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_gradient_projection.py +317 -0
  10. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_penalized_projection.py +225 -0
  11. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_relaxed_gradient_projection.py +580 -0
  12. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_shape_fraction_optimization.py +622 -0
  13. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_steepest_descent.py +255 -0
  14. KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_trust_region.py +657 -0
  15. KratosMultiphysics/ShapeOptimizationApplication/analyzers/__init__.py +0 -0
  16. KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_base.py +30 -0
  17. KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_empty.py +21 -0
  18. KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_factory.py +310 -0
  19. KratosMultiphysics/ShapeOptimizationApplication/analyzers/analyzer_internal.py +155 -0
  20. KratosMultiphysics/ShapeOptimizationApplication/communicator_factory.py +259 -0
  21. KratosMultiphysics/ShapeOptimizationApplication/custom_ios/__init__.py +0 -0
  22. KratosMultiphysics/ShapeOptimizationApplication/custom_ios/wrl_io.py +82 -0
  23. KratosMultiphysics/ShapeOptimizationApplication/custom_ios/wrl_reader.py +119 -0
  24. KratosMultiphysics/ShapeOptimizationApplication/loggers/__init__.py +0 -0
  25. KratosMultiphysics/ShapeOptimizationApplication/loggers/data_logger_factory.py +199 -0
  26. KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_base.py +28 -0
  27. KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_gid.py +112 -0
  28. KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_unv.py +73 -0
  29. KratosMultiphysics/ShapeOptimizationApplication/loggers/design_logger_vtk.py +96 -0
  30. KratosMultiphysics/ShapeOptimizationApplication/loggers/sensitivity_heatmap_logger.py +91 -0
  31. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_base.py +128 -0
  32. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_bead_optimization.py +79 -0
  33. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_gradient_projection.py +80 -0
  34. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_penalized_projection.py +79 -0
  35. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_relaxed_gradient_projection.py +108 -0
  36. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_shape_fraction_optimization.py +92 -0
  37. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_steepest_descent.py +64 -0
  38. KratosMultiphysics/ShapeOptimizationApplication/loggers/value_logger_trust_region.py +100 -0
  39. KratosMultiphysics/ShapeOptimizationApplication/mapper_factory.py +100 -0
  40. KratosMultiphysics/ShapeOptimizationApplication/mapping/__init__.py +0 -0
  41. KratosMultiphysics/ShapeOptimizationApplication/mapping/in_plane_vertex_morphing_mapper.py +140 -0
  42. KratosMultiphysics/ShapeOptimizationApplication/mapping/sliding_vertex_morphing_mapper.py +378 -0
  43. KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/__init__.py +0 -0
  44. KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/mesh_controller_base.py +22 -0
  45. KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/mesh_controller_basic_updating.py +35 -0
  46. KratosMultiphysics/ShapeOptimizationApplication/mesh_controllers/mesh_controller_with_solver.py +237 -0
  47. KratosMultiphysics/ShapeOptimizationApplication/model_part_controller_factory.py +197 -0
  48. KratosMultiphysics/ShapeOptimizationApplication/optimizer_factory.py +235 -0
  49. KratosMultiphysics/ShapeOptimizationApplication/response_functions/__init__.py +0 -0
  50. KratosMultiphysics/ShapeOptimizationApplication/response_functions/airfoil_2d_responses.py +189 -0
  51. KratosMultiphysics/ShapeOptimizationApplication/response_functions/face_angle.py +140 -0
  52. KratosMultiphysics/ShapeOptimizationApplication/response_functions/mesh_based_packaging.py +70 -0
  53. KratosMultiphysics/ShapeOptimizationApplication/response_functions/packaging_response_base.py +156 -0
  54. KratosMultiphysics/ShapeOptimizationApplication/response_functions/plane_based_packaging.py +61 -0
  55. KratosMultiphysics/ShapeOptimizationApplication/response_functions/response_function_factory.py +40 -0
  56. KratosMultiphysics/ShapeOptimizationApplication/response_functions/surface_normal_shape_change.py +125 -0
  57. KratosMultiphysics/ShapeOptimizationApplication/response_functions/total_volume.py +56 -0
  58. KratosMultiphysics/ShapeOptimizationApplication/utilities/__init__.py +0 -0
  59. KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_math.py +443 -0
  60. KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_sensitivity_heatmap.py +235 -0
  61. KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_timer.py +47 -0
  62. KratosMultiphysics/ShapeOptimizationApplication/utilities/custom_variable_utilities.py +54 -0
  63. kratosshapeoptimizationapplication-10.4.4.dist-info/METADATA +31 -0
  64. kratosshapeoptimizationapplication-10.4.4.dist-info/RECORD +69 -0
  65. kratosshapeoptimizationapplication-10.4.4.dist-info/WHEEL +6 -0
  66. kratosshapeoptimizationapplication.dylibs/libKratosCore.dylib +4 -0
  67. kratosshapeoptimizationapplication.dylibs/libgcc_s.1.1.dylib +0 -0
  68. kratosshapeoptimizationapplication.dylibs/libgomp.1.dylib +0 -0
  69. kratosshapeoptimizationapplication.dylibs/libstdc++.6.dylib +0 -0
@@ -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)
@@ -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.")