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
KratosMultiphysics/ShapeOptimizationApplication/algorithms/algorithm_shape_fraction_optimization.py
ADDED
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# KratosShapeOptimizationApplication
|
|
3
|
+
#
|
|
4
|
+
# License: BSD License
|
|
5
|
+
# license: ShapeOptimizationApplication/license.txt
|
|
6
|
+
#
|
|
7
|
+
# Main authors: Schmölz David, https://github.com/dschmoelz
|
|
8
|
+
#
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
# Making KratosMultiphysics backward compatible with python 2.6 and 2.7
|
|
12
|
+
from __future__ import print_function, absolute_import, division
|
|
13
|
+
|
|
14
|
+
# Kratos Core and Apps
|
|
15
|
+
import KratosMultiphysics as Kratos
|
|
16
|
+
import KratosMultiphysics.ShapeOptimizationApplication as KSO
|
|
17
|
+
from KratosMultiphysics.LinearSolversApplication import dense_linear_solver_factory
|
|
18
|
+
|
|
19
|
+
# Additional imports
|
|
20
|
+
from KratosMultiphysics.ShapeOptimizationApplication.utilities import custom_math as cm
|
|
21
|
+
from KratosMultiphysics.ShapeOptimizationApplication.algorithms.algorithm_base import OptimizationAlgorithm
|
|
22
|
+
from KratosMultiphysics.ShapeOptimizationApplication import mapper_factory
|
|
23
|
+
from KratosMultiphysics.ShapeOptimizationApplication.loggers import data_logger_factory
|
|
24
|
+
from KratosMultiphysics.ShapeOptimizationApplication.utilities.custom_timer import Timer
|
|
25
|
+
from KratosMultiphysics.ShapeOptimizationApplication.utilities.custom_variable_utilities import WriteDictionaryDataOnNodalVariable, ReadNodalVariableToList
|
|
26
|
+
|
|
27
|
+
import math
|
|
28
|
+
import time as timer
|
|
29
|
+
|
|
30
|
+
# ==============================================================================
|
|
31
|
+
class AlgorithmShapeFractionOptimization(OptimizationAlgorithm):
|
|
32
|
+
# --------------------------------------------------------------------------
|
|
33
|
+
def __init__(self, optimization_settings, analyzer, communicator, model_part_controller):
|
|
34
|
+
default_algorithm_settings = Kratos.Parameters("""
|
|
35
|
+
{
|
|
36
|
+
"name" : "shape_fraction_optimization",
|
|
37
|
+
"max_correction_share" : 0.75,
|
|
38
|
+
"max_iterations" : 100,
|
|
39
|
+
"relative_tolerance" : 1e-3,
|
|
40
|
+
"shape_fraction" : {
|
|
41
|
+
"penalty_method" : "exterior",
|
|
42
|
+
"max_fraction" : 0.5,
|
|
43
|
+
"nodal_tolerance" : 0.1,
|
|
44
|
+
"inner_tolerance" : 0.01,
|
|
45
|
+
"max_inner_steps" : 10,
|
|
46
|
+
"initial_penalty_factor": 0.1,
|
|
47
|
+
"penalty_scale_factor": 1.25
|
|
48
|
+
},
|
|
49
|
+
"line_search" : {
|
|
50
|
+
"line_search_type" : "manual_stepping",
|
|
51
|
+
"normalize_search_direction" : true,
|
|
52
|
+
"step_size" : 1.0
|
|
53
|
+
}
|
|
54
|
+
}""")
|
|
55
|
+
self.algorithm_settings = optimization_settings["optimization_algorithm"]
|
|
56
|
+
self.algorithm_settings.RecursivelyValidateAndAssignDefaults(default_algorithm_settings)
|
|
57
|
+
|
|
58
|
+
self.optimization_settings = optimization_settings
|
|
59
|
+
self.mapper_settings = optimization_settings["design_variables"]["filter"]
|
|
60
|
+
|
|
61
|
+
self.analyzer = analyzer
|
|
62
|
+
self.communicator = communicator
|
|
63
|
+
self.model_part_controller = model_part_controller
|
|
64
|
+
|
|
65
|
+
self.design_surface = None
|
|
66
|
+
self.mapper = None
|
|
67
|
+
self.data_logger = None
|
|
68
|
+
self.optimization_utilities = None
|
|
69
|
+
|
|
70
|
+
self.objectives = optimization_settings["objectives"]
|
|
71
|
+
self.constraints = optimization_settings["constraints"]
|
|
72
|
+
self.constraint_gradient_variables = {}
|
|
73
|
+
for itr, constraint in enumerate(self.constraints.values()):
|
|
74
|
+
self.constraint_gradient_variables.update({
|
|
75
|
+
constraint["identifier"].GetString() : {
|
|
76
|
+
"gradient": Kratos.KratosGlobals.GetVariable("DC"+str(itr+1)+"DX"),
|
|
77
|
+
"mapped_gradient": Kratos.KratosGlobals.GetVariable("DC"+str(itr+1)+"DX_MAPPED")
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
self.max_correction_share = self.algorithm_settings["max_correction_share"].GetDouble()
|
|
81
|
+
|
|
82
|
+
self.step_size = self.algorithm_settings["line_search"]["step_size"].GetDouble()
|
|
83
|
+
self.max_iterations = self.algorithm_settings["max_iterations"].GetInt() + 1
|
|
84
|
+
self.relative_tolerance = self.algorithm_settings["relative_tolerance"].GetDouble()
|
|
85
|
+
|
|
86
|
+
self.optimization_model_part = model_part_controller.GetOptimizationModelPart()
|
|
87
|
+
self.optimization_model_part.AddNodalSolutionStepVariable(KSO.SEARCH_DIRECTION)
|
|
88
|
+
self.optimization_model_part.AddNodalSolutionStepVariable(KSO.CORRECTION)
|
|
89
|
+
self.optimization_model_part.AddNodalSolutionStepVariable(KSO.DP1DX)
|
|
90
|
+
self.optimization_model_part.AddNodalSolutionStepVariable(KSO.DPF1DX)
|
|
91
|
+
|
|
92
|
+
# shape fraction related settings
|
|
93
|
+
self.penalty_method = self.algorithm_settings["shape_fraction"]["penalty_method"].GetString()
|
|
94
|
+
self.max_shape_fraction = self.algorithm_settings["shape_fraction"]["max_fraction"].GetDouble()
|
|
95
|
+
self.frac_tolerance = self.algorithm_settings["shape_fraction"]["nodal_tolerance"].GetDouble()
|
|
96
|
+
self.inner_tolerance = self.algorithm_settings["shape_fraction"]["inner_tolerance"].GetDouble()
|
|
97
|
+
self.max_inner_steps = self.algorithm_settings["shape_fraction"]["max_inner_steps"].GetDouble()
|
|
98
|
+
self.initial_penalty_factor = self.algorithm_settings["shape_fraction"]["initial_penalty_factor"].GetDouble()
|
|
99
|
+
self.gamma = self.algorithm_settings["shape_fraction"]["penalty_scale_factor"].GetDouble()
|
|
100
|
+
if self.gamma <= 1.0:
|
|
101
|
+
raise RuntimeError("Shape fraction algorithm: 'penalty_scale_factor' has to be larger than 1.0!")
|
|
102
|
+
self.inner_step = 0
|
|
103
|
+
self.penalty_factor = 0.0
|
|
104
|
+
self.epsilon = -0.2
|
|
105
|
+
self.previous_objective_value = None
|
|
106
|
+
|
|
107
|
+
# --------------------------------------------------------------------------
|
|
108
|
+
def CheckApplicability(self):
|
|
109
|
+
if self.objectives.size() > 1:
|
|
110
|
+
raise RuntimeError("Shape fraction algorithm only supports one objective function!")
|
|
111
|
+
|
|
112
|
+
# --------------------------------------------------------------------------
|
|
113
|
+
def InitializeOptimizationLoop(self):
|
|
114
|
+
self.model_part_controller.Initialize()
|
|
115
|
+
|
|
116
|
+
self.analyzer.InitializeBeforeOptimizationLoop()
|
|
117
|
+
|
|
118
|
+
self.design_surface = self.model_part_controller.GetDesignSurface()
|
|
119
|
+
|
|
120
|
+
self.mapper = mapper_factory.CreateMapper(self.design_surface, self.design_surface, self.mapper_settings)
|
|
121
|
+
self.mapper.Initialize()
|
|
122
|
+
self.model_part_controller.InitializeDamping()
|
|
123
|
+
|
|
124
|
+
self.data_logger = data_logger_factory.CreateDataLogger(self.model_part_controller, self.communicator, self.optimization_settings)
|
|
125
|
+
self.data_logger.InitializeDataLogging()
|
|
126
|
+
|
|
127
|
+
self.optimization_utilities = KSO.OptimizationUtilities
|
|
128
|
+
|
|
129
|
+
# --------------------------------------------------------------------------
|
|
130
|
+
def RunOptimizationLoop(self):
|
|
131
|
+
timer = Timer()
|
|
132
|
+
timer.StartTimer()
|
|
133
|
+
|
|
134
|
+
for self.optimization_iteration in range(1,self.max_iterations):
|
|
135
|
+
Kratos.Logger.Print("")
|
|
136
|
+
Kratos.Logger.Print("===============================================================================")
|
|
137
|
+
Kratos.Logger.PrintInfo("ShapeOpt", timer.GetTimeStamp(), ": Starting optimization iteration ", self.optimization_iteration)
|
|
138
|
+
Kratos.Logger.Print("===============================================================================\n")
|
|
139
|
+
|
|
140
|
+
timer.StartNewLap()
|
|
141
|
+
|
|
142
|
+
self.__initializeNewShape()
|
|
143
|
+
|
|
144
|
+
self.__analyzeShape()
|
|
145
|
+
|
|
146
|
+
self.__computeShapeUpdate()
|
|
147
|
+
|
|
148
|
+
self.__logCurrentOptimizationStep()
|
|
149
|
+
|
|
150
|
+
Kratos.Logger.Print("")
|
|
151
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Time needed for current optimization step = ", timer.GetLapTime(), "s")
|
|
152
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Time needed for total optimization so far = ", timer.GetTotalTime(), "s")
|
|
153
|
+
|
|
154
|
+
if self.__isAlgorithmConverged():
|
|
155
|
+
break
|
|
156
|
+
else:
|
|
157
|
+
self.__determineAbsoluteChanges()
|
|
158
|
+
|
|
159
|
+
# --------------------------------------------------------------------------
|
|
160
|
+
def FinalizeOptimizationLoop(self):
|
|
161
|
+
self.data_logger.FinalizeDataLogging()
|
|
162
|
+
self.analyzer.FinalizeAfterOptimizationLoop()
|
|
163
|
+
|
|
164
|
+
# --------------------------------------------------------------------------
|
|
165
|
+
def __initializeNewShape(self):
|
|
166
|
+
self.model_part_controller.UpdateTimeStep(self.optimization_iteration)
|
|
167
|
+
self.model_part_controller.UpdateMeshAccordingInputVariable(KSO.SHAPE_UPDATE)
|
|
168
|
+
self.model_part_controller.SetReferenceMeshToMesh()
|
|
169
|
+
|
|
170
|
+
# --------------------------------------------------------------------------
|
|
171
|
+
def __analyzeShape(self):
|
|
172
|
+
self.communicator.initializeCommunication()
|
|
173
|
+
self.communicator.requestValueOf(self.objectives[0]["identifier"].GetString())
|
|
174
|
+
self.communicator.requestGradientOf(self.objectives[0]["identifier"].GetString())
|
|
175
|
+
|
|
176
|
+
for constraint in self.constraints.values():
|
|
177
|
+
con_id = constraint["identifier"].GetString()
|
|
178
|
+
self.communicator.requestValueOf(con_id)
|
|
179
|
+
self.communicator.requestGradientOf(con_id)
|
|
180
|
+
|
|
181
|
+
self.analyzer.AnalyzeDesignAndReportToCommunicator(self.optimization_model_part, self.optimization_iteration, self.communicator)
|
|
182
|
+
|
|
183
|
+
# compute normals only if required
|
|
184
|
+
surface_normals_required = self.objectives[0]["project_gradient_on_surface_normals"].GetBool()
|
|
185
|
+
for constraint in self.constraints.values():
|
|
186
|
+
if constraint["project_gradient_on_surface_normals"].GetBool():
|
|
187
|
+
surface_normals_required = True
|
|
188
|
+
|
|
189
|
+
if surface_normals_required:
|
|
190
|
+
self.model_part_controller.ComputeUnitSurfaceNormals()
|
|
191
|
+
|
|
192
|
+
# objective value
|
|
193
|
+
objective_value = self.communicator.getStandardizedValue(self.objectives[0]["identifier"].GetString())
|
|
194
|
+
|
|
195
|
+
# objective gradient
|
|
196
|
+
objGradientDict = self.communicator.getStandardizedGradient(self.objectives[0]["identifier"].GetString())
|
|
197
|
+
WriteDictionaryDataOnNodalVariable(objGradientDict, self.optimization_model_part, KSO.DF1DX)
|
|
198
|
+
objective_gradient = Kratos.Vector()
|
|
199
|
+
self.optimization_utilities.AssembleVector(self.optimization_model_part, objective_gradient, KSO.DF1DX)
|
|
200
|
+
objective_gradient_norm = objective_gradient.norm_2()
|
|
201
|
+
|
|
202
|
+
# response value g of penalty method
|
|
203
|
+
response_value = self.__computeResponseValue()
|
|
204
|
+
Kratos.Logger.PrintInfo("ShapeFractionOptimization", f"Shape Fraction value = {response_value}")
|
|
205
|
+
# penalty value
|
|
206
|
+
# exterior: p = max(0, g)**2
|
|
207
|
+
# extended interior: p = -1/g if g =< epsilon (interior penalty)
|
|
208
|
+
# p = - 2*epsilon - g / epsilon**2 if g > epsilon (exterior penalty)
|
|
209
|
+
self.penalty_value = self.__computePenaltyValue(response_value)
|
|
210
|
+
|
|
211
|
+
# set up initial penalty factor
|
|
212
|
+
if self.penalty_factor == 0.0:
|
|
213
|
+
self.__SetUpInitialPenaltyFactor(objective_value, objective_gradient_norm)
|
|
214
|
+
self.__IncrementInnerStep()
|
|
215
|
+
|
|
216
|
+
# pseudo objective value
|
|
217
|
+
pseudo_objective_value = self.__computePseudoObjectiveValue(objective_value)
|
|
218
|
+
|
|
219
|
+
# check inner convergence
|
|
220
|
+
inner_converged = self.__checkInnerConvergence(pseudo_objective_value)
|
|
221
|
+
# update penalty factor if inner loop converged
|
|
222
|
+
if inner_converged:
|
|
223
|
+
Kratos.Logger.PrintInfo("ShapeFractionOptimization", "Updating penalty factor.")
|
|
224
|
+
self.__updatePenaltyFactor()
|
|
225
|
+
|
|
226
|
+
# update pseudo objective value
|
|
227
|
+
self.pseudo_objective_value = self.__computePseudoObjectiveValue(objective_value)
|
|
228
|
+
|
|
229
|
+
# compute penalty gradient
|
|
230
|
+
# exterior: p = 2 * g * dg/dx
|
|
231
|
+
# extended interior: p = -1/g if g =< epsilon (interior penalty)
|
|
232
|
+
# p = - 2*epsilon - g / epsilon**2 if g > epsilon (exterior penalty)
|
|
233
|
+
penalty_gradient = self.__computePenaltyGradient(response_value)
|
|
234
|
+
self.optimization_utilities.AssignVectorToVariable(self.optimization_model_part, penalty_gradient, KSO.DP1DX)
|
|
235
|
+
|
|
236
|
+
# pseudo objective gradient
|
|
237
|
+
pseudo_objective_gradient = objective_gradient + self.penalty_factor * penalty_gradient
|
|
238
|
+
self.optimization_utilities.AssignVectorToVariable(self.optimization_model_part, pseudo_objective_gradient, KSO.DPF1DX)
|
|
239
|
+
|
|
240
|
+
# project and damp objective gradients
|
|
241
|
+
if self.objectives[0]["project_gradient_on_surface_normals"].GetBool():
|
|
242
|
+
self.model_part_controller.ProjectNodalVariableOnUnitSurfaceNormals(KSO.DPF1DX)
|
|
243
|
+
|
|
244
|
+
self.model_part_controller.DampNodalSensitivityVariableIfSpecified(KSO.DPF1DX)
|
|
245
|
+
|
|
246
|
+
# project and damp constraint gradients
|
|
247
|
+
for constraint in self.constraints.values():
|
|
248
|
+
con_id = constraint["identifier"].GetString()
|
|
249
|
+
conGradientDict = self.communicator.getStandardizedGradient(con_id)
|
|
250
|
+
gradient_variable = self.constraint_gradient_variables[con_id]["gradient"]
|
|
251
|
+
WriteDictionaryDataOnNodalVariable(conGradientDict, self.optimization_model_part, gradient_variable)
|
|
252
|
+
|
|
253
|
+
if constraint["project_gradient_on_surface_normals"].GetBool():
|
|
254
|
+
self.model_part_controller.ProjectNodalVariableOnUnitSurfaceNormals(gradient_variable)
|
|
255
|
+
|
|
256
|
+
self.model_part_controller.DampNodalSensitivityVariableIfSpecified(gradient_variable)
|
|
257
|
+
|
|
258
|
+
# --------------------------------------------------------------------------
|
|
259
|
+
def __computePenaltyValue(self, response_value):
|
|
260
|
+
|
|
261
|
+
if self.penalty_method == "extended_interior":
|
|
262
|
+
g = response_value
|
|
263
|
+
if g > self.epsilon:
|
|
264
|
+
penalty_value = - (2*self.epsilon - g) / self.epsilon**2
|
|
265
|
+
else:
|
|
266
|
+
penalty_value = - 1 / g
|
|
267
|
+
|
|
268
|
+
elif self.penalty_method == "exterior":
|
|
269
|
+
penalty_value = response_value
|
|
270
|
+
|
|
271
|
+
return penalty_value
|
|
272
|
+
|
|
273
|
+
# --------------------------------------------------------------------------
|
|
274
|
+
def __computeResponseValue(self):
|
|
275
|
+
|
|
276
|
+
def __calculateNodalValueExterior(self, node, quantile):
|
|
277
|
+
shape_change = node.GetSolutionStepValue(KSO.SHAPE_CHANGE)
|
|
278
|
+
norm = cm.Norm2(shape_change)
|
|
279
|
+
|
|
280
|
+
if norm > quantile:
|
|
281
|
+
return 0.0
|
|
282
|
+
|
|
283
|
+
if norm > self.frac_tolerance:
|
|
284
|
+
return norm
|
|
285
|
+
else:
|
|
286
|
+
return 0.0
|
|
287
|
+
|
|
288
|
+
def __calculateNodalValueExtendedInterior(self, node, quantile):
|
|
289
|
+
shape_change = node.GetSolutionStepValue(KSO.SHAPE_CHANGE)
|
|
290
|
+
norm = cm.Norm2(shape_change)
|
|
291
|
+
|
|
292
|
+
if norm > quantile:
|
|
293
|
+
return 0.0
|
|
294
|
+
else:
|
|
295
|
+
return norm
|
|
296
|
+
|
|
297
|
+
def __calculateIntegralToleranceOfNode(self, node, quantile):
|
|
298
|
+
shape_change = node.GetSolutionStepValue(KSO.SHAPE_CHANGE)
|
|
299
|
+
norm = cm.Norm2(shape_change)
|
|
300
|
+
|
|
301
|
+
if norm > quantile:
|
|
302
|
+
return 0.0
|
|
303
|
+
else:
|
|
304
|
+
return norm
|
|
305
|
+
|
|
306
|
+
Kratos.Logger.PrintInfo("ShapeFractionOptimization", "Starting calculation of penalty response value:")
|
|
307
|
+
|
|
308
|
+
startTime = timer.time()
|
|
309
|
+
total_integral = 0.0
|
|
310
|
+
integral_tol = 0.0
|
|
311
|
+
quantile = self.__getQuantile()
|
|
312
|
+
for node in self.design_surface.Nodes:
|
|
313
|
+
|
|
314
|
+
if self.penalty_method == "exterior":
|
|
315
|
+
g_i = __calculateNodalValueExterior(self, node, quantile)
|
|
316
|
+
elif self.penalty_method == "extended_interior":
|
|
317
|
+
g_i = __calculateNodalValueExtendedInterior(self, node, quantile)
|
|
318
|
+
g_tol_i = __calculateIntegralToleranceOfNode(self, node, quantile)
|
|
319
|
+
integral_tol += g_tol_i
|
|
320
|
+
|
|
321
|
+
total_integral += g_i
|
|
322
|
+
|
|
323
|
+
if self.penalty_method == "extended_interior":
|
|
324
|
+
g = total_integral - integral_tol
|
|
325
|
+
|
|
326
|
+
elif self.penalty_method == "exterior":
|
|
327
|
+
g = total_integral
|
|
328
|
+
|
|
329
|
+
Kratos.Logger.PrintInfo("ShapeFractionOptimization", "Time needed for calculating the penalty response value = ",round(timer.time() - startTime,2),"s")
|
|
330
|
+
|
|
331
|
+
return g
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# --------------------------------------------------------------------------
|
|
335
|
+
def __computePenaltyGradient(self, penalty_value):
|
|
336
|
+
|
|
337
|
+
def __calculateNodalGradient(self, node, quantile):
|
|
338
|
+
shape_change = node.GetSolutionStepValue(KSO.SHAPE_CHANGE)
|
|
339
|
+
norm = cm.Norm2(shape_change)
|
|
340
|
+
|
|
341
|
+
if norm > self.frac_tolerance:
|
|
342
|
+
if norm > quantile:
|
|
343
|
+
return [0.0, 0.0, 0.0]
|
|
344
|
+
else:
|
|
345
|
+
return [
|
|
346
|
+
shape_change[0] / norm,
|
|
347
|
+
shape_change[1] / norm,
|
|
348
|
+
shape_change[2] / norm
|
|
349
|
+
]
|
|
350
|
+
else:
|
|
351
|
+
return [0.0, 0.0, 0.0]
|
|
352
|
+
|
|
353
|
+
Kratos.Logger.PrintInfo("ShapeFractionOptimization", "Starting calculation of penalty gradient:")
|
|
354
|
+
|
|
355
|
+
startTime = timer.time()
|
|
356
|
+
penalty_gradient = Kratos.Vector(3*len(self.design_surface.Nodes))
|
|
357
|
+
quantile = self.__getQuantile()
|
|
358
|
+
|
|
359
|
+
for i, node in enumerate(self.design_surface.Nodes):
|
|
360
|
+
if self.penalty_method == "exterior":
|
|
361
|
+
if penalty_value > 0.0:
|
|
362
|
+
gradient_i = __calculateNodalGradient(self, node, quantile)
|
|
363
|
+
else:
|
|
364
|
+
gradient_i = [0.0, 0.0, 0.0]
|
|
365
|
+
|
|
366
|
+
elif self.penalty_method == "extended_interior":
|
|
367
|
+
gradient_i = __calculateNodalGradient(self, node, quantile)
|
|
368
|
+
if penalty_value > self.epsilon:
|
|
369
|
+
gradient_i[0] /= self.epsilon**2
|
|
370
|
+
gradient_i[1] /= self.epsilon**2
|
|
371
|
+
gradient_i[2] /= self.epsilon**2
|
|
372
|
+
else:
|
|
373
|
+
gradient_i[0] /= penalty_value**2
|
|
374
|
+
gradient_i[1] /= penalty_value**2
|
|
375
|
+
gradient_i[2] /= penalty_value**2
|
|
376
|
+
|
|
377
|
+
penalty_gradient[3*i:3*i+3] = gradient_i
|
|
378
|
+
|
|
379
|
+
Kratos.Logger.PrintInfo("ShapeFractionOptimization", "Time needed for calculating the penalty gradient = ",round(timer.time() - startTime,2),"s")
|
|
380
|
+
|
|
381
|
+
return penalty_gradient
|
|
382
|
+
|
|
383
|
+
def __getQuantile(self):
|
|
384
|
+
|
|
385
|
+
nodal_variable = Kratos.KratosGlobals.GetVariable("SHAPE_CHANGE")
|
|
386
|
+
shape_change = ReadNodalVariableToList(self.design_surface, nodal_variable)
|
|
387
|
+
|
|
388
|
+
shape_change_norm = []
|
|
389
|
+
|
|
390
|
+
for i in range(int(len(shape_change)/3)):
|
|
391
|
+
shape_change_norm.append(cm.Norm2(shape_change[3*i:3*i+3]))
|
|
392
|
+
|
|
393
|
+
shape_change_norm.sort()
|
|
394
|
+
|
|
395
|
+
index = round(len(shape_change_norm)*(1-self.max_shape_fraction))
|
|
396
|
+
|
|
397
|
+
quantile = shape_change_norm[index]
|
|
398
|
+
|
|
399
|
+
Kratos.Logger.PrintInfo(f"ShapeFractionOptimization: Shape change quantile {(1-self.max_shape_fraction)} = {quantile}")
|
|
400
|
+
|
|
401
|
+
return quantile
|
|
402
|
+
|
|
403
|
+
def __computeShapeFraction(self):
|
|
404
|
+
|
|
405
|
+
n_active = 0
|
|
406
|
+
for node in self.design_surface.Nodes:
|
|
407
|
+
shape_change = node.GetSolutionStepValue(KSO.SHAPE_CHANGE)
|
|
408
|
+
norm = cm.Norm2(shape_change)
|
|
409
|
+
|
|
410
|
+
if norm > self.frac_tolerance:
|
|
411
|
+
n_active += 1
|
|
412
|
+
|
|
413
|
+
total_nodes = len(self.design_surface.Nodes)
|
|
414
|
+
|
|
415
|
+
shape_fraction = n_active / total_nodes
|
|
416
|
+
|
|
417
|
+
return shape_fraction
|
|
418
|
+
|
|
419
|
+
def __computePseudoObjectiveValue(self, objective_value):
|
|
420
|
+
if self.penalty_method == "exterior":
|
|
421
|
+
pseudo_objective_value = objective_value + self.penalty_factor * max(0, self.penalty_value)**2
|
|
422
|
+
elif self.penalty_method == "extended_interior":
|
|
423
|
+
pseudo_objective_value = objective_value + self.penalty_factor * self.penalty_value
|
|
424
|
+
|
|
425
|
+
return pseudo_objective_value
|
|
426
|
+
|
|
427
|
+
# --------------------------------------------------------------------------
|
|
428
|
+
def __IncrementInnerStep(self):
|
|
429
|
+
if self.penalty_method == "exterior" and self.penalty_value > 0:
|
|
430
|
+
self.inner_step += 1
|
|
431
|
+
elif self.penalty_method == "extended_interior":
|
|
432
|
+
self.inner_step += 1
|
|
433
|
+
|
|
434
|
+
# --------------------------------------------------------------------------
|
|
435
|
+
def __checkInnerConvergence(self, objective_value):
|
|
436
|
+
|
|
437
|
+
tolerance = self.inner_tolerance
|
|
438
|
+
|
|
439
|
+
is_converged = False
|
|
440
|
+
if self.previous_objective_value is None:
|
|
441
|
+
self.reference_objective_value = objective_value
|
|
442
|
+
self.previous_objective_value = objective_value
|
|
443
|
+
self.relative_change = 0
|
|
444
|
+
elif self.inner_step >= self.max_inner_steps:
|
|
445
|
+
self.previous_objective_value = objective_value
|
|
446
|
+
is_converged = True
|
|
447
|
+
else:
|
|
448
|
+
self.relative_change = (objective_value - self.previous_objective_value) / self.reference_objective_value
|
|
449
|
+
if abs(self.relative_change) < tolerance:
|
|
450
|
+
is_converged = True
|
|
451
|
+
|
|
452
|
+
self.previous_objective_value = objective_value
|
|
453
|
+
|
|
454
|
+
if is_converged:
|
|
455
|
+
self.inner_step = 0
|
|
456
|
+
self.reference_objective_value = objective_value
|
|
457
|
+
|
|
458
|
+
return is_converged
|
|
459
|
+
|
|
460
|
+
# --------------------------------------------------------------------------
|
|
461
|
+
def __SetUpInitialPenaltyFactor(self, objective_value, objective_gradient_norm):
|
|
462
|
+
if self.penalty_method == "exterior":
|
|
463
|
+
# compute penalty gradients for start penalty factor
|
|
464
|
+
penalty_gradient = self.__computePenaltyGradient(self.penalty_value)
|
|
465
|
+
penalty_gradient_norm = penalty_gradient.norm_2()
|
|
466
|
+
if penalty_gradient_norm > 0:
|
|
467
|
+
self.penalty_factor = self.initial_penalty_factor * objective_gradient_norm / penalty_gradient_norm
|
|
468
|
+
elif self.penalty_method == "extended_interior":
|
|
469
|
+
self.penalty_factor = self.initial_penalty_factor * abs(objective_value / self.penalty_value)
|
|
470
|
+
a = 0.5
|
|
471
|
+
self.C = - self.epsilon / (self.penalty_factor**a)
|
|
472
|
+
|
|
473
|
+
# --------------------------------------------------------------------------
|
|
474
|
+
def __updatePenaltyFactor(self):
|
|
475
|
+
|
|
476
|
+
if self.penalty_method == "exterior" and self.penalty_value > 0.0:
|
|
477
|
+
self.penalty_factor *= self.gamma # increase penalty factor by 25%
|
|
478
|
+
|
|
479
|
+
elif self.penalty_method == "extended_interior":
|
|
480
|
+
self.penalty_factor *= self.gamma # increase penalty factor by 25%
|
|
481
|
+
a = 0.5
|
|
482
|
+
self.epsilon = - self.C * (self.penalty_factor**a)
|
|
483
|
+
|
|
484
|
+
Kratos.Logger.PrintInfo("ShapeOpt", f"New penalty factor = {self.penalty_factor}")
|
|
485
|
+
|
|
486
|
+
# --------------------------------------------------------------------------
|
|
487
|
+
def __computeShapeUpdate(self):
|
|
488
|
+
self.mapper.Update()
|
|
489
|
+
self.mapper.InverseMap(KSO.DPF1DX, KSO.DF1DX_MAPPED)
|
|
490
|
+
|
|
491
|
+
for constraint in self.constraints.values():
|
|
492
|
+
con_id = constraint["identifier"].GetString()
|
|
493
|
+
gradient_variable = self.constraint_gradient_variables[con_id]["gradient"]
|
|
494
|
+
mapped_gradient_variable = self.constraint_gradient_variables[con_id]["mapped_gradient"]
|
|
495
|
+
self.mapper.InverseMap(gradient_variable, mapped_gradient_variable)
|
|
496
|
+
|
|
497
|
+
self.__computeControlPointUpdate()
|
|
498
|
+
|
|
499
|
+
self.mapper.Map(KSO.CONTROL_POINT_UPDATE, KSO.SHAPE_UPDATE)
|
|
500
|
+
self.model_part_controller.DampNodalUpdateVariableIfSpecified(KSO.SHAPE_UPDATE)
|
|
501
|
+
|
|
502
|
+
# --------------------------------------------------------------------------
|
|
503
|
+
def __computeControlPointUpdate(self):
|
|
504
|
+
"""adapted from https://msulaiman.org/onewebmedia/GradProj_2.pdf"""
|
|
505
|
+
g_a, g_a_variables = self.__getActiveConstraints()
|
|
506
|
+
|
|
507
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Assemble vector of objective gradient.")
|
|
508
|
+
nabla_f = Kratos.Vector()
|
|
509
|
+
self.optimization_utilities.AssembleVector(self.design_surface, nabla_f, KSO.DF1DX_MAPPED)
|
|
510
|
+
|
|
511
|
+
s = Kratos.Vector()
|
|
512
|
+
|
|
513
|
+
if len(g_a) == 0:
|
|
514
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "No constraints active, use negative objective gradient as search direction.")
|
|
515
|
+
s = nabla_f * (-1.0)
|
|
516
|
+
s *= self.step_size / s.norm_inf()
|
|
517
|
+
self.optimization_utilities.AssignVectorToVariable(self.design_surface, s, KSO.SEARCH_DIRECTION)
|
|
518
|
+
self.optimization_utilities.AssignVectorToVariable(self.design_surface, [0.0]*len(s), KSO.CORRECTION)
|
|
519
|
+
self.optimization_utilities.AssignVectorToVariable(self.design_surface, s, KSO.CONTROL_POINT_UPDATE)
|
|
520
|
+
return
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Assemble matrix of constraint gradient.")
|
|
524
|
+
N = Kratos.Matrix()
|
|
525
|
+
self.optimization_utilities.AssembleMatrix(self.design_surface, N, g_a_variables)
|
|
526
|
+
|
|
527
|
+
settings = Kratos.Parameters('{ "solver_type" : "LinearSolversApplication.dense_col_piv_householder_qr" }')
|
|
528
|
+
solver = dense_linear_solver_factory.ConstructSolver(settings)
|
|
529
|
+
|
|
530
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Calculate projected search direction and correction.")
|
|
531
|
+
c = Kratos.Vector()
|
|
532
|
+
self.optimization_utilities.CalculateProjectedSearchDirectionAndCorrection(
|
|
533
|
+
nabla_f,
|
|
534
|
+
N,
|
|
535
|
+
g_a,
|
|
536
|
+
solver,
|
|
537
|
+
s,
|
|
538
|
+
c)
|
|
539
|
+
|
|
540
|
+
if c.norm_inf() != 0.0:
|
|
541
|
+
if c.norm_inf() <= self.max_correction_share * self.step_size:
|
|
542
|
+
delta = self.step_size - c.norm_inf()
|
|
543
|
+
s *= delta/s.norm_inf()
|
|
544
|
+
else:
|
|
545
|
+
Kratos.Logger.PrintWarning("ShapeOpt", f"Correction is scaled down from {c.norm_inf()} to {self.max_correction_share * self.step_size}.")
|
|
546
|
+
c *= self.max_correction_share * self.step_size / c.norm_inf()
|
|
547
|
+
s *= (1.0 - self.max_correction_share) * self.step_size / s.norm_inf()
|
|
548
|
+
else:
|
|
549
|
+
s *= self.step_size / s.norm_inf()
|
|
550
|
+
|
|
551
|
+
self.optimization_utilities.AssignVectorToVariable(self.design_surface, s, KSO.SEARCH_DIRECTION)
|
|
552
|
+
self.optimization_utilities.AssignVectorToVariable(self.design_surface, c, KSO.CORRECTION)
|
|
553
|
+
self.optimization_utilities.AssignVectorToVariable(self.design_surface, s+c, KSO.CONTROL_POINT_UPDATE)
|
|
554
|
+
|
|
555
|
+
# --------------------------------------------------------------------------
|
|
556
|
+
def __getActiveConstraints(self):
|
|
557
|
+
active_constraint_values = []
|
|
558
|
+
active_constraint_variables = []
|
|
559
|
+
|
|
560
|
+
for constraint in self.constraints.values():
|
|
561
|
+
if self.__isConstraintActive(constraint):
|
|
562
|
+
identifier = constraint["identifier"].GetString()
|
|
563
|
+
constraint_value = self.communicator.getStandardizedValue(identifier)
|
|
564
|
+
active_constraint_values.append(constraint_value)
|
|
565
|
+
active_constraint_variables.append(
|
|
566
|
+
self.constraint_gradient_variables[identifier]["mapped_gradient"])
|
|
567
|
+
|
|
568
|
+
return active_constraint_values, active_constraint_variables
|
|
569
|
+
|
|
570
|
+
# --------------------------------------------------------------------------
|
|
571
|
+
def __isConstraintActive(self, constraint):
|
|
572
|
+
identifier = constraint["identifier"].GetString()
|
|
573
|
+
constraint_value = self.communicator.getStandardizedValue(identifier)
|
|
574
|
+
if constraint["type"].GetString() == "=" or constraint_value >= 0:
|
|
575
|
+
gradient_norm = self.optimization_utilities.ComputeMaxNormOfNodalVariable(
|
|
576
|
+
self.design_surface, self.constraint_gradient_variables[identifier]["mapped_gradient"]
|
|
577
|
+
)
|
|
578
|
+
if math.isclose(gradient_norm, 0.0, abs_tol=1e-16):
|
|
579
|
+
Kratos.Logger.PrintWarning("ShapeOpt", f"Gradient for constraint {identifier} is 0.0 - will not be considered!")
|
|
580
|
+
return False
|
|
581
|
+
return True
|
|
582
|
+
else:
|
|
583
|
+
return False
|
|
584
|
+
|
|
585
|
+
# --------------------------------------------------------------------------
|
|
586
|
+
def __logCurrentOptimizationStep(self):
|
|
587
|
+
additional_values_to_log = {}
|
|
588
|
+
additional_values_to_log["penalty_value"] = self.penalty_value
|
|
589
|
+
additional_values_to_log["penalty_factor"] = self.penalty_factor
|
|
590
|
+
additional_values_to_log["f_p"] = self.pseudo_objective_value
|
|
591
|
+
additional_values_to_log["df_rel_p"] = self.relative_change
|
|
592
|
+
additional_values_to_log["shape_fraction"] = self.__computeShapeFraction()
|
|
593
|
+
additional_values_to_log["step_size"] = self.step_size
|
|
594
|
+
additional_values_to_log["inf_norm_s"] = self.optimization_utilities.ComputeMaxNormOfNodalVariable(self.design_surface, KSO.SEARCH_DIRECTION)
|
|
595
|
+
additional_values_to_log["inf_norm_c"] = self.optimization_utilities.ComputeMaxNormOfNodalVariable(self.design_surface, KSO.CORRECTION)
|
|
596
|
+
self.data_logger.LogCurrentValues(self.optimization_iteration, additional_values_to_log)
|
|
597
|
+
self.data_logger.LogCurrentDesign(self.optimization_iteration)
|
|
598
|
+
|
|
599
|
+
# --------------------------------------------------------------------------
|
|
600
|
+
def __isAlgorithmConverged(self):
|
|
601
|
+
|
|
602
|
+
if self.optimization_iteration > 1 :
|
|
603
|
+
|
|
604
|
+
# Check if maximum iterations were reached
|
|
605
|
+
if self.optimization_iteration == self.max_iterations:
|
|
606
|
+
Kratos.Logger.Print("")
|
|
607
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Maximal iterations of optimization problem reached!")
|
|
608
|
+
return True
|
|
609
|
+
|
|
610
|
+
# Check for relative tolerance
|
|
611
|
+
relative_change_of_objective_value = self.data_logger.GetValues("rel_change_objective")[self.optimization_iteration]
|
|
612
|
+
if abs(relative_change_of_objective_value) < self.relative_tolerance:
|
|
613
|
+
Kratos.Logger.Print("")
|
|
614
|
+
Kratos.Logger.PrintInfo("ShapeOpt", "Optimization problem converged within a relative objective tolerance of ",self.relative_tolerance,"%.")
|
|
615
|
+
return True
|
|
616
|
+
|
|
617
|
+
# --------------------------------------------------------------------------
|
|
618
|
+
def __determineAbsoluteChanges(self):
|
|
619
|
+
self.optimization_utilities.AddFirstVariableToSecondVariable(self.design_surface, KSO.CONTROL_POINT_UPDATE, KSO.CONTROL_POINT_CHANGE)
|
|
620
|
+
self.optimization_utilities.AddFirstVariableToSecondVariable(self.design_surface, KSO.SHAPE_UPDATE, KSO.SHAPE_CHANGE)
|
|
621
|
+
|
|
622
|
+
# ==============================================================================
|