From 4d3aad01f086967843b6f8525bc48c6f7e6d50de Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Tue, 23 Jul 2024 11:28:01 +0200 Subject: [PATCH 01/36] introduce response utilities --- .../utilities/response_utilities.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 applications/OptimizationApplication/python_scripts/utilities/response_utilities.py diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py new file mode 100644 index 000000000000..df0bdf012e4d --- /dev/null +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -0,0 +1,34 @@ +import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction +from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.buffered_dict import BufferedDict + +def EvaluateGradient(response_function: ResponseFunction, response_buffered_data: BufferedDict, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]': + # first get the sub_collective expressions for implemented physical kratos variables + resp_physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} + for variable, collective_expression in physical_variable_collective_expressions.items(): + if variable in response_function.GetImplementedPhysicalKratosVariables(): + resp_physical_variable_collective_expressions[variable] = collective_expression.Clone() + + resp_physical_variable_collective_expressions_to_evaluate: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} + for variable, collective_expression in resp_physical_variable_collective_expressions.items(): + # first check whether the gradients have been already evaluated. if so, take the gradients. + if response_buffered_data.HasValue(f"d{response_function.GetName()}_d{variable.Name()}"): + resp_physical_variable_collective_expressions[variable] = response_buffered_data.GetValue(f"d{response_function.GetName()}_d{variable.Name()}") + else: + # gradients have not yet evaluated. put it to the dictionary for later evaluation + resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression + + if len(resp_physical_variable_collective_expressions_to_evaluate) != 0: + response_function.CalculateGradient(resp_physical_variable_collective_expressions_to_evaluate) + for variable, collective_expression in resp_physical_variable_collective_expressions_to_evaluate.items(): + response_buffered_data.SetValue(f"d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) + resp_physical_variable_collective_expressions[variable] = collective_expression + + # now add zero values collective expressions to variables for which the response function does not have dependence + for variable, collective_expression in physical_variable_collective_expressions.items(): + if variable not in response_function.GetImplementedPhysicalKratosVariables(): + resp_physical_variable_collective_expressions[variable] = collective_expression * 0.0 + + return resp_physical_variable_collective_expressions \ No newline at end of file From 2c5feeee8759e013666d8ca2d14392ec49345c89 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Tue, 23 Jul 2024 11:28:11 +0200 Subject: [PATCH 02/36] add binary operator resp func --- .../binary_operator_response_function.py | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py new file mode 100644 index 000000000000..e23a9e23b72d --- /dev/null +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -0,0 +1,112 @@ +from typing import Optional +from enum import Enum +from math import log + +import KratosMultiphysics as Kratos +import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction +from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.model_part_utilities import ModelPartOperation +from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem +from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView + +class BinaryOperatorResponseFunction(ResponseFunction): + class BinaryOperator(str, Enum): + ADD = "+" + SUBTRACT = "-" + MULTIPLY = "*" + DIVIDE = "/" + POWER = "^" + + def __init__(self, model: Kratos.Model, response_function_1: ResponseFunction, response_function_2: ResponseFunction, binary_operator: BinaryOperator, optimization_problem: OptimizationProblem): + super().__init__(f"({response_function_1.GetName()}{binary_operator.value}{response_function_2.GetName()})") + + if response_function_1 not in optimization_problem.GetListOfResponses(): + optimization_problem.AddComponent(response_function_1) + + if response_function_2 not in optimization_problem.GetListOfResponses(): + optimization_problem.AddComponent(response_function_2) + + self.model = model + self.optimization_problem = optimization_problem + self.response_function_1 = response_function_1 + self.response_function_2 = response_function_2 + self.binary_operator = binary_operator + self.model_part: Optional[Kratos.ModelPart] = None + + def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]': + vars_list = self.response_function_1.GetImplementedPhysicalKratosVariables() + vars_list.extend(self.response_function_2.GetImplementedPhysicalKratosVariables()) + return vars_list + + def Initialize(self) -> None: + self.response_function_1.Initialize() + self.response_function_2.Initialize() + self.model_part = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", [self.response_function_1.GetInfluencingModelPart().FullName(), self.response_function_2.GetInfluencingModelPart().FullName()], False).GetModelPart() + + def Check(self) -> None: + self.response_function_1.Check() + self.response_function_2.Check() + + def Finalize(self) -> None: + self.response_function_1.Finalize() + self.response_function_2.Finalize() + + def GetInfluencingModelPart(self) -> Kratos.ModelPart: + return self.model_part + + def CalculateValue(self) -> float: + # check whether the response function 1 is already evaluated for current step + resp_1_buffered_data = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData() + if resp_1_buffered_data.HasValue("value"): + # if it is evaluated, then take the value + resp_1_value = resp_1_buffered_data.GetValue("value") + else: + # if not, compute the value + resp_1_value = self.response_function_1.CalculateValue() + resp_1_buffered_data.SetValue("value", resp_1_value) + + # check whether the response function 2 is already evaluated for current step + resp_2_buffered_data = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData() + if resp_2_buffered_data.HasValue("value"): + # if it is evaluated, then take the value + resp_2_value = resp_2_buffered_data.GetValue("value") + else: + # if not, compute the value + resp_2_value = self.response_function_1.CalculateValue() + resp_2_buffered_data.SetValue("value", resp_2_value) + + # now do the binary arithmetics. + if self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.ADD: + return resp_1_value + resp_2_value + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.SUBTRACT: + return resp_1_value - resp_2_value + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.MULTIPLY: + return resp_1_value * resp_2_value + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.DIVIDE: + return resp_1_value / resp_2_value + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.POWER: + return resp_1_value ** resp_2_value + + def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: + resp_1_physical_variable_collective_expressions = self.__EvaluateGradient(self.response_function_1, physical_variable_collective_expressions) + resp_2_physical_variable_collective_expressions = self.__EvaluateGradient(self.response_function_2, physical_variable_collective_expressions) + v1 = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData().GetValue("value") + v2 = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData().GetValue("value") + + for variable, collective_expression in physical_variable_collective_expressions.items(): + for result, g1, g2 in zip(collective_expression.GetContainerExpressions(), resp_1_physical_variable_collective_expressions[variable].GetContainerExpressions(), resp_2_physical_variable_collective_expressions[variable].GetContainerExpressions()): + if self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.ADD: + result.SetExpression((g1 + g2).GetExpression()) + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.SUBTRACT: + result.SetExpression((g1 - g2).GetExpression()) + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.MULTIPLY: + result.SetExpression((g1 * v2 + g2 * v1).GetExpression()) + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.DIVIDE: + result.SetExpression((g1 / v2 - g2 * (v1 / v2 ** 2)).GetExpression()) + elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.POWER: + result.SetExpression(((g1 * v2 / v1 + g2 * log(v1)) * (v1 ** v2)).GetExpression()) + + def __str__(self) -> str: + return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = {self.model_part.FullName()}]" \ No newline at end of file From c5339797239c936325199c4813b43ceb926cb5a9 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Tue, 23 Jul 2024 13:14:25 +0200 Subject: [PATCH 03/36] minor --- .../responses/binary_operator_response_function.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index e23a9e23b72d..b8cf4b99edd2 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -10,6 +10,7 @@ from KratosMultiphysics.OptimizationApplication.utilities.model_part_utilities import ModelPartOperation from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView +from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateGradient class BinaryOperatorResponseFunction(ResponseFunction): class BinaryOperator(str, Enum): @@ -90,10 +91,13 @@ def CalculateValue(self) -> float: return resp_1_value ** resp_2_value def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: - resp_1_physical_variable_collective_expressions = self.__EvaluateGradient(self.response_function_1, physical_variable_collective_expressions) - resp_2_physical_variable_collective_expressions = self.__EvaluateGradient(self.response_function_2, physical_variable_collective_expressions) - v1 = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData().GetValue("value") - v2 = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData().GetValue("value") + resp_1_buffered_data = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData() + resp_2_buffered_data = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData() + + resp_1_physical_variable_collective_expressions = EvaluateGradient(self.response_function_1, resp_1_buffered_data, physical_variable_collective_expressions) + resp_2_physical_variable_collective_expressions = EvaluateGradient(self.response_function_2, resp_2_buffered_data, physical_variable_collective_expressions) + v1: float = resp_1_buffered_data.GetValue("value") + v2: float = resp_2_buffered_data.GetValue("value") for variable, collective_expression in physical_variable_collective_expressions.items(): for result, g1, g2 in zip(collective_expression.GetContainerExpressions(), resp_1_physical_variable_collective_expressions[variable].GetContainerExpressions(), resp_2_physical_variable_collective_expressions[variable].GetContainerExpressions()): @@ -106,7 +110,7 @@ def CalculateGradient(self, physical_variable_collective_expressions: 'dict[Supp elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.DIVIDE: result.SetExpression((g1 / v2 - g2 * (v1 / v2 ** 2)).GetExpression()) elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.POWER: - result.SetExpression(((g1 * v2 / v1 + g2 * log(v1)) * (v1 ** v2)).GetExpression()) + result.SetExpression(((g1 * (v2 / v1) + g2 * log(v1)) * (v1 ** v2)).GetExpression()) def __str__(self) -> str: return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = {self.model_part.FullName()}]" \ No newline at end of file From 59cd9359841ac9ffb5af3224643727bb704bc7f4 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Tue, 23 Jul 2024 13:24:15 +0200 Subject: [PATCH 04/36] add literal value response function --- .../literal_value_response_function.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 applications/OptimizationApplication/python_scripts/responses/literal_value_response_function.py diff --git a/applications/OptimizationApplication/python_scripts/responses/literal_value_response_function.py b/applications/OptimizationApplication/python_scripts/responses/literal_value_response_function.py new file mode 100644 index 000000000000..f9b55a812697 --- /dev/null +++ b/applications/OptimizationApplication/python_scripts/responses/literal_value_response_function.py @@ -0,0 +1,35 @@ +import KratosMultiphysics as Kratos +import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction +from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes + +class LiteralValueResponseFunction(ResponseFunction): + def __init__(self, value: float): + super().__init__(str(value)) + + self.value = value + + def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]': + return [] + + def Initialize(self) -> None: + pass + + def Check(self) -> None: + pass + + def Finalize(self) -> None: + pass + + def GetInfluencingModelPart(self) -> Kratos.ModelPart: + raise RuntimeError(f"The literal value response function does not have an influencing model part.") + + def CalculateValue(self) -> float: + return self.value + + def CalculateGradient(self, _: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: + raise RuntimeError(f"The literal value response function does not depend on any variable, hence no gradients.") + + def __str__(self) -> str: + return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}]" \ No newline at end of file From 28263398fb5782c89df049f7e7c04544d53bbdab Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 14:48:05 +0530 Subject: [PATCH 05/36] update binary operator resp func --- .../binary_operator_response_function.py | 84 +++++++------------ 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index b8cf4b99edd2..bff306526c9d 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -1,5 +1,4 @@ from typing import Optional -from enum import Enum from math import log import KratosMultiphysics as Kratos @@ -10,25 +9,14 @@ from KratosMultiphysics.OptimizationApplication.utilities.model_part_utilities import ModelPartOperation from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView +from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateValue from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateGradient +from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import BinaryOperator class BinaryOperatorResponseFunction(ResponseFunction): - class BinaryOperator(str, Enum): - ADD = "+" - SUBTRACT = "-" - MULTIPLY = "*" - DIVIDE = "/" - POWER = "^" - def __init__(self, model: Kratos.Model, response_function_1: ResponseFunction, response_function_2: ResponseFunction, binary_operator: BinaryOperator, optimization_problem: OptimizationProblem): super().__init__(f"({response_function_1.GetName()}{binary_operator.value}{response_function_2.GetName()})") - if response_function_1 not in optimization_problem.GetListOfResponses(): - optimization_problem.AddComponent(response_function_1) - - if response_function_2 not in optimization_problem.GetListOfResponses(): - optimization_problem.AddComponent(response_function_2) - self.model = model self.optimization_problem = optimization_problem self.response_function_1 = response_function_1 @@ -44,7 +32,13 @@ def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFie def Initialize(self) -> None: self.response_function_1.Initialize() self.response_function_2.Initialize() - self.model_part = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", [self.response_function_1.GetInfluencingModelPart().FullName(), self.response_function_2.GetInfluencingModelPart().FullName()], False).GetModelPart() + + if len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0 and len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0: + self.model_part = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", [self.response_function_1.GetInfluencingModelPart().FullName(), self.response_function_2.GetInfluencingModelPart().FullName()], False).GetModelPart() + elif len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0: + self.model_part = self.response_function_1.GetInfluencingModelPart() + elif len(self.response_function_2.GetImplementedPhysicalKratosVariables()) != 0: + self.model_part = self.response_function_2.GetInfluencingModelPart() def Check(self) -> None: self.response_function_1.Check() @@ -58,59 +52,45 @@ def GetInfluencingModelPart(self) -> Kratos.ModelPart: return self.model_part def CalculateValue(self) -> float: - # check whether the response function 1 is already evaluated for current step - resp_1_buffered_data = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData() - if resp_1_buffered_data.HasValue("value"): - # if it is evaluated, then take the value - resp_1_value = resp_1_buffered_data.GetValue("value") - else: - # if not, compute the value - resp_1_value = self.response_function_1.CalculateValue() - resp_1_buffered_data.SetValue("value", resp_1_value) - - # check whether the response function 2 is already evaluated for current step - resp_2_buffered_data = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData() - if resp_2_buffered_data.HasValue("value"): - # if it is evaluated, then take the value - resp_2_value = resp_2_buffered_data.GetValue("value") - else: - # if not, compute the value - resp_2_value = self.response_function_1.CalculateValue() - resp_2_buffered_data.SetValue("value", resp_2_value) + v1 = EvaluateValue(self.response_function_1, self.optimization_problem) + v2 = EvaluateValue(self.response_function_2, self.optimization_problem) # now do the binary arithmetics. - if self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.ADD: - return resp_1_value + resp_2_value - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.SUBTRACT: - return resp_1_value - resp_2_value - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.MULTIPLY: - return resp_1_value * resp_2_value - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.DIVIDE: - return resp_1_value / resp_2_value - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.POWER: - return resp_1_value ** resp_2_value + if self.binary_operator == BinaryOperator.ADD: + return v1 + v2 + elif self.binary_operator == BinaryOperator.SUBTRACT: + return v1 - v2 + elif self.binary_operator == BinaryOperator.MULTIPLY: + return v1 * v2 + elif self.binary_operator == BinaryOperator.DIVIDE: + return v1 / v2 + elif self.binary_operator == BinaryOperator.POWER: + return v1 ** v2 def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: resp_1_buffered_data = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData() resp_2_buffered_data = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData() + v1 = EvaluateValue(self.response_function_1, self.optimization_problem) + v2 = EvaluateValue(self.response_function_2, self.optimization_problem) resp_1_physical_variable_collective_expressions = EvaluateGradient(self.response_function_1, resp_1_buffered_data, physical_variable_collective_expressions) resp_2_physical_variable_collective_expressions = EvaluateGradient(self.response_function_2, resp_2_buffered_data, physical_variable_collective_expressions) - v1: float = resp_1_buffered_data.GetValue("value") - v2: float = resp_2_buffered_data.GetValue("value") for variable, collective_expression in physical_variable_collective_expressions.items(): for result, g1, g2 in zip(collective_expression.GetContainerExpressions(), resp_1_physical_variable_collective_expressions[variable].GetContainerExpressions(), resp_2_physical_variable_collective_expressions[variable].GetContainerExpressions()): - if self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.ADD: + if self.binary_operator == BinaryOperator.ADD: result.SetExpression((g1 + g2).GetExpression()) - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.SUBTRACT: + elif self.binary_operator == BinaryOperator.SUBTRACT: result.SetExpression((g1 - g2).GetExpression()) - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.MULTIPLY: + elif self.binary_operator == BinaryOperator.MULTIPLY: result.SetExpression((g1 * v2 + g2 * v1).GetExpression()) - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.DIVIDE: + elif self.binary_operator == BinaryOperator.DIVIDE: result.SetExpression((g1 / v2 - g2 * (v1 / v2 ** 2)).GetExpression()) - elif self.binary_operator == BinaryOperatorResponseFunction.BinaryOperator.POWER: + elif self.binary_operator == BinaryOperator.POWER: result.SetExpression(((g1 * (v2 / v1) + g2 * log(v1)) * (v1 ** v2)).GetExpression()) def __str__(self) -> str: - return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = {self.model_part.FullName()}]" \ No newline at end of file + if self.model_part is not None: + return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = {self.model_part.FullName()}]" + else: + return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = n/a ]" \ No newline at end of file From 1adf72ef34a77a39ca92902bedc83e23afd9cc1a Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 14:48:15 +0530 Subject: [PATCH 06/36] update response utilities --- .../utilities/response_utilities.py | 144 +++++++++++++++++- 1 file changed, 136 insertions(+), 8 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index df0bdf012e4d..758a1d885d01 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -1,10 +1,39 @@ +from enum import Enum + +import KratosMultiphysics as Kratos import KratosMultiphysics.OptimizationApplication as KratosOA from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes from KratosMultiphysics.OptimizationApplication.utilities.buffered_dict import BufferedDict +from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem +from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView + +class BinaryOperator(str, Enum): + POWER = "^" + DIVIDE = "/" + MULTIPLY = "*" + ADD = "+" + SUBTRACT = "-" + +BinaryOperatorValues = [operator.value for operator in BinaryOperator] + +BinaryOperatorValuesMap = dict([(operator.value, operator) for operator in BinaryOperator]) + +def EvaluateValue(response_function: ResponseFunction, optimization_problem: OptimizationProblem) -> float: + if optimization_problem.HasResponse(response_function): + response_data = ComponentDataView(response_function, optimization_problem) + if response_data.HasDataBuffer(): + response_data_buffer = response_data.GetBufferedData() + if not response_data_buffer.HasValue("value"): + response_data_buffer.SetValue("value", response_function.CalculateValue()) + return response_data_buffer.GetValue("value") + else: + return response_function.CalculateValue() + else: + return response_function.CalculateValue() -def EvaluateGradient(response_function: ResponseFunction, response_buffered_data: BufferedDict, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]': +def EvaluateGradient(response_function: ResponseFunction, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]', optimization_problem: OptimizationProblem) -> 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]': # first get the sub_collective expressions for implemented physical kratos variables resp_physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} for variable, collective_expression in physical_variable_collective_expressions.items(): @@ -12,18 +41,26 @@ def EvaluateGradient(response_function: ResponseFunction, response_buffered_data resp_physical_variable_collective_expressions[variable] = collective_expression.Clone() resp_physical_variable_collective_expressions_to_evaluate: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} - for variable, collective_expression in resp_physical_variable_collective_expressions.items(): - # first check whether the gradients have been already evaluated. if so, take the gradients. - if response_buffered_data.HasValue(f"d{response_function.GetName()}_d{variable.Name()}"): - resp_physical_variable_collective_expressions[variable] = response_buffered_data.GetValue(f"d{response_function.GetName()}_d{variable.Name()}") - else: + response_data = ComponentDataView(response_function, optimization_problem) + if response_data.HasDataBuffer(): + response_data_buffer = response_data.GetBufferedData() + for variable, collective_expression in resp_physical_variable_collective_expressions.items(): + # first check whether the gradients have been already evaluated. if so, take the gradients. + if response_data_buffer.HasValue(f"d{response_function.GetName()}_d{variable.Name()}"): + resp_physical_variable_collective_expressions[variable] = response_data_buffer.GetValue(f"d{response_function.GetName()}_d{variable.Name()}") + else: + # gradients have not yet evaluated. put it to the dictionary for later evaluation + resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression + else: + for variable, collective_expression in resp_physical_variable_collective_expressions.items(): # gradients have not yet evaluated. put it to the dictionary for later evaluation resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression if len(resp_physical_variable_collective_expressions_to_evaluate) != 0: response_function.CalculateGradient(resp_physical_variable_collective_expressions_to_evaluate) for variable, collective_expression in resp_physical_variable_collective_expressions_to_evaluate.items(): - response_buffered_data.SetValue(f"d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) + if response_data.HasDataBuffer(): + response_data.GetBufferedData().SetValue(f"d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) resp_physical_variable_collective_expressions[variable] = collective_expression # now add zero values collective expressions to variables for which the response function does not have dependence @@ -31,4 +68,95 @@ def EvaluateGradient(response_function: ResponseFunction, response_buffered_data if variable not in response_function.GetImplementedPhysicalKratosVariables(): resp_physical_variable_collective_expressions[variable] = collective_expression * 0.0 - return resp_physical_variable_collective_expressions \ No newline at end of file + return resp_physical_variable_collective_expressions + +def GetResponseFunction(current_value: str, optimization_problem: OptimizationProblem) -> ResponseFunction: + from KratosMultiphysics.OptimizationApplication.responses.literal_value_response_function import LiteralValueResponseFunction + list_of_response_names: 'list[str]' = [response.GetName() for response in optimization_problem.GetListOfResponses()] + if current_value == "": + return LiteralValueResponseFunction(0.0) + else: + try: + return LiteralValueResponseFunction(float(current_value)) + except: + # first check whether the value exists in list of responses + if current_value in list_of_response_names: + return optimization_problem.GetResponse(current_value) + else: + raise RuntimeError(f"The response named \"{current_value}\" not defined.") + +def GetValuesAndOperators(response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': + value = "" + responses: 'list[ResponseFunction]' = [] + operators: 'list[str]' = [] + for c in response_expression: + if c in BinaryOperatorValues: + responses.append(GetResponseFunction(value, optimization_problem)) + operators.append(c) + value = "" + else: + value += c + + # add the last value + responses.append(GetResponseFunction(value, optimization_problem)) + + return responses, operators + +def EvaluateResponseExpression(model: Kratos.Model, response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: + from KratosMultiphysics.OptimizationApplication.responses.binary_operator_response_function import BinaryOperatorResponseFunction + + response_expression = response_expression.replace(" ", "") + responses, operators = GetValuesAndOperators(response_expression, optimization_problem) + + def __evaluate_operator(list_of_operators: 'list[str]') -> None: + operator_index = 0 + while operator_index < len(operators): + if operators[operator_index] in list_of_operators: + left_operand = responses[operator_index] + right_operand = responses[operator_index + 1] + + if not optimization_problem.HasResponse(left_operand) and left_operand.GetImplementedPhysicalKratosVariables() != []: + # the left operand is not in the optimization, and it will not be accessible since it will + # be within the BinaryOperatorResponseFunction. Hence adding it to optimization problem + # so that we can put the intermediate values and gradients to the optimization problem. + optimization_problem.AddComponent(left_operand) + + if not optimization_problem.HasResponse(right_operand) and right_operand.GetImplementedPhysicalKratosVariables() != []: + # the right operand is not in the optimization, and it will not be accessible since it will + # be within the BinaryOperatorResponseFunction. Hence adding it to optimization problem + # so that we can put the intermediate values and gradients to the optimization problem. + optimization_problem.AddComponent(right_operand) + + # now check whether the left operand is there in the optimization problem + if optimization_problem.HasResponse(left_operand): + left_operand_component_data_view = ComponentDataView(left_operand, optimization_problem) + # if it is in the optimization problem, and no data buffer has been defined, then define one. + if not left_operand_component_data_view.HasDataBuffer(): + left_operand_component_data_view.SetDataBuffer(1) + + # now check whether the right operand is there in the optimization problem + if optimization_problem.HasResponse(right_operand): + right_operand_component_data_view = ComponentDataView(right_operand, optimization_problem) + # if it is in the optimization problem, and no data buffer has been defined, then define one. + if not right_operand_component_data_view.HasDataBuffer(): + right_operand_component_data_view.SetDataBuffer(1) + + resultant_operator = BinaryOperatorResponseFunction(model, left_operand, right_operand, BinaryOperatorValuesMap[operators[operator_index]], optimization_problem) + if not optimization_problem.HasResponse(resultant_operator.GetName()): + # There is no already existing response with the same operation. Then use the new one. + responses[operator_index] = resultant_operator + else: + # There is an already existing response with the same operation. Then take it. + responses[operator_index] = optimization_problem.GetResponse(resultant_operator.GetName()) + + del responses[operator_index + 1] + del operators[operator_index] + else: + operator_index += 1 + + # according to BODMAS + __evaluate_operator(["^"]) + __evaluate_operator(["*", "/"]) + __evaluate_operator(["+", "-"]) + + return responses[0] \ No newline at end of file From ed91d626580e0d32b3878317c40d9a965cb68a1b Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 14:48:22 +0530 Subject: [PATCH 07/36] add tests --- .../tests/test_response_utilities.py | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 applications/OptimizationApplication/tests/test_response_utilities.py diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py new file mode 100644 index 000000000000..e9add720684b --- /dev/null +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -0,0 +1,177 @@ + +import KratosMultiphysics as Kratos +import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem +from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView +from KratosMultiphysics.OptimizationApplication.responses.discrete_value_residual_response_function import DiscreteValueResidualResponseFunction +from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateResponseExpression + +# Import KratosUnittest +import KratosMultiphysics.KratosUnittest as kratos_unittest + +class TestResponseUtilities(kratos_unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.model = Kratos.Model() + cls.model_part = cls.model.CreateModelPart("test") + + for i in range(10): + node: Kratos.Node = cls.model_part.CreateNewNode(i + 1, i, i + 1, i + 2) + node.SetValue(Kratos.PRESSURE, i + 1) + + r1_params = Kratos.Parameters("""{ + "evaluated_model_part_names" : [ + "test" + ], + "container_type" : "node_non_historical", + "variable_name" : "PRESSURE", + "residual_type" : "exact", + "list_of_discrete_values": [-1.0, -2.0, -3.0] + }""") + cls.r1 = DiscreteValueResidualResponseFunction("r1", cls.model, r1_params) + cls.r1.Initialize() + + r2_params = Kratos.Parameters("""{ + "evaluated_model_part_names" : [ + "test" + ], + "container_type" : "node_non_historical", + "variable_name" : "PRESSURE", + "residual_type" : "exact", + "list_of_discrete_values": [-10.0, -20.0, -30.0] + }""") + cls.r2 = DiscreteValueResidualResponseFunction("r2", cls.model, r2_params) + cls.r2.Initialize() + + def setUp(self) -> None: + self.optimization_problem = OptimizationProblem() + self.optimization_problem.AddComponent(self.r1) + ComponentDataView(self.r1, self.optimization_problem).SetDataBuffer(1) + self.optimization_problem.AddComponent(self.r2) + ComponentDataView(self.r2, self.optimization_problem).SetDataBuffer(1) + + def test_LiteralResponseCalculateValue1(self): + eval_resp = EvaluateResponseExpression(self.model, "4.0 + 6.0", self.optimization_problem) + eval_resp.Initialize() + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, 10.0) + + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + self.assertFalse(self.optimization_problem.HasResponse("6.0")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0+6.0)")) + + def test_LiteralResponseCalculateValue2(self): + eval_resp = EvaluateResponseExpression(self.model, "4.0 * 6.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, 24.0) + + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + self.assertFalse(self.optimization_problem.HasResponse("6.0")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0*6.0)")) + + def test_LiteralResponseCalculateValue3(self): + eval_resp = EvaluateResponseExpression(self.model, "4.0 / 8.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, 0.5) + + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + self.assertFalse(self.optimization_problem.HasResponse("8.0")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0/6.0)")) + + def test_LiteralResponseCalculateValue4(self): + eval_resp = EvaluateResponseExpression(self.model, "4.0 - 8.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, -4.0) + + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + self.assertFalse(self.optimization_problem.HasResponse("8.0")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0-6.0)")) + + def test_LiteralResponseCalculateValue5(self): + eval_resp = EvaluateResponseExpression(self.model, "4.0 ^ 2.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, 16.0) + + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + self.assertFalse(self.optimization_problem.HasResponse("8.0")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0^6.0)")) + + def test_LiteralResponseCalculateValue6(self): + eval_resp = EvaluateResponseExpression(self.model, "4.0 - 8.0 + 3.0 * 2.0 / 5.0 ^ 2", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, -3.76) + + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + self.assertFalse(self.optimization_problem.HasResponse("8.0")) + self.assertFalse(self.optimization_problem.HasResponse("3.0")) + self.assertFalse(self.optimization_problem.HasResponse("2.0")) + self.assertFalse(self.optimization_problem.HasResponse("5.0")) + + def test_CombinedResponseCalculateValue1(self): + eval_resp = EvaluateResponseExpression(self.model, "r1", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, self.r1.CalculateValue()) + + self.assertTrue(self.optimization_problem.HasResponse(eval_resp)) + self.assertTrue(self.optimization_problem.HasResponse("r1")) + self.assertEqual(eval_resp.GetName(), "r1") + + # following is the resultant response function, hence the value storage is managed by the ResponseRoutine + self.assertFalse(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + + def test_CombinedResponseCalculateValue2(self): + eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + r2", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, self.r1.CalculateValue() + 2.0 * self.r2.CalculateValue()) + + # followings are intermediate responses, or leaf responses. Therefore they need to be + # in the optimization problem + self.assertTrue(self.optimization_problem.HasResponse("r1")) + self.assertTrue(self.optimization_problem.HasResponse("r2")) + self.assertTrue(self.optimization_problem.HasResponse("(r1+r2)")) + + # followings are intermediate responses, so Algorithm, ResponseRoutine do not see them. Therefore + # the value storage is managed by the ResponseFunction it self. + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1+r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) + + # following is the resultant response function, hence the value storage is managed by the ResponseRoutine + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+r2)")) + + # def test_CombinedResponseCalculateValue3(self): + # eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + 4.0", self.optimization_problem) + # eval_resp.Initialize() + # self.assertEqual(eval_resp.CalculateValue(), self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0) + # self.assertTrue(eval_resp in self.optimization_problem.GetListOfResponses()) + + # def test_CombinedResponseCalculateValue4(self): + # eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 + r2 / 3.0 + 4.0", self.optimization_problem) + # eval_resp.Initialize() + # self.assertEqual(eval_resp.CalculateValue(), self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 + 4.0 + 3.5) + # self.assertTrue(eval_resp in self.optimization_problem.GetListOfResponses()) + + # def test_CombinedResponseCalculateValue5(self): + # eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 * r2 / 3.0 + 4.0", self.optimization_problem) + # eval_resp.Initialize() + # self.assertEqual(eval_resp.CalculateValue(), self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 + 4.0 + 3.5) + # print(self.optimization_problem.GetProblemDataContainer()) + # self.assertTrue(eval_resp in self.optimization_problem.GetListOfResponses()) + +if __name__ == "__main__": + kratos_unittest.main() \ No newline at end of file From 2f454225930c124ed08f314c222bbecb4b0e4a2a Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 14:54:32 +0530 Subject: [PATCH 08/36] bug fix --- .../responses/binary_operator_response_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index bff306526c9d..c1c43a9c3d79 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -33,7 +33,7 @@ def Initialize(self) -> None: self.response_function_1.Initialize() self.response_function_2.Initialize() - if len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0 and len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0: + if len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0 and len(self.response_function_2.GetImplementedPhysicalKratosVariables()) != 0: self.model_part = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", [self.response_function_1.GetInfluencingModelPart().FullName(), self.response_function_2.GetInfluencingModelPart().FullName()], False).GetModelPart() elif len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0: self.model_part = self.response_function_1.GetInfluencingModelPart() From 6df4f1fca10c55b4945c0a556d51620a95b43378 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 15:16:31 +0530 Subject: [PATCH 09/36] add more tests --- .../tests/test_response_utilities.py | 104 ++++++++++++++---- 1 file changed, 82 insertions(+), 22 deletions(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index e9add720684b..98dad7549451 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -17,7 +17,7 @@ def setUpClass(cls): for i in range(10): node: Kratos.Node = cls.model_part.CreateNewNode(i + 1, i, i + 1, i + 2) - node.SetValue(Kratos.PRESSURE, i + 1) + node.SetValue(Kratos.PRESSURE, (i + 1) / 100.0) r1_params = Kratos.Parameters("""{ "evaluated_model_part_names" : [ @@ -26,7 +26,7 @@ def setUpClass(cls): "container_type" : "node_non_historical", "variable_name" : "PRESSURE", "residual_type" : "exact", - "list_of_discrete_values": [-1.0, -2.0, -3.0] + "list_of_discrete_values": [-1.0e-2, -2.0e-2, -3.0e-2] }""") cls.r1 = DiscreteValueResidualResponseFunction("r1", cls.model, r1_params) cls.r1.Initialize() @@ -38,7 +38,7 @@ def setUpClass(cls): "container_type" : "node_non_historical", "variable_name" : "PRESSURE", "residual_type" : "exact", - "list_of_discrete_values": [-10.0, -20.0, -30.0] + "list_of_discrete_values": [-1e-3, -2e-3, -3e-3] }""") cls.r2 = DiscreteValueResidualResponseFunction("r2", cls.model, r2_params) cls.r2.Initialize() @@ -136,7 +136,7 @@ def test_CombinedResponseCalculateValue2(self): eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + r2", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() - self.assertEqual(eval_value, self.r1.CalculateValue() + 2.0 * self.r2.CalculateValue()) + self.assertAlmostEqual(eval_value, self.r1.CalculateValue() + 2.0 * self.r2.CalculateValue(), 12) # followings are intermediate responses, or leaf responses. Therefore they need to be # in the optimization problem @@ -154,24 +154,84 @@ def test_CombinedResponseCalculateValue2(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+r2)")) - # def test_CombinedResponseCalculateValue3(self): - # eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + 4.0", self.optimization_problem) - # eval_resp.Initialize() - # self.assertEqual(eval_resp.CalculateValue(), self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0) - # self.assertTrue(eval_resp in self.optimization_problem.GetListOfResponses()) - - # def test_CombinedResponseCalculateValue4(self): - # eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 + r2 / 3.0 + 4.0", self.optimization_problem) - # eval_resp.Initialize() - # self.assertEqual(eval_resp.CalculateValue(), self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 + 4.0 + 3.5) - # self.assertTrue(eval_resp in self.optimization_problem.GetListOfResponses()) - - # def test_CombinedResponseCalculateValue5(self): - # eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 * r2 / 3.0 + 4.0", self.optimization_problem) - # eval_resp.Initialize() - # self.assertEqual(eval_resp.CalculateValue(), self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 + 4.0 + 3.5) - # print(self.optimization_problem.GetProblemDataContainer()) - # self.assertTrue(eval_resp in self.optimization_problem.GetListOfResponses()) + def test_CombinedResponseCalculateValue3(self): + eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + 4.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0) + + # followings are intermediate responses, or leaf responses. Therefore they need to be + # in the optimization problem + self.assertTrue(self.optimization_problem.HasResponse("r1")) + self.assertTrue(self.optimization_problem.HasResponse("r2")) + self.assertTrue(self.optimization_problem.HasResponse("(r1+r2)")) + self.assertFalse(self.optimization_problem.HasResponse("4.0")) + + # followings are intermediate responses, so Algorithm, ResponseRoutine do not see them. Therefore + # the value storage is managed by the ResponseFunction it self. + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1+r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) + + # following is the resultant response function, hence the value storage is managed by the ResponseRoutine + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+4.0)")) + + def test_CombinedResponseCalculateValue4(self): + eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 + r2 / 3.0 - 4.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 - 4.0 + 3.5) + + # followings are intermediate responses, or leaf responses. Therefore they need to be + # in the optimization problem + self.assertTrue(self.optimization_problem.HasResponse("r1")) + self.assertTrue(self.optimization_problem.HasResponse("r2")) + self.assertTrue(self.optimization_problem.HasResponse("(r1*2.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(r2/3.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(3.5+(r1*2.0))")) + self.assertTrue(self.optimization_problem.HasResponse("((3.5+(r1*2.0))+(r2/3.0))")) + + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r2/3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(r1*2.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(r1*2.0))+(r2/3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + + # following is the resultant response function, hence the value storage is managed by the ResponseRoutine + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse("(((3.5+(r1*2.0))+(r2/3.0))-4.0)")) + + def test_CombinedResponseCalculateValue5(self): + eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 * r2 / 3.0 - 4.0 + r1 ^ r2", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 - 4.0 + 3.5 + self.r1.CalculateValue() ** self.r2.CalculateValue()) + + # followings are intermediate responses, or leaf responses. Therefore they need to be + # in the optimization problem + self.assertTrue(self.optimization_problem.HasResponse("r1")) + self.assertTrue(self.optimization_problem.HasResponse("r2")) + self.assertTrue(self.optimization_problem.HasResponse("(r1*2.0)")) + self.assertTrue(self.optimization_problem.HasResponse("((r1*2.0)*r2)")) + self.assertTrue(self.optimization_problem.HasResponse("(((r1*2.0)*r2)/3.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(r1^r2)")) + self.assertTrue(self.optimization_problem.HasResponse("(3.5+(((r1*2.0)*r2)/3.0))")) + self.assertTrue(self.optimization_problem.HasResponse("((3.5+(((r1*2.0)*r2)/3.0))-4.0)")) + + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((r1*2.0)*r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(((r1*2.0)*r2)/3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1^r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(((r1*2.0)*r2)/3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(((r1*2.0)*r2)/3.0))-4.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + + # following is the resultant response function, hence the value storage is managed by the ResponseRoutine + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse(eval_resp.GetName())) if __name__ == "__main__": kratos_unittest.main() \ No newline at end of file From 994b27df3060ac0ef769bc958488c9aeebd6e1a7 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 16:01:31 +0530 Subject: [PATCH 10/36] add log response func --- .../responses/log_response_function.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 applications/OptimizationApplication/python_scripts/responses/log_response_function.py diff --git a/applications/OptimizationApplication/python_scripts/responses/log_response_function.py b/applications/OptimizationApplication/python_scripts/responses/log_response_function.py new file mode 100644 index 000000000000..aaaadec6a0a1 --- /dev/null +++ b/applications/OptimizationApplication/python_scripts/responses/log_response_function.py @@ -0,0 +1,44 @@ +from math import log +import KratosMultiphysics as Kratos +import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction +from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem +from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateValue +from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateGradient + +class LogResponseFunction(ResponseFunction): + def __init__(self, response_function: ResponseFunction, optimization_problem: OptimizationProblem): + super().__init__(f"log({response_function.GetName()})") + self.response_function = response_function + self.optimization_problem = optimization_problem + + def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]': + return self.response_function.GetImplementedPhysicalKratosVariables() + + def Initialize(self) -> None: + self.response_function.Initialize() + + def Check(self) -> None: + self.response_function.Check() + + def Finalize(self) -> None: + self.response_function.Finalize() + + def GetInfluencingModelPart(self) -> Kratos.ModelPart: + return self.response_function.GetInfluencingModelPart() + + def CalculateValue(self) -> float: + return log(EvaluateValue(self.response_function, self.optimization_problem)) + + def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: + v = EvaluateValue(self.response_function, self.optimization_problem) + resp_physical_variable_collective_expressions = EvaluateGradient(self.response_function, physical_variable_collective_expressions, self.optimization_problem) + + for variable, collective_expression in physical_variable_collective_expressions.items(): + for result, g in zip(collective_expression.GetContainerExpressions(), resp_physical_variable_collective_expressions[variable].GetContainerExpressions()): + result.SetExpression((g / v).GetExpression()) + + def __str__(self) -> str: + return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}]" \ No newline at end of file From 28fbcac8d277ee3ede9cbee316908747dac3ef5c Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 16:01:38 +0530 Subject: [PATCH 11/36] update to while loop --- .../utilities/response_utilities.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index 758a1d885d01..d10aec807191 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -5,7 +5,6 @@ from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes -from KratosMultiphysics.OptimizationApplication.utilities.buffered_dict import BufferedDict from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView @@ -20,6 +19,13 @@ class BinaryOperator(str, Enum): BinaryOperatorValuesMap = dict([(operator.value, operator) for operator in BinaryOperator]) +def GetFunctionsMap() -> 'dict[str, ResponseFunction]': + # import functions + from KratosMultiphysics.OptimizationApplication.responses.log_response_function import LogResponseFunction + return { + "log": LogResponseFunction + } + def EvaluateValue(response_function: ResponseFunction, optimization_problem: OptimizationProblem) -> float: if optimization_problem.HasResponse(response_function): response_data = ComponentDataView(response_function, optimization_problem) @@ -86,19 +92,25 @@ def GetResponseFunction(current_value: str, optimization_problem: OptimizationPr raise RuntimeError(f"The response named \"{current_value}\" not defined.") def GetValuesAndOperators(response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': - value = "" responses: 'list[ResponseFunction]' = [] operators: 'list[str]' = [] - for c in response_expression: - if c in BinaryOperatorValues: - responses.append(GetResponseFunction(value, optimization_problem)) - operators.append(c) - value = "" + + index = 0 + current_word = "" + while index < len(response_expression): + current_char = response_expression[index] + + if current_char in BinaryOperatorValues: + responses.append(GetResponseFunction(current_word, optimization_problem)) + operators.append(current_char) + current_word = "" else: - value += c + current_word += current_char + + index += 1 - # add the last value - responses.append(GetResponseFunction(value, optimization_problem)) + # add the last current_word + responses.append(GetResponseFunction(current_word, optimization_problem)) return responses, operators From 46200d46cbcd8150fe012cb88ce86da206e872dc Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 22:52:17 +0530 Subject: [PATCH 12/36] fix division operator --- .../responses/binary_operator_response_function.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index c1c43a9c3d79..77814e8b98e9 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -15,7 +15,10 @@ class BinaryOperatorResponseFunction(ResponseFunction): def __init__(self, model: Kratos.Model, response_function_1: ResponseFunction, response_function_2: ResponseFunction, binary_operator: BinaryOperator, optimization_problem: OptimizationProblem): - super().__init__(f"({response_function_1.GetName()}{binary_operator.value}{response_function_2.GetName()})") + if binary_operator == BinaryOperator.DIVIDE: + super().__init__(f"({response_function_1.GetName()}÷{response_function_2.GetName()})") + else: + super().__init__(f"({response_function_1.GetName()}{binary_operator.value}{response_function_2.GetName()})") self.model = model self.optimization_problem = optimization_problem From 0e8ff56c0d36797494b58dd4caebd51f9de642c5 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 22:52:35 +0530 Subject: [PATCH 13/36] add bracket usage for response expression --- .../utilities/response_utilities.py | 90 ++++++++++++++++--- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index d10aec807191..2e51250c38ec 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -1,3 +1,4 @@ +import re from enum import Enum import KratosMultiphysics as Kratos @@ -19,12 +20,30 @@ class BinaryOperator(str, Enum): BinaryOperatorValuesMap = dict([(operator.value, operator) for operator in BinaryOperator]) -def GetFunctionsMap() -> 'dict[str, ResponseFunction]': +def GetClosingBracketIndex(expression: str) -> int: + # this function assumes the starting point is without the opening bracket. + # it will give the position of the respective closing bracket + index = 0 + brackets_counter = 1 + while index < len(expression): + brackets_counter += (expression[index] == "(") + (expression[index] == ")") * -1 + if brackets_counter == 0: + return index + index += 1 + + raise RuntimeError(f"The closing bracket not found for expression ({expression}") + +def GetFunction(function_name: str, optimization_problem: OptimizationProblem, *args) -> ResponseFunction: # import functions from KratosMultiphysics.OptimizationApplication.responses.log_response_function import LogResponseFunction - return { + + functions_map: 'dict[str, type[ResponseFunction]]' = { "log": LogResponseFunction } + if function_name in functions_map.keys(): + return functions_map[function_name](*args, optimization_problem) + else: + raise RuntimeError(f"Undefined \"{function_name}\" function. Followings are supported function names:\n\t" + "\n\t".join(functions_map.keys())) def EvaluateValue(response_function: ResponseFunction, optimization_problem: OptimizationProblem) -> float: if optimization_problem.HasResponse(response_function): @@ -76,22 +95,56 @@ def EvaluateGradient(response_function: ResponseFunction, physical_variable_coll return resp_physical_variable_collective_expressions -def GetResponseFunction(current_value: str, optimization_problem: OptimizationProblem) -> ResponseFunction: +def GetResponseFunction(model: Kratos.Model, current_value: str, optimization_problem: OptimizationProblem) -> ResponseFunction: from KratosMultiphysics.OptimizationApplication.responses.literal_value_response_function import LiteralValueResponseFunction - list_of_response_names: 'list[str]' = [response.GetName() for response in optimization_problem.GetListOfResponses()] if current_value == "": return LiteralValueResponseFunction(0.0) else: try: + # first try to get a literal response function return LiteralValueResponseFunction(float(current_value)) except: - # first check whether the value exists in list of responses - if current_value in list_of_response_names: + # literal response function fails. Then the current value may hold one of the followings + # 1. A leaf response function name + # 2. A response expression + # 3. A function call + + if re.match(r"(^\w+)$", current_value) is not None: + # the match is a leaf response function name return optimization_problem.GetResponse(current_value) + elif re.match(r"(^\w+\()", current_value) is not None: + # the match is a function call + args_starting_index = current_value.index("(") + function_name = current_value[:args_starting_index] + + # now need to find the args correctly + args_list: 'list[str]' = [] + index = args_starting_index + 1 + current_arg = "" + while index < len(current_value) - 1: + current_char = current_value[index] + + if current_char == "(": + closing_bracket_pos = GetClosingBracketIndex(current_value[index+1:]) + current_arg += current_value[index:index+closing_bracket_pos+2] + index += closing_bracket_pos + 2 + continue + + if current_char == ",": + args_list.append(current_arg) + current_arg = "" + else: + current_arg += current_char + + index += 1 + + args_list.append(current_arg) + return GetFunction(function_name, optimization_problem, *[EvaluateResponseExpression(model, arg, optimization_problem) for arg in args_list]) else: - raise RuntimeError(f"The response named \"{current_value}\" not defined.") + # this is a response expression. + return EvaluateResponseExpression(model, current_value, optimization_problem) -def GetValuesAndOperators(response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': +def GetValuesAndOperators(model: Kratos.Model, response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': responses: 'list[ResponseFunction]' = [] operators: 'list[str]' = [] @@ -100,17 +153,28 @@ def GetValuesAndOperators(response_expression: str, optimization_problem: Optimi while index < len(response_expression): current_char = response_expression[index] + if current_char == "(": + # found opening bracket + closing_bracket_position = GetClosingBracketIndex(response_expression[index + 1:]) + if current_word != "": + current_word = f"{current_word}{response_expression[index:index+closing_bracket_position+2]}" + else: + current_word = response_expression[index + 1:index+closing_bracket_position + 1] + index += closing_bracket_position + 2 + continue + if current_char in BinaryOperatorValues: - responses.append(GetResponseFunction(current_word, optimization_problem)) + responses.append(GetResponseFunction(model, current_word, optimization_problem)) operators.append(current_char) current_word = "" - else: - current_word += current_char + index += 1 + continue + current_word += current_char index += 1 # add the last current_word - responses.append(GetResponseFunction(current_word, optimization_problem)) + responses.append(GetResponseFunction(model, current_word, optimization_problem)) return responses, operators @@ -118,7 +182,7 @@ def EvaluateResponseExpression(model: Kratos.Model, response_expression: str, op from KratosMultiphysics.OptimizationApplication.responses.binary_operator_response_function import BinaryOperatorResponseFunction response_expression = response_expression.replace(" ", "") - responses, operators = GetValuesAndOperators(response_expression, optimization_problem) + responses, operators = GetValuesAndOperators(model, response_expression, optimization_problem) def __evaluate_operator(list_of_operators: 'list[str]') -> None: operator_index = 0 From edc1eb7fd1cb91d9d5b9c54f18049630e2a21d51 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 22:52:50 +0530 Subject: [PATCH 14/36] add tests --- .../tests/test_response_utilities.py | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 98dad7549451..8f54f4622aa3 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -1,4 +1,6 @@ +from math import log + import KratosMultiphysics as Kratos import KratosMultiphysics.OptimizationApplication as KratosOA from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem @@ -188,16 +190,16 @@ def test_CombinedResponseCalculateValue4(self): self.assertTrue(self.optimization_problem.HasResponse("r1")) self.assertTrue(self.optimization_problem.HasResponse("r2")) self.assertTrue(self.optimization_problem.HasResponse("(r1*2.0)")) - self.assertTrue(self.optimization_problem.HasResponse("(r2/3.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(r2÷3.0)")) self.assertTrue(self.optimization_problem.HasResponse("(3.5+(r1*2.0))")) - self.assertTrue(self.optimization_problem.HasResponse("((3.5+(r1*2.0))+(r2/3.0))")) + self.assertTrue(self.optimization_problem.HasResponse("((3.5+(r1*2.0))+(r2÷3.0))")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r2/3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r2÷3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(r1*2.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(r1*2.0))+(r2/3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(r1*2.0))+(r2÷3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) @@ -215,19 +217,64 @@ def test_CombinedResponseCalculateValue5(self): self.assertTrue(self.optimization_problem.HasResponse("r2")) self.assertTrue(self.optimization_problem.HasResponse("(r1*2.0)")) self.assertTrue(self.optimization_problem.HasResponse("((r1*2.0)*r2)")) - self.assertTrue(self.optimization_problem.HasResponse("(((r1*2.0)*r2)/3.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(((r1*2.0)*r2)÷3.0)")) self.assertTrue(self.optimization_problem.HasResponse("(r1^r2)")) - self.assertTrue(self.optimization_problem.HasResponse("(3.5+(((r1*2.0)*r2)/3.0))")) - self.assertTrue(self.optimization_problem.HasResponse("((3.5+(((r1*2.0)*r2)/3.0))-4.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(3.5+(((r1*2.0)*r2)÷3.0))")) + self.assertTrue(self.optimization_problem.HasResponse("((3.5+(((r1*2.0)*r2)÷3.0))-4.0)")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((r1*2.0)*r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(((r1*2.0)*r2)/3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(((r1*2.0)*r2)÷3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1^r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(((r1*2.0)*r2)/3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(((r1*2.0)*r2)/3.0))-4.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(((r1*2.0)*r2)÷3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(((r1*2.0)*r2)÷3.0))-4.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + + # following is the resultant response function, hence the value storage is managed by the ResponseRoutine + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse(eval_resp.GetName())) + + def test_BracketResponseCalculateValue1(self): + eval_resp = EvaluateResponseExpression(self.model, "(4.0)", self.optimization_problem) + eval_resp.Initialize() + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, 4.0) + + def test_BracketResponseCalculateValue2(self): + eval_resp = EvaluateResponseExpression(self.model, "(4.0 + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, 72.0) + + def test_BracketResponseCalculateValue3(self): + eval_resp = EvaluateResponseExpression(self.model, "(4.0 + (r1 * 2) * (r2 ^ 2) + log((2+6)*(3+(4-2)/4)+6*r1) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) + eval_resp.Initialize() + eval_value = eval_resp.CalculateValue() + self.assertEqual(eval_value, (4.0 + (self.r1.CalculateValue() * 2) * (self.r2.CalculateValue() ** 2) + log((2+6)*(3+(4-2)/4)+6*self.r1.CalculateValue()) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0) + + # followings are intermediate responses, or leaf responses. Therefore they need to be + # in the optimization problem + self.assertTrue(self.optimization_problem.HasResponse("r1")) + self.assertTrue(self.optimization_problem.HasResponse("r2")) + self.assertTrue(self.optimization_problem.HasResponse("(r1*2.0)")) + self.assertTrue(self.optimization_problem.HasResponse("(r2^2.0)")) + self.assertTrue(self.optimization_problem.HasResponse("((r1*2.0)*(r2^2.0))")) + self.assertTrue(self.optimization_problem.HasResponse("(6.0*r1)")) + self.assertTrue(self.optimization_problem.HasResponse("((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))")) + self.assertTrue(self.optimization_problem.HasResponse("(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))")) + self.assertTrue(self.optimization_problem.HasResponse("log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))")) + + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r2^2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((r1*2.0)*(r2^2.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(6.0*r1)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))"), self.optimization_problem).GetBufferedData().HasValue("value")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) From ab522e51bd52e1f81f7a15c5889846125a648a42 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 28 Jul 2024 23:07:06 +0530 Subject: [PATCH 15/36] minor --- .../tests/test_response_utilities.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 8f54f4622aa3..30ecc6d0ac06 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -20,6 +20,7 @@ def setUpClass(cls): for i in range(10): node: Kratos.Node = cls.model_part.CreateNewNode(i + 1, i, i + 1, i + 2) node.SetValue(Kratos.PRESSURE, (i + 1) / 100.0) + node.SetValue(Kratos.TEMPERATURE, (i + 10) / 100.0) r1_params = Kratos.Parameters("""{ "evaluated_model_part_names" : [ @@ -45,12 +46,26 @@ def setUpClass(cls): cls.r2 = DiscreteValueResidualResponseFunction("r2", cls.model, r2_params) cls.r2.Initialize() + r3_params = Kratos.Parameters("""{ + "evaluated_model_part_names" : [ + "test" + ], + "container_type" : "node_non_historical", + "variable_name" : "TEMPERATURE", + "residual_type" : "exact", + "list_of_discrete_values": [-2e-3, -3e-3, -4e-3] + }""") + cls.r3 = DiscreteValueResidualResponseFunction("r3", cls.model, r3_params) + cls.r3.Initialize() + def setUp(self) -> None: self.optimization_problem = OptimizationProblem() self.optimization_problem.AddComponent(self.r1) ComponentDataView(self.r1, self.optimization_problem).SetDataBuffer(1) self.optimization_problem.AddComponent(self.r2) ComponentDataView(self.r2, self.optimization_problem).SetDataBuffer(1) + self.optimization_problem.AddComponent(self.r3) + ComponentDataView(self.r3, self.optimization_problem).SetDataBuffer(1) def test_LiteralResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression(self.model, "4.0 + 6.0", self.optimization_problem) @@ -125,7 +140,7 @@ def test_CombinedResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression(self.model, "r1", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() - self.assertEqual(eval_value, self.r1.CalculateValue()) + self.assertAlmostEqual(eval_value, self.r1.CalculateValue(), 12) self.assertTrue(self.optimization_problem.HasResponse(eval_resp)) self.assertTrue(self.optimization_problem.HasResponse("r1")) From c3a74809e772db814def3b5e6f50245bac9041a7 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 00:09:09 +0530 Subject: [PATCH 16/36] fix binary opt response func --- .../responses/binary_operator_response_function.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index 77814e8b98e9..80ffd8550cc0 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -71,13 +71,10 @@ def CalculateValue(self) -> float: return v1 ** v2 def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: - resp_1_buffered_data = ComponentDataView(self.response_function_1, self.optimization_problem).GetBufferedData() - resp_2_buffered_data = ComponentDataView(self.response_function_2, self.optimization_problem).GetBufferedData() - v1 = EvaluateValue(self.response_function_1, self.optimization_problem) v2 = EvaluateValue(self.response_function_2, self.optimization_problem) - resp_1_physical_variable_collective_expressions = EvaluateGradient(self.response_function_1, resp_1_buffered_data, physical_variable_collective_expressions) - resp_2_physical_variable_collective_expressions = EvaluateGradient(self.response_function_2, resp_2_buffered_data, physical_variable_collective_expressions) + resp_1_physical_variable_collective_expressions = EvaluateGradient(self.response_function_1, physical_variable_collective_expressions, self.optimization_problem) + resp_2_physical_variable_collective_expressions = EvaluateGradient(self.response_function_2, physical_variable_collective_expressions, self.optimization_problem) for variable, collective_expression in physical_variable_collective_expressions.items(): for result, g1, g2 in zip(collective_expression.GetContainerExpressions(), resp_1_physical_variable_collective_expressions[variable].GetContainerExpressions(), resp_2_physical_variable_collective_expressions[variable].GetContainerExpressions()): From e0e7c8742b8cff1aad6fe32d30b322b3122127ad Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 00:09:20 +0530 Subject: [PATCH 17/36] fix error msg dic_val_res func --- .../responses/discrete_value_residual_response_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/discrete_value_residual_response_function.py b/applications/OptimizationApplication/python_scripts/responses/discrete_value_residual_response_function.py index bf7206d1a2a4..63e78226c25c 100644 --- a/applications/OptimizationApplication/python_scripts/responses/discrete_value_residual_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/discrete_value_residual_response_function.py @@ -130,4 +130,4 @@ def CalculateGradient(self, physical_variable_collective_expressions: 'dict[Supp exp.SetExpression(exp.GetExpression() + (((values - value_i) ** (-2)) * (values - value_i) * 2.0).GetExpression()) exp.SetExpression(Kratos.Expression.Utils.Collapse(exp).GetExpression()) else: - raise RuntimeError(f"Unsupported sensitivity w.r.t. {physical_variable.Name()} requested. Followings are supported sensitivity variables:\n\tSHAPE") + raise RuntimeError(f"Unsupported sensitivity w.r.t. {physical_variable.Name()} requested. Followings are supported sensitivity variables:\n\t{self.variable.Name()}") From c495f748927e1d9c8ba923f1710a391ab88c7ef2 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 00:09:40 +0530 Subject: [PATCH 18/36] update for gradients -> response_utils --- .../utilities/response_utilities.py | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index 2e51250c38ec..66c2e0eed045 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -66,14 +66,19 @@ def EvaluateGradient(response_function: ResponseFunction, physical_variable_coll resp_physical_variable_collective_expressions[variable] = collective_expression.Clone() resp_physical_variable_collective_expressions_to_evaluate: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} - response_data = ComponentDataView(response_function, optimization_problem) - if response_data.HasDataBuffer(): - response_data_buffer = response_data.GetBufferedData() - for variable, collective_expression in resp_physical_variable_collective_expressions.items(): - # first check whether the gradients have been already evaluated. if so, take the gradients. - if response_data_buffer.HasValue(f"d{response_function.GetName()}_d{variable.Name()}"): - resp_physical_variable_collective_expressions[variable] = response_data_buffer.GetValue(f"d{response_function.GetName()}_d{variable.Name()}") - else: + if optimization_problem.HasResponse(response_function): + response_data = ComponentDataView(response_function, optimization_problem) + if response_data.HasDataBuffer(): + response_data_buffer = response_data.GetBufferedData() + for variable, collective_expression in resp_physical_variable_collective_expressions.items(): + # first check whether the gradients have been already evaluated. if so, take the gradients. + if response_data_buffer.HasValue(f"d{response_function.GetName()}_d{variable.Name()}"): + resp_physical_variable_collective_expressions[variable] = response_data_buffer.GetValue(f"d{response_function.GetName()}_d{variable.Name()}") + else: + # gradients have not yet evaluated. put it to the dictionary for later evaluation + resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression + else: + for variable, collective_expression in resp_physical_variable_collective_expressions.items(): # gradients have not yet evaluated. put it to the dictionary for later evaluation resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression else: @@ -84,8 +89,10 @@ def EvaluateGradient(response_function: ResponseFunction, physical_variable_coll if len(resp_physical_variable_collective_expressions_to_evaluate) != 0: response_function.CalculateGradient(resp_physical_variable_collective_expressions_to_evaluate) for variable, collective_expression in resp_physical_variable_collective_expressions_to_evaluate.items(): - if response_data.HasDataBuffer(): - response_data.GetBufferedData().SetValue(f"d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) + if optimization_problem.HasResponse(response_function): + response_data = ComponentDataView(response_function, optimization_problem) + if response_data.HasDataBuffer(): + response_data.GetBufferedData().SetValue(f"d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) resp_physical_variable_collective_expressions[variable] = collective_expression # now add zero values collective expressions to variables for which the response function does not have dependence From 9cd411cc693d056b70578547a95237877a652b7f Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 00:09:49 +0530 Subject: [PATCH 19/36] test gradients --- .../tests/test_response_utilities.py | 69 +++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 30ecc6d0ac06..dd5a8138b64c 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -1,12 +1,14 @@ - +import numpy as np from math import log import KratosMultiphysics as Kratos import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView from KratosMultiphysics.OptimizationApplication.responses.discrete_value_residual_response_function import DiscreteValueResidualResponseFunction from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import EvaluateResponseExpression +from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes # Import KratosUnittest import KratosMultiphysics.KratosUnittest as kratos_unittest @@ -58,6 +60,31 @@ def setUpClass(cls): cls.r3 = DiscreteValueResidualResponseFunction("r3", cls.model, r3_params) cls.r3.Initialize() + def __CheckGradients(self, sensitivity_variables: 'list[SupportedSensitivityFieldVariableTypes]', response_function: ResponseFunction, analytical_lambda, delta: float, precision: int) -> None: + ref_value = analytical_lambda() + self.assertAlmostEqual(ref_value, response_function.CalculateValue(), precision) + + physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} + fd_gradients: 'dict[SupportedSensitivityFieldVariableTypes, list[float]]' = {} + for sensitivity_variable in sensitivity_variables: + nodal_exp = Kratos.Expression.NodalExpression(self.model_part) + Kratos.Expression.LiteralExpressionIO.SetData(nodal_exp, 0.0) + physical_variable_collective_expressions[sensitivity_variable] = KratosOA.CollectiveExpression([nodal_exp]) + fd_gradients[sensitivity_variable] = [] + + response_function.CalculateGradient(physical_variable_collective_expressions) + + for node in self.model_part.Nodes: + for sensitivity_variable in sensitivity_variables: + node.SetValue(sensitivity_variable, node.GetValue(sensitivity_variable) + delta) + fd_gradients[sensitivity_variable].append((analytical_lambda() - ref_value) / delta) + node.SetValue(sensitivity_variable, node.GetValue(sensitivity_variable) - delta) + + for sensitivity_variable in sensitivity_variables: + nodal_exp = Kratos.Expression.NodalExpression(self.model_part) + Kratos.Expression.CArrayExpressionIO.Read(nodal_exp, np.array(fd_gradients[sensitivity_variable], np.float64)) + self.assertAlmostEqual(Kratos.Expression.Utils.NormInf(nodal_exp - physical_variable_collective_expressions[sensitivity_variable].GetContainerExpressions()[0]), 0.0, precision) + def setUp(self) -> None: self.optimization_problem = OptimizationProblem() self.optimization_problem.AddComponent(self.r1) @@ -79,6 +106,8 @@ def test_LiteralResponseCalculateValue1(self): self.assertFalse(self.optimization_problem.HasResponse("6.0")) self.assertFalse(self.optimization_problem.HasResponse("(4.0+6.0)")) + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 + 6.0, 1e-8, 12) + def test_LiteralResponseCalculateValue2(self): eval_resp = EvaluateResponseExpression(self.model, "4.0 * 6.0", self.optimization_problem) eval_resp.Initialize() @@ -90,6 +119,8 @@ def test_LiteralResponseCalculateValue2(self): self.assertFalse(self.optimization_problem.HasResponse("6.0")) self.assertFalse(self.optimization_problem.HasResponse("(4.0*6.0)")) + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 * 6.0, 1e-8, 12) + def test_LiteralResponseCalculateValue3(self): eval_resp = EvaluateResponseExpression(self.model, "4.0 / 8.0", self.optimization_problem) eval_resp.Initialize() @@ -99,7 +130,9 @@ def test_LiteralResponseCalculateValue3(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("4.0")) self.assertFalse(self.optimization_problem.HasResponse("8.0")) - self.assertFalse(self.optimization_problem.HasResponse("(4.0/6.0)")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0/8.0)")) + + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 / 8.0, 1e-8, 12) def test_LiteralResponseCalculateValue4(self): eval_resp = EvaluateResponseExpression(self.model, "4.0 - 8.0", self.optimization_problem) @@ -110,7 +143,9 @@ def test_LiteralResponseCalculateValue4(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("4.0")) self.assertFalse(self.optimization_problem.HasResponse("8.0")) - self.assertFalse(self.optimization_problem.HasResponse("(4.0-6.0)")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0-8.0)")) + + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 - 8.0, 1e-8, 12) def test_LiteralResponseCalculateValue5(self): eval_resp = EvaluateResponseExpression(self.model, "4.0 ^ 2.0", self.optimization_problem) @@ -121,7 +156,9 @@ def test_LiteralResponseCalculateValue5(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("4.0")) self.assertFalse(self.optimization_problem.HasResponse("8.0")) - self.assertFalse(self.optimization_problem.HasResponse("(4.0^6.0)")) + self.assertFalse(self.optimization_problem.HasResponse("(4.0^2.0)")) + + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 ** 2.0, 1e-8, 12) def test_LiteralResponseCalculateValue6(self): eval_resp = EvaluateResponseExpression(self.model, "4.0 - 8.0 + 3.0 * 2.0 / 5.0 ^ 2", self.optimization_problem) @@ -136,6 +173,8 @@ def test_LiteralResponseCalculateValue6(self): self.assertFalse(self.optimization_problem.HasResponse("2.0")) self.assertFalse(self.optimization_problem.HasResponse("5.0")) + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 - 8.0 + 3.0 * 2.0 / 5.0 ** 2, 1e-8, 12) + def test_CombinedResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression(self.model, "r1", self.optimization_problem) eval_resp.Initialize() @@ -149,6 +188,8 @@ def test_CombinedResponseCalculateValue1(self): # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue(), 1e-8, 9) + def test_CombinedResponseCalculateValue2(self): eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + r2", self.optimization_problem) eval_resp.Initialize() @@ -171,6 +212,8 @@ def test_CombinedResponseCalculateValue2(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+r2)")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() * 2, 1e-8, 9) + def test_CombinedResponseCalculateValue3(self): eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + 4.0", self.optimization_problem) eval_resp.Initialize() @@ -194,6 +237,8 @@ def test_CombinedResponseCalculateValue3(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+4.0)")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0, 1e-7, 8) + def test_CombinedResponseCalculateValue4(self): eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 + r2 / 3.0 - 4.0", self.optimization_problem) eval_resp.Initialize() @@ -220,6 +265,8 @@ def test_CombinedResponseCalculateValue4(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("(((3.5+(r1*2.0))+(r2/3.0))-4.0)")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 3.5 + self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 - 4.0, 1e-6, 7) + def test_CombinedResponseCalculateValue5(self): eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 * r2 / 3.0 - 4.0 + r1 ^ r2", self.optimization_problem) eval_resp.Initialize() @@ -250,6 +297,8 @@ def test_CombinedResponseCalculateValue5(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse(eval_resp.GetName())) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 3.5 + self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 - 4.0 + self.r1.CalculateValue() ** self.r2.CalculateValue(), 1e-7, 8) + def test_BracketResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression(self.model, "(4.0)", self.optimization_problem) eval_resp.Initialize() @@ -257,12 +306,17 @@ def test_BracketResponseCalculateValue1(self): eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 4.0) + with self.assertRaises(RuntimeError): + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 4.0, 1e-7, 8) + def test_BracketResponseCalculateValue2(self): eval_resp = EvaluateResponseExpression(self.model, "(4.0 + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 72.0) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : (4.0 + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0, 1e-7, 8) + def test_BracketResponseCalculateValue3(self): eval_resp = EvaluateResponseExpression(self.model, "(4.0 + (r1 * 2) * (r2 ^ 2) + log((2+6)*(3+(4-2)/4)+6*r1) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) eval_resp.Initialize() @@ -295,5 +349,12 @@ def test_BracketResponseCalculateValue3(self): self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse(eval_resp.GetName())) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : (4.0 + (self.r1.CalculateValue() * 2) * (self.r2.CalculateValue() ** 2) + log((2+6)*(3+(4-2)/4)+6*self.r1.CalculateValue()) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0, 1e-6, 7) + + def test_MultipleControlVars(self): + eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + r3 + log(r3)", self.optimization_problem) + eval_resp.Initialize() + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + self.r3.CalculateValue() + log(self.r3.CalculateValue()), 1e-8, 5) + if __name__ == "__main__": kratos_unittest.main() \ No newline at end of file From 002b1b0f7cd9dae95e5eda8fb405f4dedc7ecc0e Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 00:12:47 +0530 Subject: [PATCH 20/36] add tests to suite --- .../tests/test_OptimizationApplication.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/OptimizationApplication/tests/test_OptimizationApplication.py b/applications/OptimizationApplication/tests/test_OptimizationApplication.py index 6a50890aff9e..c00268e737d1 100644 --- a/applications/OptimizationApplication/tests/test_OptimizationApplication.py +++ b/applications/OptimizationApplication/tests/test_OptimizationApplication.py @@ -19,6 +19,7 @@ import test_execution_policies import test_optimization_info import test_optimization_utils +import test_response_utilities import responses_tests.test_response_routine import responses_tests.test_overhang_response_function import responses_tests.test_mass_response_function @@ -87,6 +88,7 @@ def AssembleTestSuites(): smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([test_optimization_utils.TestOptimizationUtils])) smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([test_model_part_utils.TestOptAppModelPartUtils])) smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([test_model_part_utils.TestModelPartUtilities])) + smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([test_response_utilities.TestResponseUtilities])) smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([test_container_expression_utils.TestContainerExpressionUtils])) smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([responses_tests.test_response_routine.TestResponseRoutine])) From 1502175cd94a004312d173345bf0f8b0f71c96c2 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 08:06:34 +0530 Subject: [PATCH 21/36] minor --- .../responses/binary_operator_response_function.py | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index 80ffd8550cc0..a2e8f7097201 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -16,6 +16,7 @@ class BinaryOperatorResponseFunction(ResponseFunction): def __init__(self, model: Kratos.Model, response_function_1: ResponseFunction, response_function_2: ResponseFunction, binary_operator: BinaryOperator, optimization_problem: OptimizationProblem): if binary_operator == BinaryOperator.DIVIDE: + # this is because, the optimization_problem data container uses "/" as a path separator. super().__init__(f"({response_function_1.GetName()}÷{response_function_2.GetName()})") else: super().__init__(f"({response_function_1.GetName()}{binary_operator.value}{response_function_2.GetName()})") From c7eb8135a019406b238cac4a4a36793516553977 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 29 Jul 2024 09:39:55 +0530 Subject: [PATCH 22/36] remove model dependence --- .../binary_operator_response_function.py | 5 ++-- .../utilities/response_utilities.py | 18 +++++------ .../tests/test_response_utilities.py | 30 +++++++++---------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index a2e8f7097201..ce2a880a7e8c 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -14,14 +14,13 @@ from KratosMultiphysics.OptimizationApplication.utilities.response_utilities import BinaryOperator class BinaryOperatorResponseFunction(ResponseFunction): - def __init__(self, model: Kratos.Model, response_function_1: ResponseFunction, response_function_2: ResponseFunction, binary_operator: BinaryOperator, optimization_problem: OptimizationProblem): + def __init__(self, response_function_1: ResponseFunction, response_function_2: ResponseFunction, binary_operator: BinaryOperator, optimization_problem: OptimizationProblem): if binary_operator == BinaryOperator.DIVIDE: # this is because, the optimization_problem data container uses "/" as a path separator. super().__init__(f"({response_function_1.GetName()}÷{response_function_2.GetName()})") else: super().__init__(f"({response_function_1.GetName()}{binary_operator.value}{response_function_2.GetName()})") - self.model = model self.optimization_problem = optimization_problem self.response_function_1 = response_function_1 self.response_function_2 = response_function_2 @@ -38,7 +37,7 @@ def Initialize(self) -> None: self.response_function_2.Initialize() if len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0 and len(self.response_function_2.GetImplementedPhysicalKratosVariables()) != 0: - self.model_part = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", [self.response_function_1.GetInfluencingModelPart().FullName(), self.response_function_2.GetInfluencingModelPart().FullName()], False).GetModelPart() + self.model_part = ModelPartOperation(self.response_function_1.GetInfluencingModelPart().GetModel(), ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", [self.response_function_1.GetInfluencingModelPart().FullName(), self.response_function_2.GetInfluencingModelPart().FullName()], False).GetModelPart() elif len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0: self.model_part = self.response_function_1.GetInfluencingModelPart() elif len(self.response_function_2.GetImplementedPhysicalKratosVariables()) != 0: diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index 66c2e0eed045..8f8565313cb8 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -102,7 +102,7 @@ def EvaluateGradient(response_function: ResponseFunction, physical_variable_coll return resp_physical_variable_collective_expressions -def GetResponseFunction(model: Kratos.Model, current_value: str, optimization_problem: OptimizationProblem) -> ResponseFunction: +def GetResponseFunction(current_value: str, optimization_problem: OptimizationProblem) -> ResponseFunction: from KratosMultiphysics.OptimizationApplication.responses.literal_value_response_function import LiteralValueResponseFunction if current_value == "": return LiteralValueResponseFunction(0.0) @@ -146,12 +146,12 @@ def GetResponseFunction(model: Kratos.Model, current_value: str, optimization_pr index += 1 args_list.append(current_arg) - return GetFunction(function_name, optimization_problem, *[EvaluateResponseExpression(model, arg, optimization_problem) for arg in args_list]) + return GetFunction(function_name, optimization_problem, *[EvaluateResponseExpression(arg, optimization_problem) for arg in args_list]) else: # this is a response expression. - return EvaluateResponseExpression(model, current_value, optimization_problem) + return EvaluateResponseExpression(current_value, optimization_problem) -def GetValuesAndOperators(model: Kratos.Model, response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': +def GetValuesAndOperators(response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': responses: 'list[ResponseFunction]' = [] operators: 'list[str]' = [] @@ -171,7 +171,7 @@ def GetValuesAndOperators(model: Kratos.Model, response_expression: str, optimiz continue if current_char in BinaryOperatorValues: - responses.append(GetResponseFunction(model, current_word, optimization_problem)) + responses.append(GetResponseFunction(current_word, optimization_problem)) operators.append(current_char) current_word = "" index += 1 @@ -181,15 +181,15 @@ def GetValuesAndOperators(model: Kratos.Model, response_expression: str, optimiz index += 1 # add the last current_word - responses.append(GetResponseFunction(model, current_word, optimization_problem)) + responses.append(GetResponseFunction(current_word, optimization_problem)) return responses, operators -def EvaluateResponseExpression(model: Kratos.Model, response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: +def EvaluateResponseExpression(response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: from KratosMultiphysics.OptimizationApplication.responses.binary_operator_response_function import BinaryOperatorResponseFunction response_expression = response_expression.replace(" ", "") - responses, operators = GetValuesAndOperators(model, response_expression, optimization_problem) + responses, operators = GetValuesAndOperators(response_expression, optimization_problem) def __evaluate_operator(list_of_operators: 'list[str]') -> None: operator_index = 0 @@ -224,7 +224,7 @@ def __evaluate_operator(list_of_operators: 'list[str]') -> None: if not right_operand_component_data_view.HasDataBuffer(): right_operand_component_data_view.SetDataBuffer(1) - resultant_operator = BinaryOperatorResponseFunction(model, left_operand, right_operand, BinaryOperatorValuesMap[operators[operator_index]], optimization_problem) + resultant_operator = BinaryOperatorResponseFunction(left_operand, right_operand, BinaryOperatorValuesMap[operators[operator_index]], optimization_problem) if not optimization_problem.HasResponse(resultant_operator.GetName()): # There is no already existing response with the same operation. Then use the new one. responses[operator_index] = resultant_operator diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index dd5a8138b64c..057872b288b8 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -95,7 +95,7 @@ def setUp(self) -> None: ComponentDataView(self.r3, self.optimization_problem).SetDataBuffer(1) def test_LiteralResponseCalculateValue1(self): - eval_resp = EvaluateResponseExpression(self.model, "4.0 + 6.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("4.0 + 6.0", self.optimization_problem) eval_resp.Initialize() eval_resp.Initialize() eval_value = eval_resp.CalculateValue() @@ -109,7 +109,7 @@ def test_LiteralResponseCalculateValue1(self): self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 + 6.0, 1e-8, 12) def test_LiteralResponseCalculateValue2(self): - eval_resp = EvaluateResponseExpression(self.model, "4.0 * 6.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("4.0 * 6.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 24.0) @@ -122,7 +122,7 @@ def test_LiteralResponseCalculateValue2(self): self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 * 6.0, 1e-8, 12) def test_LiteralResponseCalculateValue3(self): - eval_resp = EvaluateResponseExpression(self.model, "4.0 / 8.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("4.0 / 8.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 0.5) @@ -135,7 +135,7 @@ def test_LiteralResponseCalculateValue3(self): self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 / 8.0, 1e-8, 12) def test_LiteralResponseCalculateValue4(self): - eval_resp = EvaluateResponseExpression(self.model, "4.0 - 8.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("4.0 - 8.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, -4.0) @@ -148,7 +148,7 @@ def test_LiteralResponseCalculateValue4(self): self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 - 8.0, 1e-8, 12) def test_LiteralResponseCalculateValue5(self): - eval_resp = EvaluateResponseExpression(self.model, "4.0 ^ 2.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("4.0 ^ 2.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 16.0) @@ -161,7 +161,7 @@ def test_LiteralResponseCalculateValue5(self): self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 ** 2.0, 1e-8, 12) def test_LiteralResponseCalculateValue6(self): - eval_resp = EvaluateResponseExpression(self.model, "4.0 - 8.0 + 3.0 * 2.0 / 5.0 ^ 2", self.optimization_problem) + eval_resp = EvaluateResponseExpression("4.0 - 8.0 + 3.0 * 2.0 / 5.0 ^ 2", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, -3.76) @@ -176,7 +176,7 @@ def test_LiteralResponseCalculateValue6(self): self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : 4.0 - 8.0 + 3.0 * 2.0 / 5.0 ** 2, 1e-8, 12) def test_CombinedResponseCalculateValue1(self): - eval_resp = EvaluateResponseExpression(self.model, "r1", self.optimization_problem) + eval_resp = EvaluateResponseExpression("r1", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertAlmostEqual(eval_value, self.r1.CalculateValue(), 12) @@ -191,7 +191,7 @@ def test_CombinedResponseCalculateValue1(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue(), 1e-8, 9) def test_CombinedResponseCalculateValue2(self): - eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + r2", self.optimization_problem) + eval_resp = EvaluateResponseExpression("r1 + r2 + r2", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertAlmostEqual(eval_value, self.r1.CalculateValue() + 2.0 * self.r2.CalculateValue(), 12) @@ -215,7 +215,7 @@ def test_CombinedResponseCalculateValue2(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() * 2, 1e-8, 9) def test_CombinedResponseCalculateValue3(self): - eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + 4.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("r1 + r2 + 4.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0) @@ -240,7 +240,7 @@ def test_CombinedResponseCalculateValue3(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0, 1e-7, 8) def test_CombinedResponseCalculateValue4(self): - eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 + r2 / 3.0 - 4.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("3.5 + r1 * 2.0 + r2 / 3.0 - 4.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 - 4.0 + 3.5) @@ -268,7 +268,7 @@ def test_CombinedResponseCalculateValue4(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 3.5 + self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 - 4.0, 1e-6, 7) def test_CombinedResponseCalculateValue5(self): - eval_resp = EvaluateResponseExpression(self.model, "3.5 + r1 * 2.0 * r2 / 3.0 - 4.0 + r1 ^ r2", self.optimization_problem) + eval_resp = EvaluateResponseExpression("3.5 + r1 * 2.0 * r2 / 3.0 - 4.0 + r1 ^ r2", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 - 4.0 + 3.5 + self.r1.CalculateValue() ** self.r2.CalculateValue()) @@ -300,7 +300,7 @@ def test_CombinedResponseCalculateValue5(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 3.5 + self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 - 4.0 + self.r1.CalculateValue() ** self.r2.CalculateValue(), 1e-7, 8) def test_BracketResponseCalculateValue1(self): - eval_resp = EvaluateResponseExpression(self.model, "(4.0)", self.optimization_problem) + eval_resp = EvaluateResponseExpression("(4.0)", self.optimization_problem) eval_resp.Initialize() eval_resp.Initialize() eval_value = eval_resp.CalculateValue() @@ -310,7 +310,7 @@ def test_BracketResponseCalculateValue1(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 4.0, 1e-7, 8) def test_BracketResponseCalculateValue2(self): - eval_resp = EvaluateResponseExpression(self.model, "(4.0 + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("(4.0 + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 72.0) @@ -318,7 +318,7 @@ def test_BracketResponseCalculateValue2(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : (4.0 + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0, 1e-7, 8) def test_BracketResponseCalculateValue3(self): - eval_resp = EvaluateResponseExpression(self.model, "(4.0 + (r1 * 2) * (r2 ^ 2) + log((2+6)*(3+(4-2)/4)+6*r1) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) + eval_resp = EvaluateResponseExpression("(4.0 + (r1 * 2) * (r2 ^ 2) + log((2+6)*(3+(4-2)/4)+6*r1) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, (4.0 + (self.r1.CalculateValue() * 2) * (self.r2.CalculateValue() ** 2) + log((2+6)*(3+(4-2)/4)+6*self.r1.CalculateValue()) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0) @@ -352,7 +352,7 @@ def test_BracketResponseCalculateValue3(self): self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : (4.0 + (self.r1.CalculateValue() * 2) * (self.r2.CalculateValue() ** 2) + log((2+6)*(3+(4-2)/4)+6*self.r1.CalculateValue()) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0, 1e-6, 7) def test_MultipleControlVars(self): - eval_resp = EvaluateResponseExpression(self.model, "r1 + r2 + r3 + log(r3)", self.optimization_problem) + eval_resp = EvaluateResponseExpression("r1 + r2 + r3 + log(r3)", self.optimization_problem) eval_resp.Initialize() self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + self.r3.CalculateValue() + log(self.r3.CalculateValue()), 1e-8, 5) From 8796d897c940dac821ac6d7ebf42bfe41a80b9c2 Mon Sep 17 00:00:00 2001 From: Suneth Warnakulasuriya <7856520+sunethwarna@users.noreply.github.com> Date: Sun, 4 Aug 2024 11:04:21 +0530 Subject: [PATCH 23/36] Update applications/OptimizationApplication/tests/test_response_utilities.py Co-authored-by: IAntonau --- .../OptimizationApplication/tests/test_response_utilities.py | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 057872b288b8..4eaa04acfa72 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -97,7 +97,6 @@ def setUp(self) -> None: def test_LiteralResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression("4.0 + 6.0", self.optimization_problem) eval_resp.Initialize() - eval_resp.Initialize() eval_value = eval_resp.CalculateValue() self.assertEqual(eval_value, 10.0) From d23ddcde245f05bd4cee3f3742cd41569becac87 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:12:45 +0530 Subject: [PATCH 24/36] add GetChildResponses method --- .../python_scripts/responses/response_function.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/applications/OptimizationApplication/python_scripts/responses/response_function.py b/applications/OptimizationApplication/python_scripts/responses/response_function.py index 96b646aa16dd..bf7f27ad6048 100644 --- a/applications/OptimizationApplication/python_scripts/responses/response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/response_function.py @@ -111,3 +111,16 @@ def GetInfluencingModelPart(self) -> Kratos.ModelPart: Kratos.ModelPart: Response function model part which influences the response value. """ pass + + def GetChildResponses(self) -> 'list[ResponseFunction]': + """Returns the list of child responses. + + This method returns list of child responses. If the current response is a leaf response, + then it will return empty list. + + Needs to be implemented in non-leaf response functions. + + Returns: + list[ResponseFunction]: List of child responses. + """ + return [] From ed9bd08cb85e70c900202eeeaecd609e3bcade8f Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:13:05 +0530 Subject: [PATCH 25/36] update GetChildResponses --- .../responses/binary_operator_response_function.py | 3 +++ .../python_scripts/responses/log_response_function.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py index ce2a880a7e8c..6572a8a7e85d 100644 --- a/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/binary_operator_response_function.py @@ -89,6 +89,9 @@ def CalculateGradient(self, physical_variable_collective_expressions: 'dict[Supp elif self.binary_operator == BinaryOperator.POWER: result.SetExpression(((g1 * (v2 / v1) + g2 * log(v1)) * (v1 ** v2)).GetExpression()) + def GetChildResponses(self) -> 'list[ResponseFunction]': + return [self.response_function_1, self.response_function_2] + def __str__(self) -> str: if self.model_part is not None: return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = {self.model_part.FullName()}]" diff --git a/applications/OptimizationApplication/python_scripts/responses/log_response_function.py b/applications/OptimizationApplication/python_scripts/responses/log_response_function.py index aaaadec6a0a1..9d6b81708ef5 100644 --- a/applications/OptimizationApplication/python_scripts/responses/log_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/log_response_function.py @@ -40,5 +40,8 @@ def CalculateGradient(self, physical_variable_collective_expressions: 'dict[Supp for result, g in zip(collective_expression.GetContainerExpressions(), resp_physical_variable_collective_expressions[variable].GetContainerExpressions()): result.SetExpression((g / v).GetExpression()) + def GetChildResponses(self) -> 'list[ResponseFunction]': + return [self.response_function] + def __str__(self) -> str: return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}]" \ No newline at end of file From 94655d961183e887d8f3160a2f2319103f2e2533 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:13:15 +0530 Subject: [PATCH 26/36] add EvaluationResponseFunction --- .../responses/evaluation_response_function.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py diff --git a/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py b/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py new file mode 100644 index 000000000000..a0b3190d3be5 --- /dev/null +++ b/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py @@ -0,0 +1,68 @@ +import KratosMultiphysics as Kratos +import KratosMultiphysics.OptimizationApplication as KratosOA +from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction +from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes +from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView +from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem +from KratosMultiphysics.OptimizationApplication.utilities.buffered_dict import BufferedDict + +class EvaluationResponseFunction(ResponseFunction): + def __init__(self, response_function: ResponseFunction, optimization_problem: OptimizationProblem): + super().__init__(f"EvaluationResponse_{response_function.GetName()}") + self.response_function = response_function + self.optimization_problem = optimization_problem + + def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]': + return self.response_function.GetImplementedPhysicalKratosVariables() + + def Initialize(self) -> None: + self.response_function.Initialize() + + def Check(self) -> None: + self.response_function.Check() + + def Finalize(self) -> None: + self.response_function.Finalize() + + def GetInfluencingModelPart(self) -> Kratos.ModelPart: + return self.response_function.GetInfluencingModelPart() + + def CalculateValue(self) -> float: + # this response is used at the top most level of the evaluated chained responses. + # so this creates a new data container under the optimization problem to avoid + # having to compute the same response value twice. + + unbuffered_data = ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData() + + # reset data of the evaluation + self.__ResetEvaluationData(self, unbuffered_data, "values") + + # now calculate + return self.response_function.CalculateValue() + + def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: + # this response is used at the top most level of the evaluated chained responses. + # so this creates a new data container under the optimization problem to avoid + # having to compute the same response gradient twice. + + unbuffered_data = ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData() + + # reset data of the evaluation + self.__ResetEvaluationData(self, unbuffered_data, "gradients") + + return self.response_function.CalculateGradient(physical_variable_collective_expressions) + + def GetChildResponses(self) -> 'list[ResponseFunction]': + return [self.response_function] + + def __str__(self) -> str: + return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}]" + + @staticmethod + def __ResetEvaluationData(response_function: ResponseFunction, unbuffered_data: BufferedDict, prefix: str) -> None: + if unbuffered_data.HasValue(f"{prefix}/{response_function.GetName()}"): + del unbuffered_data[f"{prefix}/{response_function.GetName()}"] + for child_response in response_function.GetChildResponses(): + # now reset the children + EvaluationResponseFunction.__ResetEvaluationData(child_response, unbuffered_data) From 8cc75c4312d03709d604bb168c25e0ad68f306f0 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:13:26 +0530 Subject: [PATCH 27/36] update response utilities --- .../utilities/response_utilities.py | 47 ++++++------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index 8f8565313cb8..19d9a093e473 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -4,6 +4,7 @@ import KratosMultiphysics as Kratos import KratosMultiphysics.OptimizationApplication as KratosOA from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction +from KratosMultiphysics.OptimizationApplication.responses.evaluation_response_function import EvaluationResponseFunction from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem @@ -46,17 +47,12 @@ def GetFunction(function_name: str, optimization_problem: OptimizationProblem, * raise RuntimeError(f"Undefined \"{function_name}\" function. Followings are supported function names:\n\t" + "\n\t".join(functions_map.keys())) def EvaluateValue(response_function: ResponseFunction, optimization_problem: OptimizationProblem) -> float: - if optimization_problem.HasResponse(response_function): - response_data = ComponentDataView(response_function, optimization_problem) - if response_data.HasDataBuffer(): - response_data_buffer = response_data.GetBufferedData() - if not response_data_buffer.HasValue("value"): - response_data_buffer.SetValue("value", response_function.CalculateValue()) - return response_data_buffer.GetValue("value") - else: - return response_function.CalculateValue() - else: - return response_function.CalculateValue() + response_data = ComponentDataView("evaluated_responses", optimization_problem).GetUnBufferedData() + + if not response_data.HasValue(f"values/{response_function.GetName()}"): + response_data.SetValue(f"values/{response_function.GetName()}", response_function.CalculateValue()) + + return response_data.GetValue(f"values/{response_function.GetName()}") def EvaluateGradient(response_function: ResponseFunction, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]', optimization_problem: OptimizationProblem) -> 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]': # first get the sub_collective expressions for implemented physical kratos variables @@ -66,33 +62,20 @@ def EvaluateGradient(response_function: ResponseFunction, physical_variable_coll resp_physical_variable_collective_expressions[variable] = collective_expression.Clone() resp_physical_variable_collective_expressions_to_evaluate: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]' = {} - if optimization_problem.HasResponse(response_function): - response_data = ComponentDataView(response_function, optimization_problem) - if response_data.HasDataBuffer(): - response_data_buffer = response_data.GetBufferedData() - for variable, collective_expression in resp_physical_variable_collective_expressions.items(): - # first check whether the gradients have been already evaluated. if so, take the gradients. - if response_data_buffer.HasValue(f"d{response_function.GetName()}_d{variable.Name()}"): - resp_physical_variable_collective_expressions[variable] = response_data_buffer.GetValue(f"d{response_function.GetName()}_d{variable.Name()}") - else: - # gradients have not yet evaluated. put it to the dictionary for later evaluation - resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression + response_data = ComponentDataView("evaluated_responses", optimization_problem).GetUnBufferedData() + + for variable, collective_expression in resp_physical_variable_collective_expressions.items(): + # first check whether the gradients have been already evaluated. if so, take the gradients. + if response_data.HasValue(f"gradients/d{response_function.GetName()}_d{variable.Name()}"): + resp_physical_variable_collective_expressions[variable] = response_data.GetValue(f"gradients/d{response_function.GetName()}_d{variable.Name()}") else: - for variable, collective_expression in resp_physical_variable_collective_expressions.items(): - # gradients have not yet evaluated. put it to the dictionary for later evaluation - resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression - else: - for variable, collective_expression in resp_physical_variable_collective_expressions.items(): # gradients have not yet evaluated. put it to the dictionary for later evaluation resp_physical_variable_collective_expressions_to_evaluate[variable] = collective_expression if len(resp_physical_variable_collective_expressions_to_evaluate) != 0: response_function.CalculateGradient(resp_physical_variable_collective_expressions_to_evaluate) for variable, collective_expression in resp_physical_variable_collective_expressions_to_evaluate.items(): - if optimization_problem.HasResponse(response_function): - response_data = ComponentDataView(response_function, optimization_problem) - if response_data.HasDataBuffer(): - response_data.GetBufferedData().SetValue(f"d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) + response_data.SetValue(f"gradients/d{response_function.GetName()}_d{variable.Name()}", collective_expression.Clone()) resp_physical_variable_collective_expressions[variable] = collective_expression # now add zero values collective expressions to variables for which the response function does not have dependence @@ -242,4 +225,4 @@ def __evaluate_operator(list_of_operators: 'list[str]') -> None: __evaluate_operator(["*", "/"]) __evaluate_operator(["+", "-"]) - return responses[0] \ No newline at end of file + return EvaluationResponseFunction(responses[0], optimization_problem) \ No newline at end of file From 33e58b3546ac10e1e8d6ade55b635426469e1ab9 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:13:34 +0530 Subject: [PATCH 28/36] update tests --- .../tests/test_response_utilities.py | 128 ++++++++++++++---- 1 file changed, 98 insertions(+), 30 deletions(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 4eaa04acfa72..8fe1fc902814 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -203,16 +203,24 @@ def test_CombinedResponseCalculateValue2(self): # followings are intermediate responses, so Algorithm, ResponseRoutine do not see them. Therefore # the value storage is managed by the ResponseFunction it self. - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1+r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"values/r1")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"values/r2")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"values/(r1+r2)")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) - self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+r2)")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"values/((r1+r2)+r2)")) + + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/dr1_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/dr2_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/d(r1+r2)_dPRESSURE")) self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() * 2, 1e-8, 9) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/dr1_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/dr2_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/d(r1+r2)_dPRESSURE")) + def test_CombinedResponseCalculateValue3(self): eval_resp = EvaluateResponseExpression("r1 + r2 + 4.0", self.optimization_problem) eval_resp.Initialize() @@ -228,16 +236,24 @@ def test_CombinedResponseCalculateValue3(self): # followings are intermediate responses, so Algorithm, ResponseRoutine do not see them. Therefore # the value storage is managed by the ResponseFunction it self. - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1+r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r1")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r2")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r1+r2)")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("((r1+r2)+4.0)")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1+r2)_dPRESSURE")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0, 1e-7, 8) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1+r2)_dPRESSURE")) + def test_CombinedResponseCalculateValue4(self): eval_resp = EvaluateResponseExpression("3.5 + r1 * 2.0 + r2 / 3.0 - 4.0", self.optimization_problem) eval_resp.Initialize() @@ -253,19 +269,33 @@ def test_CombinedResponseCalculateValue4(self): self.assertTrue(self.optimization_problem.HasResponse("(3.5+(r1*2.0))")) self.assertTrue(self.optimization_problem.HasResponse("((3.5+(r1*2.0))+(r2÷3.0))")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r2÷3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(r1*2.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(r1*2.0))+(r2÷3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r1")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r2")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r1*2.0)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r2÷3.0)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(3.5+(r1*2.0))")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/((3.5+(r1*2.0))+(r2÷3.0))")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse("(((3.5+(r1*2.0))+(r2/3.0))-4.0)")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1*2.0)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r2÷3.0)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(3.5+(r1*2.0))_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((3.5+(r1*2.0))+(r2÷3.0))_dPRESSURE")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 3.5 + self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 - 4.0, 1e-6, 7) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1*2.0)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r2÷3.0)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(3.5+(r1*2.0))_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((3.5+(r1*2.0))+(r2÷3.0))_dPRESSURE")) + def test_CombinedResponseCalculateValue5(self): eval_resp = EvaluateResponseExpression("3.5 + r1 * 2.0 * r2 / 3.0 - 4.0 + r1 ^ r2", self.optimization_problem) eval_resp.Initialize() @@ -283,21 +313,39 @@ def test_CombinedResponseCalculateValue5(self): self.assertTrue(self.optimization_problem.HasResponse("(3.5+(((r1*2.0)*r2)÷3.0))")) self.assertTrue(self.optimization_problem.HasResponse("((3.5+(((r1*2.0)*r2)÷3.0))-4.0)")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((r1*2.0)*r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(((r1*2.0)*r2)÷3.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1^r2)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(3.5+(((r1*2.0)*r2)÷3.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((3.5+(((r1*2.0)*r2)÷3.0))-4.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r1")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r2")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r1*2.0)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/((r1*2.0)*r2)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(((r1*2.0)*r2)÷3.0)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r1^r2)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(3.5+(((r1*2.0)*r2)÷3.0))")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/((3.5+(((r1*2.0)*r2)÷3.0))-4.0)")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse(eval_resp.GetName())) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1*2.0)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1*2.0)*r2)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((r1*2.0)*r2)÷3.0)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1^r2)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(3.5+(((r1*2.0)*r2)÷3.0))_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((3.5+(((r1*2.0)*r2)÷3.0))-4.0)_dPRESSURE")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : 3.5 + self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 - 4.0 + self.r1.CalculateValue() ** self.r2.CalculateValue(), 1e-7, 8) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1*2.0)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1*2.0)*r2)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((r1*2.0)*r2)÷3.0)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1^r2)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(3.5+(((r1*2.0)*r2)÷3.0))_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((3.5+(((r1*2.0)*r2)÷3.0))-4.0)_dPRESSURE")) + def test_BracketResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression("(4.0)", self.optimization_problem) eval_resp.Initialize() @@ -334,22 +382,42 @@ def test_BracketResponseCalculateValue3(self): self.assertTrue(self.optimization_problem.HasResponse("(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))")) self.assertTrue(self.optimization_problem.HasResponse("log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("r2"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r1*2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(r2^2.0)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((r1*2.0)*(r2^2.0))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(6.0*r1)"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))"), self.optimization_problem).GetBufferedData().HasValue("value")) - self.assertTrue(ComponentDataView(self.optimization_problem.GetResponse("log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))"), self.optimization_problem).GetBufferedData().HasValue("value")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r1")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/r2")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r1*2.0)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(r2^2.0)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/((r1*2.0)*(r2^2.0))")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(6.0*r1)")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("values/log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertFalse(self.optimization_problem.HasResponse(eval_resp.GetName())) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1*2.0)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r2^2.0)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1*2.0)*(r2^2.0))_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(6.0*r1)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dlog((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))_dPRESSURE")) + self.__CheckGradients([Kratos.PRESSURE], eval_resp, lambda : (4.0 + (self.r1.CalculateValue() * 2) * (self.r2.CalculateValue() ** 2) + log((2+6)*(3+(4-2)/4)+6*self.r1.CalculateValue()) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0, 1e-6, 7) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1*2.0)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r2^2.0)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1*2.0)*(r2^2.0))_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(6.0*r1)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dlog((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))_dPRESSURE")) + def test_MultipleControlVars(self): eval_resp = EvaluateResponseExpression("r1 + r2 + r3 + log(r3)", self.optimization_problem) eval_resp.Initialize() From 8a0540ee00b301f447833eafbc288ece8c6ca1d2 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:33:32 +0530 Subject: [PATCH 29/36] add more tests --- .../tests/test_response_utilities.py | 102 +++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 8fe1fc902814..d93413680251 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -13,6 +13,36 @@ # Import KratosUnittest import KratosMultiphysics.KratosUnittest as kratos_unittest +class TestResponseFunctionWrapper(ResponseFunction): + def __init__(self, response_function: ResponseFunction) -> None: + super().__init__(response_function.GetName()) + self.response_function = response_function + + def Initialize(self) -> None: + self.calculate_value_calls = 0 + self.calculate_gradient_calls = 0 + return self.response_function.Initialize() + + def Check(self) -> None: + return self.response_function.Check() + + def Finalize(self) -> None: + return self.response_function.Finalize() + + def GetImplementedPhysicalKratosVariables(self) -> SupportedSensitivityFieldVariableTypes: + return self.response_function.GetImplementedPhysicalKratosVariables() + + def GetInfluencingModelPart(self) -> Kratos.ModelPart: + return self.response_function.GetInfluencingModelPart() + + def CalculateValue(self) -> float: + self.calculate_value_calls += 1 + return self.response_function.CalculateValue() + + def CalculateGradient(self, physical_variable_collective_expressions: 'dict[SupportedSensitivityFieldVariableTypes, KratosOA.CollectiveExpression]') -> None: + self.calculate_gradient_calls += 1 + return self.response_function.CalculateGradient(physical_variable_collective_expressions) + class TestResponseUtilities(kratos_unittest.TestCase): @classmethod def setUpClass(cls): @@ -33,7 +63,7 @@ def setUpClass(cls): "residual_type" : "exact", "list_of_discrete_values": [-1.0e-2, -2.0e-2, -3.0e-2] }""") - cls.r1 = DiscreteValueResidualResponseFunction("r1", cls.model, r1_params) + cls.r1 = TestResponseFunctionWrapper(DiscreteValueResidualResponseFunction("r1", cls.model, r1_params)) cls.r1.Initialize() r2_params = Kratos.Parameters("""{ @@ -45,7 +75,7 @@ def setUpClass(cls): "residual_type" : "exact", "list_of_discrete_values": [-1e-3, -2e-3, -3e-3] }""") - cls.r2 = DiscreteValueResidualResponseFunction("r2", cls.model, r2_params) + cls.r2 = TestResponseFunctionWrapper(DiscreteValueResidualResponseFunction("r2", cls.model, r2_params)) cls.r2.Initialize() r3_params = Kratos.Parameters("""{ @@ -57,7 +87,7 @@ def setUpClass(cls): "residual_type" : "exact", "list_of_discrete_values": [-2e-3, -3e-3, -4e-3] }""") - cls.r3 = DiscreteValueResidualResponseFunction("r3", cls.model, r3_params) + cls.r3 = TestResponseFunctionWrapper(DiscreteValueResidualResponseFunction("r3", cls.model, r3_params)) cls.r3.Initialize() def __CheckGradients(self, sensitivity_variables: 'list[SupportedSensitivityFieldVariableTypes]', response_function: ResponseFunction, analytical_lambda, delta: float, precision: int) -> None: @@ -193,8 +223,13 @@ def test_CombinedResponseCalculateValue2(self): eval_resp = EvaluateResponseExpression("r1 + r2 + r2", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() + + self.assertEqual(self.r1.calculate_value_calls, 1) + self.assertEqual(self.r2.calculate_value_calls, 1) + self.assertAlmostEqual(eval_value, self.r1.CalculateValue() + 2.0 * self.r2.CalculateValue(), 12) + # followings are intermediate responses, or leaf responses. Therefore they need to be # in the optimization problem self.assertTrue(self.optimization_problem.HasResponse("r1")) @@ -221,10 +256,17 @@ def test_CombinedResponseCalculateValue2(self): self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/dr2_dPRESSURE")) self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue(f"gradients/d(r1+r2)_dPRESSURE")) + self.assertEqual(self.r1.calculate_gradient_calls, 1) + self.assertEqual(self.r2.calculate_gradient_calls, 1) + def test_CombinedResponseCalculateValue3(self): eval_resp = EvaluateResponseExpression("r1 + r2 + 4.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() + + self.assertEqual(self.r1.calculate_value_calls, 1) + self.assertEqual(self.r2.calculate_value_calls, 1) + self.assertEqual(eval_value, self.r1.CalculateValue() + self.r2.CalculateValue() + 4.0) # followings are intermediate responses, or leaf responses. Therefore they need to be @@ -254,10 +296,17 @@ def test_CombinedResponseCalculateValue3(self): self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1+r2)_dPRESSURE")) + self.assertEqual(self.r1.calculate_gradient_calls, 1) + self.assertEqual(self.r2.calculate_gradient_calls, 1) + def test_CombinedResponseCalculateValue4(self): eval_resp = EvaluateResponseExpression("3.5 + r1 * 2.0 + r2 / 3.0 - 4.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() + + self.assertEqual(self.r1.calculate_value_calls, 1) + self.assertEqual(self.r2.calculate_value_calls, 1) + self.assertEqual(eval_value, self.r1.CalculateValue() * 2.0 + self.r2.CalculateValue() / 3.0 - 4.0 + 3.5) # followings are intermediate responses, or leaf responses. Therefore they need to be @@ -296,10 +345,17 @@ def test_CombinedResponseCalculateValue4(self): self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(3.5+(r1*2.0))_dPRESSURE")) self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((3.5+(r1*2.0))+(r2÷3.0))_dPRESSURE")) + self.assertEqual(self.r1.calculate_gradient_calls, 1) + self.assertEqual(self.r2.calculate_gradient_calls, 1) + def test_CombinedResponseCalculateValue5(self): eval_resp = EvaluateResponseExpression("3.5 + r1 * 2.0 * r2 / 3.0 - 4.0 + r1 ^ r2", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() + + self.assertEqual(self.r1.calculate_value_calls, 1) + self.assertEqual(self.r2.calculate_value_calls, 1) + self.assertEqual(eval_value, self.r1.CalculateValue() * 2.0 * self.r2.CalculateValue() / 3.0 - 4.0 + 3.5 + self.r1.CalculateValue() ** self.r2.CalculateValue()) # followings are intermediate responses, or leaf responses. Therefore they need to be @@ -346,6 +402,9 @@ def test_CombinedResponseCalculateValue5(self): self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(3.5+(((r1*2.0)*r2)÷3.0))_dPRESSURE")) self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((3.5+(((r1*2.0)*r2)÷3.0))-4.0)_dPRESSURE")) + self.assertEqual(self.r1.calculate_gradient_calls, 1) + self.assertEqual(self.r2.calculate_gradient_calls, 1) + def test_BracketResponseCalculateValue1(self): eval_resp = EvaluateResponseExpression("(4.0)", self.optimization_problem) eval_resp.Initialize() @@ -368,6 +427,10 @@ def test_BracketResponseCalculateValue3(self): eval_resp = EvaluateResponseExpression("(4.0 + (r1 * 2) * (r2 ^ 2) + log((2+6)*(3+(4-2)/4)+6*r1) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0", self.optimization_problem) eval_resp.Initialize() eval_value = eval_resp.CalculateValue() + + self.assertEqual(self.r1.calculate_value_calls, 1) + self.assertEqual(self.r2.calculate_value_calls, 1) + self.assertEqual(eval_value, (4.0 + (self.r1.CalculateValue() * 2) * (self.r2.CalculateValue() ** 2) + log((2+6)*(3+(4-2)/4)+6*self.r1.CalculateValue()) + (6.0 / 3.0 + 3 * (2 + 8))) * 2.0) # followings are intermediate responses, or leaf responses. Therefore they need to be @@ -418,10 +481,43 @@ def test_BracketResponseCalculateValue3(self): self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((4.0+((r1*2.0)*(r2^2.0)))+log((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1))))+((6.0÷3.0)+(3.0*(2.0+8.0))))_dPRESSURE")) self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dlog((((2.0+6.0)*(3.0+((4.0-2.0)÷4.0)))+(6.0*r1)))_dPRESSURE")) + self.assertEqual(self.r1.calculate_gradient_calls, 1) + self.assertEqual(self.r2.calculate_gradient_calls, 1) + def test_MultipleControlVars(self): eval_resp = EvaluateResponseExpression("r1 + r2 + r3 + log(r3)", self.optimization_problem) eval_resp.Initialize() + + eval_resp.CalculateValue() + + self.assertEqual(self.r1.calculate_value_calls, 1) + self.assertEqual(self.r2.calculate_value_calls, 1) + self.assertEqual(self.r3.calculate_value_calls, 1) + + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr3_dTEMPERATURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dlog(r3)_dTEMPERATURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1+r2)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr((r1+r2)+r3)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr(((r1+r2)+r3)+log(r3)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1+r2)+r3)_dTEMPERATURE")) + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + self.r3.CalculateValue() + log(self.r3.CalculateValue()), 1e-8, 5) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr2_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr3_dTEMPERATURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dlog(r3)_dTEMPERATURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(r1+r2)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1+r2)+r3)_dPRESSURE")) + self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1+r2)+r3)_dTEMPERATURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((r1+r2)+r3)+log(r3)_dPRESSURE")) + self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d(((r1+r2)+r3)+log(r3)_dTEMPERATURE")) + + self.assertEqual(self.r1.calculate_gradient_calls, 1) + self.assertEqual(self.r2.calculate_gradient_calls, 1) + self.assertEqual(self.r3.calculate_gradient_calls, 1) + if __name__ == "__main__": kratos_unittest.main() \ No newline at end of file From eb24c271c9eff220ec54ed9693393e550376e252 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:34:07 +0530 Subject: [PATCH 30/36] minor --- .../OptimizationApplication/tests/test_response_utilities.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index d93413680251..5f1e29f8bf0c 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -488,6 +488,10 @@ def test_MultipleControlVars(self): eval_resp = EvaluateResponseExpression("r1 + r2 + r3 + log(r3)", self.optimization_problem) eval_resp.Initialize() + self.assertEqual(self.r1.calculate_value_calls, 0) + self.assertEqual(self.r2.calculate_value_calls, 0) + self.assertEqual(self.r3.calculate_value_calls, 0) + eval_resp.CalculateValue() self.assertEqual(self.r1.calculate_value_calls, 1) From 65710c726ee539576e61160034715944e52035b5 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 14:34:41 +0530 Subject: [PATCH 31/36] minor --- .../OptimizationApplication/tests/test_response_utilities.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 5f1e29f8bf0c..2730a1e19242 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -507,6 +507,10 @@ def test_MultipleControlVars(self): self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr(((r1+r2)+r3)+log(r3)_dPRESSURE")) self.assertFalse(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/d((r1+r2)+r3)_dTEMPERATURE")) + self.assertEqual(self.r1.calculate_gradient_calls, 0) + self.assertEqual(self.r2.calculate_gradient_calls, 0) + self.assertEqual(self.r3.calculate_gradient_calls, 0) + self.__CheckGradients([Kratos.PRESSURE, Kratos.TEMPERATURE], eval_resp, lambda : self.r1.CalculateValue() + self.r2.CalculateValue() + self.r3.CalculateValue() + log(self.r3.CalculateValue()), 1e-8, 5) self.assertTrue(ComponentDataView("evaluated_responses", self.optimization_problem).GetUnBufferedData().HasValue("gradients/dr1_dPRESSURE")) From 9f02bb85d69b0fd15532ced8c0e0314b5c8980e9 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 18:03:40 +0530 Subject: [PATCH 32/36] fix recursion --- .../python_scripts/responses/evaluation_response_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py b/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py index a0b3190d3be5..b903e07c8582 100644 --- a/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py @@ -65,4 +65,4 @@ def __ResetEvaluationData(response_function: ResponseFunction, unbuffered_data: del unbuffered_data[f"{prefix}/{response_function.GetName()}"] for child_response in response_function.GetChildResponses(): # now reset the children - EvaluationResponseFunction.__ResetEvaluationData(child_response, unbuffered_data) + EvaluationResponseFunction.__ResetEvaluationData(child_response, unbuffered_data, prefix) From 16c966c718ee3bc0838f2d1f974d5595f2a81b7c Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Sun, 4 Aug 2024 18:04:45 +0530 Subject: [PATCH 33/36] fix resp utils --- .../python_scripts/utilities/response_utilities.py | 11 +++++++---- .../tests/test_response_utilities.py | 3 +-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index 19d9a093e473..64ead9105039 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -129,10 +129,10 @@ def GetResponseFunction(current_value: str, optimization_problem: OptimizationPr index += 1 args_list.append(current_arg) - return GetFunction(function_name, optimization_problem, *[EvaluateResponseExpression(arg, optimization_problem) for arg in args_list]) + return GetFunction(function_name, optimization_problem, *[__EvaluateResponseExpressionImpl(arg, optimization_problem) for arg in args_list]) else: # this is a response expression. - return EvaluateResponseExpression(current_value, optimization_problem) + return __EvaluateResponseExpressionImpl(current_value, optimization_problem) def GetValuesAndOperators(response_expression: str, optimization_problem: OptimizationProblem) -> 'tuple[list[ResponseFunction], list[str]]': responses: 'list[ResponseFunction]' = [] @@ -168,7 +168,7 @@ def GetValuesAndOperators(response_expression: str, optimization_problem: Optimi return responses, operators -def EvaluateResponseExpression(response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: +def __EvaluateResponseExpressionImpl(response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: from KratosMultiphysics.OptimizationApplication.responses.binary_operator_response_function import BinaryOperatorResponseFunction response_expression = response_expression.replace(" ", "") @@ -225,4 +225,7 @@ def __evaluate_operator(list_of_operators: 'list[str]') -> None: __evaluate_operator(["*", "/"]) __evaluate_operator(["+", "-"]) - return EvaluationResponseFunction(responses[0], optimization_problem) \ No newline at end of file + return responses[0] + +def EvaluateResponseExpression(response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: + return EvaluationResponseFunction(__EvaluateResponseExpressionImpl(response_expression, optimization_problem), optimization_problem) \ No newline at end of file diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 2730a1e19242..4a9c989ab091 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -210,9 +210,8 @@ def test_CombinedResponseCalculateValue1(self): eval_value = eval_resp.CalculateValue() self.assertAlmostEqual(eval_value, self.r1.CalculateValue(), 12) - self.assertTrue(self.optimization_problem.HasResponse(eval_resp)) + self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) self.assertTrue(self.optimization_problem.HasResponse("r1")) - self.assertEqual(eval_resp.GetName(), "r1") # following is the resultant response function, hence the value storage is managed by the ResponseRoutine self.assertFalse(ComponentDataView(self.optimization_problem.GetResponse("r1"), self.optimization_problem).GetBufferedData().HasValue("value")) From ed815c149fee69e5c9e6e078e2ced84843e7f853 Mon Sep 17 00:00:00 2001 From: sunethwarna Date: Mon, 5 Aug 2024 10:10:25 +0530 Subject: [PATCH 34/36] minor fix --- .../utilities/response_utilities.py | 17 ++++++++++++++++- .../tests/test_response_utilities.py | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index 64ead9105039..a4c9caee446a 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -228,4 +228,19 @@ def __evaluate_operator(list_of_operators: 'list[str]') -> None: return responses[0] def EvaluateResponseExpression(response_expression: str, optimization_problem: OptimizationProblem) -> ResponseFunction: - return EvaluationResponseFunction(__EvaluateResponseExpressionImpl(response_expression, optimization_problem), optimization_problem) \ No newline at end of file + evaluated_response_impl = __EvaluateResponseExpressionImpl(response_expression, optimization_problem) + if not evaluated_response_impl.GetChildResponses() and not evaluated_response_impl.GetImplementedPhysicalKratosVariables(): + # if the response has children and has some dependence on the variables, then + # we need to use the EvaluationResponseFunction to clear the evaluation data + # whenever CalculateValue, CalculateGradient is used. + evaluated_response = EvaluationResponseFunction(__EvaluateResponseExpressionImpl(response_expression, optimization_problem), optimization_problem) + optimization_problem.AddComponent(evaluated_response) + return evaluated_response + else: + # this means the following cases + # 1. No children, but has dependence variables -> Leaf responses such as mass. + # 2. No children, no dependence variables -> Leaf responses such as LiteralValueResponse. + # 3. Has children, no dependence variables -> An expression with only LiteralValueResponses and functions, without any responses such as mass. + # in the above cases, there is no need to clear the response evaluation data, hence the original evaluated + # response is returned without the EvaluationResponseFunction wrapper. + return evaluated_response_impl \ No newline at end of file diff --git a/applications/OptimizationApplication/tests/test_response_utilities.py b/applications/OptimizationApplication/tests/test_response_utilities.py index 4a9c989ab091..62d7e07095f0 100644 --- a/applications/OptimizationApplication/tests/test_response_utilities.py +++ b/applications/OptimizationApplication/tests/test_response_utilities.py @@ -210,7 +210,7 @@ def test_CombinedResponseCalculateValue1(self): eval_value = eval_resp.CalculateValue() self.assertAlmostEqual(eval_value, self.r1.CalculateValue(), 12) - self.assertFalse(self.optimization_problem.HasResponse(eval_resp)) + self.assertTrue(self.optimization_problem.HasResponse(eval_resp)) self.assertTrue(self.optimization_problem.HasResponse("r1")) # following is the resultant response function, hence the value storage is managed by the ResponseRoutine From 932884fb341f6c75438a66c4f2e2c97511b299e6 Mon Sep 17 00:00:00 2001 From: Suneth Warnakulasuriya <7856520+sunethwarna@users.noreply.github.com> Date: Mon, 5 Aug 2024 10:13:39 +0530 Subject: [PATCH 35/36] name change --- .../python_scripts/responses/evaluation_response_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py b/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py index b903e07c8582..df1e14a2ade9 100644 --- a/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py +++ b/applications/OptimizationApplication/python_scripts/responses/evaluation_response_function.py @@ -9,7 +9,7 @@ class EvaluationResponseFunction(ResponseFunction): def __init__(self, response_function: ResponseFunction, optimization_problem: OptimizationProblem): - super().__init__(f"EvaluationResponse_{response_function.GetName()}") + super().__init__(f"Eval_{response_function.GetName()}") self.response_function = response_function self.optimization_problem = optimization_problem From fee23b918e34a0b1d251be6a839b04ba4ab536d4 Mon Sep 17 00:00:00 2001 From: Suneth Warnakulasuriya <7856520+sunethwarna@users.noreply.github.com> Date: Mon, 5 Aug 2024 10:17:57 +0530 Subject: [PATCH 36/36] repeated eval fix --- .../python_scripts/utilities/response_utilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py index a4c9caee446a..7942c0cca722 100644 --- a/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py +++ b/applications/OptimizationApplication/python_scripts/utilities/response_utilities.py @@ -233,7 +233,7 @@ def EvaluateResponseExpression(response_expression: str, optimization_problem: O # if the response has children and has some dependence on the variables, then # we need to use the EvaluationResponseFunction to clear the evaluation data # whenever CalculateValue, CalculateGradient is used. - evaluated_response = EvaluationResponseFunction(__EvaluateResponseExpressionImpl(response_expression, optimization_problem), optimization_problem) + evaluated_response = EvaluationResponseFunction(evaluated_response_impl, optimization_problem) optimization_problem.AddComponent(evaluated_response) return evaluated_response else: @@ -243,4 +243,4 @@ def EvaluateResponseExpression(response_expression: str, optimization_problem: O # 3. Has children, no dependence variables -> An expression with only LiteralValueResponses and functions, without any responses such as mass. # in the above cases, there is no need to clear the response evaluation data, hence the original evaluated # response is returned without the EvaluationResponseFunction wrapper. - return evaluated_response_impl \ No newline at end of file + return evaluated_response_impl