def __analyzeShape(self): self.communicator.initializeCommunication() self.communicator.requestValueOf(self.objectives[0]["identifier"].GetString()) self.communicator.requestGradientOf(self.objectives[0]["identifier"].GetString()) self.communicator.requestValueOf(self.constraints[0]["identifier"].GetString()) self.communicator.requestGradientOf(self.constraints[0]["identifier"].GetString()) self.analyzer.AnalyzeDesignAndReportToCommunicator(self.design_surface, self.optimization_iteration, self.communicator) objGradientDict = self.communicator.getStandardizedGradient(self.objectives[0]["identifier"].GetString()) conGradientDict = self.communicator.getStandardizedGradient(self.constraints[0]["identifier"].GetString()) WriteDictionaryDataOnNodalVariable(objGradientDict, self.optimization_model_part, DF1DX) WriteDictionaryDataOnNodalVariable(conGradientDict, self.optimization_model_part, DC1DX) if self.objectives[0]["project_gradient_on_surface_normals"].GetBool() or self.constraints[0]["project_gradient_on_surface_normals"].GetBool(): self.model_part_controller.ComputeUnitSurfaceNormals() if self.objectives[0]["project_gradient_on_surface_normals"].GetBool(): self.model_part_controller.ProjectNodalVariableOnUnitSurfaceNormals(DF1DX) if self.constraints[0]["project_gradient_on_surface_normals"].GetBool(): self.model_part_controller.ProjectNodalVariableOnUnitSurfaceNormals(DC1DX) self.model_part_controller.DampNodalVariableIfSpecified(DF1DX) self.model_part_controller.DampNodalVariableIfSpecified(DC1DX)
def __analyzeShape(self): self.Communicator.initializeCommunication() self.Communicator.requestValueOf(self.only_obj["identifier"].GetString()) self.Communicator.requestGradientOf(self.only_obj["identifier"].GetString()) self.Communicator.requestValueOf(self.only_con["identifier"].GetString()) self.Communicator.requestGradientOf(self.only_con["identifier"].GetString()) self.Analyzer.AnalyzeDesignAndReportToCommunicator(self.DesignSurface, self.optimizationIteration, self.Communicator) objGradientDict = self.Communicator.getStandardizedGradient(self.only_obj["identifier"].GetString()) conGradientDict = self.Communicator.getStandardizedGradient(self.only_con["identifier"].GetString()) WriteDictionaryDataOnNodalVariable(objGradientDict, self.OptimizationModelPart, DF1DX) WriteDictionaryDataOnNodalVariable(conGradientDict, self.OptimizationModelPart, DC1DX) if self.only_obj["project_gradient_on_surface_normals"].GetBool() or self.only_con["project_gradient_on_surface_normals"].GetBool(): self.GeometryUtilities.ComputeUnitSurfaceNormals() if self.only_obj["project_gradient_on_surface_normals"].GetBool(): self.GeometryUtilities.ProjectNodalVariableOnUnitSurfaceNormals(DF1DX) if self.only_con["project_gradient_on_surface_normals"].GetBool(): self.GeometryUtilities.ProjectNodalVariableOnUnitSurfaceNormals(DC1DX) if self.isDampingSpecified: self.DampingUtilities.DampNodalVariable(DF1DX) self.DampingUtilities.DampNodalVariable(DC1DX)
def RunOptimizationLoop(self): timer = Timer() timer.StartTimer() current_lambda = self.lambda0 penalty_scaling = self.penalty_scaling_0 penalty_factor = self.penalty_factor_0 total_iteration = 0 is_design_converged = False is_max_total_iterations_reached = False previos_L = None for outer_iteration in range(1, self.max_outer_iterations + 1): for inner_iteration in range(1, self.max_inner_iterations + 1): total_iteration += 1 timer.StartNewLap() print( "\n>=======================================================================================" ) print("> ", timer.GetTimeStamp(), ": Starting iteration ", outer_iteration, ".", inner_iteration, ".", total_iteration, "(outer . inner . total)") print( ">=======================================================================================\n" ) # Initialize new shape self.model_part_controller.UpdateTimeStep(total_iteration) for node in self.design_surface.Nodes: new_shape_change = node.GetSolutionStepValue( ALPHA_MAPPED) * node.GetValue( BEAD_DIRECTION) * self.bead_height node.SetSolutionStepValue(SHAPE_CHANGE, new_shape_change) self.model_part_controller.DampNodalVariableIfSpecified( SHAPE_CHANGE) for node in self.design_surface.Nodes: shape_update = node.GetSolutionStepValue( SHAPE_CHANGE, 0) - node.GetSolutionStepValue( SHAPE_CHANGE, 1) node.SetSolutionStepValue(SHAPE_UPDATE, shape_update) self.model_part_controller.UpdateMeshAccordingInputVariable( SHAPE_UPDATE) self.model_part_controller.SetReferenceMeshToMesh() # Analyze shape self.communicator.initializeCommunication() self.communicator.requestValueOf( self.objectives[0]["identifier"].GetString()) self.communicator.requestGradientOf( self.objectives[0]["identifier"].GetString()) self.analyzer.AnalyzeDesignAndReportToCommunicator( self.design_surface, total_iteration, self.communicator) objective_value = self.communicator.getStandardizedValue( self.objectives[0]["identifier"].GetString()) objGradientDict = self.communicator.getStandardizedGradient( self.objectives[0]["identifier"].GetString()) WriteDictionaryDataOnNodalVariable( objGradientDict, self.optimization_model_part, DF1DX) self.model_part_controller.DampNodalVariableIfSpecified(DF1DX) # Compute sensitivities w.r.t. scalar design variable alpha for node in self.design_surface.Nodes: raw_gradient = node.GetSolutionStepValue(DF1DX) bead_dir = node.GetValue(BEAD_DIRECTION) dF1dalpha_i = self.bead_height * ( raw_gradient[0] * bead_dir[0] + raw_gradient[1] * bead_dir[1] + raw_gradient[2] * bead_dir[2]) node.SetSolutionStepValue(DF1DALPHA, dF1dalpha_i) # Map gradient of objective self.mapper.InverseMap(DF1DALPHA, DF1DALPHA_MAPPED) # Compute scaling max_norm_objective_gradient = self.optimization_utilities.ComputeMaxNormOfNodalVariable( DF1DALPHA_MAPPED) if outer_iteration == 1 and inner_iteration == min( 3, self.max_inner_iterations): if self.bead_side == "positive" or self.bead_side == "negative": max_norm_penalty_gradient = 1.0 elif self.bead_side == "both": max_norm_penalty_gradient = 2.0 penalty_scaling = max_norm_objective_gradient / max_norm_penalty_gradient # Compute penalization term penalty_value = 0.0 if self.bead_side == "positive": for node in self.design_surface.Nodes: if not node.Is(BOUNDARY): alpha_i = node.GetSolutionStepValue(ALPHA) penalty_value += penalty_scaling * (alpha_i - alpha_i**2) penalty_gradient_i = penalty_scaling * ( 1 - 2 * alpha_i) node.SetSolutionStepValue(DPDALPHA, penalty_gradient_i) elif self.bead_side == "negative": for node in self.design_surface.Nodes: if not node.Is(BOUNDARY): alpha_i = node.GetSolutionStepValue(ALPHA) penalty_value += penalty_scaling * (-alpha_i - alpha_i**2) penalty_gradient_i = penalty_scaling * ( -1 - 2 * alpha_i) node.SetSolutionStepValue(DPDALPHA, penalty_gradient_i) elif self.bead_side == "both": for node in self.design_surface.Nodes: if not node.Is(BOUNDARY): alpha_i = node.GetSolutionStepValue(ALPHA) penalty_value += penalty_scaling * (-alpha_i**2 + 1) penalty_gradient_i = penalty_scaling * (-2 * alpha_i) node.SetSolutionStepValue(DPDALPHA, penalty_gradient_i) # Filter penalty term if specified if self.filter_penalty_term: self.mapper.InverseMap(DPDALPHA, DPDALPHA_MAPPED) # Compute value of Lagrange function L = objective_value + current_lambda * penalty_value + 0.5 * penalty_factor * penalty_value**2 if inner_iteration == 1: dL_relative = 0.0 else: dL_relative = 100 * (L / previos_L - 1) # Compute gradient of Lagrange function if self.filter_penalty_term: penalty_gradient_variable = DPDALPHA_MAPPED else: penalty_gradient_variable = DPDALPHA for node in self.design_surface.Nodes: dLdalpha_i = node.GetSolutionStepValue( DF1DALPHA_MAPPED ) + current_lambda * node.GetSolutionStepValue( penalty_gradient_variable) node.SetSolutionStepValue(DLDALPHA, dLdalpha_i) # Normalization using infinity norm dLdalpha_for_normalization = {} for node in self.design_surface.Nodes: nodal_alpha = node.GetSolutionStepValue(ALPHA) if nodal_alpha == self.lower_bound or nodal_alpha == self.upper_bound or node.Is( BOUNDARY): dLdalpha_for_normalization[node.Id] = 0.0 else: dLdalpha_for_normalization[ node.Id] = node.GetSolutionStepValue(DLDALPHA)**2 max_value = math.sqrt(max(dLdalpha_for_normalization.values())) if max_value == 0.0: max_value = 1.0 # Compute updated design variable for node in self.design_surface.Nodes: dalpha = -self.step_size * node.GetSolutionStepValue( DLDALPHA) / max_value alpha_new = node.GetSolutionStepValue(ALPHA) + dalpha # Enforce bounds alpha_new = max(alpha_new, self.lower_bound) alpha_new = min(alpha_new, self.upper_bound) # Enforce constraints if node.Is(BOUNDARY): alpha_new = 0.0 node.SetSolutionStepValue(ALPHA, alpha_new) alpha_new_vectorized = alpha_new * node.GetValue( BEAD_DIRECTION) node.SetSolutionStepValue(CONTROL_POINT_CHANGE, alpha_new_vectorized) # Map design variables self.mapper.Map(ALPHA, ALPHA_MAPPED) # Log current optimization step and store values for next iteration additional_values_to_log = {} additional_values_to_log[ "step_size"] = self.algorithm_settings["line_search"][ "step_size"].GetDouble() additional_values_to_log["outer_iteration"] = outer_iteration additional_values_to_log["inner_iteration"] = inner_iteration additional_values_to_log["lagrange_value"] = L additional_values_to_log[ "lagrange_value_relative_change"] = dL_relative additional_values_to_log["penalty_value"] = penalty_value additional_values_to_log["penalty_lambda"] = current_lambda additional_values_to_log["penalty_scaling"] = penalty_scaling additional_values_to_log["penalty_factor"] = penalty_factor additional_values_to_log[ "max_norm_objective_gradient"] = max_norm_objective_gradient self.data_logger.LogCurrentValues(total_iteration, additional_values_to_log) self.data_logger.LogCurrentDesign(total_iteration) previos_L = L # Convergence check of inner loop if total_iteration == self.max_total_iterations: is_max_total_iterations_reached = True break if inner_iteration >= self.min_inner_iterations and inner_iteration > 1: # In the first outer iteration, the constraint is not yet active and properly scaled. Therefore, the objective is used to check the relative improvement if outer_iteration == 1: if abs( self.data_logger.GetValue( "rel_change_obj", total_iteration) ) < self.inner_iteration_tolerance: break else: if abs(dL_relative) < self.inner_iteration_tolerance: break if penalty_value == 0.0: is_design_converged = True break print("\n> Time needed for current optimization step = ", timer.GetLapTime(), "s") print("> Time needed for total optimization so far = ", timer.GetTotalTime(), "s") # Compute penalty factor such that estimated Lagrange multiplier is obtained if outer_iteration == 1: penalty_factor = self.estimated_lagrange_multiplier / penalty_value # Update lambda current_lambda = current_lambda + penalty_factor * penalty_value print("\n> Time needed for current optimization step = ", timer.GetLapTime(), "s") print("> Time needed for total optimization so far = ", timer.GetTotalTime(), "s") # Check convergence of outer loop if outer_iteration == self.max_outer_iterations: print( "\n> Maximal outer iterations of optimization problem reached!" ) break if is_max_total_iterations_reached: print( "\n> Maximal total iterations of optimization problem reached!" ) break if is_design_converged: print( "\n> Update of design variables is zero. Optimization converged!" ) break
def __PostProcessGradientsObtainedFromAnalysis(self): # Compute surface normals if required if self.objectives[0]["project_gradient_on_surface_normals"].GetBool(): self.geometry_utilities.ComputeUnitSurfaceNormals() else: for itr in range(self.constraints.size()): if self.constraints[itr][ "project_gradient_on_surface_normals"]: self.geometry_utilities.ComputeUnitSurfaceNormals() # Process objective gradients obj = self.objectives[0] obj_id = obj["identifier"].GetString() obj_gradients_dict = self.communicator.getStandardizedGradient(obj_id) nodal_variable = KratosGlobals.GetVariable("DF1DX") WriteDictionaryDataOnNodalVariable(obj_gradients_dict, self.optimization_model_part, nodal_variable) # Projection on surface normals if obj["project_gradient_on_surface_normals"].GetBool(): self.geometry_utilities.ProjectNodalVariableOnUnitSurfaceNormals( nodal_variable) # Damping if self.is_damping_specified: self.damping_utilities.DampNodalVariable(nodal_variable) # Mapping nodal_variable_mapped = KratosGlobals.GetVariable("DF1DX_MAPPED") self.mapper.MapToDesignSpace(nodal_variable, nodal_variable_mapped) self.mapper.MapToGeometrySpace(nodal_variable_mapped, nodal_variable_mapped) # Damping if self.is_damping_specified: self.damping_utilities.DampNodalVariable(nodal_variable_mapped) # Process constraint gradients for itr in range(self.constraints.size()): con = self.constraints[itr] con_id = con["identifier"].GetString() eq_gradients_dict = self.communicator.getStandardizedGradient( con_id) nodal_variable = KratosGlobals.GetVariable("DC" + str(itr + 1) + "DX") WriteDictionaryDataOnNodalVariable(eq_gradients_dict, self.optimization_model_part, nodal_variable) # Projection on surface normals if con["project_gradient_on_surface_normals"].GetBool(): self.geometry_utilities.ProjectNodalVariableOnUnitSurfaceNormals( nodal_variable) # Damping if self.is_damping_specified: self.damping_utilities.DampNodalVariable(nodal_variable) # Mapping nodal_variable_mapped = KratosGlobals.GetVariable("DC" + str(itr + 1) + "DX_MAPPED") self.mapper.MapToDesignSpace(nodal_variable, nodal_variable_mapped) self.mapper.MapToGeometrySpace(nodal_variable_mapped, nodal_variable_mapped) # Damping if self.is_damping_specified: self.damping_utilities.DampNodalVariable(nodal_variable_mapped)