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,125 @@
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
+ import KratosMultiphysics.ShapeOptimizationApplication as KSO
14
+ from KratosMultiphysics import Logger
15
+ from KratosMultiphysics.response_functions.response_function_interface import ResponseFunctionInterface
16
+
17
+ class SurfaceNormalShapeChange(ResponseFunctionInterface):
18
+ """
19
+ This simple geometric response function calculates the sum of all shape updates in
20
+ surface normal direction. Shape update in surface normal direction is considered positive,
21
+ in the opposite direction negative.
22
+
23
+ Note that the direction of the surface normals changes in every step,
24
+ this is why the response value is not a meaningful measure but only an indication on how much the shape changed.
25
+
26
+ Important settings:
27
+ flip_normal_direction : boolean flag for changing on which side of the surface the update is considered positive.
28
+ """
29
+
30
+ def __init__(self, identifier, response_settings, model):
31
+ self.identifier = identifier
32
+
33
+ response_settings.ValidateAndAssignDefaults(self.GetDefaultParameters())
34
+
35
+ self.response_settings = response_settings
36
+ self.model = model
37
+
38
+ self._model_part_name = response_settings["model_part_name"].GetString()
39
+ input_type = response_settings["model_import_settings"]["input_type"].GetString()
40
+ if input_type == "mdpa":
41
+ self.model_part = self.model.CreateModelPart(self._model_part_name)
42
+ domain_size = response_settings["domain_size"].GetInt()
43
+ if domain_size not in [2, 3]:
44
+ raise Exception("SurfaceNormalShapeChange: Invalid 'domain_size': {}".format(domain_size))
45
+ self.model_part.ProcessInfo.SetValue(KM.DOMAIN_SIZE, domain_size)
46
+ elif input_type == "use_input_model_part":
47
+ self.model_part = None # will be retrieved in Initialize()
48
+ else:
49
+ raise Exception("SurfaceNormalShapeChange: '{}' model part input type not implemented.".format(input_type))
50
+
51
+ self.signed_distances = None
52
+ self.directions = None
53
+
54
+ self.previous_value = 0.0
55
+ self.value = 0.0
56
+
57
+ self.gradient = {}
58
+
59
+ self.flip_normal_direction = self.response_settings["flip_normal_direction"].GetBool()
60
+
61
+ @classmethod
62
+ def GetDefaultParameters(cls):
63
+ this_defaults = KM.Parameters("""{
64
+ "response_type" : "surface_normal_shape_change",
65
+ "model_part_name" : "UNKNOWN_NAME",
66
+ "domain_size" : 3,
67
+ "model_import_settings" : {
68
+ "input_type" : "use_input_model_part",
69
+ "input_filename" : "UNKNOWN_NAME"
70
+ },
71
+ "flip_normal_direction" : false
72
+ }""")
73
+ return this_defaults
74
+
75
+ def Initialize(self):
76
+ if self.response_settings["model_import_settings"]["input_type"].GetString() == "mdpa":
77
+ file_name = self.response_settings["model_import_settings"]["input_filename"].GetString()
78
+ model_part_io = KM.ModelPartIO(file_name)
79
+ model_part_io.ReadModelPart(self.model_part)
80
+ else:
81
+ self.model_part = self.model.GetModelPart(self._model_part_name)
82
+
83
+ def InitializeSolutionStep(self):
84
+ self.previous_value = self.value
85
+ self.value = None
86
+ self.gradient = {}
87
+ KSO.GeometryUtilities(self.model_part).ComputeUnitSurfaceNormals()
88
+
89
+ def CalculateValue(self):
90
+ Logger.PrintInfo("\n> Starting primal analysis for response", self.identifier)
91
+
92
+ startTime = timer.time()
93
+
94
+ value = 0.0
95
+ for node in self.model_part.Nodes:
96
+ shape_update = node.GetSolutionStepValue(KSO.SHAPE_UPDATE)
97
+ normalized_normal = node.GetSolutionStepValue(KSO.NORMALIZED_SURFACE_NORMAL)
98
+ value += shape_update[0] * normalized_normal[0]
99
+ value += shape_update[1] * normalized_normal[1]
100
+ value += shape_update[2] * normalized_normal[2]
101
+
102
+ self.value = value + self.previous_value
103
+
104
+ Logger.PrintInfo("> Time needed for calculating the response value = ", round(timer.time() - startTime,2), "s")
105
+
106
+ def CalculateGradient(self):
107
+ Logger.PrintInfo("\n> Starting gradient calculation for response", self.identifier)
108
+
109
+ startTime = timer.time()
110
+
111
+ for node in self.model_part.Nodes:
112
+ normalized_normal = node.GetSolutionStepValue(KSO.NORMALIZED_SURFACE_NORMAL)
113
+ self.gradient[node.Id] = normalized_normal
114
+
115
+ Logger.PrintInfo("> Time needed for calculating gradients = ", round(timer.time() - startTime,2), "s")
116
+
117
+ def GetValue(self):
118
+ return self.value
119
+
120
+ def GetNodalGradient(self, variable):
121
+ if not self.gradient:
122
+ raise RuntimeError("Gradient was not calculated")
123
+ if variable != KM.SHAPE_SENSITIVITY:
124
+ raise RuntimeError("GetNodalGradient: No gradient for {}!".format(variable.Name))
125
+ return self.gradient
@@ -0,0 +1,56 @@
1
+ """
2
+ This module contains an interface to the total volume response function
3
+ """
4
+ import time as timer
5
+ import KratosMultiphysics as Kratos
6
+ import KratosMultiphysics.ShapeOptimizationApplication as KSO
7
+ from KratosMultiphysics.response_functions.response_function_interface import ResponseFunctionInterface
8
+
9
+ class TotalVolume(ResponseFunctionInterface):
10
+ def __init__(self, identifier, response_settings, model):
11
+ self.identifier = identifier
12
+ self.response_settings = response_settings
13
+
14
+ default_parameters = Kratos.Parameters("""
15
+ {
16
+ "response_type" : "total_volume",
17
+ "model_part_name" : "PLEASE_SPECIFY_MODEL_PART_NAME"
18
+ }
19
+ """)
20
+
21
+ self.response_settings.ValidateAndAssignDefaults(default_parameters)
22
+ self.model = model
23
+
24
+ def Initialize(self):
25
+ self.model_part = self.model[self.response_settings["model_part_name"].GetString()]
26
+
27
+ def CalculateValue(self):
28
+ start_time = timer.time()
29
+
30
+ self.value = KSO.GeometryUtilities(self.model_part).ComputeVolume()
31
+
32
+ Kratos.Logger.PrintInfo(self._GetLabel(), "Time needed for calculating the response value = ",round(timer.time() - start_time,2),"s")
33
+
34
+ def CalculateGradient(self):
35
+ start_time = timer.time()
36
+
37
+ KSO.GeometryUtilities(self.model_part).ComputeVolumeShapeDerivatives(Kratos.SHAPE_SENSITIVITY)
38
+ self.gradient = {}
39
+ for node in self.model_part.Nodes:
40
+ self.gradient[node.Id] = node.GetSolutionStepValue(Kratos.SHAPE_SENSITIVITY)
41
+
42
+ Kratos.Logger.PrintInfo(self._GetLabel(), "Time needed for calculating the gradient = ",round(timer.time() - start_time,2),"s")
43
+
44
+ def GetValue(self):
45
+ return self.value
46
+
47
+ def GetNodalGradient(self, variable):
48
+ if variable != Kratos.SHAPE_SENSITIVITY:
49
+ raise RuntimeError("GetNodalGradient: No gradient for {}!".format(variable.Name))
50
+ return self.gradient
51
+
52
+ @staticmethod
53
+ def _GetLabel():
54
+ return "TotalVolume"
55
+
56
+
@@ -0,0 +1,443 @@
1
+ # ==============================================================================
2
+ # KratosShapeOptimizationApplication
3
+ #
4
+ # License: BSD License
5
+ # license: ShapeOptimizationApplication/license.txt
6
+ #
7
+ # Main authors: Baumgaertner Daniel, https://github.com/dbaumgaertner
8
+ #
9
+ # ==============================================================================
10
+
11
+
12
+ # importing the Kratos Library
13
+ import KratosMultiphysics as KM
14
+
15
+ # Import additional libraries
16
+ import math
17
+
18
+ # ==============================================================================
19
+ def SafeConvertVectorToMatrix(_L):
20
+ if IsVector(_L):
21
+ return [_L]
22
+ else:
23
+ return _L
24
+
25
+ # ------------------------------------------------------------------------------
26
+ def IsVector(_X):
27
+ if len(_X)==0 or isinstance(_X[0],(float,int)):
28
+ return True
29
+ else:
30
+ return False
31
+
32
+ # ------------------------------------------------------------------------------
33
+ def IsEmpty(_A):
34
+ _A = SafeConvertVectorToMatrix(_A)
35
+
36
+ if len(_A)==0 or len(_A[0])==0:
37
+ return True
38
+ return False
39
+
40
+ # ------------------------------------------------------------------------------
41
+ def ZeroVector(m):
42
+ return [0.0 for i in range(m)]
43
+
44
+ # ------------------------------------------------------------------------------
45
+ def ZeroMatrix(m,n):
46
+ A = [[0.0 for i in range(m)] for j in range(n)]
47
+ return A
48
+
49
+ # ------------------------------------------------------------------------------
50
+ def Ones(n):
51
+ return [1.0 for i in range(n)]
52
+
53
+ # ------------------------------------------------------------------------------
54
+ def RowSize(_A):
55
+ _A = SafeConvertVectorToMatrix(_A)
56
+
57
+ if len(_A)==0:
58
+ return 0
59
+ return len(_A[0])
60
+
61
+ # ------------------------------------------------------------------------------
62
+ def CollSize(_A):
63
+ _A = SafeConvertVectorToMatrix(_A)
64
+
65
+ return len(_A)
66
+
67
+ # ------------------------------------------------------------------------------
68
+ # horizontal concatenation
69
+ def HorzCat(_A,_B):
70
+ _A = SafeConvertVectorToMatrix(_A)
71
+ _B = SafeConvertVectorToMatrix(_B)
72
+
73
+ if IsEmpty(_B):
74
+ return _A
75
+ if IsEmpty(_A):
76
+ return _B
77
+
78
+ return _A+_B
79
+
80
+ # ------------------------------------------------------------------------------
81
+ # vertical concatenation
82
+ def VertCat(_A,_B):
83
+ _A = SafeConvertVectorToMatrix(_A)
84
+ _B = SafeConvertVectorToMatrix(_B)
85
+
86
+ if IsEmpty(_B):
87
+ return _A
88
+ if IsEmpty(_A):
89
+ return _B
90
+
91
+ coll_size_A = CollSize(_A)
92
+ coll_size_B = CollSize(_B)
93
+
94
+ if coll_size_A!=coll_size_B:
95
+ raise ValueError("custom_math::VertCat: Wrong size in vertical concatenation detected!")
96
+
97
+ _C = []
98
+ for i in range(coll_size_A):
99
+ _C.append(_A[i]+_B[i])
100
+
101
+ if CollSize(_C) == 1:
102
+ return _C[0]
103
+ else:
104
+ return _C
105
+
106
+ # ------------------------------------------------------------------------------
107
+ def Norm2(_X):
108
+ temp_vec = [x**2 for x in _X]
109
+ temp_sum = sum(temp_vec)
110
+ return math.sqrt(temp_sum)
111
+
112
+ # ------------------------------------------------------------------------------
113
+ def NormInf3D(_X):
114
+ temp_vec = [_X[3*i]**2 + _X[3*i+1]**2 + _X[3*i+2]**2 for i in range(int(len(_X)/3))]
115
+ max_squared_value = max(temp_vec)
116
+ return math.sqrt(max_squared_value)
117
+
118
+ # ------------------------------------------------------------------------------
119
+ def Dot(_X, _Y):
120
+ if len(_X) != len(_Y):
121
+ raise RuntimeError("custom_math::Dot: Dot product to be computed but _X and _Y do not have the same dimension!")
122
+ return sum( [_X[i]*_Y[i] for i in range(len(_X))] )
123
+
124
+ # ------------------------------------------------------------------------------
125
+ def Plus(_X, _Y):
126
+ if len(_X)!=len(_Y):
127
+ raise ValueError("custom_math::Plus: Wrong size of input variables!")
128
+ return [ _X[i]+_Y[i] for i in range(len(_X)) ]
129
+
130
+ # ------------------------------------------------------------------------------
131
+ def ScalarVectorProduct(scal, _X):
132
+ return [x*scal for x in _X]
133
+
134
+ # ------------------------------------------------------------------------------
135
+ def ScalarMatrixProduct(scal, _A):
136
+ return [ [ scal*_A[i][j] for j in range(RowSize(_A)) ] for i in range(CollSize(_A)) ]
137
+
138
+ # ------------------------------------------------------------------------------
139
+ def Prod(_A,_B):
140
+ _A = SafeConvertVectorToMatrix(_A)
141
+ _B = SafeConvertVectorToMatrix(_B)
142
+
143
+ row_size_A = RowSize(_A)
144
+ row_size_B = RowSize(_B)
145
+ coll_size_A = CollSize(_A)
146
+ coll_size_B = CollSize(_B)
147
+
148
+ if IsEmpty(_A) or IsEmpty(_B):
149
+ return []
150
+ if coll_size_A!=row_size_B:
151
+ raise ValueError("cusom_math::Prod: Product detected wrong size in the specified input array!")
152
+
153
+ result = [ [ sum(_A[k][i]*_B[j][k] for k in range(coll_size_A)) for i in range(row_size_A) ] for j in range(coll_size_B) ]
154
+
155
+ if CollSize(result) == 1:
156
+ return result[0]
157
+ else:
158
+ return result
159
+
160
+ # ------------------------------------------------------------------------------
161
+ def ElemwiseProd(_X,_Y):
162
+ if len(_X)!=len(_Y):
163
+ raise ValueError("custom_math::ElemwiseProd: Wrong size of input vectors!")
164
+ return [_X[i]*_Y[i] for i in range(len(_X))]
165
+
166
+ # ------------------------------------------------------------------------------
167
+ def Trans(_A):
168
+ _A = SafeConvertVectorToMatrix(_A)
169
+
170
+ if IsEmpty(_A):
171
+ return []
172
+ return list(map(list, zip(*_A)))
173
+ # ------------------------------------------------------------------------------
174
+ def Minus(_X,_Y):
175
+ return [ _X[i]-_Y[i] for i in range(len(_X))]
176
+
177
+ # ------------------------------------------------------------------------------
178
+ def TranslateToNewBasis(_A, basis):
179
+ _A = SafeConvertVectorToMatrix(_A)
180
+
181
+ if IsEmpty(_A):
182
+ return []
183
+
184
+ trans_basis = Trans(basis)
185
+ return Prod(trans_basis,_A)
186
+
187
+ # ------------------------------------------------------------------------------
188
+ def TranslateToOriginalBasis(_A, basis):
189
+ _A = SafeConvertVectorToMatrix(_A)
190
+
191
+ if IsEmpty(_A):
192
+ return []
193
+
194
+ return Prod(basis,_A)
195
+
196
+ # ------------------------------------------------------------------------------
197
+ def SolveLinearSystem(A,b):
198
+ A = Trans(A)
199
+ n = len(A)
200
+ for i in range(n):
201
+ A[i].append(b[i])
202
+ for i in range(0, n):
203
+ # Search for maximum in this column
204
+ maxEl = abs(A[i][i])
205
+ maxRow = i
206
+ for k in range(i+1, n):
207
+ if abs(A[k][i]) > maxEl:
208
+ maxEl = abs(A[k][i])
209
+ maxRow = k
210
+
211
+ # Swap maximum row with current row (column by column)
212
+ for k in range(i, n+1):
213
+ tmp = A[maxRow][k]
214
+ A[maxRow][k] = A[i][k]
215
+ A[i][k] = tmp
216
+
217
+ # Make all rows below this one 0 in current column
218
+ for k in range(i+1, n):
219
+ c = -A[k][i]/A[i][i]
220
+ for j in range(i, n+1):
221
+ if i == j:
222
+ A[k][j] = 0
223
+ else:
224
+ A[k][j] += c * A[i][j]
225
+
226
+ # Solve equation Ax=b for an upper triangular matrix A
227
+ x = [0 for i in range(n)]
228
+ for i in range(n-1, -1, -1):
229
+ x[i] = A[i][n]/A[i][i]
230
+ for k in range(i-1, -1, -1):
231
+ A[k][n] -= A[k][i] * x[i]
232
+ return x
233
+
234
+ # ------------------------------------------------------------------------------
235
+ # Interior point algorithm
236
+ # Nocedal & Wright, Numerical Optimization, 2nd edition, Algorithm 16.4, p.484, Springer
237
+ # Solves min x'*x with A*x<=b (x is of size n and A of size m*n)
238
+ # Note that original algorithm solves min x'*x with A*x>=b --> negation of input values in the beginning
239
+ def QuadProg(A, b, max_itr, tolerance):
240
+ m = RowSize(A)
241
+ n = CollSize(A)
242
+
243
+ A = ScalarMatrixProduct(-1.0,A)
244
+ b = ScalarVectorProduct(-1.0,b)
245
+
246
+ def rdrp(x,y,l,A,b):
247
+ temp_vec = Prod(Trans(A),l)
248
+ rd = Minus(x,temp_vec)
249
+
250
+ temp_vec = Prod(A,x)
251
+ rp = Minus(Minus(temp_vec,y),b)
252
+ return (rd,rp)
253
+
254
+ def GradResidu(y,l,A):
255
+ m = RowSize(A)
256
+ n = CollSize(A)
257
+ grad = [ZeroVector(n+2*m) for i in range(n+2*m)]
258
+ for i in range(n):
259
+ grad[i][i] = 1
260
+ for i in range(n):
261
+ for j in range(m):
262
+ grad[n+m+j][i] = -A[i][j]
263
+ for i in range(m):
264
+ for j in range(n):
265
+ grad[j][n+i] = A[j][i]
266
+ for i in range(m):
267
+ grad[n+i][n+i] = -1
268
+ for i in range(m):
269
+ grad[n+i][n+m+i] = l[i]
270
+ grad[n+m+i][n+m+i] = y[i]
271
+ return grad
272
+
273
+ #init
274
+ x = ZeroVector(n)
275
+ y = Ones(m) # slack variables
276
+ l = Ones(m) # lagrange multipliers
277
+
278
+ rd, rp = rdrp(x,y,l,A,b)
279
+ k = 0
280
+ error = 100000
281
+ while error>tolerance and k <= max_itr:
282
+
283
+ # solve affine delta
284
+ gradRes = GradResidu(y,l,A)
285
+ deltaXYLAff = ZeroVector(n+2*m)
286
+ rhs = ScalarVectorProduct(-1, VertCat(rd,VertCat(rp,ElemwiseProd(y,l))))
287
+
288
+ deltaXYLAff = SolveLinearSystem(gradRes,rhs)
289
+
290
+ # Drop and error if divergence is detected
291
+ if Norm2(deltaXYLAff)>1e20:
292
+ KM.Logger.PrintWarning("ShapeOpt::custom_math::quadprodg", "deltaXYLAff is NAN. The reason is, that feasible domain might be empty. This happens e.g. when the dJdX is parallel to dCdX (like at convergence with a single constraint)")
293
+ exit_code = 2
294
+ return
295
+
296
+ dxAff = deltaXYLAff[:n]
297
+ dyAff = deltaXYLAff[n:n+m]
298
+ dlAff = deltaXYLAff[n+m:]
299
+
300
+ mu = Dot(y,l)/m
301
+ alphaS = 1
302
+ alphaZ = 1
303
+ for i in range(m):
304
+ if dyAff[i]<0:
305
+ alphaS = min(alphaS, abs(y[i]/dyAff[i]))
306
+ if dlAff[i]<0:
307
+ alphaZ = min(alphaZ, abs(l[i]/dlAff[i]))
308
+ alphaAff = 0.8*min(alphaS,alphaZ)
309
+
310
+ muAff = Dot(Plus(y,ScalarVectorProduct(alphaAff,dyAff)),Plus(l,ScalarVectorProduct(alphaAff,dlAff))) / m
311
+
312
+ sigma = (muAff/mu)**3
313
+
314
+ # solve for delta
315
+ rhs = ScalarVectorProduct(-1, VertCat(rd,VertCat(rp, Plus( Plus(ElemwiseProd(y,l),ElemwiseProd(dyAff,dlAff)) , ScalarVectorProduct(-sigma*mu,Ones(m)) ))))
316
+ deltaXYL = SolveLinearSystem(gradRes,rhs)
317
+
318
+ dx = deltaXYL[:n]
319
+ dy = deltaXYL[n:n+m]
320
+ dl = deltaXYL[n+m:]
321
+
322
+ alphaS = 1
323
+ alphaZ = 1
324
+ for i in range(m):
325
+ if dy[i]<0:
326
+ alphaS = min(alphaS, abs(y[i]/dy[i]))
327
+ if dl[i]<0:
328
+ alphaZ = min(alphaZ, abs(l[i]/dl[i]))
329
+ alpha = 0.8*min(alphaS,alphaZ)
330
+
331
+ # apply delta
332
+ x = Plus(x,ScalarVectorProduct(alpha,dx))
333
+ y = Plus(y,ScalarVectorProduct(alpha,dy))
334
+ l = Plus(l,ScalarVectorProduct(alpha,dl))
335
+ rd,rp = rdrp(x,y,l,A,b)
336
+ k = k+1
337
+
338
+ # Determine current error
339
+ error = Norm2(VertCat(rd,rp))
340
+
341
+ if k==max_itr:
342
+ raise RuntimeError("custom_math::quadprodg: Suboptimization for projection to halfspaces did not converge in the specified number of iterations")
343
+ else:
344
+ # Exit code of 0 if convergence is reached
345
+ exit_code = 0
346
+
347
+ return x, k, error, exit_code
348
+
349
+ # ------------------------------------------------------------------------------
350
+ def PerformBisectioning(func, a, b, target, tolerance, max_itr):
351
+
352
+ fa_value_0, fa_is_converged = func(a)
353
+ fb_value_0, fb_is_converged = func(b)
354
+
355
+ error = None
356
+ itr = 0
357
+
358
+ # Always safe last argument for which function converges and return this if no solution is found
359
+ if fa_is_converged:
360
+ last_allowed_function_argument = a
361
+ elif fb_is_converged:
362
+ last_allowed_function_argument = b
363
+
364
+ # Check special cases
365
+ if abs(fa_value_0-target) < tolerance:
366
+ res_function_argument = a
367
+ error = abs(fa_value_0-target)
368
+
369
+ elif abs(fb_value_0-target) < tolerance:
370
+ res_function_argument = b
371
+ error = abs(fb_value_0-target)
372
+
373
+ elif abs(fa_value_0-fb_value_0) < 1e-13:
374
+ KM.Logger.PrintWarning("ShapeOpt::PerformBisectioning", "Bisectioning intervall yiels to identical function values!")
375
+ res_function_argument = a
376
+ error = abs(fb_value_0-target)
377
+
378
+ elif (fa_value_0-target)*(fb_value_0-target)>0:
379
+ KM.Logger.PrintWarning("ShapeOpt::PerformBisectioning", "Bisectioning on function, that has no root in specified intervall!! Returning the argument which yiels a closer value to the target.")
380
+
381
+ if abs(fa_value_0-target) < abs(fb_value_0-target):
382
+ res_function_argument = a
383
+ error = fa_value_0-target
384
+ else:
385
+ res_function_argument = b
386
+ error = fb_value_0-target
387
+
388
+ # Perform bisectioning if no special case applies
389
+ else:
390
+ p = (a + b)/2
391
+
392
+ for itr in range(1,max_itr+1):
393
+ fa_value, fa_is_converged = func(a)
394
+ fp_value, fp_is_converged = func(p)
395
+
396
+ if (fa_value-target)*(fp_value-target) < 0:
397
+ b = p
398
+ else:
399
+ a = p
400
+
401
+ p = (a + b)/2
402
+
403
+ fp_value, fp_is_converged = func(p)
404
+ error = abs(fp_value-target)
405
+
406
+ if fp_is_converged:
407
+ last_allowed_function_argument = p
408
+
409
+ if itr == max_itr:
410
+ res_function_argument = last_allowed_function_argument
411
+ KM.Logger.PrintWarning("ShapeOpt::PerformBisectioning", "Bisectioning did not converge in the specified maximum number of iterations!")
412
+ break
413
+
414
+ elif error < tolerance:
415
+ res_function_argument = p
416
+ break
417
+
418
+ return res_function_argument, itr, error
419
+
420
+ # --------------------------------------------------------------------------
421
+ def PerformGramSchmidtOrthogonalization(vector_space):
422
+ V = vector_space
423
+ B = []
424
+
425
+ # Orthogonalization
426
+ norm2_V0 = Norm2(V[0])
427
+ B.append( ScalarVectorProduct(1/norm2_V0,V[0]) )
428
+ for v in V[1:]:
429
+ for b in B:
430
+ norm2_b = Norm2(b)
431
+ v = Minus( v , ScalarVectorProduct( Dot(v,b)/norm2_b**2 , b ) )
432
+
433
+ # Add only if vector is independent
434
+ norm2_v = Norm2(v)
435
+ if norm2_v>1e-10:
436
+ B.append( ScalarVectorProduct(1/norm2_v,v) )
437
+ else:
438
+ KM.Logger.PrintWarning("ShapeOpt::PerformGramSchmidtOrthogonalization", "Zero basis vector after Gram-Schmidt orthogonalization!")
439
+ B.append(v)
440
+
441
+ return B
442
+
443
+ # ==============================================================================