KratosOptimizationApplication 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/KratosOptimizationApplication.so +0 -0
- KratosMultiphysics/.libs/libKratosOptimizationCore.dylib +0 -0
- KratosMultiphysics/OptimizationApplication/__init__.py +19 -0
- KratosMultiphysics/OptimizationApplication/algorithms/NLOPT_algorithms.py +255 -0
- KratosMultiphysics/OptimizationApplication/algorithms/SciPy_algorithms.py +154 -0
- KratosMultiphysics/OptimizationApplication/algorithms/__init__.py +0 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm.py +78 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_adam.py +68 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_base.py +393 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_gradient_projection.py +355 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_momentum_relaxed_gradient_projection.py +70 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_nesterov_accelarated_gradient.py +60 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_relaxed_gradient_projection.py +263 -0
- KratosMultiphysics/OptimizationApplication/algorithms/algorithm_steepest_descent.py +205 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_NLOPT_constraint.py +194 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_NLOPT_objective.py +191 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_SciPy_constraint.py +123 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_SciPy_objective.py +148 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_constraint.py +180 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_objective.py +133 -0
- KratosMultiphysics/OptimizationApplication/algorithms/standardized_rgp_constraint.py +268 -0
- KratosMultiphysics/OptimizationApplication/analyses_controller.py +75 -0
- KratosMultiphysics/OptimizationApplication/controls/control.py +123 -0
- KratosMultiphysics/OptimizationApplication/controls/external_response_function_control.py +149 -0
- KratosMultiphysics/OptimizationApplication/controls/master_control.py +198 -0
- KratosMultiphysics/OptimizationApplication/controls/material/material_properties_control.py +115 -0
- KratosMultiphysics/OptimizationApplication/controls/material/simp_control.py +229 -0
- KratosMultiphysics/OptimizationApplication/controls/shape/vertex_morphing_shape_control.py +289 -0
- KratosMultiphysics/OptimizationApplication/controls/thickness/shell_thickness_control.py +180 -0
- KratosMultiphysics/OptimizationApplication/controls_controller.py +205 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/avg_abs_improvement_conv_criterion.py +98 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/combined_conv_criterion.py +87 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/constraint_conv_criterion.py +68 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/convergence_criterion.py +26 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/l2_conv_criterion.py +77 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/magnitude_reduction_conv_criterion.py +85 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/max_iter_conv_criterion.py +48 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/patience_conv_criterion.py +116 -0
- KratosMultiphysics/OptimizationApplication/convergence_criteria/target_value_conv_criterion.py +64 -0
- KratosMultiphysics/OptimizationApplication/execution_policies/execution_policy.py +46 -0
- KratosMultiphysics/OptimizationApplication/execution_policies/execution_policy_decorator.py +67 -0
- KratosMultiphysics/OptimizationApplication/execution_policies/independent_analysis_execution_policy.py +78 -0
- KratosMultiphysics/OptimizationApplication/execution_policies/kratos_analysis_execution_policy.py +137 -0
- KratosMultiphysics/OptimizationApplication/execution_policies/stepping_analysis_execution_policy.py +88 -0
- KratosMultiphysics/OptimizationApplication/filtering/explicit_filter.py +124 -0
- KratosMultiphysics/OptimizationApplication/filtering/filter.py +188 -0
- KratosMultiphysics/OptimizationApplication/filtering/filter_utils.py +55 -0
- KratosMultiphysics/OptimizationApplication/filtering/helmholtz_analysis.py +139 -0
- KratosMultiphysics/OptimizationApplication/filtering/helmholtz_scalar_solver.py +39 -0
- KratosMultiphysics/OptimizationApplication/filtering/helmholtz_shape_solver.py +67 -0
- KratosMultiphysics/OptimizationApplication/filtering/helmholtz_solver_base.py +275 -0
- KratosMultiphysics/OptimizationApplication/filtering/helmholtz_vector_solver.py +39 -0
- KratosMultiphysics/OptimizationApplication/filtering/implicit_filter.py +192 -0
- KratosMultiphysics/OptimizationApplication/filtering/python_solvers_wrapper_implicit_filters.py +56 -0
- KratosMultiphysics/OptimizationApplication/model_part_controllers/connectivity_preserving_model_part_controller.py +72 -0
- KratosMultiphysics/OptimizationApplication/model_part_controllers/external_response_function_model_part_controller.py +110 -0
- KratosMultiphysics/OptimizationApplication/model_part_controllers/mdpa_model_part_controller.py +44 -0
- KratosMultiphysics/OptimizationApplication/model_part_controllers/model_part_controller.py +19 -0
- KratosMultiphysics/OptimizationApplication/model_parts_controller.py +92 -0
- KratosMultiphysics/OptimizationApplication/optimization_analysis.py +146 -0
- KratosMultiphysics/OptimizationApplication/optimizations_controller.py +223 -0
- KratosMultiphysics/OptimizationApplication/optimizer.py +61 -0
- KratosMultiphysics/OptimizationApplication/processes/optimization_problem_ascii_output_process.py +212 -0
- KratosMultiphysics/OptimizationApplication/processes/optimization_problem_field_output_process.py +122 -0
- KratosMultiphysics/OptimizationApplication/processes/optimization_problem_graph_output_process.py +184 -0
- KratosMultiphysics/OptimizationApplication/processes/optimization_problem_hdf5_output_process.py +84 -0
- KratosMultiphysics/OptimizationApplication/processes/optimization_problem_vtu_output_process.py +94 -0
- KratosMultiphysics/OptimizationApplication/processes/spatial_statistics_computation_process.py +81 -0
- KratosMultiphysics/OptimizationApplication/responses/__init__.py +0 -0
- KratosMultiphysics/OptimizationApplication/responses/additive_manufacturing_responses.py +410 -0
- KratosMultiphysics/OptimizationApplication/responses/analysis_based_response_function_factory.py +14 -0
- KratosMultiphysics/OptimizationApplication/responses/analysis_free_response_function_factory.py +14 -0
- KratosMultiphysics/OptimizationApplication/responses/base_response.py +66 -0
- KratosMultiphysics/OptimizationApplication/responses/binary_operator_response_function.py +103 -0
- KratosMultiphysics/OptimizationApplication/responses/combined_response_function.py +127 -0
- KratosMultiphysics/OptimizationApplication/responses/discrete_value_residual_response_function.py +131 -0
- KratosMultiphysics/OptimizationApplication/responses/evaluation_response_function.py +68 -0
- KratosMultiphysics/OptimizationApplication/responses/external_response_function.py +87 -0
- KratosMultiphysics/OptimizationApplication/responses/geometric_centroid_deviation_response_function.py +97 -0
- KratosMultiphysics/OptimizationApplication/responses/linear_strain_energy_response_function.py +72 -0
- KratosMultiphysics/OptimizationApplication/responses/literal_value_response_function.py +33 -0
- KratosMultiphysics/OptimizationApplication/responses/log_response_function.py +47 -0
- KratosMultiphysics/OptimizationApplication/responses/mass_response_function.py +71 -0
- KratosMultiphysics/OptimizationApplication/responses/response_function.py +126 -0
- KratosMultiphysics/OptimizationApplication/responses/response_routine.py +158 -0
- KratosMultiphysics/OptimizationApplication/responses/structural_responses.py +335 -0
- KratosMultiphysics/OptimizationApplication/responses_controller.py +390 -0
- KratosMultiphysics/OptimizationApplication/utilities/buffered_dict.py +399 -0
- KratosMultiphysics/OptimizationApplication/utilities/component_data_view.py +74 -0
- KratosMultiphysics/OptimizationApplication/utilities/helper_utilities.py +40 -0
- KratosMultiphysics/OptimizationApplication/utilities/logger_utilities.py +222 -0
- KratosMultiphysics/OptimizationApplication/utilities/model_part_utilities.py +117 -0
- KratosMultiphysics/OptimizationApplication/utilities/opt_line_search.py +173 -0
- KratosMultiphysics/OptimizationApplication/utilities/opt_projection.py +191 -0
- KratosMultiphysics/OptimizationApplication/utilities/optimization_problem.py +237 -0
- KratosMultiphysics/OptimizationApplication/utilities/optimization_problem_utilities.py +237 -0
- KratosMultiphysics/OptimizationApplication/utilities/response_utilities.py +249 -0
- KratosMultiphysics/OptimizationApplication/utilities/timer.py +45 -0
- KratosMultiphysics/OptimizationApplication/utilities/union_utilities.py +6 -0
- kratosoptimizationapplication-10.4.4.dist-info/METADATA +25 -0
- kratosoptimizationapplication-10.4.4.dist-info/RECORD +106 -0
- kratosoptimizationapplication-10.4.4.dist-info/WHEEL +6 -0
- kratosoptimizationapplication.dylibs/libKratosCore.dylib +4 -0
- kratosoptimizationapplication.dylibs/libgcc_s.1.1.dylib +0 -0
- kratosoptimizationapplication.dylibs/libgomp.1.dylib +0 -0
- kratosoptimizationapplication.dylibs/libstdc++.6.dylib +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
3
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_routine import ResponseRoutine
|
|
4
|
+
from KratosMultiphysics.OptimizationApplication.controls.master_control import MasterControl
|
|
5
|
+
from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView
|
|
6
|
+
from KratosMultiphysics.OptimizationApplication.utilities.logger_utilities import DictLogger
|
|
7
|
+
from KratosMultiphysics.OptimizationApplication.utilities.logger_utilities import TimeLogger
|
|
8
|
+
from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateResponseExpression
|
|
9
|
+
from KratosMultiphysics.kratos_utilities import IssueDeprecationWarning
|
|
10
|
+
|
|
11
|
+
class StandardizedObjective(ResponseRoutine):
|
|
12
|
+
"""Standardized objective response function
|
|
13
|
+
|
|
14
|
+
This class transformed a user-given optimization problem into the standard format.
|
|
15
|
+
Supported objective types:
|
|
16
|
+
"minimization",
|
|
17
|
+
"maximization"
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
def __init__(self, parameters: Kratos.Parameters, master_control: MasterControl, optimization_problem: OptimizationProblem, required_buffer_size: int = 2):
|
|
21
|
+
# backward compatibility
|
|
22
|
+
if parameters.Has("response_name"):
|
|
23
|
+
IssueDeprecationWarning(self.__class__.__name__, "\"response_name\" is deprecated. Please use \"response_expression\".")
|
|
24
|
+
parameters.AddString("response_expression", parameters["response_name"].GetString())
|
|
25
|
+
parameters.RemoveValue("response_name")
|
|
26
|
+
|
|
27
|
+
default_parameters = Kratos.Parameters("""{
|
|
28
|
+
"response_expression": "",
|
|
29
|
+
"type" : "",
|
|
30
|
+
"scaling" : 1.0
|
|
31
|
+
}""")
|
|
32
|
+
parameters.ValidateAndAssignDefaults(default_parameters)
|
|
33
|
+
|
|
34
|
+
response = EvaluateResponseExpression(parameters["response_expression"].GetString(), optimization_problem)
|
|
35
|
+
|
|
36
|
+
super().__init__(master_control, response)
|
|
37
|
+
|
|
38
|
+
if required_buffer_size < 2:
|
|
39
|
+
raise RuntimeError(f"Standardized objective requires 2 as minimum buffer size. [ response name = {self.GetResponseName()} ]")
|
|
40
|
+
|
|
41
|
+
component_data_view = ComponentDataView(response, optimization_problem)
|
|
42
|
+
component_data_view.SetDataBuffer(required_buffer_size)
|
|
43
|
+
|
|
44
|
+
self.__optimization_problem = optimization_problem
|
|
45
|
+
self.__buffered_data = component_data_view.GetBufferedData()
|
|
46
|
+
self.__unbuffered_data = component_data_view.GetUnBufferedData()
|
|
47
|
+
|
|
48
|
+
scaling = parameters["scaling"].GetDouble()
|
|
49
|
+
if scaling < 0.0:
|
|
50
|
+
raise RuntimeError(f"Scaling should be always positive [ given scale = {scaling}]")
|
|
51
|
+
|
|
52
|
+
self.__objective_type = parameters["type"].GetString()
|
|
53
|
+
if self.__objective_type == "minimization":
|
|
54
|
+
self.__scaling = scaling
|
|
55
|
+
elif self.__objective_type == "maximization":
|
|
56
|
+
self.__scaling = -scaling
|
|
57
|
+
else:
|
|
58
|
+
raise RuntimeError(f"Requesting unsupported type {self.__objective_type} for objective response function. Supported types are: \n\tminimization\n\tmaximization")
|
|
59
|
+
|
|
60
|
+
def GetInitialValue(self) -> float:
|
|
61
|
+
if self.__unbuffered_data.HasValue("initial_value"):
|
|
62
|
+
return self.__unbuffered_data["initial_value"] * self.__scaling
|
|
63
|
+
else:
|
|
64
|
+
raise RuntimeError(f"Response value for {self.GetResponseName()} is not calculated yet.")
|
|
65
|
+
|
|
66
|
+
def CalculateStandardizedValue(self, control_field: Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor, save_value: bool = True) -> float:
|
|
67
|
+
with TimeLogger(f"StandardizedObjective::Calculate {self.GetResponseName()} value", None, "Finished"):
|
|
68
|
+
response_value = self.CalculateValue(control_field)
|
|
69
|
+
standardized_response_value = response_value * self.__scaling
|
|
70
|
+
|
|
71
|
+
if not self.__unbuffered_data.HasValue("initial_value"):
|
|
72
|
+
self.__unbuffered_data["initial_value"] = response_value
|
|
73
|
+
|
|
74
|
+
if save_value:
|
|
75
|
+
if self.__buffered_data.HasValue("value"): del self.__buffered_data["value"]
|
|
76
|
+
self.__buffered_data["value"] = response_value
|
|
77
|
+
|
|
78
|
+
DictLogger("Objective info",self.GetInfo())
|
|
79
|
+
|
|
80
|
+
return standardized_response_value
|
|
81
|
+
|
|
82
|
+
def GetValue(self, step_index: int = 0) -> float:
|
|
83
|
+
return self.__buffered_data.GetValue("value", step_index)
|
|
84
|
+
|
|
85
|
+
def GetStandardizedValue(self, step_index: int = 0) -> float:
|
|
86
|
+
return self.GetValue(step_index) * self.__scaling
|
|
87
|
+
|
|
88
|
+
def CalculateStandardizedGradient(self, save_field: bool = True) -> Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor:
|
|
89
|
+
|
|
90
|
+
with TimeLogger(f"StandardizedObjective::Calculate {self.GetResponseName()} gradients", None, "Finished"):
|
|
91
|
+
gradient_cta = self.CalculateGradient()
|
|
92
|
+
if save_field:
|
|
93
|
+
# save the physical gradients for post processing in unbuffered data container.
|
|
94
|
+
for physical_var, physical_gradient in self.GetRequiredPhysicalGradients().items():
|
|
95
|
+
variable_name = f"d{self.GetResponseName()}_d{physical_var.Name()}"
|
|
96
|
+
if self.__unbuffered_data.HasValue(variable_name): del self.__unbuffered_data[variable_name]
|
|
97
|
+
self.__unbuffered_data[variable_name] = Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor(physical_gradient)
|
|
98
|
+
|
|
99
|
+
# save the filtered gradients for post processing in unbuffered data container.
|
|
100
|
+
for gradient_ta, control in zip(gradient_cta.GetTensorAdaptors(), self.GetMasterControl().GetListOfControls()):
|
|
101
|
+
variable_name = f"d{self.GetResponseName()}_d{control.GetName()}"
|
|
102
|
+
if self.__unbuffered_data.HasValue(variable_name): del self.__unbuffered_data[variable_name]
|
|
103
|
+
# cloning is a cheap operation, it only moves underlying pointers
|
|
104
|
+
# does not create additional memory.
|
|
105
|
+
self.__unbuffered_data[variable_name] = Kratos.TensorAdaptors.DoubleTensorAdaptor(gradient_ta)
|
|
106
|
+
|
|
107
|
+
gradient_cta.data[:] *= self.__scaling
|
|
108
|
+
Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor(gradient_cta, perform_store_data_recursively=False, copy=False).StoreData()
|
|
109
|
+
return gradient_cta
|
|
110
|
+
|
|
111
|
+
def GetRelativeChange(self) -> float:
|
|
112
|
+
if self.__optimization_problem.GetStep() > 0:
|
|
113
|
+
return self.GetStandardizedValue() / self.GetStandardizedValue(1) - 1.0 if abs(self.GetStandardizedValue(1)) > 1e-12 else self.GetStandardizedValue()
|
|
114
|
+
else:
|
|
115
|
+
return 0.0
|
|
116
|
+
|
|
117
|
+
def GetAbsoluteChange(self) -> float:
|
|
118
|
+
return self.GetStandardizedValue() - self.GetInitialValue()
|
|
119
|
+
|
|
120
|
+
def GetInfo(self) -> dict:
|
|
121
|
+
info = {
|
|
122
|
+
"name": self.GetResponseName(),
|
|
123
|
+
"type": self.__objective_type,
|
|
124
|
+
"value": self.GetValue(),
|
|
125
|
+
"std_value": self.GetStandardizedValue(),
|
|
126
|
+
"abs_change": self.GetAbsoluteChange(),
|
|
127
|
+
"rel_change [%]": self.GetRelativeChange() * 100.0
|
|
128
|
+
}
|
|
129
|
+
init_value = self.GetInitialValue()
|
|
130
|
+
if init_value:
|
|
131
|
+
info["abs_change [%]"] = self.GetAbsoluteChange()/init_value * 100
|
|
132
|
+
|
|
133
|
+
return info
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
import KratosMultiphysics.OptimizationApplication as KratosOA
|
|
3
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
4
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_routine import ResponseRoutine
|
|
5
|
+
from KratosMultiphysics.OptimizationApplication.controls.master_control import MasterControl
|
|
6
|
+
from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView
|
|
7
|
+
from KratosMultiphysics.OptimizationApplication.utilities.logger_utilities import DictLogger
|
|
8
|
+
from KratosMultiphysics.OptimizationApplication.utilities.logger_utilities import TimeLogger
|
|
9
|
+
from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateResponseExpression
|
|
10
|
+
from KratosMultiphysics.kratos_utilities import IssueDeprecationWarning
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConstraintType(Enum):
|
|
15
|
+
EQUAL = "="
|
|
16
|
+
LOWER = "<"
|
|
17
|
+
LOWEREQ = "<="
|
|
18
|
+
GREATER = ">"
|
|
19
|
+
GREATEREQ = ">="
|
|
20
|
+
|
|
21
|
+
class StandardizedRGPConstraint(ResponseRoutine):
|
|
22
|
+
"""Standardized RGP constraint response function
|
|
23
|
+
|
|
24
|
+
This class creates instances to standardize any response function for the specified type of the constraint.
|
|
25
|
+
Supported constraint types:
|
|
26
|
+
"=",
|
|
27
|
+
"<",
|
|
28
|
+
"<=,
|
|
29
|
+
">",
|
|
30
|
+
">="
|
|
31
|
+
|
|
32
|
+
The reference value for the constraint either can be the "initial_value" or a specified value.
|
|
33
|
+
|
|
34
|
+
It can be used only with relaxed_gradient_projection_algorithm. The class contains necessary functions to compute critical zones.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
def __init__(self, parameters: Kratos.Parameters, master_control: MasterControl, optimization_problem: OptimizationProblem, required_buffer_size: int = 4):
|
|
38
|
+
# backward compatibility
|
|
39
|
+
if parameters.Has("response_name"):
|
|
40
|
+
IssueDeprecationWarning(self.__class__.__name__, "\"response_name\" is deprecated. Please use \"response_expression\".")
|
|
41
|
+
parameters.AddString("response_expression", parameters["response_name"].GetString())
|
|
42
|
+
parameters.RemoveValue("response_name")
|
|
43
|
+
|
|
44
|
+
default_parameters = Kratos.Parameters("""{
|
|
45
|
+
"response_expression" : "",
|
|
46
|
+
"type" : "",
|
|
47
|
+
"scaled_ref_value" : 0.0,
|
|
48
|
+
"buffer_factor" : 2.0,
|
|
49
|
+
"max_w_c" : 10.0,
|
|
50
|
+
"tolerance" : 1e-3
|
|
51
|
+
}""")
|
|
52
|
+
|
|
53
|
+
if parameters.Has("scaled_ref_value") and parameters["scaled_ref_value"].IsDouble():
|
|
54
|
+
default_parameters["scaled_ref_value"].SetDouble(0.0)
|
|
55
|
+
elif parameters.Has("scaled_ref_value") and parameters["scaled_ref_value"].GetString() == "initial_value":
|
|
56
|
+
default_parameters["scaled_ref_value"].SetString("initial_value")
|
|
57
|
+
else:
|
|
58
|
+
type = parameters["scaled_ref_value"].GetString()
|
|
59
|
+
raise RuntimeError(f"Provided \"scaled_ref_value\" = {type} is not supported for constraint response functions. Followings are supported options: \n\tinitial_value\n\tfloat value")
|
|
60
|
+
|
|
61
|
+
parameters.ValidateAndAssignDefaults(default_parameters)
|
|
62
|
+
|
|
63
|
+
response = EvaluateResponseExpression(parameters["response_expression"].GetString(), optimization_problem)
|
|
64
|
+
|
|
65
|
+
super().__init__(master_control, response)
|
|
66
|
+
|
|
67
|
+
if required_buffer_size < 4:
|
|
68
|
+
raise RuntimeError(f"Standardized RGP constraint requires 4 as minimum buffer size. [ response name = {self.GetResponseName()} ]")
|
|
69
|
+
|
|
70
|
+
component_data_view = ComponentDataView(response, optimization_problem)
|
|
71
|
+
component_data_view.SetDataBuffer(required_buffer_size)
|
|
72
|
+
|
|
73
|
+
self.__optimization_problem = optimization_problem
|
|
74
|
+
self.__buffered_data = component_data_view.GetBufferedData()
|
|
75
|
+
self.__unbuffered_data = component_data_view.GetUnBufferedData()
|
|
76
|
+
|
|
77
|
+
if parameters["scaled_ref_value"].IsDouble():
|
|
78
|
+
self.__reference_value = parameters["scaled_ref_value"].GetDouble()
|
|
79
|
+
elif parameters["scaled_ref_value"].IsString() and parameters["scaled_ref_value"].GetString() == "initial_value":
|
|
80
|
+
self.__reference_value = None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
self.__constraint_type = ConstraintType(parameters["type"].GetString())
|
|
84
|
+
if self.__constraint_type in [ConstraintType.EQUAL, ConstraintType.LOWEREQ, ConstraintType.LOWER]:
|
|
85
|
+
self.__scaling = 1.0
|
|
86
|
+
elif self.__constraint_type in [ConstraintType.GREATEREQ, ConstraintType.GREATER]:
|
|
87
|
+
self.__scaling = -1.0
|
|
88
|
+
if not self.__reference_value is None: self.__reference_value *= -1
|
|
89
|
+
else:
|
|
90
|
+
raise RuntimeError(f"Provided \"type\" = {self.__constraint_type} is not supported in constraint response functions. Followings are supported options: \n\t=\n\t<=\n\t<\n\t>=\n\t>")
|
|
91
|
+
|
|
92
|
+
# buffer coefficients
|
|
93
|
+
self.BSF = parameters["buffer_factor"].GetDouble()
|
|
94
|
+
self.BSF_init = self.BSF
|
|
95
|
+
self.CBV = 0.0
|
|
96
|
+
self.BS = 1e-6
|
|
97
|
+
self.max_w_c = parameters["max_w_c"].GetDouble()
|
|
98
|
+
self.CF = 1.0
|
|
99
|
+
|
|
100
|
+
self.tolerance = parameters["tolerance"].GetDouble()
|
|
101
|
+
|
|
102
|
+
def IsEqualityType(self) -> str:
|
|
103
|
+
return self.__constraint_type == ConstraintType.EQUAL
|
|
104
|
+
|
|
105
|
+
def ComputeW(self) -> float:
|
|
106
|
+
if self.IsEqualityType():
|
|
107
|
+
return max(abs(self.GetStandardizedValue() - (self.CBV - self.BS)), 0.0) / self.BS
|
|
108
|
+
else:
|
|
109
|
+
return max(self.GetStandardizedValue() - (self.CBV - self.BS), 0.0) / self.BS
|
|
110
|
+
|
|
111
|
+
def PredictW(self, value):
|
|
112
|
+
if self.IsEqualityType():
|
|
113
|
+
return max(abs(value - (self.CBV - self.BS)), 0.0) / self.BS
|
|
114
|
+
else:
|
|
115
|
+
return max(value - (self.CBV - self.BS), 0.0) / self.BS
|
|
116
|
+
|
|
117
|
+
def Compute_W_relax(self, w:float) -> float:
|
|
118
|
+
if w <= 0.0:
|
|
119
|
+
return 0.0
|
|
120
|
+
elif w <= 1.0:
|
|
121
|
+
return w
|
|
122
|
+
else:
|
|
123
|
+
return 1.0
|
|
124
|
+
|
|
125
|
+
def Compute_W_correction(self, w:float) -> float:
|
|
126
|
+
if w <= 1.0:
|
|
127
|
+
return 0.0
|
|
128
|
+
elif w <= self.max_w_c:
|
|
129
|
+
return (w - 1.0) * self.BSF_init * self.CF
|
|
130
|
+
else:
|
|
131
|
+
return (self.max_w_c - 1) * self.BSF_init * self.CF
|
|
132
|
+
|
|
133
|
+
def UpdateBufferSize(self):
|
|
134
|
+
step = self.__optimization_problem.GetStep() + 1
|
|
135
|
+
if step == 1:
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
max_step = min(step, self.__buffered_data.GetBufferSize())
|
|
139
|
+
values = []
|
|
140
|
+
delta_values = []
|
|
141
|
+
abs_delta_values = []
|
|
142
|
+
for index in range(max_step):
|
|
143
|
+
values.append(self.GetStandardizedValue(index))
|
|
144
|
+
if index > 0:
|
|
145
|
+
delta_values.append(values[index - 1] - values[index])
|
|
146
|
+
abs_delta_values.append(abs(values[index - 1] - values[index]))
|
|
147
|
+
|
|
148
|
+
max_delta = max(abs_delta_values)
|
|
149
|
+
|
|
150
|
+
# # Update CBV if violating
|
|
151
|
+
if self.IsEqualityType():
|
|
152
|
+
if step > 1:
|
|
153
|
+
if values[0] > 0.0 and values[1] > 0.0 and delta_values[0] > 0.0:
|
|
154
|
+
self.CF += 0.5
|
|
155
|
+
|
|
156
|
+
if values[0] < 0.0 and values[1] < 0.0 and delta_values[0] < 0.0:
|
|
157
|
+
self.CF += 0.5
|
|
158
|
+
else:
|
|
159
|
+
if step > 1:
|
|
160
|
+
if values[0] > 0.0 and values[1] > 0.0 and delta_values[0] > 0.0:
|
|
161
|
+
self.CF += 0.5
|
|
162
|
+
|
|
163
|
+
if values[0] < 0.0 and values[1] < 0.0 and delta_values[0] < 0.0:
|
|
164
|
+
self.CF -= 0.5
|
|
165
|
+
self.CF = max (self.CF, 1.0)
|
|
166
|
+
|
|
167
|
+
self.BS = max(self.BSF * max_delta, 1e-6)
|
|
168
|
+
# Update BSF if zig zaging
|
|
169
|
+
if step > 3:
|
|
170
|
+
if delta_values[0] * delta_values[1] < 0.0 and delta_values[1] * delta_values[2] < 0.0:
|
|
171
|
+
self.BSF += 1
|
|
172
|
+
|
|
173
|
+
print(f"RGP Constraint {self.GetResponseName()}:: UpdateBufferSize")
|
|
174
|
+
print(f"RGP Constraint {self.GetResponseName()}:: CBV = {self.CBV}")
|
|
175
|
+
print(f"RGP Constraint {self.GetResponseName()}:: BSF = {self.BSF}")
|
|
176
|
+
print(f"RGP Constraint {self.GetResponseName()}:: BS = {self.BS}")
|
|
177
|
+
print(f"RGP Constraint {self.GetResponseName()}:: CF = {self.CF}")
|
|
178
|
+
|
|
179
|
+
def IsActive(self):
|
|
180
|
+
return self.ComputeW() > 0.0
|
|
181
|
+
|
|
182
|
+
def IsSatisfied(self, value=None):
|
|
183
|
+
if not value:
|
|
184
|
+
value = abs(self.GetStandardizedValue()) if self.IsEqualityType() else self.GetStandardizedValue()
|
|
185
|
+
if self.IsEqualityType():
|
|
186
|
+
if abs(value) <= self.tolerance: return True
|
|
187
|
+
else: return False
|
|
188
|
+
else:
|
|
189
|
+
if value <= self.tolerance: return True
|
|
190
|
+
else: return False
|
|
191
|
+
|
|
192
|
+
def GetReferenceValue(self) -> float:
|
|
193
|
+
if self.__reference_value is not None:
|
|
194
|
+
return self.__reference_value
|
|
195
|
+
else:
|
|
196
|
+
if self.__unbuffered_data.HasValue("initial_value"):
|
|
197
|
+
return self.__unbuffered_data["initial_value"] * self.__scaling
|
|
198
|
+
else:
|
|
199
|
+
raise RuntimeError(f"Response value for {self.GetResponseName()} is not calculated yet.")
|
|
200
|
+
|
|
201
|
+
def CalculateStandardizedValue(self, control_field: Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor, save_value: bool = True) -> float:
|
|
202
|
+
with TimeLogger(f"StandardizedRGPConstraint::Calculate {self.GetResponseName()} value", None, "Finished"):
|
|
203
|
+
response_value = self.CalculateValue(control_field)
|
|
204
|
+
standardized_response_value = response_value * self.__scaling
|
|
205
|
+
|
|
206
|
+
if not self.__unbuffered_data.HasValue("initial_value"):
|
|
207
|
+
self.__unbuffered_data["initial_value"] = response_value
|
|
208
|
+
|
|
209
|
+
standardized_response_value -= self.GetReferenceValue()
|
|
210
|
+
|
|
211
|
+
if save_value:
|
|
212
|
+
if self.__buffered_data.HasValue("value"): del self.__buffered_data["value"]
|
|
213
|
+
if self.__buffered_data.HasValue("std_value"): del self.__buffered_data["std_value"]
|
|
214
|
+
self.__buffered_data["value"] = response_value
|
|
215
|
+
self.__buffered_data["std_value"] = standardized_response_value
|
|
216
|
+
|
|
217
|
+
DictLogger("Constraint info",self.GetInfo())
|
|
218
|
+
|
|
219
|
+
return standardized_response_value
|
|
220
|
+
|
|
221
|
+
def CalculateStandardizedGradient(self) -> Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor:
|
|
222
|
+
with TimeLogger(f"StandardizedRGPConstraint::Calculate {self.GetResponseName()} gradients", None, "Finished"):
|
|
223
|
+
gradient_cta = self.CalculateGradient()
|
|
224
|
+
if self.IsEqualityType():
|
|
225
|
+
factor = 1.0 if self.GetStandardizedValue() > 0.0 else -1.0
|
|
226
|
+
else:
|
|
227
|
+
factor = 1.0
|
|
228
|
+
gradient_cta.data[:] *= self.__scaling * factor
|
|
229
|
+
return gradient_cta
|
|
230
|
+
|
|
231
|
+
def GetValue(self, step_index: int = 0) -> float:
|
|
232
|
+
return self.__buffered_data.GetValue("value", step_index)
|
|
233
|
+
|
|
234
|
+
def GetStandardizedValue(self, step_index: int = 0) -> float:
|
|
235
|
+
return self.GetValue(step_index) * self.__scaling - self.GetReferenceValue()
|
|
236
|
+
|
|
237
|
+
def GetIfConstraintActive(self, step_index: int = 0) -> bool:
|
|
238
|
+
return True
|
|
239
|
+
|
|
240
|
+
def GetAbsoluteViolation(self, step_index: int = 0) -> float:
|
|
241
|
+
is_violated = self.IsEqualityType() or self.GetStandardizedValue(step_index) >= 0.0
|
|
242
|
+
return self.GetStandardizedValue(step_index) * is_violated
|
|
243
|
+
|
|
244
|
+
def GetRelativeViolation(self, step_index: int = 0) -> float:
|
|
245
|
+
return self.GetAbsoluteViolation(step_index) / self.GetReferenceValue() if abs(self.GetReferenceValue()) > 1e-12 else 0.0
|
|
246
|
+
|
|
247
|
+
def GetRelativeChange(self) -> float:
|
|
248
|
+
if self.__optimization_problem.GetStep() > 1:
|
|
249
|
+
return self.GetStandardizedValue() / self.GetStandardizedValue(1) - 1.0 if abs(self.GetStandardizedValue(1)) > 1e-12 else self.GetStandardizedValue()
|
|
250
|
+
else:
|
|
251
|
+
return 0.0
|
|
252
|
+
|
|
253
|
+
def GetAbsoluteChange(self) -> float:
|
|
254
|
+
return self.GetValue() - self.__unbuffered_data["initial_value"] if abs(self.GetReferenceValue()) > 1e-12 else self.GetStandardizedValue()
|
|
255
|
+
|
|
256
|
+
def GetInfo(self) -> dict:
|
|
257
|
+
info = {
|
|
258
|
+
"name": self.GetResponseName(),
|
|
259
|
+
"value": self.GetValue(),
|
|
260
|
+
"type": self.__constraint_type,
|
|
261
|
+
"ref_value": self.GetReferenceValue(),
|
|
262
|
+
"standartiezed_value": self.GetStandardizedValue(),
|
|
263
|
+
"abs_change": self.GetAbsoluteChange(),
|
|
264
|
+
"abs_violation": self.GetAbsoluteViolation(),
|
|
265
|
+
"rel_change [%]": self.GetRelativeChange() * 100.0,
|
|
266
|
+
"rel_violation [%]": self.GetRelativeViolation() * 100.0 if not self.GetReferenceValue() == 0.0 else str(None)
|
|
267
|
+
}
|
|
268
|
+
return info
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# | / |
|
|
2
|
+
# ' / __| _` | __| _ \ __|
|
|
3
|
+
# . \ | ( | | ( |\__ `
|
|
4
|
+
# _|\_\_| \__,_|\__|\___/ ____/
|
|
5
|
+
# Multi-Physics
|
|
6
|
+
#
|
|
7
|
+
# License: BSD License
|
|
8
|
+
# license: OptimizationApplication/license.txt
|
|
9
|
+
#
|
|
10
|
+
# Main authors: Reza Najian Asl, https://github.com/RezaNajian
|
|
11
|
+
# Suneth Warnakulasuriya
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
# additional imports
|
|
15
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
16
|
+
from KratosMultiphysics.OptimizationApplication.execution_policies.execution_policy_decorator import ExecutionPolicyDecorator
|
|
17
|
+
from KratosMultiphysics.OptimizationApplication.utilities.logger_utilities import TimeLogger
|
|
18
|
+
|
|
19
|
+
# ==============================================================================
|
|
20
|
+
def CreateController(execution_policies_settings,model,model_parts_controller):
|
|
21
|
+
return AnalysesController(execution_policies_settings,model,model_parts_controller)
|
|
22
|
+
|
|
23
|
+
# ==============================================================================
|
|
24
|
+
class AnalysesController:
|
|
25
|
+
# --------------------------------------------------------------------------
|
|
26
|
+
def __init__(self, execution_policies_settings, model, model_parts_controller):
|
|
27
|
+
self.execution_policies_settings = execution_policies_settings
|
|
28
|
+
self.model_parts_controller = model_parts_controller
|
|
29
|
+
self.model = model
|
|
30
|
+
|
|
31
|
+
self.execution_policy_decorators = {}
|
|
32
|
+
for execution_policy_settings in self.execution_policies_settings:
|
|
33
|
+
execution_policy_wrapper = ExecutionPolicyDecorator(self.model, execution_policy_settings, OptimizationProblem())
|
|
34
|
+
if not execution_policy_wrapper.GetName() in self.execution_policy_decorators.keys():
|
|
35
|
+
self.execution_policy_decorators[execution_policy_wrapper.GetName()] = execution_policy_wrapper
|
|
36
|
+
else:
|
|
37
|
+
raise RuntimeError(f"Found already existing execution policy with the name \"{execution_policy_wrapper.GetExecutionPolicyName()}\". Please provide unique names.")
|
|
38
|
+
|
|
39
|
+
# --------------------------------------------------------------------------
|
|
40
|
+
def GetExecutionPolicyDecorator(self, execution_policy_name: str) -> ExecutionPolicyDecorator:
|
|
41
|
+
if not execution_policy_name in self.execution_policy_decorators.keys():
|
|
42
|
+
raise RuntimeError("AnalysesController: Try to get an execution policy {} which does not exist.".format(execution_policy_name))
|
|
43
|
+
else:
|
|
44
|
+
return self.execution_policy_decorators[execution_policy_name]
|
|
45
|
+
|
|
46
|
+
# --------------------------------------------------------------------------
|
|
47
|
+
def GetAnalysis(self, execution_policy_name: str):
|
|
48
|
+
return self.GetExecutionPolicyDecorator(execution_policy_name).GetExecutionPolicy().analysis
|
|
49
|
+
|
|
50
|
+
# --------------------------------------------------------------------------
|
|
51
|
+
def Initialize(self):
|
|
52
|
+
for execution_policy_wrapper in self.execution_policy_decorators.values():
|
|
53
|
+
execution_policy_name = execution_policy_wrapper.GetName()
|
|
54
|
+
with TimeLogger(self.__class__.__name__, f"Initializing {execution_policy_name}...", f"Finished initializing {execution_policy_name}"):
|
|
55
|
+
execution_policy_wrapper.Initialize()
|
|
56
|
+
|
|
57
|
+
# --------------------------------------------------------------------------
|
|
58
|
+
def RunAnalysis(self, execution_policy_name: str):
|
|
59
|
+
with TimeLogger(self.__class__.__name__, f"Starting {execution_policy_name}...", f"Finished execution of {execution_policy_name}."):
|
|
60
|
+
self.GetExecutionPolicyDecorator(execution_policy_name).Execute()
|
|
61
|
+
|
|
62
|
+
# --------------------------------------------------------------------------
|
|
63
|
+
def RunAll(self):
|
|
64
|
+
for name in self.analyses.keys():
|
|
65
|
+
self.RunAnalysis(name)
|
|
66
|
+
|
|
67
|
+
# --------------------------------------------------------------------------
|
|
68
|
+
def RunAnalyses(self, analyses_name):
|
|
69
|
+
if not isinstance(analyses_name, list):
|
|
70
|
+
raise RuntimeError("AnalysesController: RunAnalyses requires list of analysis names")
|
|
71
|
+
for analysis_name in analyses_name:
|
|
72
|
+
self.RunAnalysis(analysis_name)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import KratosMultiphysics as Kratos
|
|
3
|
+
from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes
|
|
4
|
+
|
|
5
|
+
class Control(ABC):
|
|
6
|
+
"""Base abstract control class.
|
|
7
|
+
|
|
8
|
+
The base abstract control class has the following responsibilities.
|
|
9
|
+
1. Initializes and finalizes the control.
|
|
10
|
+
2. Maps physical space gradients to control space gradients, if needed. Otherwise, it passes.
|
|
11
|
+
2. Updating the controlled parts of the model part with new given design.
|
|
12
|
+
3. Retrieve control field.
|
|
13
|
+
4. Retrieve physical space kratos variables (If more than one physical space kratos variables are controlled by the given control field.)
|
|
14
|
+
|
|
15
|
+
This control should only work on one model part and one kratos control variable. Hence, if multiple model parts required then,
|
|
16
|
+
a single model part should be created using Kratos.ModelPartOperationUtilities.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
def __init__(self, control_name: str) -> None:
|
|
20
|
+
self.__name = control_name
|
|
21
|
+
|
|
22
|
+
def GetName(self) -> str:
|
|
23
|
+
return self.__name
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def Initialize(self) -> None:
|
|
27
|
+
"""Initializes the control.
|
|
28
|
+
|
|
29
|
+
This method initializes the control. This is only called once in the whole optimization process.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def Check(self) -> None:
|
|
36
|
+
"""Checks the control.
|
|
37
|
+
|
|
38
|
+
This method checks the control. This is only called once in the whole optimization process.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def Finalize(self) -> None:
|
|
45
|
+
"""Finalizes the control.
|
|
46
|
+
|
|
47
|
+
This method finalizes the control. This is only called once in the whole optimization process.
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def GetPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]':
|
|
54
|
+
"""Returns list of physical control variables controlled by this control.
|
|
55
|
+
|
|
56
|
+
This method returns list of physical variable which are controlled by the control. In most of the cases, there is only one
|
|
57
|
+
control variable per control. But in some rare cases, there may be the requirement to control two physical variables. Anyhow,
|
|
58
|
+
the control space should only have one variable per control in the control space, but they can have multiple physical variables
|
|
59
|
+
in their design space.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
list[SupportedSensitivityFieldVariableTypes]: List of physical control variables.
|
|
63
|
+
"""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def GetEmptyField(self) -> Kratos.TensorAdaptors.DoubleTensorAdaptor:
|
|
68
|
+
"""Returns a new empty data field holder with correct dimensionality information.
|
|
69
|
+
|
|
70
|
+
This returns a new empty data field holder to give information about on which model part's container
|
|
71
|
+
this model part is acting on. It does not read any data from respective model part's container.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Kratos.TensorAdaptors.DoubleTensorAdaptor: Returns a new empty @ref Kratos.TensorAdaptor corresponding to control's model part's respective container.
|
|
75
|
+
"""
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def GetControlField(self) -> Kratos.TensorAdaptors.DoubleTensorAdaptor:
|
|
80
|
+
"""Returns the current control field of the control.
|
|
81
|
+
|
|
82
|
+
This method returns the control field of the current design.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Kratos.TensorAdaptors.DoubleTensorAdaptor: Current designs control field.
|
|
86
|
+
"""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def MapGradient(self, physical_variable_gradient_map: 'dict[SupportedSensitivityFieldVariableTypes, Kratos.TensorAdaptors.DoubleTensorAdaptor]') -> Kratos.TensorAdaptors.DoubleTensorAdaptor:
|
|
91
|
+
"""Maps physical space gradients to the control space.
|
|
92
|
+
|
|
93
|
+
This method is used to map the given physical space gradients to the control space. The input should be as in the following example:
|
|
94
|
+
physical_variable_gradient_map = {
|
|
95
|
+
Kratos.YOUNG_MODULUS: Kratos.DoubleTensorAdaptor,
|
|
96
|
+
Kratos.DENSITY : Kratos.DoubleTensorAdaptor,
|
|
97
|
+
Kratos.SHAPE : Kratos.DoubleTensorAdaptor
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
All the gradients w.r.t. @see GetPhysicalKratosVariables() variables will be given in @ref physical_variable_gradient_map.
|
|
101
|
+
If the response does not depend on some of them or all, then @ref Kratos.TensorAdaptor with correctly sized zero tensors will be passed.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
physical_variable_gradient_map (dict[SupportedSensitivityFieldVariableTypes, Kratos.TensorAdaptors.DoubleTensorAdaptor]): Map of physical space variable and @ref Kratos::TensorAdaptor with sensitivities.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Kratos.TensorAdaptors.DoubleTensorAdaptor: Gradients mapped in to control space.
|
|
108
|
+
"""
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def Update(self, control_field: Kratos.TensorAdaptors.DoubleTensorAdaptor) -> bool:
|
|
113
|
+
"""Modifies the current control with the given control field.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
control_field (Kratos.TensorAdaptors.DoubleTensorAdaptor): The control field in control space.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
bool: True if the control field was applied to obtain a new design, otherwise False
|
|
120
|
+
"""
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
|