-
Notifications
You must be signed in to change notification settings - Fork 249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[OptApp] Adding the ability to have arithmetic operators for responses #12580
Changes from 25 commits
4d3aad0
2c5feee
c533979
59cd935
611aac0
2826339
1adf72e
ed91d62
2f45422
6df4f1f
994b27d
28fbcac
46200d4
0e8ff56
edc1eb7
ab522e5
c3a7480
e0e7c87
c495f74
9cd411c
002b1b0
e103eb8
1502175
c7eb813
a316e70
8796d89
d23ddcd
ed9bd08
94655d9
8cc75c4
33e58b3
8a0540e
eb24c27
65710c7
9f02bb8
16c966c
ed815c1
932884f
fee23b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from typing import Optional | ||
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 | ||
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): | ||
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.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() | ||
|
||
if len(self.response_function_1.GetImplementedPhysicalKratosVariables()) != 0 and len(self.response_function_2.GetImplementedPhysicalKratosVariables()) != 0: | ||
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: | ||
self.model_part = self.response_function_2.GetInfluencingModelPart() | ||
|
||
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: | ||
v1 = EvaluateValue(self.response_function_1, self.optimization_problem) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand the concept of evaluate. Why can't we just use Response Routine and it deifnes if smth should be computed or return from value directly ? It looks like dublication or bad design of response / response routine . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assume the scenario where "r1 + r2 * r1", where r1 needs to be evaluated twice. Instead of evaluating them twice, I use the EvaluteValue method to store the first evaluation of r1, and then call it in the second evaluation. I agree to your point that, instead of saving it in the OptProb, under respones, I will use a different place to save it. So, saving the value under the OptProb is decided by the response routine, and the call to CalculateValue will be normal per one evaluation. That means, for one call to CalculateValue/CalculateGradients of "r1 + r2 * r1", it will only evaluate r1 once, but for multiple CalculateValue/CalculateGradients, r1 and r2 will be evaluated once per CalculateValue/CalculateGradients calls. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for the gradients |
||
v2 = EvaluateValue(self.response_function_2, self.optimization_problem) | ||
|
||
# now do the binary arithmetics. | ||
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: | ||
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, 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()): | ||
if self.binary_operator == BinaryOperator.ADD: | ||
result.SetExpression((g1 + g2).GetExpression()) | ||
elif self.binary_operator == BinaryOperator.SUBTRACT: | ||
result.SetExpression((g1 - g2).GetExpression()) | ||
elif self.binary_operator == BinaryOperator.MULTIPLY: | ||
result.SetExpression((g1 * v2 + g2 * v1).GetExpression()) | ||
elif self.binary_operator == BinaryOperator.DIVIDE: | ||
result.SetExpression((g1 / v2 - g2 * (v1 / v2 ** 2)).GetExpression()) | ||
elif self.binary_operator == BinaryOperator.POWER: | ||
result.SetExpression(((g1 * (v2 / v1) + g2 * log(v1)) * (v1 ** v2)).GetExpression()) | ||
|
||
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()}]" | ||
else: | ||
return f"Response [type = {self.__class__.__name__}, name = {self.GetName()}, model part name = n/a ]" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()}]" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()}]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add class describtion please