KratosSystemIdentificationApplication 10.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- KratosMultiphysics/.libs/KratosSystemIdentificationApplication.so +0 -0
- KratosMultiphysics/.libs/libKratosSystemIdentificationCore.dylib +0 -0
- KratosMultiphysics/SystemIdentificationApplication/__init__.py +13 -0
- KratosMultiphysics/SystemIdentificationApplication/controls/data_values_control.py +210 -0
- KratosMultiphysics/SystemIdentificationApplication/controls/material_properties_control.py +212 -0
- KratosMultiphysics/SystemIdentificationApplication/convergence_criteria/sensor_error.py +93 -0
- KratosMultiphysics/SystemIdentificationApplication/processes/sensor_output_process.py +83 -0
- KratosMultiphysics/SystemIdentificationApplication/python_registry_lists.py +9 -0
- KratosMultiphysics/SystemIdentificationApplication/responses/damage_detection_response.py +190 -0
- KratosMultiphysics/SystemIdentificationApplication/responses/pressure_detection_response.py +20 -0
- KratosMultiphysics/SystemIdentificationApplication/responses/temperature_detection_response.py +20 -0
- KratosMultiphysics/SystemIdentificationApplication/sensor_generator_analysis.py +130 -0
- KratosMultiphysics/SystemIdentificationApplication/sensor_io/sensor_model_part_controller.py +58 -0
- KratosMultiphysics/SystemIdentificationApplication/sensor_sensitivity_solvers/response_sensitivity_analysis.py +41 -0
- KratosMultiphysics/SystemIdentificationApplication/sensor_sensitivity_solvers/response_sensitivity_interface.py +34 -0
- KratosMultiphysics/SystemIdentificationApplication/sensor_sensitivity_solvers/sensor_sensitivity_adjoint_static_solver.py +50 -0
- KratosMultiphysics/SystemIdentificationApplication/sensor_sensitivity_solvers/system_identification_static_analysis.py +52 -0
- KratosMultiphysics/SystemIdentificationApplication/utilities/data_utils.py +67 -0
- KratosMultiphysics/SystemIdentificationApplication/utilities/sensor_utils.py +196 -0
- KratosMultiphysics/SystemIdentificationApplication/utilities/tensor_adaptor_utils.py +63 -0
- kratossystemidentificationapplication-10.4.4.dist-info/METADATA +11 -0
- kratossystemidentificationapplication-10.4.4.dist-info/RECORD +27 -0
- kratossystemidentificationapplication-10.4.4.dist-info/WHEEL +6 -0
- kratossystemidentificationapplication.dylibs/libKratosCore.dylib +4 -0
- kratossystemidentificationapplication.dylibs/libgcc_s.1.1.dylib +0 -0
- kratossystemidentificationapplication.dylibs/libgomp.1.dylib +0 -0
- kratossystemidentificationapplication.dylibs/libstdc++.6.dylib +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import csv
|
|
3
|
+
|
|
4
|
+
import KratosMultiphysics as Kratos
|
|
5
|
+
import KratosMultiphysics.SystemIdentificationApplication as KratosSI
|
|
6
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction
|
|
7
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes
|
|
8
|
+
from KratosMultiphysics.OptimizationApplication.utilities.model_part_utilities import ModelPartOperation
|
|
9
|
+
from KratosMultiphysics.OptimizationApplication.execution_policies.execution_policy_decorator import ExecutionPolicyDecorator
|
|
10
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
11
|
+
from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView
|
|
12
|
+
from KratosMultiphysics.SystemIdentificationApplication.sensor_sensitivity_solvers.system_identification_static_analysis import SystemIdentificationStaticAnalysis
|
|
13
|
+
from KratosMultiphysics.SystemIdentificationApplication.utilities.sensor_utils import GetSensors
|
|
14
|
+
from KratosMultiphysics.SystemIdentificationApplication.utilities.sensor_utils import SetSensorNormalizationFactor
|
|
15
|
+
|
|
16
|
+
def Factory(model: Kratos.Model, parameters: Kratos.Parameters, optimization_problem: OptimizationProblem) -> ResponseFunction:
|
|
17
|
+
if not parameters.Has("name"):
|
|
18
|
+
raise RuntimeError(f"DamageDetectionResponse instantiation requires a \"name\" in parameters [ parameters = {parameters}].")
|
|
19
|
+
if not parameters.Has("settings"):
|
|
20
|
+
raise RuntimeError(f"DamageDetectionResponse instantiation requires a \"settings\" in parameters [ parameters = {parameters}].")
|
|
21
|
+
return DamageDetectionResponse(parameters["name"].GetString(), model, parameters["settings"], optimization_problem)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DamageDetectionResponse(ResponseFunction):
|
|
25
|
+
def __init__(self, name: str, model: Kratos.Model, parameters: Kratos.Parameters, optimization_problem: OptimizationProblem):
|
|
26
|
+
super().__init__(name)
|
|
27
|
+
|
|
28
|
+
default_settings = Kratos.Parameters("""{
|
|
29
|
+
"sensor_group_name" : "",
|
|
30
|
+
"p_coefficient" : 1,
|
|
31
|
+
"adjoint_parameters" : {},
|
|
32
|
+
"evaluated_model_part_names" : [
|
|
33
|
+
"PLEASE_PROVIDE_A_MODEL_PART_NAME"
|
|
34
|
+
],
|
|
35
|
+
"test_analysis_list": [
|
|
36
|
+
{
|
|
37
|
+
"primal_analysis_name": "Structure_static",
|
|
38
|
+
"sensor_measurement_csv_file": "measurement_data.csv",
|
|
39
|
+
"weight": 1.0,
|
|
40
|
+
"sensor_normalization_settings": {
|
|
41
|
+
"type": "none"
|
|
42
|
+
},
|
|
43
|
+
"variable_io_settings": {
|
|
44
|
+
"model_part_name" : "Structure",
|
|
45
|
+
"nodal_hist_variable_names" : [],
|
|
46
|
+
"nodal_non_hist_variable_names": [],
|
|
47
|
+
"condition_variable_names" : [],
|
|
48
|
+
"element_variable_names" : [],
|
|
49
|
+
"condition_property_names" : [],
|
|
50
|
+
"element_property_names" : []
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}""")
|
|
55
|
+
parameters.ValidateAndAssignDefaults(default_settings)
|
|
56
|
+
|
|
57
|
+
self.model = model
|
|
58
|
+
self.sensor_group_name = parameters["sensor_group_name"].GetString()
|
|
59
|
+
|
|
60
|
+
if self.sensor_group_name == "":
|
|
61
|
+
raise RuntimeError(f"The sensor group name cannot be empty.")
|
|
62
|
+
|
|
63
|
+
self.p_coefficient = parameters["p_coefficient"].GetDouble()
|
|
64
|
+
|
|
65
|
+
evaluated_model_part_names = parameters["evaluated_model_part_names"].GetStringArray()
|
|
66
|
+
if len(evaluated_model_part_names) == 0:
|
|
67
|
+
raise RuntimeError(f"No model parts were provided for {self._GetResponsePrefix()}. [ response name = \"{self.GetName()}\"]")
|
|
68
|
+
|
|
69
|
+
# reading test analsis list
|
|
70
|
+
self.list_of_test_analysis_data: 'list[tuple[ExecutionPolicyDecorator, str, Kratos.Parameters, float]]' = []
|
|
71
|
+
for params in parameters["test_analysis_list"].values():
|
|
72
|
+
params.ValidateAndAssignDefaults(default_settings["test_analysis_list"][0])
|
|
73
|
+
primal_analysis_name = params["primal_analysis_name"].GetString()
|
|
74
|
+
sensor_measurement_data_file_name = params["sensor_measurement_csv_file"].GetString()
|
|
75
|
+
sensor_normalization_settings = params["sensor_normalization_settings"]
|
|
76
|
+
weight = params["weight"].GetDouble()
|
|
77
|
+
self.list_of_test_analysis_data.append((optimization_problem.GetExecutionPolicy(primal_analysis_name), sensor_measurement_data_file_name, sensor_normalization_settings, weight))
|
|
78
|
+
|
|
79
|
+
self.model_part_operation = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_{self.GetName()}", evaluated_model_part_names, False)
|
|
80
|
+
self.model_part: Optional[Kratos.ModelPart] = None
|
|
81
|
+
|
|
82
|
+
self.analysis_model_part_operation = ModelPartOperation(self.model, ModelPartOperation.OperationType.UNION, f"response_test_analysis_{self.GetName()}", [exec.GetAnalysisModelPart().FullName() for exec, _, _, _ in self.list_of_test_analysis_data], False)
|
|
83
|
+
self.analysis_model_part: Optional[Kratos.ModelPart] = None
|
|
84
|
+
|
|
85
|
+
self.adjoint_analysis = SystemIdentificationStaticAnalysis(self.model, parameters["adjoint_parameters"])
|
|
86
|
+
|
|
87
|
+
self.sensor_name_dict: 'dict[str, KratosSI.Sensors.Sensor]' = {}
|
|
88
|
+
self.optimization_problem = optimization_problem
|
|
89
|
+
|
|
90
|
+
def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]':
|
|
91
|
+
return [Kratos.YOUNG_MODULUS]
|
|
92
|
+
|
|
93
|
+
def Initialize(self) -> None:
|
|
94
|
+
self.model_part = self.model_part_operation.GetModelPart()
|
|
95
|
+
self.analysis_model_part = self.analysis_model_part_operation.GetModelPart()
|
|
96
|
+
|
|
97
|
+
self.adjoint_analysis.Initialize()
|
|
98
|
+
|
|
99
|
+
self.damage_response_function = KratosSI.Responses.MeasurementResidualResponseFunction(self.p_coefficient)
|
|
100
|
+
|
|
101
|
+
if not self.optimization_problem.GetProblemDataContainer()["object"].HasValue(self.sensor_group_name):
|
|
102
|
+
raise RuntimeError(f"The sensor group \"{self.sensor_group_name}\" not found. Followings are available: \n\t" + "\n\t".join(self.optimization_problem.GetProblemDataContainer()["object"].GetSubItems().keys()))
|
|
103
|
+
|
|
104
|
+
sensor_group_data = ComponentDataView(self.sensor_group_name, self.optimization_problem)
|
|
105
|
+
self.list_of_sensors = GetSensors(sensor_group_data)
|
|
106
|
+
for sensor in self.list_of_sensors:
|
|
107
|
+
sensor.GetNode().SetValue(KratosSI.SENSOR_MEASURED_VALUE, 0.0)
|
|
108
|
+
sensor.GetNode().SetValue(KratosSI.SENSOR_NORMALIZATION_FACTOR, 1.0)
|
|
109
|
+
self.damage_response_function.AddSensor(sensor)
|
|
110
|
+
|
|
111
|
+
self.damage_response_function.Initialize()
|
|
112
|
+
|
|
113
|
+
for sensor in self.list_of_sensors:
|
|
114
|
+
self.sensor_name_dict[sensor.GetName()] = sensor
|
|
115
|
+
|
|
116
|
+
def Check(self) -> None:
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
def Finalize(self) -> None:
|
|
120
|
+
self.adjoint_analysis.Finalize()
|
|
121
|
+
|
|
122
|
+
def GetInfluencingModelPart(self) -> Kratos.ModelPart:
|
|
123
|
+
if self.analysis_model_part is None:
|
|
124
|
+
raise RuntimeError(f"Please call {self._GetResponsePrefix()}::Initialize first.")
|
|
125
|
+
return self.analysis_model_part
|
|
126
|
+
|
|
127
|
+
def CalculateValue(self) -> float:
|
|
128
|
+
result = 0.0
|
|
129
|
+
for exec_policy, sensor_measurement_data_file_name, sensor_normalization_settings, test_case_weight in self.list_of_test_analysis_data:
|
|
130
|
+
# first run the primal analysis.
|
|
131
|
+
exec_policy.Execute()
|
|
132
|
+
|
|
133
|
+
self.__SetSensorMeasuredValue(sensor_measurement_data_file_name)
|
|
134
|
+
SetSensorNormalizationFactor(self.list_of_sensors, sensor_normalization_settings)
|
|
135
|
+
|
|
136
|
+
result += test_case_weight * self.damage_response_function.CalculateValue(exec_policy.GetAnalysisModelPart())
|
|
137
|
+
Kratos.Logger.PrintInfo(self._GetResponsePrefix(), f"Computed \"{exec_policy.GetName()}\".")
|
|
138
|
+
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
def CalculateGradient(self, physical_variable_gradient_map: 'dict[SupportedSensitivityFieldVariableTypes, Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor]') -> None:
|
|
142
|
+
# make everything zeros
|
|
143
|
+
for physical_variable, cta in physical_variable_gradient_map.items():
|
|
144
|
+
cta.data[:] = 0.0
|
|
145
|
+
Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor(cta, perform_store_data_recursively=False, copy=False).StoreData()
|
|
146
|
+
|
|
147
|
+
# now compute sensitivities for each test scenario
|
|
148
|
+
for exec_policy, sensor_measurement_data_file_name, sensor_normalization_settings, test_case_weight in self.list_of_test_analysis_data:
|
|
149
|
+
# read and replace the measurement data for each test scenario
|
|
150
|
+
self.__SetSensorMeasuredValue(sensor_measurement_data_file_name)
|
|
151
|
+
SetSensorNormalizationFactor(self.list_of_sensors, sensor_normalization_settings)
|
|
152
|
+
|
|
153
|
+
# run a single adjoint for each test scenario
|
|
154
|
+
self.adjoint_analysis._GetSolver().GetComputingModelPart().ProcessInfo[KratosSI.TEST_ANALYSIS_NAME] = exec_policy.GetName()
|
|
155
|
+
self.adjoint_analysis._GetSolver().GetComputingModelPart().ProcessInfo[Kratos.STEP] = self.optimization_problem.GetStep()
|
|
156
|
+
self.adjoint_analysis.CalculateGradient(self.damage_response_function)
|
|
157
|
+
|
|
158
|
+
for physical_variable, cta in physical_variable_gradient_map.items():
|
|
159
|
+
sensitivity_variable = Kratos.KratosGlobals.GetVariable(Kratos.SensitivityUtilities.GetSensitivityVariableName(physical_variable))
|
|
160
|
+
for ta in cta.GetTensorAdaptors():
|
|
161
|
+
current_gradient = ta.Clone()
|
|
162
|
+
self.adjoint_analysis.GetGradient(sensitivity_variable, current_gradient)
|
|
163
|
+
ta.data[:] -= current_gradient.data[:] * test_case_weight
|
|
164
|
+
Kratos.TensorAdaptors.DoubleCombinedTensorAdaptor(cta, perform_collect_data_recursively=False, copy=False).CollectData()
|
|
165
|
+
|
|
166
|
+
def __GetSensor(self, sensor_name: str) -> KratosSI.Sensors.Sensor:
|
|
167
|
+
return self.sensor_name_dict[sensor_name]
|
|
168
|
+
|
|
169
|
+
def __GetHeaderIndices(self, csv_stream: csv.reader) -> 'tuple[int, int]':
|
|
170
|
+
headers = [s.strip() for s in next(csv_stream)]
|
|
171
|
+
name_index = headers.index("name")
|
|
172
|
+
value_index = headers.index("value")
|
|
173
|
+
return name_index, value_index
|
|
174
|
+
|
|
175
|
+
def __SetSensorMeasuredValue(self, sensor_measurement_data_file_name: str) -> None:
|
|
176
|
+
with open(sensor_measurement_data_file_name, "r") as csv_measurement_file:
|
|
177
|
+
csv_measurement_stream = csv.reader(csv_measurement_file, delimiter=",")
|
|
178
|
+
measured_name_index, measured_value_index = self.__GetHeaderIndices(csv_measurement_stream)
|
|
179
|
+
|
|
180
|
+
for measured_row in csv_measurement_stream:
|
|
181
|
+
measured_sensor_name = measured_row[measured_name_index].strip()
|
|
182
|
+
measured_value = float(measured_row[measured_value_index])
|
|
183
|
+
if measured_sensor_name in self.sensor_name_dict:
|
|
184
|
+
self.__GetSensor(measured_sensor_name).GetNode().SetValue(KratosSI.SENSOR_MEASURED_VALUE, measured_value)
|
|
185
|
+
|
|
186
|
+
def _GetResponsePrefix(self) -> str:
|
|
187
|
+
return "DamageDetectionResponse"
|
|
188
|
+
|
|
189
|
+
def __str__(self) -> str:
|
|
190
|
+
return f"Response [type = {self._GetResponsePrefix()}, name = {self.GetName()}, model part name = {self.model_part.FullName()}]"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction
|
|
3
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes
|
|
4
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
5
|
+
from KratosMultiphysics.SystemIdentificationApplication.responses.damage_detection_response import DamageDetectionResponse
|
|
6
|
+
|
|
7
|
+
def Factory(model: Kratos.Model, parameters: Kratos.Parameters, optimization_problem: OptimizationProblem) -> ResponseFunction:
|
|
8
|
+
if not parameters.Has("name"):
|
|
9
|
+
raise RuntimeError(f"PressureDetectionResponse instantiation requires a \"name\" in parameters [ parameters = {parameters}].")
|
|
10
|
+
if not parameters.Has("settings"):
|
|
11
|
+
raise RuntimeError(f"PressureDetectionResponse instantiation requires a \"settings\" in parameters [ parameters = {parameters}].")
|
|
12
|
+
return PressureDetectionResponse(parameters["name"].GetString(), model, parameters["settings"], optimization_problem)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PressureDetectionResponse(DamageDetectionResponse):
|
|
16
|
+
def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]':
|
|
17
|
+
return [Kratos.PRESSURE]
|
|
18
|
+
|
|
19
|
+
def _GetResponsePrefix(self) -> str:
|
|
20
|
+
return "PressureDetectionResponse"
|
KratosMultiphysics/SystemIdentificationApplication/responses/temperature_detection_response.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_function import ResponseFunction
|
|
3
|
+
from KratosMultiphysics.OptimizationApplication.responses.response_function import SupportedSensitivityFieldVariableTypes
|
|
4
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
5
|
+
from KratosMultiphysics.SystemIdentificationApplication.responses.damage_detection_response import DamageDetectionResponse
|
|
6
|
+
|
|
7
|
+
def Factory(model: Kratos.Model, parameters: Kratos.Parameters, optimization_problem: OptimizationProblem) -> ResponseFunction:
|
|
8
|
+
if not parameters.Has("name"):
|
|
9
|
+
raise RuntimeError(f"TemperatureDetectionResponse instantiation requires a \"name\" in parameters [ parameters = {parameters}].")
|
|
10
|
+
if not parameters.Has("settings"):
|
|
11
|
+
raise RuntimeError(f"TemperatureDetectionResponse instantiation requires a \"settings\" in parameters [ parameters = {parameters}].")
|
|
12
|
+
return TemperatureDetectionResponse(parameters["name"].GetString(), model, parameters["settings"], optimization_problem)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TemperatureDetectionResponse(DamageDetectionResponse):
|
|
16
|
+
def GetImplementedPhysicalKratosVariables(self) -> 'list[SupportedSensitivityFieldVariableTypes]':
|
|
17
|
+
return [Kratos.TEMPERATURE]
|
|
18
|
+
|
|
19
|
+
def _GetResponsePrefix(self) -> str:
|
|
20
|
+
return "TemperatureDetectionResponse"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
import KratosMultiphysics as Kratos
|
|
5
|
+
import KratosMultiphysics.SystemIdentificationApplication as KratosSI
|
|
6
|
+
|
|
7
|
+
class SensorGeneratorAnalysis:
|
|
8
|
+
def __init__(self, model: Kratos.Model, parameters: Kratos.Parameters) -> None:
|
|
9
|
+
default_settings = Kratos.Parameters("""{
|
|
10
|
+
"model_part_name" : "PLEASE_SPECIFY_MODEL_PART_NAME",
|
|
11
|
+
"mdpa_file_name" : "PlEASE_SPECIFY_FILE_NAME",
|
|
12
|
+
"output_file_name" : "sensor_data.json",
|
|
13
|
+
"csv_output_file_name": "sensor_data.csv",
|
|
14
|
+
"sensor_groups" : [
|
|
15
|
+
{
|
|
16
|
+
"generation_method": "mesh_based",
|
|
17
|
+
"container_type" : "elements",
|
|
18
|
+
"location" : "center",
|
|
19
|
+
"sensor_settings" : {}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}""")
|
|
23
|
+
|
|
24
|
+
self.parameters = parameters
|
|
25
|
+
self.parameters.ValidateAndAssignDefaults(default_settings)
|
|
26
|
+
self.model = model
|
|
27
|
+
|
|
28
|
+
model_part_name = self.parameters["model_part_name"].GetString()
|
|
29
|
+
root_model_part = model_part_name.split(".")[0]
|
|
30
|
+
self.root_model_part = self.model.CreateModelPart(root_model_part)
|
|
31
|
+
|
|
32
|
+
def Run(self) -> None:
|
|
33
|
+
Kratos.ModelPartIO(self.parameters["mdpa_file_name"].GetString(), Kratos.ModelPartIO.READ | Kratos.ModelPartIO.MESH_ONLY).ReadModelPart(self.root_model_part)
|
|
34
|
+
self.model_part = self.model[self.parameters["model_part_name"].GetString()]
|
|
35
|
+
|
|
36
|
+
sensors_dict = {"list_of_sensors": []}
|
|
37
|
+
for sensor_group in self.parameters["sensor_groups"].values():
|
|
38
|
+
generation_method = sensor_group["generation_method"].GetString()
|
|
39
|
+
if generation_method == "mesh_based":
|
|
40
|
+
self.__GenerateMeshBased(sensors_dict, sensor_group)
|
|
41
|
+
elif generation_method == "bounding_surface":
|
|
42
|
+
self.__GenerationBoundingSurfaceBased(sensors_dict, sensor_group)
|
|
43
|
+
else:
|
|
44
|
+
raise RuntimeError("Unsupported generation method")
|
|
45
|
+
|
|
46
|
+
with open(self.parameters["output_file_name"].GetString(), "w") as file_output:
|
|
47
|
+
file_output.write(json.dumps(sensors_dict, indent=4))
|
|
48
|
+
|
|
49
|
+
with open(self.parameters["csv_output_file_name"].GetString(), "w") as file_output:
|
|
50
|
+
file_output.write("#,name,location_0,location_1,location_2\n")
|
|
51
|
+
for i, sensor_info in enumerate(sensors_dict["list_of_sensors"]):
|
|
52
|
+
name = sensor_info["name"]
|
|
53
|
+
location = sensor_info["location"]
|
|
54
|
+
file_output.write(f"{i+1},{name},{location[0]},{location[1]},{location[2]}\n")
|
|
55
|
+
|
|
56
|
+
def __GenerationBoundingSurfaceBased(self, sensors_dict, sensor_group_params: Kratos.Parameters):
|
|
57
|
+
defaults = Kratos.Parameters("""{
|
|
58
|
+
"generation_method" : "bounding_surface",
|
|
59
|
+
"bounding_surface_corner_1": [0.0, 0.0, 0.0],
|
|
60
|
+
"bounding_surface_corner_2": [0.0, 0.0, 0.0],
|
|
61
|
+
"number_of_sensors" : [1, 1, 1],
|
|
62
|
+
"sensor_settings" : {}
|
|
63
|
+
}""")
|
|
64
|
+
sensor_group_params.ValidateAndAssignDefaults(defaults)
|
|
65
|
+
|
|
66
|
+
point_locator = Kratos.BruteForcePointLocator(self.model_part)
|
|
67
|
+
|
|
68
|
+
corner_1 = sensor_group_params["bounding_surface_corner_1"].GetVector()
|
|
69
|
+
corner_2 = sensor_group_params["bounding_surface_corner_2"].GetVector()
|
|
70
|
+
number_of_sensors = sensor_group_params["number_of_sensors"].GetVector()
|
|
71
|
+
number_of_sensors = [int(v) for v in number_of_sensors]
|
|
72
|
+
|
|
73
|
+
sensor_params = sensor_group_params["sensor_settings"]
|
|
74
|
+
if not sensor_params.Has("location"):
|
|
75
|
+
sensor_params.AddVector("location", Kratos.Vector())
|
|
76
|
+
|
|
77
|
+
distance = corner_2 - corner_1
|
|
78
|
+
index = 1
|
|
79
|
+
for i_x in range(number_of_sensors[0]):
|
|
80
|
+
x_coord = corner_1[0] + distance[0] * (i_x + 1) / (number_of_sensors[0] + 1)
|
|
81
|
+
for i_y in range(number_of_sensors[1]):
|
|
82
|
+
y_coord = corner_1[1] + distance[1] * (i_y + 1) / (number_of_sensors[1] + 1)
|
|
83
|
+
for i_z in range(number_of_sensors[2]):
|
|
84
|
+
z_coord = corner_1[2] + distance[2] * (i_z + 1) / (number_of_sensors[2] + 1)
|
|
85
|
+
loc = Kratos.Point(x_coord, y_coord, z_coord)
|
|
86
|
+
shape_funcs = Kratos.Vector()
|
|
87
|
+
elem_id = point_locator.FindElement(loc, shape_funcs, Kratos.Configuration.Initial, 1e-8)
|
|
88
|
+
if elem_id != -1 and KratosSI.SensorUtils.IsPointInGeometry(loc, self.model_part.GetElement(elem_id).GetGeometry()):
|
|
89
|
+
current_params = sensor_params.Clone()
|
|
90
|
+
current_params["location"].SetVector(loc)
|
|
91
|
+
json_dict = json.loads(current_params.WriteJsonString().replace("<ENTITY_ID>", str(index)))
|
|
92
|
+
index += 1
|
|
93
|
+
sensors_dict["list_of_sensors"].append(json_dict)
|
|
94
|
+
|
|
95
|
+
def __GenerateMeshBased(self, sensors_dict, sensor_group_params: Kratos.Parameters):
|
|
96
|
+
defaults = Kratos.Parameters("""{
|
|
97
|
+
"generation_method": "mesh_based",
|
|
98
|
+
"container_type" : "elements",
|
|
99
|
+
"location" : "center",
|
|
100
|
+
"sensor_settings" : {}
|
|
101
|
+
}""")
|
|
102
|
+
sensor_group_params.ValidateAndAssignDefaults(defaults)
|
|
103
|
+
container_type = sensor_group_params["container_type"].GetString()
|
|
104
|
+
container: 'typing.Union[Kratos.ElementsArray, Kratos.ConditionsArray]'
|
|
105
|
+
if container_type == "nodes":
|
|
106
|
+
container = self.model_part.Nodes
|
|
107
|
+
elif container_type == "elements":
|
|
108
|
+
container = self.model_part.Elements
|
|
109
|
+
elif container_type == "conditions":
|
|
110
|
+
container = self.model_part.Conditions
|
|
111
|
+
else:
|
|
112
|
+
raise RuntimeError(f"Unsupported container_type = \"{container_type}\".")
|
|
113
|
+
|
|
114
|
+
sensor_params = sensor_group_params["sensor_settings"]
|
|
115
|
+
if not sensor_params.Has("location"):
|
|
116
|
+
sensor_params.AddVector("location", Kratos.Vector())
|
|
117
|
+
|
|
118
|
+
location = sensor_group_params["location"].GetString()
|
|
119
|
+
if container_type == "nodes":
|
|
120
|
+
location_retrieval = lambda x: [x.X, x.Y, x.Z]
|
|
121
|
+
elif location == "center":
|
|
122
|
+
location_retrieval = lambda x: x.GetGeometry().Center()
|
|
123
|
+
else:
|
|
124
|
+
raise RuntimeError(f"Unsupported location = \"{location}\".")
|
|
125
|
+
|
|
126
|
+
for entity in container:
|
|
127
|
+
current_params = sensor_params.Clone()
|
|
128
|
+
current_params["location"].SetVector(location_retrieval(entity))
|
|
129
|
+
json_dict = json.loads(current_params.WriteJsonString().replace("<ENTITY_ID>", str(entity.Id)))
|
|
130
|
+
sensors_dict["list_of_sensors"].append(json_dict)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
from KratosMultiphysics.OptimizationApplication.model_part_controllers.model_part_controller import ModelPartController
|
|
3
|
+
from KratosMultiphysics.OptimizationApplication.utilities.optimization_problem import OptimizationProblem
|
|
4
|
+
from KratosMultiphysics.SystemIdentificationApplication.utilities.sensor_utils import CreateSensors
|
|
5
|
+
from KratosMultiphysics.SystemIdentificationApplication.utilities.sensor_utils import SetSensors
|
|
6
|
+
from KratosMultiphysics.OptimizationApplication.utilities.component_data_view import ComponentDataView
|
|
7
|
+
|
|
8
|
+
def Factory(model: Kratos.Model, parameters: Kratos.Parameters, optimization_problem: OptimizationProblem) -> ModelPartController:
|
|
9
|
+
if not parameters.Has("settings"):
|
|
10
|
+
raise RuntimeError(f"SensorModelPartController instantiation requires a \"settings\" in parameters [ parameters = {parameters}].")
|
|
11
|
+
return SensorModelPartController(model, parameters["settings"], optimization_problem)
|
|
12
|
+
|
|
13
|
+
class SensorModelPartController(ModelPartController):
|
|
14
|
+
def __init__(self, model: Kratos.Model, parameters: Kratos.Parameters, optimization_problem: OptimizationProblem):
|
|
15
|
+
default_settings = Kratos.Parameters("""{
|
|
16
|
+
"sensor_group_name" : "",
|
|
17
|
+
"domain_model_part_name" : "",
|
|
18
|
+
"list_of_sensors" : [],
|
|
19
|
+
"debug_output" : false,
|
|
20
|
+
"debug_output_filename" : "domain_model_part.vtu"
|
|
21
|
+
}""")
|
|
22
|
+
|
|
23
|
+
self.model = model
|
|
24
|
+
self.optimization_problem = optimization_problem
|
|
25
|
+
self.parameters = parameters
|
|
26
|
+
self.parameters.ValidateAndAssignDefaults(default_settings)
|
|
27
|
+
|
|
28
|
+
self.domain_model_part_name = parameters["domain_model_part_name"].GetString()
|
|
29
|
+
self.sensor_group_name = parameters["sensor_group_name"].GetString()
|
|
30
|
+
self.debug_output = parameters["debug_output"].GetBool()
|
|
31
|
+
self.debug_output_filename = parameters["debug_output_filename"].GetString()
|
|
32
|
+
|
|
33
|
+
if model.HasModelPart(self.sensor_group_name):
|
|
34
|
+
raise RuntimeError(f"The sensor model part name \"{self.sensor_group_name}\" already exists. Please provide a different \"sensor_group_name\".")
|
|
35
|
+
self.sensor_model_part = model.CreateModelPart(self.sensor_group_name)
|
|
36
|
+
|
|
37
|
+
# this dummy component data view is used to create the proper sensor group within the optimization problem.
|
|
38
|
+
ComponentDataView(self.sensor_group_name, self.optimization_problem)
|
|
39
|
+
|
|
40
|
+
def ImportModelPart(self) -> None:
|
|
41
|
+
self.domain_model_part = self.model[self.domain_model_part_name]
|
|
42
|
+
|
|
43
|
+
if self.debug_output:
|
|
44
|
+
vtu_output: Kratos.VtuOutput = Kratos.VtuOutput(self.domain_model_part)
|
|
45
|
+
vtu_output.PrintOutput(self.debug_output_filename)
|
|
46
|
+
|
|
47
|
+
self.list_of_sensors = CreateSensors(self.sensor_model_part, self.domain_model_part, self.parameters["list_of_sensors"].values())
|
|
48
|
+
|
|
49
|
+
# add the list of sensors to optimization problem
|
|
50
|
+
sensor_group_data = ComponentDataView(self.sensor_group_name, self.optimization_problem)
|
|
51
|
+
sensor_group_data.SetDataBuffer(1)
|
|
52
|
+
|
|
53
|
+
SetSensors(sensor_group_data, self.list_of_sensors)
|
|
54
|
+
|
|
55
|
+
def GetModelPart(self) -> Kratos.ModelPart:
|
|
56
|
+
return self.sensor_model_part
|
|
57
|
+
|
|
58
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
import KratosMultiphysics as Kratos
|
|
4
|
+
|
|
5
|
+
from KratosMultiphysics.analysis_stage import AnalysisStage
|
|
6
|
+
from KratosMultiphysics.OptimizationApplication.utilities.union_utilities import SupportedSensitivityFieldVariableTypes
|
|
7
|
+
|
|
8
|
+
class ResponseSensitivityAnalysis(AnalysisStage, abc.ABC):
|
|
9
|
+
@abc.abstractmethod
|
|
10
|
+
def CalculateGradient(self, response_function: Kratos.AdjointResponseFunction) -> None:
|
|
11
|
+
"""Calculate the gradient using the provided adjoint response function.
|
|
12
|
+
|
|
13
|
+
This method calculate all the sensitivities using the given adjoint response function.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
response_function (Kratos.AdjointResponseFunction): Adjoint response function to be used for sensitivity computation
|
|
17
|
+
"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def GetGradient(self, sensitivity_variable: SupportedSensitivityFieldVariableTypes, gradient_tensor_adaptor: Kratos.TensorAdaptors.DoubleTensorAdaptor) -> None:
|
|
21
|
+
"""Returns the gradients in the domain represented by gradient_tensor_adaptor tensor adaptor.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
sensitivity_variable (SupportedSensitivityFieldVariableTypes): Sensitivity variable
|
|
25
|
+
gradient_tensor_adaptor (Kratos.TensorAdaptors.DoubleTensorAdaptor): TensorAdaptor to hold the gradients.
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(gradient_tensor_adaptor.GetContainer(), Kratos.NodesArray):
|
|
28
|
+
Kratos.TensorAdaptors.HistoricalVariableTensorAdaptor(gradient_tensor_adaptor, sensitivity_variable, copy=False).CollectData()
|
|
29
|
+
else:
|
|
30
|
+
Kratos.TensorAdaptors.VariableTensorAdaptor(gradient_tensor_adaptor, sensitivity_variable, copy=False).CollectData()
|
|
31
|
+
|
|
32
|
+
def GetProcessesOrder(self) -> 'list[str]':
|
|
33
|
+
"""The order of execution of the process categories.
|
|
34
|
+
|
|
35
|
+
This defines the order of execution of the processes defined under "processes"
|
|
36
|
+
in either "kratos_process" or "optimization_data_processes".
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
list[str]: List of strings for process categories.
|
|
40
|
+
"""
|
|
41
|
+
return ["auxiliary_processes", "output_processes"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import KratosMultiphysics as Kratos
|
|
3
|
+
|
|
4
|
+
class ResponseSensitivityInterface(abc.ABC):
|
|
5
|
+
@abc.abstractmethod
|
|
6
|
+
def SetResponseFunction(self, response_function: Kratos.AdjointResponseFunction) -> None:
|
|
7
|
+
"""Set the response function to compute the sensitivities using adjoint method.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
response_function (Kratos.AdjointResponseFunction): Response function to be used to compute the sensitivities.
|
|
11
|
+
"""
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def GetResponseFunction(self) -> Kratos.AdjointResponseFunction:
|
|
16
|
+
"""Returns the response function used to compute the sensitivities.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Kratos.AdjointResponseFunction: Adjoint response function used in adjoint based sensitivity computation.
|
|
20
|
+
"""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abc.abstractmethod
|
|
24
|
+
def GetSensitivityModelPart(self) -> Kratos.ModelPart:
|
|
25
|
+
"""Returns the model part used on which the sensitivities are computed.
|
|
26
|
+
|
|
27
|
+
This is the model part on which the sensitivities are computed, not the model part on which
|
|
28
|
+
the adjoint solution is computed.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Kratos.ModelPart: Sensitivity model part on which the sensitivities are computed.
|
|
32
|
+
"""
|
|
33
|
+
pass
|
|
34
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
import KratosMultiphysics.SystemIdentificationApplication as KratosSI
|
|
3
|
+
from KratosMultiphysics.StructuralMechanicsApplication.structural_mechanics_adjoint_static_solver import StructuralMechanicsAdjointStaticSolver
|
|
4
|
+
from KratosMultiphysics.SystemIdentificationApplication.sensor_sensitivity_solvers.response_sensitivity_interface import ResponseSensitivityInterface
|
|
5
|
+
|
|
6
|
+
class SensorSensitivityAdjointStaticSolver(StructuralMechanicsAdjointStaticSolver, ResponseSensitivityInterface):
|
|
7
|
+
def __init__(self, model: Kratos.Model, custom_settings: Kratos.Parameters):
|
|
8
|
+
super().__init__(model, custom_settings)
|
|
9
|
+
self.sensitivity_builder_initialized = False
|
|
10
|
+
|
|
11
|
+
default_response_function_settings = Kratos.Parameters("""{
|
|
12
|
+
"perturbation_size" : 1e-8,
|
|
13
|
+
"adapt_perturbation_size": true
|
|
14
|
+
}""")
|
|
15
|
+
self.settings["response_function_settings"].ValidateAndAssignDefaults(default_response_function_settings)
|
|
16
|
+
|
|
17
|
+
def Initialize(self) -> None:
|
|
18
|
+
model_part = self.GetComputingModelPart()
|
|
19
|
+
|
|
20
|
+
response_function_settings: Kratos.Parameters = self.settings["response_function_settings"]
|
|
21
|
+
model_part.ProcessInfo[KratosSI.PERTURBATION_SIZE] = response_function_settings["perturbation_size"].GetDouble()
|
|
22
|
+
model_part.ProcessInfo[KratosSI.ADAPT_PERTURBATION_SIZE] = response_function_settings["adapt_perturbation_size"].GetBool()
|
|
23
|
+
|
|
24
|
+
# create sensitivity builder
|
|
25
|
+
self.sensitivity_builder = Kratos.SensitivityBuilder(self.settings["sensitivity_settings"], self.main_model_part, None)
|
|
26
|
+
|
|
27
|
+
# skip calling Initialize of StructuralMechanicsAdjointStaticSolver and call the its base class Initialize
|
|
28
|
+
super(StructuralMechanicsAdjointStaticSolver, self).Initialize()
|
|
29
|
+
|
|
30
|
+
Kratos.Logger.PrintInfo(self.__class__.__name__, "Finished initialization.")
|
|
31
|
+
|
|
32
|
+
def SetResponseFunction(self, response_function: Kratos.AdjointResponseFunction) -> None:
|
|
33
|
+
self.response_function = response_function
|
|
34
|
+
self.sensitivity_builder.SetResponseFunction(response_function)
|
|
35
|
+
self._GetScheme().SetResponseFunction(response_function)
|
|
36
|
+
if not self.sensitivity_builder_initialized:
|
|
37
|
+
self.sensitivity_builder.Initialize()
|
|
38
|
+
self.sensitivity_builder_initialized = True
|
|
39
|
+
|
|
40
|
+
def GetResponseFunction(self) -> Kratos.AdjointResponseFunction:
|
|
41
|
+
return self._GetScheme().GetResponseFunction()
|
|
42
|
+
|
|
43
|
+
def GetSensitivityModelPart(self) -> Kratos.ModelPart:
|
|
44
|
+
return self.main_model_part.GetSubModelPart(self.settings["sensitivity_settings"]["sensitivity_model_part_name"].GetString())
|
|
45
|
+
|
|
46
|
+
def SolveSolutionStep(self) -> bool:
|
|
47
|
+
return super(StructuralMechanicsAdjointStaticSolver, self).SolveSolutionStep()
|
|
48
|
+
|
|
49
|
+
def _CreateScheme(self):
|
|
50
|
+
return Kratos.ResidualBasedAdjointStaticScheme(None)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import KratosMultiphysics as Kratos
|
|
2
|
+
import KratosMultiphysics.SystemIdentificationApplication as KratosSI
|
|
3
|
+
|
|
4
|
+
from KratosMultiphysics.SystemIdentificationApplication.sensor_sensitivity_solvers.response_sensitivity_analysis import ResponseSensitivityAnalysis
|
|
5
|
+
from KratosMultiphysics.SystemIdentificationApplication.sensor_sensitivity_solvers.sensor_sensitivity_adjoint_static_solver import SensorSensitivityAdjointStaticSolver
|
|
6
|
+
|
|
7
|
+
def Factory(model: Kratos.Model, parameters: Kratos.Parameters, _):
|
|
8
|
+
return SystemIdentificationStaticAnalysis(model, parameters["settings"])
|
|
9
|
+
|
|
10
|
+
class SystemIdentificationStaticAnalysis(ResponseSensitivityAnalysis):
|
|
11
|
+
def _CreateSolver(self) -> SensorSensitivityAdjointStaticSolver:
|
|
12
|
+
return SensorSensitivityAdjointStaticSolver(self.model, self.project_parameters["solver_settings"])
|
|
13
|
+
|
|
14
|
+
def _GetSimulationName(self) -> str:
|
|
15
|
+
return "::[SystemIdentificationAnalysis]:: "
|
|
16
|
+
|
|
17
|
+
def CalculateGradient(self, response_function: Kratos.AdjointResponseFunction) -> None:
|
|
18
|
+
self._GetSolver().SetResponseFunction(response_function)
|
|
19
|
+
self.InitializeSolutionStep()
|
|
20
|
+
response_function.InitializeSolutionStep()
|
|
21
|
+
self._GetSolver().SolveSolutionStep()
|
|
22
|
+
response_function.FinalizeSolutionStep()
|
|
23
|
+
self.FinalizeSolutionStep()
|
|
24
|
+
self.OutputSolutionStep()
|
|
25
|
+
|
|
26
|
+
def PrintAnalysisStageProgressInformation(self):
|
|
27
|
+
process_info = self._GetSolver().GetComputingModelPart().ProcessInfo
|
|
28
|
+
Kratos.Logger.PrintInfo(self._GetSimulationName(), f"Step {process_info[Kratos.STEP]}: Computed sensitivities for response \"{process_info[KratosSI.SENSOR_NAME]}\" using \"{process_info[KratosSI.TEST_ANALYSIS_NAME]}\" analysis.")
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
from sys import argv
|
|
32
|
+
|
|
33
|
+
if len(argv) > 2:
|
|
34
|
+
err_msg = 'Too many input arguments!\n'
|
|
35
|
+
err_msg += 'Use this script in the following way:\n'
|
|
36
|
+
err_msg += '- With default ProjectParameters (read from "ProjectParameters.json"):\n'
|
|
37
|
+
err_msg += ' "python3 structural_mechanics_analysis.py"\n'
|
|
38
|
+
err_msg += '- With custom ProjectParameters:\n'
|
|
39
|
+
err_msg += ' "python3 structural_mechanics_analysis.py CustomProjectParameters.json"\n'
|
|
40
|
+
raise Exception(err_msg)
|
|
41
|
+
|
|
42
|
+
if len(argv) == 2: # ProjectParameters is being passed from outside
|
|
43
|
+
project_parameters_file_name = argv[1]
|
|
44
|
+
else: # using default name
|
|
45
|
+
project_parameters_file_name = "ProjectParameters.json"
|
|
46
|
+
|
|
47
|
+
with open(project_parameters_file_name,'r') as parameter_file:
|
|
48
|
+
parameters = Kratos.Parameters(parameter_file.read())
|
|
49
|
+
|
|
50
|
+
model = Kratos.Model()
|
|
51
|
+
simulation = SystemIdentificationStaticAnalysis(model, parameters)
|
|
52
|
+
simulation.Run()
|