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
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# KratosShapeOptimizationApplication
|
|
3
|
+
#
|
|
4
|
+
# License: BSD License
|
|
5
|
+
# license: ShapeOptimizationApplication/license.txt
|
|
6
|
+
#
|
|
7
|
+
# Main authors: Baumgaertner Daniel, https://github.com/dbaumgaertner
|
|
8
|
+
# Geiser Armin, https://github.com/armingeiser
|
|
9
|
+
#
|
|
10
|
+
# ==============================================================================
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Kratos Core and Apps
|
|
14
|
+
import KratosMultiphysics as KM
|
|
15
|
+
import KratosMultiphysics.ShapeOptimizationApplication as KSO
|
|
16
|
+
|
|
17
|
+
# Additional imports
|
|
18
|
+
from KratosMultiphysics.ShapeOptimizationApplication.algorithms.algorithm_base import OptimizationAlgorithm
|
|
19
|
+
from KratosMultiphysics.ShapeOptimizationApplication import mapper_factory
|
|
20
|
+
from KratosMultiphysics.ShapeOptimizationApplication.loggers import data_logger_factory
|
|
21
|
+
from KratosMultiphysics.ShapeOptimizationApplication.utilities.custom_timer import Timer
|
|
22
|
+
from KratosMultiphysics.ShapeOptimizationApplication.utilities.custom_variable_utilities import WriteDictionaryDataOnNodalVariable
|
|
23
|
+
|
|
24
|
+
# ==============================================================================
|
|
25
|
+
class AlgorithmSteepestDescent(OptimizationAlgorithm):
|
|
26
|
+
# --------------------------------------------------------------------------
|
|
27
|
+
def __init__(self, optimization_settings, analyzer, communicator, model_part_controller):
|
|
28
|
+
default_algorithm_settings = KM.Parameters("""
|
|
29
|
+
{
|
|
30
|
+
"name" : "steepest_descent",
|
|
31
|
+
"max_iterations" : 100,
|
|
32
|
+
"relative_tolerance" : 1e-3,
|
|
33
|
+
"gradient_tolerance" : 1e-5,
|
|
34
|
+
"line_search" : {
|
|
35
|
+
"line_search_type" : "manual_stepping",
|
|
36
|
+
"normalize_search_direction" : true,
|
|
37
|
+
"step_size" : 1.0,
|
|
38
|
+
"estimation_tolerance" : 0.1,
|
|
39
|
+
"increase_factor" : 1.1,
|
|
40
|
+
"max_increase_factor" : 10.0
|
|
41
|
+
}
|
|
42
|
+
}""")
|
|
43
|
+
self.algorithm_settings = optimization_settings["optimization_algorithm"]
|
|
44
|
+
self.algorithm_settings.RecursivelyValidateAndAssignDefaults(default_algorithm_settings)
|
|
45
|
+
|
|
46
|
+
self.optimization_settings = optimization_settings
|
|
47
|
+
self.mapper_settings = optimization_settings["design_variables"]["filter"]
|
|
48
|
+
|
|
49
|
+
self.analyzer = analyzer
|
|
50
|
+
self.communicator = communicator
|
|
51
|
+
self.model_part_controller = model_part_controller
|
|
52
|
+
|
|
53
|
+
self.design_surface = None
|
|
54
|
+
self.mapper = None
|
|
55
|
+
self.data_logger = None
|
|
56
|
+
self.optimization_utilities = None
|
|
57
|
+
|
|
58
|
+
self.objectives = optimization_settings["objectives"]
|
|
59
|
+
self.constraints = optimization_settings["constraints"]
|
|
60
|
+
|
|
61
|
+
self.previos_objective_value = None
|
|
62
|
+
|
|
63
|
+
self.max_iterations = self.algorithm_settings["max_iterations"].GetInt() + 1
|
|
64
|
+
self.relative_tolerance = self.algorithm_settings["relative_tolerance"].GetDouble()
|
|
65
|
+
self.gradient_tolerance = self.algorithm_settings["gradient_tolerance"].GetDouble()
|
|
66
|
+
self.line_search_type = self.algorithm_settings["line_search"]["line_search_type"].GetString()
|
|
67
|
+
self.estimation_tolerance = self.algorithm_settings["line_search"]["estimation_tolerance"].GetDouble()
|
|
68
|
+
self.step_size = self.algorithm_settings["line_search"]["step_size"].GetDouble()
|
|
69
|
+
self.increase_factor = self.algorithm_settings["line_search"]["increase_factor"].GetDouble()
|
|
70
|
+
self.max_step_size = self.step_size*self.algorithm_settings["line_search"]["max_increase_factor"].GetDouble()
|
|
71
|
+
|
|
72
|
+
self.optimization_model_part = model_part_controller.GetOptimizationModelPart()
|
|
73
|
+
self.optimization_model_part.AddNodalSolutionStepVariable(KSO.SEARCH_DIRECTION)
|
|
74
|
+
|
|
75
|
+
# --------------------------------------------------------------------------
|
|
76
|
+
def CheckApplicability(self):
|
|
77
|
+
if self.objectives.size() > 1:
|
|
78
|
+
raise RuntimeError("Steepest descent algorithm only supports one objective function!")
|
|
79
|
+
if self.constraints.size() > 0:
|
|
80
|
+
raise RuntimeError("Steepest descent algorithm does not allow for any constraints!")
|
|
81
|
+
|
|
82
|
+
# --------------------------------------------------------------------------
|
|
83
|
+
def InitializeOptimizationLoop(self):
|
|
84
|
+
self.model_part_controller.Initialize()
|
|
85
|
+
|
|
86
|
+
self.analyzer.InitializeBeforeOptimizationLoop()
|
|
87
|
+
|
|
88
|
+
self.design_surface = self.model_part_controller.GetDesignSurface()
|
|
89
|
+
|
|
90
|
+
self.mapper = mapper_factory.CreateMapper(self.design_surface, self.design_surface, self.mapper_settings)
|
|
91
|
+
self.mapper.Initialize()
|
|
92
|
+
|
|
93
|
+
self.model_part_controller.InitializeDamping()
|
|
94
|
+
self.data_logger = data_logger_factory.CreateDataLogger(self.model_part_controller, self.communicator, self.optimization_settings)
|
|
95
|
+
self.data_logger.InitializeDataLogging()
|
|
96
|
+
|
|
97
|
+
self.optimization_utilities = KSO.OptimizationUtilities
|
|
98
|
+
|
|
99
|
+
# --------------------------------------------------------------------------
|
|
100
|
+
def RunOptimizationLoop(self):
|
|
101
|
+
timer = Timer()
|
|
102
|
+
timer.StartTimer()
|
|
103
|
+
|
|
104
|
+
for self.optimization_iteration in range(1,self.max_iterations):
|
|
105
|
+
KM.Logger.Print("")
|
|
106
|
+
KM.Logger.Print("===============================================================================")
|
|
107
|
+
KM.Logger.PrintInfo("ShapeOpt", "",timer.GetTimeStamp(), ": Starting optimization iteration ",self.optimization_iteration)
|
|
108
|
+
KM.Logger.Print("===============================================================================\n")
|
|
109
|
+
|
|
110
|
+
timer.StartNewLap()
|
|
111
|
+
|
|
112
|
+
self.__initializeNewShape()
|
|
113
|
+
|
|
114
|
+
self.__analyzeShape()
|
|
115
|
+
|
|
116
|
+
if self.line_search_type == "adaptive_stepping" and self.optimization_iteration > 1:
|
|
117
|
+
self.__adjustStepSize()
|
|
118
|
+
|
|
119
|
+
self.__computeShapeUpdate()
|
|
120
|
+
|
|
121
|
+
self.__logCurrentOptimizationStep()
|
|
122
|
+
|
|
123
|
+
KM.Logger.Print("")
|
|
124
|
+
KM.Logger.PrintInfo("ShapeOpt", "Time needed for current optimization step = ", timer.GetLapTime(), "s")
|
|
125
|
+
KM.Logger.PrintInfo("ShapeOpt", "Time needed for total optimization so far = ", timer.GetTotalTime(), "s")
|
|
126
|
+
|
|
127
|
+
if self.__isAlgorithmConverged():
|
|
128
|
+
break
|
|
129
|
+
else:
|
|
130
|
+
self.__determineAbsoluteChanges()
|
|
131
|
+
|
|
132
|
+
# --------------------------------------------------------------------------
|
|
133
|
+
def FinalizeOptimizationLoop(self):
|
|
134
|
+
self.data_logger.FinalizeDataLogging()
|
|
135
|
+
self.analyzer.FinalizeAfterOptimizationLoop()
|
|
136
|
+
|
|
137
|
+
# --------------------------------------------------------------------------
|
|
138
|
+
def __initializeNewShape(self):
|
|
139
|
+
self.model_part_controller.UpdateTimeStep(self.optimization_iteration)
|
|
140
|
+
self.model_part_controller.UpdateMeshAccordingInputVariable(KSO.SHAPE_UPDATE)
|
|
141
|
+
self.model_part_controller.SetReferenceMeshToMesh()
|
|
142
|
+
|
|
143
|
+
# --------------------------------------------------------------------------
|
|
144
|
+
def __analyzeShape(self):
|
|
145
|
+
self.communicator.initializeCommunication()
|
|
146
|
+
self.communicator.requestValueOf(self.objectives[0]["identifier"].GetString())
|
|
147
|
+
self.communicator.requestGradientOf(self.objectives[0]["identifier"].GetString())
|
|
148
|
+
|
|
149
|
+
self.analyzer.AnalyzeDesignAndReportToCommunicator(self.optimization_model_part, self.optimization_iteration, self.communicator)
|
|
150
|
+
|
|
151
|
+
objGradientDict = self.communicator.getStandardizedGradient(self.objectives[0]["identifier"].GetString())
|
|
152
|
+
WriteDictionaryDataOnNodalVariable(objGradientDict, self.optimization_model_part, KSO.DF1DX)
|
|
153
|
+
|
|
154
|
+
if self.objectives[0]["project_gradient_on_surface_normals"].GetBool():
|
|
155
|
+
self.model_part_controller.ComputeUnitSurfaceNormals()
|
|
156
|
+
self.model_part_controller.ProjectNodalVariableOnUnitSurfaceNormals(KSO.DF1DX)
|
|
157
|
+
|
|
158
|
+
self.model_part_controller.DampNodalSensitivityVariableIfSpecified(KSO.DF1DX)
|
|
159
|
+
|
|
160
|
+
# --------------------------------------------------------------------------
|
|
161
|
+
def __adjustStepSize(self):
|
|
162
|
+
current_a = self.step_size
|
|
163
|
+
|
|
164
|
+
# Compare actual and estimated improvement using linear information from the previos step
|
|
165
|
+
dfda1 = 0.0
|
|
166
|
+
for node in self.design_surface.Nodes:
|
|
167
|
+
# The following variables are not yet updated and therefore contain the information from the previos step
|
|
168
|
+
s1 = node.GetSolutionStepValue(KSO.SEARCH_DIRECTION)
|
|
169
|
+
dfds1 = node.GetSolutionStepValue(KSO.DF1DX_MAPPED)
|
|
170
|
+
dfda1 += s1[0]*dfds1[0] + s1[1]*dfds1[1] + s1[2]*dfds1[2]
|
|
171
|
+
|
|
172
|
+
f2 = self.communicator.getStandardizedValue(self.objectives[0]["identifier"].GetString())
|
|
173
|
+
f1 = self.previos_objective_value
|
|
174
|
+
|
|
175
|
+
df_actual = f2 - f1
|
|
176
|
+
df_estimated = current_a*dfda1
|
|
177
|
+
|
|
178
|
+
# Adjust step size if necessary
|
|
179
|
+
if f2 < f1:
|
|
180
|
+
estimation_error = (df_actual-df_estimated)/df_actual
|
|
181
|
+
|
|
182
|
+
# Increase step size if estimation based on linear extrapolation matches the actual improvement within a specified tolerance
|
|
183
|
+
if estimation_error < self.estimation_tolerance:
|
|
184
|
+
new_a = min(current_a*self.increase_factor, self.max_step_size)
|
|
185
|
+
|
|
186
|
+
# Leave step size unchanged if a nonliner change in the objective is observed but still a descent direction is obtained
|
|
187
|
+
else:
|
|
188
|
+
new_a = current_a
|
|
189
|
+
else:
|
|
190
|
+
# Search approximation of optimal step using interpolation
|
|
191
|
+
a = current_a
|
|
192
|
+
corrected_step_size = - 0.5 * dfda1 * a**2 / (f2 - f1 - dfda1 * a )
|
|
193
|
+
|
|
194
|
+
# Starting from the new design, and assuming an opposite gradient direction, the step size to the approximated optimum behaves reciprocal
|
|
195
|
+
new_a = current_a-corrected_step_size
|
|
196
|
+
|
|
197
|
+
self.step_size = new_a
|
|
198
|
+
|
|
199
|
+
# --------------------------------------------------------------------------
|
|
200
|
+
def __computeShapeUpdate(self):
|
|
201
|
+
self.mapper.Update()
|
|
202
|
+
self.mapper.InverseMap(KSO.DF1DX, KSO.DF1DX_MAPPED)
|
|
203
|
+
|
|
204
|
+
self.optimization_utilities.ComputeSearchDirectionSteepestDescent(self.design_surface)
|
|
205
|
+
normalize = self.algorithm_settings["line_search"]["normalize_search_direction"].GetBool()
|
|
206
|
+
self.optimization_utilities.ComputeControlPointUpdate(self.design_surface, self.step_size, normalize)
|
|
207
|
+
|
|
208
|
+
self.mapper.Map(KSO.CONTROL_POINT_UPDATE, KSO.SHAPE_UPDATE)
|
|
209
|
+
self.model_part_controller.DampNodalUpdateVariableIfSpecified(KSO.SHAPE_UPDATE)
|
|
210
|
+
|
|
211
|
+
# --------------------------------------------------------------------------
|
|
212
|
+
def __logCurrentOptimizationStep(self):
|
|
213
|
+
|
|
214
|
+
self.previos_objective_value = self.communicator.getStandardizedValue(self.objectives[0]["identifier"].GetString())
|
|
215
|
+
self.norm_objective_gradient = self.optimization_utilities.ComputeL2NormOfNodalVariable(self.design_surface, KSO.DF1DX_MAPPED)
|
|
216
|
+
|
|
217
|
+
additional_values_to_log = {}
|
|
218
|
+
additional_values_to_log["step_size"] = self.step_size
|
|
219
|
+
additional_values_to_log["norm_objective_gradient"] = self.norm_objective_gradient
|
|
220
|
+
self.data_logger.LogSensitivityHeatmap(self.optimization_iteration, self.mapper)
|
|
221
|
+
self.data_logger.LogCurrentValues(self.optimization_iteration, additional_values_to_log)
|
|
222
|
+
self.data_logger.LogCurrentDesign(self.optimization_iteration)
|
|
223
|
+
|
|
224
|
+
# --------------------------------------------------------------------------
|
|
225
|
+
def __isAlgorithmConverged(self):
|
|
226
|
+
|
|
227
|
+
if self.optimization_iteration > 1 :
|
|
228
|
+
# Check if maximum iterations were reached
|
|
229
|
+
if self.optimization_iteration == self.max_iterations:
|
|
230
|
+
KM.Logger.Print("")
|
|
231
|
+
KM.Logger.PrintInfo("ShapeOpt", "Maximal iterations of optimization problem reached!")
|
|
232
|
+
return True
|
|
233
|
+
|
|
234
|
+
# Check gradient norm
|
|
235
|
+
if self.optimization_iteration == 2:
|
|
236
|
+
self.initial_norm_objective_gradient = self.norm_objective_gradient
|
|
237
|
+
else:
|
|
238
|
+
if self.norm_objective_gradient < self.gradient_tolerance*self.initial_norm_objective_gradient:
|
|
239
|
+
KM.Logger.Print("")
|
|
240
|
+
KM.Logger.PrintInfo("ShapeOpt", "Optimization problem converged as gradient norm reached specified tolerance of ",self.gradient_tolerance)
|
|
241
|
+
return True
|
|
242
|
+
|
|
243
|
+
# Check for relative tolerance
|
|
244
|
+
relative_change_of_objective_value = self.data_logger.GetValues("rel_change_objective")[self.optimization_iteration]
|
|
245
|
+
if abs(relative_change_of_objective_value) < self.relative_tolerance:
|
|
246
|
+
KM.Logger.Print("")
|
|
247
|
+
KM.Logger.PrintInfo("ShapeOpt", "Optimization problem converged within a relative objective tolerance of ",self.relative_tolerance,"%.")
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
# --------------------------------------------------------------------------
|
|
251
|
+
def __determineAbsoluteChanges(self):
|
|
252
|
+
self.optimization_utilities.AddFirstVariableToSecondVariable(self.design_surface, KSO.CONTROL_POINT_UPDATE, KSO.CONTROL_POINT_CHANGE)
|
|
253
|
+
self.optimization_utilities.AddFirstVariableToSecondVariable(self.design_surface, KSO.SHAPE_UPDATE, KSO.SHAPE_CHANGE)
|
|
254
|
+
|
|
255
|
+
# ==============================================================================
|