diff --git a/applications/ShapeOptimizationApplication/custom_python/add_custom_utilities_to_python.cpp b/applications/ShapeOptimizationApplication/custom_python/add_custom_utilities_to_python.cpp index 0ce3eb41d749..4cfad19dd823 100644 --- a/applications/ShapeOptimizationApplication/custom_python/add_custom_utilities_to_python.cpp +++ b/applications/ShapeOptimizationApplication/custom_python/add_custom_utilities_to_python.cpp @@ -37,6 +37,7 @@ #include "custom_utilities/input_output/universal_file_io.h" #include "custom_utilities/search_based_functions.h" #include "custom_utilities/response_functions/face_angle_response_function_utility.h" +#include "custom_utilities/response_functions/water_drain_response_function_utility.h" // ============================================================================== @@ -294,6 +295,14 @@ void AddCustomUtilitiesToPython(pybind11::module& m) .def("CalculateGradient", &FaceAngleResponseFunctionUtility::CalculateGradient) ; + py::class_(m, "WaterDrainResponseFunctionUtility") + .def(py::init()) + .def("Initialize", &WaterDrainResponseFunctionUtility::Initialize) + .def("InitializeSolutionStep", &WaterDrainResponseFunctionUtility::InitializeSolutionStep) + .def("CalculateValue", &WaterDrainResponseFunctionUtility::CalculateValue) + .def("CalculateGradient", &WaterDrainResponseFunctionUtility::CalculateGradient) + ; + // ======================================================================== // Additional operations // ======================================================================== diff --git a/applications/ShapeOptimizationApplication/custom_python/shape_optimization_python_application.cpp b/applications/ShapeOptimizationApplication/custom_python/shape_optimization_python_application.cpp index bdb0cad4e51e..db2b06c70c58 100644 --- a/applications/ShapeOptimizationApplication/custom_python/shape_optimization_python_application.cpp +++ b/applications/ShapeOptimizationApplication/custom_python/shape_optimization_python_application.cpp @@ -120,6 +120,10 @@ PYBIND11_MODULE(KratosShapeOptimizationApplication, m) KRATOS_REGISTER_IN_PYTHON_3D_VARIABLE_WITH_COMPONENTS(m, BACKGROUND_NORMAL); KRATOS_REGISTER_IN_PYTHON_3D_VARIABLE_WITH_COMPONENTS(m, OUT_OF_PLANE_DELTA); + // For water drain response + KRATOS_REGISTER_IN_PYTHON_VARIABLE(m, WATER_LEVEL); + KRATOS_REGISTER_IN_PYTHON_VARIABLE(m, WATER_VOLUMES); + // For sensitivity heatmap KRATOS_REGISTER_IN_PYTHON_3D_VARIABLE_WITH_COMPONENTS(m, HEATMAP_DF1DX); KRATOS_REGISTER_IN_PYTHON_VARIABLE(m, HEATMAP_DF1DALPHA); diff --git a/applications/ShapeOptimizationApplication/custom_utilities/response_functions/water_drain_response_function_utility.cpp b/applications/ShapeOptimizationApplication/custom_utilities/response_functions/water_drain_response_function_utility.cpp new file mode 100644 index 000000000000..2648a6ce712b --- /dev/null +++ b/applications/ShapeOptimizationApplication/custom_utilities/response_functions/water_drain_response_function_utility.cpp @@ -0,0 +1,468 @@ +// ============================================================================== +// KratosShapeOptimizationApplication +// +// License: BSD License +// license: ShapeOptimizationApplication/license.txt +// +// Main authors: Schmölz David, https://github.com/dschmoelz +// +// ============================================================================== + +// System includes + +// External includes + +// Project includes +#include "custom_utilities/response_functions/water_drain_response_function_utility.h" +#include "custom_utilities/geometry_utilities.h" +#include "shape_optimization_application.h" +#include "utilities/variable_utils.h" +#include "utilities/parallel_utilities.h" +#include "utilities/reduction_utilities.h" +#include "includes/global_pointer_variables.h" +#include "includes/model_part.h" +#include "processes/find_global_nodal_neighbours_for_entities_process.h" +#include "processes/find_global_nodal_entity_neighbours_process.h" +#include "utilities/builtin_timer.h" +#include "utilities/openmp_utils.h" + + +// ============================================================================== + +namespace Kratos +{ + +WaterDrainResponseFunctionUtility::WaterDrainResponseFunctionUtility(ModelPart& rModelPart, Parameters ResponseSettings) + : mrModelPart(rModelPart), mGeometryUtilities(GeometryUtilities(mrModelPart)) +{ + const std::size_t domain_size = mrModelPart.GetProcessInfo()[DOMAIN_SIZE]; + KRATOS_ERROR_IF(domain_size != 3) << "WaterDrainResponseFunctionUtility can only be used on 3D geometries!" << std::endl; + + mGravityDirection = ResponseSettings["gravity_direction"].GetVector(); + const double direction_norm = norm_2(mGravityDirection); + KRATOS_ERROR_IF(direction_norm < 1e-16) << "WaterDrainResponseFunctionUtility: 'main_direction' vector norm is 0!" << std::endl; + mGravityDirection /= direction_norm; + + mMaxIterations = ResponseSettings["max_iterations_volume_search"].GetInt(); + + mContinuousSens = ResponseSettings["continuous_sensitivities"].GetBool(); + mQuadraticHeightPenalization = ResponseSettings["quadratic_height_penalization"].GetBool(); + + mFreeEdgeSubModelPartName = ResponseSettings["free_edge_part_name"].GetString(); + if (mFreeEdgeSubModelPartName == "automatic") { + mFreeEdgeSubModelPartName = "free_edge"; + mDetectFreeEdgeAutomatic = true; + } +} + +void WaterDrainResponseFunctionUtility::Initialize() +{ + KRATOS_TRY; + + BuiltinTimer timer; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Starting Initialize..." << std::endl; + + // get free edges automatically + if (mDetectFreeEdgeAutomatic) { + // neighbour nodes for automatic edge extraction + FindNodalNeighboursForEntitiesProcess find_neighbours_process( + mrModelPart, + NEIGHBOUR_NODES); + find_neighbours_process.Execute(); + + // neighbour conditions for automatic edge extraction + FindGlobalNodalEntityNeighboursProcess find_condition_neighbours_process( + mrModelPart, + NEIGHBOUR_CONDITIONS); + find_condition_neighbours_process.Execute(); + + KRATOS_ERROR_IF(mrModelPart.HasSubModelPart(mFreeEdgeSubModelPartName)) << + "ShapeOpt::WaterDrain" << "Automatic free edge detection not possible, because sub model part with name " << + mFreeEdgeSubModelPartName << " already exists!" << std::endl; + ModelPart& r_free_edge = mrModelPart.CreateSubModelPart(mFreeEdgeSubModelPartName); + mGeometryUtilities.ExtractEdgeNodes(mFreeEdgeSubModelPartName); + mFreeEdgeSubModelPartName = r_free_edge.FullName(); + KRATOS_INFO("ShapeOpt::WaterDrain") << "Free edge automatically created. # Nodes = " << r_free_edge.Nodes().size() << std::endl; + } + + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished Initialize in " << timer.ElapsedSeconds() << " s." << std::endl; + + KRATOS_CATCH(""); +} + +void WaterDrainResponseFunctionUtility::InitializeSolutionStep() +{ + KRATOS_TRY; + + VariableUtils().SetHistoricalVariableToZero(WATER_LEVEL, mrModelPart.Nodes()); + VariableUtils().SetHistoricalVariableToZero(WATER_VOLUMES, mrModelPart.Nodes()); + + this->SearchWaterVolumes(); + mGeometryUtilities.CalculateNodalAreasFromConditions(); + + KRATOS_CATCH(""); +} + +double WaterDrainResponseFunctionUtility::CalculateValue() +{ + KRATOS_TRY; + + const double value = block_for_each>(mrModelPart.Nodes(), [&](Node& rNode) { + Vector normal = rNode.FastGetSolutionStepValue(NORMAL); + const double projected_nodal_area = std::abs(MathUtils::Dot3(normal, mGravityDirection)); + double g_i = rNode.FastGetSolutionStepValue(WATER_LEVEL) * projected_nodal_area; + if (mQuadraticHeightPenalization) g_i *= rNode.FastGetSolutionStepValue(WATER_LEVEL); + return g_i; + }); + + mValue = value; + + return mValue; + + KRATOS_CATCH(""); +} + +void WaterDrainResponseFunctionUtility::CalculateGradient() +{ + KRATOS_TRY; + // First gradients are initialized + VariableUtils().SetHistoricalVariableToZero(SHAPE_SENSITIVITY, mrModelPart.Nodes()); + + // TODO: Finite Difference test + // but: derivatives of the nodal area are currently neglected: + // nodal response value: gi = water_level_i * area_i + // full derivative: dgi/dx = dwater_level_i/dx * area_i + water_level_i * darea_i/dx + // These are necessary in order to set up a finite difference test! + block_for_each(mrModelPart.Nodes(), [&](Node& rNode) + { + if (rNode.FastGetSolutionStepValue(WATER_LEVEL) > 0.0) { + double projected_nodal_area = 1; + + if (!mContinuousSens) { + Vector normal = rNode.FastGetSolutionStepValue(NORMAL); + projected_nodal_area = std::abs(MathUtils::Dot3(normal, mGravityDirection)); + } + + // neglect area derivative: dgi/dx ~= dwater_level_i/dx * area_i = gravity_direction * area_i + array_3d gradient = mGravityDirection * projected_nodal_area; + if (mQuadraticHeightPenalization) gradient *= 2 * rNode.FastGetSolutionStepValue(WATER_LEVEL); + rNode.FastGetSolutionStepValue(SHAPE_SENSITIVITY) = gradient; + } + }); + + KRATOS_CATCH(""); +} + +void WaterDrainResponseFunctionUtility::SearchWaterVolumes() +{ + KRATOS_TRY; + + BuiltinTimer timer; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Starting SearchWaterVolumes..." << std::endl; + + mListOfVolumes.clear(); + + // find neighbour conditions + FindNodalNeighboursForEntitiesProcess find_neighbours_process( + mrModelPart, + NEIGHBOUR_NODES); + find_neighbours_process.Execute(); + + // determine low points + this->SearchLowPoints(); + + if (mListOfVolumes.size() == 0) { + KRATOS_INFO("ShapeOpt::WaterDrain") << "No water volume found!" << std::endl; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished SearchWaterVolumes in " << timer.ElapsedSeconds() << " s." << std::endl; + return; + } + + this->FindSteepestDescentFromEachNode(); + + this->MergeVolumes(); + + this->LevelVolumes(); + + // compute water level + block_for_each(mrModelPart.Nodes(), [&](Node& rNode) { + int volume_id = rNode.FastGetSolutionStepValue(WATER_VOLUMES); + if (volume_id != 0) { + Volume& r_volume = mListOfVolumes[volume_id-1]; + const auto& distance_vector = r_volume.mLowestPoint - rNode.Coordinates(); + double& water_level = rNode.FastGetSolutionStepValue(WATER_LEVEL); + water_level = r_volume.mMaxWaterLevel - MathUtils::Dot3(mGravityDirection, distance_vector); + } + }); + + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished SearchWaterVolumes in " << timer.ElapsedSeconds() << " s." << std::endl; + + KRATOS_CATCH(""); +} + +void WaterDrainResponseFunctionUtility::SearchLowPoints() { + + BuiltinTimer timer; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Starting SearchLowPoints..." << std::endl; + + ModelPart& r_edge_model_part = mrModelPart.GetModel().GetModelPart(mFreeEdgeSubModelPartName); + + int volume_id = 1; + for(auto& rNode : mrModelPart.Nodes()) { + + bool low_point = true; + if (r_edge_model_part.HasNode(rNode.Id())) { + low_point = false; + } + else { + auto& r_node_coordinates = rNode.Coordinates(); + const auto& r_neighbours = rNode.GetValue(NEIGHBOUR_NODES).GetContainer(); + for (const auto& r_neighbour : r_neighbours) { + const auto& r_neighbour_node_coordinates = r_neighbour->Coordinates(); + const auto& distance_vector = r_neighbour_node_coordinates - r_node_coordinates; + const double height_difference = MathUtils::Dot3(mGravityDirection, distance_vector); + + if (height_difference > 1e-8) { + low_point = false; + break; + } + } + } + + if (low_point) { + Volume new_volume; + new_volume.Id = volume_id; + rNode.FastGetSolutionStepValue(WATER_VOLUMES) = new_volume.Id; + new_volume.mLowPointId = rNode.Id(); + new_volume.mLowestPoint = Vector(rNode.Coordinates()); + mListOfVolumes.push_back(new_volume); + ++volume_id; + } + } + + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished SearchLowPoints in " << timer.ElapsedSeconds() << " s." << std::endl; +} + +void WaterDrainResponseFunctionUtility::FindSteepestDescentFromEachNode() { + + KRATOS_TRY; + + BuiltinTimer timer; + + ModelPart& r_edge_model_part = mrModelPart.GetModel().GetModelPart(mFreeEdgeSubModelPartName); + + KRATOS_INFO("ShapeOpt::WaterDrain") << "Starting FindSteepestDescentFromEachNode..." << std::endl; + block_for_each(mrModelPart.Nodes(), [&](Node& rNode) { + IndexType current_node_id = rNode.Id(); + + if (!r_edge_model_part.HasNode(current_node_id)) { + bool is_low_point = false; + for (std::vector::size_type i = 0; i nodes_visited; + while (!low_point_found && iter < mMaxIterations) { + + if (!nodes_visited.insert(current_node_id).second) break; + + NodeTypePointer p_current_node = mrModelPart.pGetNode(current_node_id); + auto& r_neighbours = p_current_node->GetValue(NEIGHBOUR_NODES); + + IndexType next_node_id; + double max_inclination = 0; + for (auto& r_neighbour : r_neighbours) { + const auto& distance_vector = r_neighbour.Coordinates() - p_current_node->Coordinates(); + const double inclination = MathUtils::Dot3(mGravityDirection, distance_vector); + if (inclination > max_inclination) { + max_inclination = inclination; + next_node_id = r_neighbour.Id(); + } + } + if (max_inclination < 1e-8) break; + + if (r_edge_model_part.HasNode(next_node_id)) break; + + for (std::vector::size_type i = 0; i::size_type i = 0; i edge_water_height(mrModelPart.NumberOfNodes(), 1e20); + std::vector edge_node_ids(mrModelPart.NumberOfNodes(), 0); + + // determine minimal edge height + const auto it_node_begin = mrModelPart.NodesBegin(); + #pragma omp parallel for + for (Kratos::ModelPart::SizeType j = 0; j < mrModelPart.NumberOfNodes(); ++j) { + auto it_node = it_node_begin + j; + int& water_volume = it_node->FastGetSolutionStepValue(WATER_VOLUMES); + if (water_volume != volume_id) { + continue; + } + + bool is_edge = false; + auto& r_neighbours = it_node->GetValue(NEIGHBOUR_NODES); + for (auto& r_neighbour : r_neighbours) { + int neighbour_water_volume = r_neighbour.FastGetSolutionStepValue(WATER_VOLUMES); + if (neighbour_water_volume != water_volume) { + is_edge = true; + break; + } + } + + if (!is_edge) { + continue; + } + const auto& distance_vector = r_volume.mLowestPoint - it_node->Coordinates(); + edge_water_height[j] = MathUtils::Dot3(mGravityDirection, distance_vector); + edge_node_ids[j] = it_node->Id(); + } + + const double min_edge_height = *std::min_element(edge_water_height.begin(), edge_water_height.end()); + + edge_node_ids.erase(std::remove(edge_node_ids.begin(), edge_node_ids.end(), 0), edge_node_ids.end()); + + // determine height tolerance by max volume edge inclination + double max_edge_inclination = 0; + for (std::vector::size_type j = 0; j < edge_node_ids.size(); ++j) { + NodeTypePointer p_node = mrModelPart.pGetNode(edge_node_ids[j]); + + auto& r_neighbours = p_node->GetValue(NEIGHBOUR_NODES); + for (auto& r_neighbour : r_neighbours) { + auto find = std::find(edge_node_ids.begin(), edge_node_ids.end(), r_neighbour.Id()); + if (find != edge_node_ids.end()) { + const auto& distance_vector = r_neighbour.Coordinates() - p_node->Coordinates(); + const double inclination = std::abs(MathUtils::Dot3(mGravityDirection, distance_vector)); + if (max_edge_inclination < inclination) max_edge_inclination = inclination; + } + } + } + double height_tol = 2*max_edge_inclination; + + // sort out nodes which are higher than the min_edge_height and determine max water level + std::vector water_level(mrModelPart.NumberOfNodes(), -1e20); + #pragma omp parallel for + for (Kratos::ModelPart::SizeType j = 0; j < mrModelPart.NumberOfNodes(); ++j) { + auto it_node = it_node_begin + j; + int& water_volume = it_node->FastGetSolutionStepValue(WATER_VOLUMES); + if (water_volume == volume_id) { + const auto& distance_vector = r_volume.mLowestPoint - it_node->Coordinates(); + const double height = MathUtils::Dot3(mGravityDirection, distance_vector); + if (height > min_edge_height+height_tol) { + water_volume = 0; + } else { + water_level[j] = height; + } + } + } + r_volume.mMaxWaterLevel = *std::max_element(water_level.begin(), water_level.end()); + } + + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished LevelVolumes in " << timer.ElapsedSeconds() << " s." << std::endl; + KRATOS_CATCH(""); +} + +void WaterDrainResponseFunctionUtility::MergeVolumes() { + + BuiltinTimer timer; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Starting FindNeighbouringVolumes..." << std::endl; + + std::vector> volume_neighbourhoods; + volume_neighbourhoods.resize(mListOfVolumes.size()); + for (auto& rNode : mrModelPart.Nodes()) { + int volume_id = rNode.FastGetSolutionStepValue(WATER_VOLUMES); + if (volume_id == 0) continue; + auto& r_neighbours = rNode.GetValue(NEIGHBOUR_NODES); + for (auto& r_neighbour : r_neighbours) { + NodeTypePointer p_neighbour = mrModelPart.pGetNode(r_neighbour.Id()); + int neighbour_volume_id = p_neighbour->FastGetSolutionStepValue(WATER_VOLUMES); + if (neighbour_volume_id > 0 && neighbour_volume_id != volume_id) { + volume_neighbourhoods[volume_id-1].insert(neighbour_volume_id); + } + } + + } + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished FindNeighbouringVolumes in " << timer.ElapsedSeconds() << " s." << std::endl; + + BuiltinTimer timer2; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Starting FindMergingVolumes..." << std::endl; + + std::set merged; + std::map> volume_to_merge; + for (std::vector>::size_type id = 1; id < volume_neighbourhoods.size()+1; ++id) { + std::vector to_visit; + std::set visited; + if (merged.find(id) == merged.end()) { + to_visit.push_back(id); + int iter = 0; + while (to_visit.size() != 0 && iter new_volumes = volume_neighbourhoods[to_visit[0]-1]; + for (int new_volume : new_volumes) { + if (visited.find(new_volume) == visited.end() && std::find(to_visit.begin(), to_visit.end(), new_volume) == to_visit.end()) { + to_visit.push_back(new_volume); + } + } + to_visit.erase(to_visit.begin()); + ++iter; + } + if (visited.size() != 0) { + volume_to_merge[id] = visited; + for (std::set::size_type visited_j : visited) { + if (visited_j != id) merged.insert(visited_j); + } + } + } + } + + std::map>::iterator it = volume_to_merge.begin(); + for (it = volume_to_merge.begin(); it != volume_to_merge.end(); it++) { + block_for_each(mrModelPart.Nodes(), [&](Node& rNode) + { + int& volume_id = rNode.FastGetSolutionStepValue(WATER_VOLUMES); + if (it->second.find(volume_id) != it->second.end()) { + volume_id = it->first; + } + }); + } + + for (int merged_volume_id : merged) { + mListOfVolumes[merged_volume_id-1].isMerged = true; + } + + KRATOS_INFO("ShapeOpt::WaterDrain") << "Number of water volumes found: " << volume_to_merge.size() << std::endl; + KRATOS_INFO("ShapeOpt::WaterDrain") << "Finished FindMergingVolumes in " << timer2.ElapsedSeconds() << " s." << std::endl; +} +} // namespace Kratos. diff --git a/applications/ShapeOptimizationApplication/custom_utilities/response_functions/water_drain_response_function_utility.h b/applications/ShapeOptimizationApplication/custom_utilities/response_functions/water_drain_response_function_utility.h new file mode 100644 index 000000000000..1323dad664b3 --- /dev/null +++ b/applications/ShapeOptimizationApplication/custom_utilities/response_functions/water_drain_response_function_utility.h @@ -0,0 +1,195 @@ +// ============================================================================== +// KratosShapeOptimizationApplication +// +// License: BSD License +// license: ShapeOptimizationApplication/license.txt +// +// Main authors: Schmölz David, https://github.com/dschmoelz +// +// ============================================================================== + +#ifndef WATER_DRAIN_FUNCTION_UTILITY_H +#define WATER_DRAIN_FUNCTION_UTILITY_H + +// ------------------------------------------------------------------------------ +// System includes +// ------------------------------------------------------------------------------ +#include +#include + +// ------------------------------------------------------------------------------ +// External includes +// ------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------ +// Project includes +// ------------------------------------------------------------------------------ +#include "includes/define.h" +#include "includes/kratos_parameters.h" +#include "includes/model_part.h" +#include "custom_utilities/geometry_utilities.h" + + +// ============================================================================== + +namespace Kratos +{ + +///@name Kratos Globals +///@{ + +///@} +///@name Type Definitions +///@{ + +///@} +///@name Enum's +///@{ + +///@} +///@name Functions +///@{ + +///@} +///@name Kratos Classes +///@{ + +typedef Node NodeType; +typedef NodeType::Pointer NodeTypePointer; +typedef std::vector NodeVector; +typedef ModelPart::NodesContainerType NodesArrayType; + +/// Short class definition. +/** + Helper structure storing the information of the pond, such as lowest node id and coordinates, maximal water level, + neighbouring ponds and if the pond has been merged into another pond. + */ +struct Volume { + IndexType Id; + IndexType mLowPointId; + Vector mLowestPoint; + double mMaxWaterLevel = 0; + bool isMerged = false; + std::vector mNeighbourVolumes; +}; + +/// Short class definition. +/** Water Drain response function to prevent ponding. + It computes the water volume by integrating the water level of the ponds. + For this, a node search is conducted which detects the nodes which are forming a pond. + */ +class KRATOS_API(SHAPE_OPTIMIZATION_APPLICATION) WaterDrainResponseFunctionUtility +{ +public: + ///@name Type Definitions + ///@{ + + typedef array_1d array_3d; + + /// Pointer definition of WaterDrainResponseFunctionUtility + KRATOS_CLASS_POINTER_DEFINITION(WaterDrainResponseFunctionUtility); + + ///@} + ///@name Life Cycle + ///@{ + + /// Default constructor. + WaterDrainResponseFunctionUtility(ModelPart& rModelPart, Parameters ResponseSettings); + + /// Destructor. + virtual ~WaterDrainResponseFunctionUtility() + {} + ///@} + ///@name Operators + ///@{ + + ///@} + ///@name Operations + ///@{ + + void Initialize(); + + void InitializeSolutionStep(); + + double CalculateValue(); + + // TOOD: include area derivatives for finite difference test + void CalculateGradient(); + + // ============================================================================== + + ///@name Input and output + ///@{ + + /// Turn back information as a string. + std::string Info() const + { + return "WaterDrainResponseFunctionUtility"; + } + + /// Print information about this object. + virtual void PrintInfo(std::ostream &rOStream) const + { + rOStream << "WaterDrainResponseFunctionUtility"; + } + + /// Print object's data. + virtual void PrintData(std::ostream &rOStream) const + { + } + + ///@} + +protected: + + // ============================================================================== + +private: + + ///@name Operations + ///@{ + + /// @brief Determines the water volumes or ponds on the surface and the water level of these. + void SearchWaterVolumes(); + + /// @brief Finds lowest points of the ponds (elliptic points of the surface). + void SearchLowPoints(); + + /// @brief Determines whereto water flows from each node and adds nodes to their respective ponds. + void FindSteepestDescentFromEachNode(); + + /// @brief Flattens the ponds so that their maximum water level is correct. + void LevelVolumes(); + + /// @brief Merges neighbouring ponds if necessary. + void MergeVolumes(); + + ///@} + ///@name Static Member Variables + ///@{ + + ///@} + ///@name Member Variables + ///@{ + + ModelPart &mrModelPart; + GeometryUtilities mGeometryUtilities; + + Vector mGravityDirection; + int mMaxIterations; + bool mContinuousSens; + bool mQuadraticHeightPenalization; + bool mDetectFreeEdgeAutomatic = false; + std::string mFreeEdgeSubModelPartName; + + std::vector mListOfVolumes; + double mValue; + ///@} + +}; // Class WaterDrainResponseFunctionUtility + +///@} + +} // namespace Kratos. + +#endif // WATER_DRAIN_FUNCTION_UTILITY_H diff --git a/applications/ShapeOptimizationApplication/python_scripts/algorithms/algorithm_relaxed_gradient_projection.py b/applications/ShapeOptimizationApplication/python_scripts/algorithms/algorithm_relaxed_gradient_projection.py index a6ccfa79fd12..a69663bc5ddb 100644 --- a/applications/ShapeOptimizationApplication/python_scripts/algorithms/algorithm_relaxed_gradient_projection.py +++ b/applications/ShapeOptimizationApplication/python_scripts/algorithms/algorithm_relaxed_gradient_projection.py @@ -302,6 +302,10 @@ def __checkConstraintValue(self): gradient = Kratos.Vector() self.optimization_utilities.AssembleVector(self.design_surface, gradient, g_a_variable) self.optimization_utilities.AssembleVector(self.design_surface, shape_update, KSO.SHAPE_UPDATE) + gradient_norm = self.optimization_utilities.ComputeMaxNormOfNodalVariable(self.design_surface, g_a_variable) + # skip constraint with zero gradients + if abs(gradient_norm) < 1e-10: + continue new_g_i = g_i + np.dot(gradient, shape_update) Kratos.Logger.PrintInfo("Constraint ", identifier, "\n Linearized new value = ", new_g_i) if new_g_i > 0.0: @@ -399,6 +403,18 @@ def __computeControlPointUpdate(self): if self.inner_iter == 1: self.g_a, self.g_a_variables, self.relaxation_coefficients, self.correction_coefficients = self.__getActiveConstraints() + # remove constraint with zero gradients + for i in range(len(self.g_a)): + g_a_variable = self.g_a_variables[i] + g_a_variable_vector = Kratos.Vector() + self.optimization_utilities.AssembleVector(self.design_surface, g_a_variable_vector, g_a_variable) + g_a_norm = self.optimization_utilities.ComputeMaxNormOfNodalVariable(self.design_surface, g_a_variable) + if abs(g_a_norm) < 1e-10: + self.g_a.pop(i) + self.g_a_variables.pop(i) + self.relaxation_coefficients.pop(i) + self.correction_coefficients.pop(i) + Kratos.Logger.PrintInfo("ShapeOpt", "Assemble vector of objective gradient.") nabla_f = Kratos.Vector() p = Kratos.Vector() diff --git a/applications/ShapeOptimizationApplication/python_scripts/analyzers/analyzer_internal.py b/applications/ShapeOptimizationApplication/python_scripts/analyzers/analyzer_internal.py index c6d8b979c4e7..c182dc64b392 100755 --- a/applications/ShapeOptimizationApplication/python_scripts/analyzers/analyzer_internal.py +++ b/applications/ShapeOptimizationApplication/python_scripts/analyzers/analyzer_internal.py @@ -121,6 +121,7 @@ def __CreateResponseFunctions( specified_responses, model ): "mesh_based_packaging", "surface_normal_shape_change", "face_angle", + "water_drain", "airfoil_angle_of_attack", "airfoil_chord_length", "airfoil_perimeter" diff --git a/applications/ShapeOptimizationApplication/python_scripts/optimizer_factory.py b/applications/ShapeOptimizationApplication/python_scripts/optimizer_factory.py index 068a95d10c36..f101aebf5408 100755 --- a/applications/ShapeOptimizationApplication/python_scripts/optimizer_factory.py +++ b/applications/ShapeOptimizationApplication/python_scripts/optimizer_factory.py @@ -79,6 +79,13 @@ def __AddVariablesToBeUsedByAllAlgorithms(self): self.optimization_settings["output"]["sensitivity_heatmap"].GetBool(): self.__AddVariablesToBeUsedBySensitivityHeatmap() + # water drain response + if self.optimization_settings["objectives"][0]["identifier"].GetString() == "water_drain": + model_part.AddNodalSolutionStepVariable(KM.NODAL_AREA) + model_part.AddNodalSolutionStepVariable(KSO.WATER_LEVEL) + model_part.AddNodalSolutionStepVariable(KSO.WATER_VOLUMES) + + def __AddVariablesToBeUsedByDesignVariables(self): if self.optimization_settings["design_variables"]["filter"].Has("in_plane_morphing") and \ self.optimization_settings["design_variables"]["filter"]["in_plane_morphing"].GetBool(): diff --git a/applications/ShapeOptimizationApplication/python_scripts/response_functions/response_function_factory.py b/applications/ShapeOptimizationApplication/python_scripts/response_functions/response_function_factory.py index 9acae52e0adc..86969554e3b3 100644 --- a/applications/ShapeOptimizationApplication/python_scripts/response_functions/response_function_factory.py +++ b/applications/ShapeOptimizationApplication/python_scripts/response_functions/response_function_factory.py @@ -12,6 +12,7 @@ from . import mesh_based_packaging from . import surface_normal_shape_change from . import face_angle +from . import water_drain from . import airfoil_2d_responses from . import total_volume @@ -26,6 +27,8 @@ def CreateResponseFunction(response_id, response_settings, model): return surface_normal_shape_change.SurfaceNormalShapeChange(response_id, response_settings, model) elif response_type == "face_angle": return face_angle.FaceAngleResponseFunction(response_id, response_settings, model) + elif response_type == "water_drain": + return water_drain.WaterDrainResponseFunction(response_id, response_settings, model) elif response_type == "airfoil_angle_of_attack": return airfoil_2d_responses.AngleOfAttackResponseFunction(response_id, response_settings, model) elif response_type == "airfoil_chord_length": diff --git a/applications/ShapeOptimizationApplication/python_scripts/response_functions/water_drain.py b/applications/ShapeOptimizationApplication/python_scripts/response_functions/water_drain.py new file mode 100755 index 000000000000..e1844c388e13 --- /dev/null +++ b/applications/ShapeOptimizationApplication/python_scripts/response_functions/water_drain.py @@ -0,0 +1,140 @@ +# importing the Kratos Library +import KratosMultiphysics as KM +from KratosMultiphysics import Parameters, Logger +from KratosMultiphysics.response_functions.response_function_interface import ResponseFunctionInterface +import KratosMultiphysics.ShapeOptimizationApplication as KSO + +import time as timer + + +def _AddConditionsFromParent(parent, child): + node_ids = set([node.Id for node in child.Nodes]) + conditions = [] + for condition in parent.Conditions: + all_nodes_found = True + for node in condition.GetNodes(): + if node.Id not in node_ids: + all_nodes_found = False + break + if all_nodes_found: + conditions.append(condition.Id) + child.AddConditions(conditions) + +# ============================================================================== +class WaterDrainResponseFunction(ResponseFunctionInterface): + """ + Water Drain response function. + It computes the water volume by integrating the water level of the surfaces which is under water. + For this, a node search is conducted which detects the nodes which are lying under water. + + Attributes + ---------- + model_part : Model part object of the response function + response_function_utility: Cpp utilities object doing the actual computation of response value and gradient. + """ + + def __init__(self, identifier, response_settings, model): + self.identifier = identifier + + response_settings.ValidateAndAssignDefaults(self.GetDefaultParameters()) + + self.response_settings = response_settings + self.model = model + self.model_part_needs_to_be_imported = False + + self.value = None + + self._model_part_name = response_settings["model_part_name"].GetString() + input_type = response_settings["model_import_settings"]["input_type"].GetString() + if input_type == "mdpa": + self.model_part = self.model.CreateModelPart(self._model_part_name, 2) + domain_size = response_settings["domain_size"].GetInt() + if domain_size not in [3]: + raise Exception("WaterDrainResponseFunction: Invalid 'domain_size': {}".format(domain_size)) + self.model_part.ProcessInfo.SetValue(KM.DOMAIN_SIZE, domain_size) + self.model_part_needs_to_be_imported = True + elif input_type == "use_input_model_part": + self.model_part = None # will be retrieved in Initialize() + else: + raise Exception("Other model part input options are not yet implemented.") + + self.response_function_utility = None # will be created in Initialize() + + self.model.GetModelPart(self._model_part_name.split(".")[0]).AddNodalSolutionStepVariable(KM.SHAPE_SENSITIVITY) + + @classmethod + def GetDefaultParameters(cls): + this_defaults = KM.Parameters("""{ + "response_type" : "UNKNOWN_TYPE", + "model_part_name" : "UNKNOWN_NAME", + "only" : "", + "domain_size" : 3, + "model_import_settings" : { + "input_type" : "use_input_model_part", + "input_filename" : "UNKNOWN_NAME" + }, + "gravity_direction": [0.0, 0.0, -1.0], + "free_edge_part_name": "automatic", + "max_iterations_volume_search": 10000, + "continuous_sensitivities": false, + "quadratic_height_penalization": false + }""") + return this_defaults + + def Initialize(self): + if self.response_settings["model_import_settings"]["input_type"].GetString() == "mdpa": + file_name = self.response_settings["model_import_settings"]["input_filename"].GetString() + model_part_io = KM.ModelPartIO(file_name) + model_part_io.ReadModelPart(self.model_part) + else: + self.model_part = self.model.GetModelPart(self._model_part_name) + + only = self.response_settings["only"].GetString() + if only != "": + only_part = self.model.GetModelPart(only) + if only_part.NumberOfConditions() == 0: + _AddConditionsFromParent(self.model_part, only_part) + Logger.PrintWarning("WaterDrainResponse", "Automatically added {} conditions to model_part '{}'.".format(only_part.NumberOfConditions(), only_part.Name)) + else: + only_part = self.model_part + + if only_part.NumberOfConditions() == 0: + raise RuntimeError("The model_part '{}' does not have any surface conditions!".format(only_part.Name)) + + self.response_function_utility = KSO.WaterDrainResponseFunctionUtility(only_part, self.response_settings) + + self.response_function_utility.Initialize() + + def UpdateDesign(self, updated_model_part, variable): + self.value = None + + def InitializeSolutionStep(self): + self.response_function_utility.InitializeSolutionStep() + + def CalculateValue(self): + Logger.PrintInfo("WaterDrainResponse", "Starting calculation of response value:", self.identifier) + + startTime = timer.time() + self.value = self.response_function_utility.CalculateValue() + Logger.PrintInfo("WaterDrainResponse", "Time needed for calculating the response value = ",round(timer.time() - startTime,2),"s") + + def CalculateGradient(self): + Logger.PrintInfo("WaterDrainResponse", "Starting gradient calculation for response", self.identifier) + + for node in self.model_part.Nodes: + node.SetSolutionStepValue(KM.SHAPE_SENSITIVITY, [0.0, 0.0, 0.0]) + + startTime = timer.time() + self.response_function_utility.CalculateGradient() + Logger.PrintInfo("WaterDrainResponse", "Time needed for calculating gradients",round(timer.time() - startTime,2),"s") + + def GetValue(self): + return self.value + + def GetNodalGradient(self, variable): + if variable != KM.SHAPE_SENSITIVITY: + raise RuntimeError("GetNodalGradient: No gradient for {}!".format(variable.Name)) + gradient = {} + for node in self.model_part.Nodes: + gradient[node.Id] = node.GetSolutionStepValue(variable) + return gradient diff --git a/applications/ShapeOptimizationApplication/shape_optimization_application.cpp b/applications/ShapeOptimizationApplication/shape_optimization_application.cpp index 0cf1d24c77ae..d5c8813ca04d 100644 --- a/applications/ShapeOptimizationApplication/shape_optimization_application.cpp +++ b/applications/ShapeOptimizationApplication/shape_optimization_application.cpp @@ -103,9 +103,13 @@ namespace Kratos KRATOS_CREATE_3D_VARIABLE_WITH_COMPONENTS(BACKGROUND_NORMAL); KRATOS_CREATE_3D_VARIABLE_WITH_COMPONENTS(OUT_OF_PLANE_DELTA); - // For face angle response + // For face angle response KRATOS_CREATE_VARIABLE(bool, CONSIDER_FACE_ANGLE); + // For water drain response + KRATOS_CREATE_VARIABLE(double, WATER_LEVEL); + KRATOS_CREATE_VARIABLE(int, WATER_VOLUMES); + // For sensitivity heatmap KRATOS_CREATE_3D_VARIABLE_WITH_COMPONENTS(HEATMAP_DF1DX); KRATOS_CREATE_VARIABLE(double, HEATMAP_DF1DALPHA); @@ -241,6 +245,10 @@ namespace Kratos // For face angle response KRATOS_REGISTER_VARIABLE(CONSIDER_FACE_ANGLE); + // For water drain response + KRATOS_REGISTER_VARIABLE(WATER_LEVEL); + KRATOS_REGISTER_VARIABLE(WATER_VOLUMES); + // For sensitivity heatmap KRATOS_REGISTER_3D_VARIABLE_WITH_COMPONENTS(HEATMAP_DF1DX); KRATOS_REGISTER_VARIABLE(HEATMAP_DF1DALPHA); diff --git a/applications/ShapeOptimizationApplication/shape_optimization_application.h b/applications/ShapeOptimizationApplication/shape_optimization_application.h index ab5fdac11e18..f5386750a01f 100644 --- a/applications/ShapeOptimizationApplication/shape_optimization_application.h +++ b/applications/ShapeOptimizationApplication/shape_optimization_application.h @@ -119,6 +119,10 @@ namespace Kratos // For face angle response KRATOS_DEFINE_APPLICATION_VARIABLE(SHAPE_OPTIMIZATION_APPLICATION, bool, CONSIDER_FACE_ANGLE); + // For water drain response + KRATOS_DEFINE_APPLICATION_VARIABLE(SHAPE_OPTIMIZATION_APPLICATION, double, WATER_LEVEL); + KRATOS_DEFINE_APPLICATION_VARIABLE(SHAPE_OPTIMIZATION_APPLICATION, int, WATER_VOLUMES); + // For sensitivity heatmap KRATOS_DEFINE_3D_APPLICATION_VARIABLE_WITH_COMPONENTS(SHAPE_OPTIMIZATION_APPLICATION, HEATMAP_DF1DX); KRATOS_DEFINE_APPLICATION_VARIABLE(SHAPE_OPTIMIZATION_APPLICATION, double, HEATMAP_DF1DALPHA); diff --git a/applications/ShapeOptimizationApplication/tests/shape_optimization_test_factory.py b/applications/ShapeOptimizationApplication/tests/shape_optimization_test_factory.py index 02baa7b447b8..777fdf67d41b 100644 --- a/applications/ShapeOptimizationApplication/tests/shape_optimization_test_factory.py +++ b/applications/ShapeOptimizationApplication/tests/shape_optimization_test_factory.py @@ -182,4 +182,8 @@ class mapper_adaptive_filter_curvature_test(ShapeOptimizationTestFactory): class sensitivity_heatmap_test(ShapeOptimizationTestFactory): execution_directory = "sensitivity_heatmap_test" execution_file = "run_test" + +class water_drain_test(ShapeOptimizationTestFactory): + execution_directory = "water_drain_test" + execution_file = "run_test" # ============================================================================== \ No newline at end of file diff --git a/applications/ShapeOptimizationApplication/tests/test_ShapeOptimizationApplication.py b/applications/ShapeOptimizationApplication/tests/test_ShapeOptimizationApplication.py index b1296c158812..52dead5d86fb 100644 --- a/applications/ShapeOptimizationApplication/tests/test_ShapeOptimizationApplication.py +++ b/applications/ShapeOptimizationApplication/tests/test_ShapeOptimizationApplication.py @@ -44,6 +44,7 @@ from wrl_io_test.test_wrl_io import WrlIOTest from surface_normal_shape_change_response_test.test_surface_normal_shape_change_response import SurfaceNormalShapeChangeTest from face_angle_response_test.test_face_angle_response import FaceAngleTest +from shape_optimization_test_factory import water_drain_test from mapper_plane_symmetry_test.plane_symmetry_test import PlaneSymmetryMapperTest from mapper_revolution_test.revolution_test import RevolutionMapperTest from shape_optimization_test_factory import direction_damping_test @@ -94,6 +95,7 @@ def AssembleTestSuites(): smallSuite.addTest(curvature_8NQuad_test('test_execution')) smallSuite.addTest(mapper_adaptive_filter_curvature_test('test_execution')) smallSuite.addTest(sensitivity_heatmap_test('test_execution')) + smallSuite.addTest(water_drain_test('test_execution')) # Adding nightly tests (tests that take < 10min) nightSuite = suites['nightly'] diff --git a/applications/ShapeOptimizationApplication/tests/water_drain_test/cosinus_shell.mdpa b/applications/ShapeOptimizationApplication/tests/water_drain_test/cosinus_shell.mdpa new file mode 100644 index 000000000000..eb3494455f3e --- /dev/null +++ b/applications/ShapeOptimizationApplication/tests/water_drain_test/cosinus_shell.mdpa @@ -0,0 +1,841 @@ +// File created on Thu Nov 21 15:20:39 2024 +// Mesh Information: +// Number of Nodes: 139 +// Number of Elements: 0 +// Number of Conditions: 245 +// Number of Properties: 1 +// Number of SubModelParts: 2 +// SubModelPart: design_surface +// Number of Nodes: 139 +// Number of Elements: 0 +// Number of Conditions: 245 +// Number of Properties: 1 +// Number of SubModelParts: 0 +// SubModelPart: free_edge +// Number of Nodes: 31 +// Number of Elements: 0 +// Number of Conditions: 0 +// Number of Properties: 0 +// Number of SubModelParts: 0 + +Begin Properties 0 +End Properties // 0 + +Begin Nodes + 1 3.1415930000 0.0000000000 2.0000000000 + 2 0.0000000000 0.0000000000 0.0000000000 + 3 -3.9269910000 0.0000000000 2.0000000000 + 4 0.5137404291 0.0000000000 0.1836701348 + 5 0.9729987878 0.0000000000 0.4905085587 + 6 1.5695516222 0.0000000000 0.9988911863 + 7 2.3089483692 0.0000000000 1.6143591702 + 8 3.0763203967 0.6370709493 2.0000000000 + 9 2.8968284055 1.2157268470 2.0000000000 + 10 2.6096006204 1.7491687110 2.0000000000 + 11 2.2253193149 2.2175573328 2.0000000000 + 12 1.7582762750 2.6034728956 2.0000000000 + 13 1.2258412740 2.8925628340 2.0000000000 + 14 0.6478160741 3.0740756191 2.0000000000 + 15 -0.0783110720 3.1406168110 2.0000000000 + 16 -0.7067922684 3.0610539471 2.0000000000 + 17 -1.2812212560 2.8684627714 2.0000000000 + 18 -1.8108389064 2.5671908836 2.0000000000 + 19 -2.2914315536 2.1491737977 2.0000000000 + 20 -2.6705418475 1.6546337414 2.0000000000 + 21 -2.9384903271 1.1112520753 2.0000000000 + 22 -3.0971535665 0.5265418913 2.0000000000 + 23 -3.1406307293 -0.0777508846 2.0000000000 + 24 -3.0673048586 -0.6791520315 2.0000000000 + 25 -2.8799030132 -1.2552948707 2.0000000000 + 26 -2.5853948452 -1.7847521039 2.0000000000 + 27 -2.1947333925 -2.2478327148 2.0000000000 + 28 -1.7224477248 -2.6273142966 2.0000000000 + 29 -1.1861025938 -2.9090835696 2.0000000000 + 30 -0.5647077426 -3.0904225832 2.0000000000 + 31 0.0390027270 -3.1413508822 2.0000000000 + 32 0.6412626483 -3.0754493645 2.0000000000 + 33 1.2260701953 -2.8924658086 2.0000000000 + 34 1.7584823153 -2.6033337328 2.0000000000 + 35 2.2279442142 -2.2149201241 2.0000000000 + 36 2.6212798298 -1.7316173456 2.0000000000 + 37 2.9049214337 -1.1962600226 2.0000000000 + 38 3.0805262563 -0.6164126555 2.0000000000 + 39 -3.8317311266 0.8597062800 2.0000000000 + 40 -3.5932285626 1.5842874774 2.0000000000 + 41 -3.1826852383 2.3003853999 2.0000000000 + 42 -2.6519961603 2.8962345692 2.0000000000 + 43 -2.0070650013 3.3753441890 2.0000000000 + 44 -1.2942072958 3.7075983857 2.0000000000 + 45 -0.5399858207 3.8896881144 2.0000000000 + 46 0.2144525282 3.9211310138 2.0000000000 + 47 0.9812100663 3.8024314747 2.0000000000 + 48 1.7089750251 3.5356276215 2.0000000000 + 49 2.4004869607 3.1078804137 2.0000000000 + 50 2.9913101385 2.5442723850 2.0000000000 + 51 3.4517975035 1.8725256498 2.0000000000 + 52 3.7501817463 1.1650730379 2.0000000000 + 53 3.9060508925 0.4049996782 2.0000000000 + 54 3.9112775142 -0.3509508813 2.0000000000 + 55 3.7644671986 -1.1180541243 2.0000000000 + 56 3.4695461212 -1.8394314413 2.0000000000 + 57 3.0084126048 -2.5240269241 2.0000000000 + 58 2.3976341976 -3.1100817624 2.0000000000 + 59 1.7147959668 -3.5328081050 2.0000000000 + 60 0.9807333205 -3.8025544662 2.0000000000 + 61 0.2186936071 -3.9208967623 2.0000000000 + 62 -0.5626044305 -3.8864810007 2.0000000000 + 63 -1.3215894317 -3.6979264038 2.0000000000 + 64 -2.0519773993 -3.3482304381 2.0000000000 + 65 -2.6821453992 -2.8683365165 2.0000000000 + 66 -3.2035814411 -2.2711944576 2.0000000000 + 67 -3.6167906384 -1.5297332421 2.0000000000 + 68 -3.8497406576 -0.7750839846 2.0000000000 + 69 0.2655191018 0.4492538138 0.1880783522 + 70 0.7209423218 0.5308651258 0.4314303240 + 71 1.2370926565 0.5853264841 0.8207568599 + 72 2.4551448554 0.9479680238 1.8184610359 + 73 2.0393298300 1.4673258807 1.7497513184 + 74 1.5642077940 1.9383354891 1.7364295948 + 75 1.0403785531 2.2920607284 1.7526618345 + 76 0.5150542100 2.6380934493 1.8476470147 + 77 -0.1880946511 2.5605626287 1.7825259350 + 78 -0.8128909345 2.4377328595 1.7838159621 + 79 -1.3798268789 2.2994163766 1.8444955311 + 80 -1.8737987316 1.8357675460 1.8138051903 + 81 -2.2151063426 1.2185332273 1.7593265645 + 82 -2.4534932014 0.6264554228 1.7617658672 + 83 -2.5766944830 0.0156627181 1.7878667519 + 84 -2.6545167167 -0.6111813642 1.8653109665 + 85 -2.3099033904 -1.1604681542 1.7925877640 + 86 -1.9247078642 -1.6540474158 1.7651017162 + 87 -1.4709670759 -2.0913429113 1.7763523582 + 88 -0.9983573315 -2.5040704144 1.8515666731 + 89 -0.3075412592 -2.5115928271 1.7606526635 + 90 0.3307074736 -2.4804418699 1.7436382574 + 91 0.8999893768 -2.4900101169 1.8269211156 + 92 1.4810943721 -2.1656451482 1.8140637536 + 93 2.0018255356 -1.7624635585 1.8370797885 + 94 2.2576284367 -1.2887065287 1.8007680689 + 95 0.2393856065 -0.4592586782 0.1859273612 + 96 1.2856373171 -0.6497179100 0.8841558405 + 97 2.1181117135 -0.6872502471 1.5539957096 + 98 1.8783338776 0.6806777188 1.3720095818 + 99 0.7354655984 -0.5320889860 0.4407336023 + 100 -0.2967978281 -0.4457578589 0.1956039714 + 101 -0.2212436004 0.3902403009 0.1498036717 + 102 -0.5247445505 -0.0024456669 0.1896635726 + 103 0.7960505075 1.0884463038 0.8031525761 + 104 -1.5261566271 1.2994396063 1.3774561485 + 105 0.8814764876 1.7070704149 1.3075022350 + 106 0.3314141984 2.1133720552 1.4865265507 + 107 -0.3835460988 1.9747140915 1.3834224073 + 108 -1.1072327089 1.8550772516 1.5031126247 + 109 -1.8475400830 0.7150862146 1.3580272026 + 110 -2.1502381766 -0.5345029692 1.5455850733 + 111 -0.0455248618 -0.7992158194 0.3628563558 + 112 -2.0406593804 0.0954067118 1.4091702506 + 113 -1.7238697747 -1.0476816217 1.3880949496 + 114 -0.7401076620 -1.9779811808 1.4649258414 + 115 -1.2482644177 -1.5290004020 1.3519418894 + 116 0.0732179231 -1.8293717403 1.2297415834 + 117 1.5093477400 -1.3730023528 1.4071387600 + 118 0.8314298815 -1.9035419425 1.4370773610 + 119 0.7310053397 -1.2033229240 0.8554115855 + 120 -0.0295981965 0.7429872219 0.3237176203 + 121 1.4150784149 1.2406265562 1.2738851407 + 122 0.3296574073 0.9268464883 0.4988529348 + 123 -0.7813360226 -0.3947305631 0.4166838151 + 124 -0.3951852714 0.7111279760 0.3720475143 + 125 0.3627076884 -0.8649004251 0.4634987800 + 126 -0.4924256923 -0.8765495583 0.5158382314 + 127 -0.6212428180 0.4099041386 0.3241963861 + 128 -1.5322999158 0.1953007112 0.9767510383 + 129 0.1456066278 1.4994738448 0.9427760428 + 130 -0.7375624624 1.3656496796 0.9833407016 + 131 -1.2660752898 0.7794555792 0.9252155664 + 132 -0.5661156704 -1.3963151646 0.9429413173 + 133 -0.2461929750 1.0737373914 0.5932020300 + 134 -1.6506540297 -0.4448659101 1.1233157147 + 135 0.0172704869 -1.1986580030 0.6741800249 + 136 -1.0746931373 -0.8870036100 0.8426334008 + 137 -0.7606732673 0.7831872829 0.5851763908 + 138 -0.9794900600 0.1947102441 0.5105360339 + 139 -1.2069718339 -0.2839051073 0.7091687689 +End Nodes + +Begin Conditions SurfaceCondition3D3N + 1 0 4 2 69 + 2 0 5 4 70 + 3 0 4 69 70 + 4 0 6 5 71 + 5 0 5 70 71 + 6 0 1 7 8 + 7 0 9 8 72 + 8 0 10 9 72 + 9 0 8 7 72 + 10 0 11 10 73 + 11 0 12 11 74 + 12 0 11 73 74 + 13 0 13 12 75 + 14 0 12 74 75 + 15 0 14 13 76 + 16 0 13 75 76 + 17 0 15 14 76 + 18 0 16 15 77 + 19 0 17 16 78 + 20 0 16 77 78 + 21 0 18 17 79 + 22 0 17 78 79 + 23 0 19 18 80 + 24 0 18 79 80 + 25 0 20 19 80 + 26 0 21 20 81 + 27 0 22 21 82 + 28 0 21 81 82 + 29 0 23 22 83 + 30 0 22 82 83 + 31 0 24 23 84 + 32 0 23 83 84 + 33 0 25 24 84 + 34 0 26 25 85 + 35 0 27 26 86 + 36 0 26 85 86 + 37 0 28 27 87 + 38 0 27 86 87 + 39 0 29 28 88 + 40 0 28 87 88 + 41 0 30 29 88 + 42 0 31 30 89 + 43 0 32 31 90 + 44 0 31 89 90 + 45 0 33 32 91 + 46 0 32 90 91 + 47 0 34 33 92 + 48 0 33 91 92 + 49 0 35 34 92 + 50 0 36 35 93 + 51 0 37 36 94 + 52 0 36 93 94 + 53 0 1 38 7 + 54 0 2 4 95 + 55 0 5 6 96 + 56 0 6 7 97 + 57 0 96 6 97 + 58 0 7 38 97 + 59 0 15 76 77 + 60 0 97 38 37 + 61 0 7 6 98 + 62 0 72 7 98 + 63 0 10 72 73 + 64 0 20 80 81 + 65 0 25 84 85 + 66 0 30 88 89 + 67 0 35 92 93 + 68 0 4 5 99 + 69 0 2 95 100 + 70 0 69 2 101 + 71 0 101 2 102 + 72 0 2 100 102 + 73 0 99 5 96 + 74 0 4 99 95 + 75 0 71 70 103 + 76 0 98 6 71 + 77 0 72 98 73 + 78 0 81 80 104 + 79 0 75 74 105 + 80 0 76 75 106 + 81 0 75 105 106 + 82 0 78 77 107 + 83 0 79 78 108 + 84 0 78 107 108 + 85 0 80 79 108 + 86 0 82 81 109 + 87 0 81 104 109 + 88 0 85 84 110 + 89 0 100 95 111 + 90 0 83 82 112 + 91 0 82 109 112 + 92 0 112 110 83 + 93 0 86 85 113 + 94 0 85 110 113 + 95 0 89 88 114 + 96 0 87 86 115 + 97 0 86 113 115 + 98 0 115 114 87 + 99 0 90 89 116 + 100 0 89 114 116 + 101 0 93 92 117 + 102 0 77 76 106 + 103 0 91 90 118 + 104 0 90 116 118 + 105 0 92 91 118 + 106 0 99 96 119 + 107 0 96 97 117 + 108 0 69 101 120 + 109 0 73 98 121 + 110 0 74 73 121 + 111 0 98 71 121 + 112 0 70 69 122 + 113 0 103 70 122 + 114 0 69 120 122 + 115 0 105 74 121 + 116 0 104 80 108 + 117 0 121 71 103 + 118 0 102 100 123 + 119 0 107 77 106 + 120 0 84 83 110 + 121 0 88 87 114 + 122 0 92 118 117 + 123 0 120 101 124 + 124 0 97 94 117 + 125 0 119 96 117 + 126 0 95 99 125 + 127 0 95 125 111 + 128 0 123 100 126 + 129 0 100 111 126 + 130 0 124 101 127 + 131 0 101 102 127 + 132 0 125 99 119 + 133 0 112 109 128 + 134 0 121 103 105 + 135 0 107 106 129 + 136 0 97 37 94 + 137 0 108 107 130 + 138 0 104 108 130 + 139 0 107 129 130 + 140 0 109 104 131 + 141 0 128 109 131 + 142 0 104 130 131 + 143 0 114 115 132 + 144 0 117 118 119 + 145 0 93 117 94 + 146 0 116 114 132 + 147 0 120 124 133 + 148 0 110 112 134 + 149 0 112 128 134 + 150 0 111 125 135 + 151 0 125 119 135 + 152 0 105 103 129 + 153 0 111 135 126 + 154 0 123 126 136 + 155 0 119 118 116 + 156 0 124 127 137 + 157 0 115 113 136 + 158 0 113 110 134 + 159 0 106 105 129 + 160 0 133 124 137 + 161 0 120 133 122 + 162 0 102 123 138 + 163 0 102 138 127 + 164 0 103 122 129 + 165 0 129 122 133 + 166 0 136 126 132 + 167 0 130 129 133 + 168 0 119 116 135 + 169 0 115 136 132 + 170 0 116 132 135 + 171 0 138 123 139 + 172 0 123 136 139 + 173 0 132 126 135 + 174 0 139 136 134 + 175 0 139 134 128 + 176 0 139 128 138 + 177 0 133 137 130 + 178 0 138 128 131 + 179 0 113 134 136 + 180 0 130 137 131 + 181 0 137 127 138 + 182 0 137 138 131 + 183 0 3 39 22 + 184 0 39 40 21 + 185 0 41 20 40 + 186 0 41 42 19 + 187 0 42 43 18 + 188 0 43 44 17 + 189 0 44 45 16 + 190 0 45 46 15 + 191 0 46 47 14 + 192 0 47 48 13 + 193 0 49 12 48 + 194 0 49 50 11 + 195 0 51 52 9 + 196 0 52 53 8 + 197 0 53 54 1 + 198 0 54 55 38 + 199 0 55 56 37 + 200 0 57 58 35 + 201 0 33 34 59 + 202 0 59 60 33 + 203 0 31 32 61 + 204 0 61 62 31 + 205 0 29 30 63 + 206 0 28 29 64 + 207 0 27 28 65 + 208 0 26 27 66 + 209 0 25 26 67 + 210 0 67 68 24 + 211 0 3 23 68 + 212 0 1 8 53 + 213 0 8 9 52 + 214 0 9 10 51 + 215 0 10 11 50 + 216 0 13 14 47 + 217 0 14 15 46 + 218 0 15 16 45 + 219 0 16 17 44 + 220 0 17 18 43 + 221 0 18 19 42 + 222 0 21 22 39 + 223 0 24 25 67 + 224 0 35 36 57 + 225 0 36 37 56 + 226 0 37 38 55 + 227 0 38 1 54 + 228 0 10 50 51 + 229 0 36 56 57 + 230 0 33 60 32 + 231 0 62 63 30 + 232 0 31 62 30 + 233 0 64 65 28 + 234 0 63 64 29 + 235 0 66 67 26 + 236 0 65 66 27 + 237 0 60 61 32 + 238 0 24 68 23 + 239 0 11 12 49 + 240 0 19 20 41 + 241 0 22 23 3 + 242 0 34 35 58 + 243 0 21 40 20 + 244 0 13 48 12 + 245 0 58 59 34 +End Conditions // SurfaceCondition3D3N + +Begin SubModelPart design_surface + Begin SubModelPartProperties + 0 + End SubModelPartProperties + Begin SubModelPartNodes + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 120 + 121 + 122 + 123 + 124 + 125 + 126 + 127 + 128 + 129 + 130 + 131 + 132 + 133 + 134 + 135 + 136 + 137 + 138 + 139 + End SubModelPartNodes + Begin SubModelPartConditions + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 120 + 121 + 122 + 123 + 124 + 125 + 126 + 127 + 128 + 129 + 130 + 131 + 132 + 133 + 134 + 135 + 136 + 137 + 138 + 139 + 140 + 141 + 142 + 143 + 144 + 145 + 146 + 147 + 148 + 149 + 150 + 151 + 152 + 153 + 154 + 155 + 156 + 157 + 158 + 159 + 160 + 161 + 162 + 163 + 164 + 165 + 166 + 167 + 168 + 169 + 170 + 171 + 172 + 173 + 174 + 175 + 176 + 177 + 178 + 179 + 180 + 181 + 182 + 183 + 184 + 185 + 186 + 187 + 188 + 189 + 190 + 191 + 192 + 193 + 194 + 195 + 196 + 197 + 198 + 199 + 200 + 201 + 202 + 203 + 204 + 205 + 206 + 207 + 208 + 209 + 210 + 211 + 212 + 213 + 214 + 215 + 216 + 217 + 218 + 219 + 220 + 221 + 222 + 223 + 224 + 225 + 226 + 227 + 228 + 229 + 230 + 231 + 232 + 233 + 234 + 235 + 236 + 237 + 238 + 239 + 240 + 241 + 242 + 243 + 244 + 245 + End SubModelPartConditions +End SubModelPart // design_surface +Begin SubModelPart free_edge + Begin SubModelPartNodes + 3 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + End SubModelPartNodes +End SubModelPart // free_edge diff --git a/applications/ShapeOptimizationApplication/tests/water_drain_test/optimization_parameters.json b/applications/ShapeOptimizationApplication/tests/water_drain_test/optimization_parameters.json new file mode 100644 index 000000000000..ad659bfeff11 --- /dev/null +++ b/applications/ShapeOptimizationApplication/tests/water_drain_test/optimization_parameters.json @@ -0,0 +1,71 @@ +{ + "optimization_settings": { + "model_settings": { + "domain_size": 3, + "model_part_name": "cosinus_shell", + "model_import_settings": { + "input_type": "mdpa", + "input_filename": "cosinus_shell" + }, + "design_surface_sub_model_part_name": "design_surface", + "damping": { + "apply_damping": true, + "damping_regions": [ + ] + }, + "mesh_motion": { + "apply_mesh_solver": false + } + }, + "objectives": [ + { + "identifier": "water_drain", + "type": "minimization", + "analyzer": "kratos", + "response_settings": { + "response_type": "water_drain", + "gravity_direction": [0.0, 0.0, -1.0], + "model_part_name": "cosinus_shell.design_surface", + "max_iterations_volume_search": 1e4, + "continuous_sensitivities": true, + "quadratic_height_penalization": true + } + } + ], + "constraints": [ + ], + "design_variables": { + "type": "vertex_morphing", + "filter": { + "filter_function_type": "gaussian", + "max_nodes_in_filter_radius": 1000, + "filter_radius": 0.000001, + "matrix_free_filtering": true + } + }, + "optimization_algorithm": { + "name": "steepest_descent", + "max_iterations": 6, + "relative_tolerance": 1e-8, + "line_search": { + "line_search_type": "manual_stepping", + "normalize_search_direction": true, + "step_size": 1.0 + } + }, + "output": { + "design_output_mode": "write_design_surface", + "nodal_results": [ + "DF1DX", + "DF1DX_MAPPED", + "SHAPE_UPDATE", + "SHAPE_CHANGE", + "WATER_LEVEL", + "WATER_VOLUMES" + ], + "output_format": { + "name": "vtk" + } + } + } +} \ No newline at end of file diff --git a/applications/ShapeOptimizationApplication/tests/water_drain_test/run_test.py b/applications/ShapeOptimizationApplication/tests/water_drain_test/run_test.py new file mode 100644 index 000000000000..52e0470d86d3 --- /dev/null +++ b/applications/ShapeOptimizationApplication/tests/water_drain_test/run_test.py @@ -0,0 +1,90 @@ +# Import Kratos core and apps +import KratosMultiphysics as KM + +# Additional imports +from KratosMultiphysics.ShapeOptimizationApplication import optimizer_factory +from KratosMultiphysics.KratosUnittest import TestCase +import KratosMultiphysics.kratos_utilities as kratos_utilities +from KratosMultiphysics.from_json_check_result_process import FromJsonCheckResultProcess +from KratosMultiphysics.json_output_process import JsonOutputProcess + +import os, csv + +# Read parameters +with open("optimization_parameters.json",'r') as parameter_file: + parameters = KM.Parameters(parameter_file.read()) + +model = KM.Model() + +# Create optimizer and perform optimization +optimizer = optimizer_factory.Create(model, parameters["optimization_settings"]) +optimizer.Optimize() + +# ======================================================================================================= +# Test results and clean directory +# ======================================================================================================= +output_directory = parameters["optimization_settings"]["output"]["output_directory"].GetString() +optimization_model_part_name = parameters["optimization_settings"]["model_settings"]["model_part_name"].GetString() +optimization_log_filename = parameters["optimization_settings"]["output"]["optimization_log_filename"].GetString() + ".csv" + +# Testing by +# 1) some values from csv output +# 2) using the "json_output_process" & "json_check_process" + +# TODO: finite difference sensitivity testing +# - implement area derivatives +# - fd sensitivities can only be computed for the infeasible nodes ("volume nodes") which are forming a pond +# - feasible nodes don't have any sensitivity + +objective_reference_result = [2.11349E+01, 5.28372E+00, 0.00000E+00] + +with open(os.path.join(output_directory, optimization_log_filename), 'r') as csvfile: + reader = csv.reader(csvfile, delimiter=',') + last_line = None + resulting_objective = [] + i = 0 + for line in reader: + if not line: + continue + else: + if i != 0: + resulting_objective.append(float(line[1].strip())) + last_line = line + i += 1 + + resulting_iteration = float(last_line[0].strip()) + resulting_abs_improvement = float(last_line[2].strip()) + + # Check against specifications + TestCase().assertEqual(resulting_iteration, 3) + TestCase().assertAlmostEqual(resulting_objective, objective_reference_result, 3) + +# # write json output +# output_process = JsonOutputProcess(model, KM.Parameters( +# """{ +# "output_variables" : ["SHAPE_CHANGE_X","SHAPE_CHANGE_Y","SHAPE_CHANGE_Z"], +# "output_file_name" : "shape_change_results.json", +# "model_part_name" : \""""+optimization_model_part_name+"""\", +# "time_frequency" : 0.0 +# }""")) + +# output_process.ExecuteInitialize() +# output_process.ExecuteBeforeSolutionLoop() +# output_process.ExecuteInitializeSolutionStep() +# output_process.ExecuteFinalizeSolutionStep() +# output_process.ExecuteFinalize() + +check_process = FromJsonCheckResultProcess(model, KM.Parameters( + """{ + "check_variables" : ["SHAPE_CHANGE_X","SHAPE_CHANGE_Y","SHAPE_CHANGE_Z"], + "input_file_name" : "shape_change_results.json", + "model_part_name" : \""""+optimization_model_part_name+"""\", + "time_frequency" : 0.0 + }""")) +check_process.ExecuteInitialize() +check_process.ExecuteBeforeSolutionLoop() +check_process.ExecuteInitializeSolutionStep() +check_process.ExecuteFinalizeSolutionStep() +check_process.ExecuteFinalize() + +# ======================================================================================================= \ No newline at end of file diff --git a/applications/ShapeOptimizationApplication/tests/water_drain_test/shape_change_results.json b/applications/ShapeOptimizationApplication/tests/water_drain_test/shape_change_results.json new file mode 100644 index 000000000000..bf0077df8ed1 --- /dev/null +++ b/applications/ShapeOptimizationApplication/tests/water_drain_test/shape_change_results.json @@ -0,0 +1,1534 @@ +{ + "TIME": [ + 3.0 + ], + "NODE_1": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_2": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 2.0 + ] + }, + "NODE_3": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_4": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.8163298652000002 + ] + }, + "NODE_5": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.5094914412999998 + ] + }, + "NODE_6": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.0011088137000002 + ] + }, + "NODE_7": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.3856408297999999 + ] + }, + "NODE_8": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_9": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_10": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_11": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_12": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_13": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_14": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_15": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_16": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_17": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_18": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_19": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_20": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_21": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_22": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_23": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_24": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_25": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_26": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_27": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_28": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_29": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_30": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_31": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_32": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_33": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_34": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_35": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_36": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_37": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_38": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_39": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_40": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_41": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_42": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_43": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_44": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_45": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_46": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_47": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_48": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_49": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_50": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_51": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_52": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_53": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_54": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_55": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_56": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_57": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_58": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_59": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_60": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_61": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_62": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_63": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_64": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_65": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_66": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_67": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_68": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.0 + ] + }, + "NODE_69": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.8119216478000002 + ] + }, + "NODE_70": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.568569676 + ] + }, + "NODE_71": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.1792431400999999 + ] + }, + "NODE_72": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.18153896410000003 + ] + }, + "NODE_73": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.2502486816 + ] + }, + "NODE_74": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.2635704052000002 + ] + }, + "NODE_75": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.24733816549999998 + ] + }, + "NODE_76": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.1523529853000002 + ] + }, + "NODE_77": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.2174740650000001 + ] + }, + "NODE_78": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.21618403789999985 + ] + }, + "NODE_79": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.15550446890000014 + ] + }, + "NODE_80": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.1861948096999999 + ] + }, + "NODE_81": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.24067343549999987 + ] + }, + "NODE_82": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.23823413279999983 + ] + }, + "NODE_83": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.2121332480999999 + ] + }, + "NODE_84": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.13468903349999994 + ] + }, + "NODE_85": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.2074122359999998 + ] + }, + "NODE_86": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.23489828379999989 + ] + }, + "NODE_87": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.22364764179999996 + ] + }, + "NODE_88": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.1484333269000001 + ] + }, + "NODE_89": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.23934733649999995 + ] + }, + "NODE_90": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.2563617426000001 + ] + }, + "NODE_91": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.17307888439999985 + ] + }, + "NODE_92": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.18593624639999995 + ] + }, + "NODE_93": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.1629202114999999 + ] + }, + "NODE_94": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.19923193109999993 + ] + }, + "NODE_95": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.8140726388 + ] + }, + "NODE_96": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.1158441595 + ] + }, + "NODE_97": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.4460042903999999 + ] + }, + "NODE_98": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6279904182 + ] + }, + "NODE_99": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.5592663977 + ] + }, + "NODE_100": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.8043960285999998 + ] + }, + "NODE_101": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.8501963283 + ] + }, + "NODE_102": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.8103364274000002 + ] + }, + "NODE_103": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.1968474239 + ] + }, + "NODE_104": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6225438514999998 + ] + }, + "NODE_105": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6924977649999998 + ] + }, + "NODE_106": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.5134734493 + ] + }, + "NODE_107": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6165775926999998 + ] + }, + "NODE_108": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.49688737530000004 + ] + }, + "NODE_109": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6419727974000001 + ] + }, + "NODE_110": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.45441492670000005 + ] + }, + "NODE_111": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.6371436442 + ] + }, + "NODE_112": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.5908297494000001 + ] + }, + "NODE_113": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6119050504 + ] + }, + "NODE_114": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.5350741586000001 + ] + }, + "NODE_115": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.6480581106000001 + ] + }, + "NODE_116": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.7702584165999999 + ] + }, + "NODE_117": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.59286124 + ] + }, + "NODE_118": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.562922639 + ] + }, + "NODE_119": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.1445884145000003 + ] + }, + "NODE_120": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.6762823797 + ] + }, + "NODE_121": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.7261148593 + ] + }, + "NODE_122": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.5011470652 + ] + }, + "NODE_123": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.5833161849000001 + ] + }, + "NODE_124": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.6279524856999998 + ] + }, + "NODE_125": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.53650122 + ] + }, + "NODE_126": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.4841617686 + ] + }, + "NODE_127": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.6758036139 + ] + }, + "NODE_128": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.0232489617000002 + ] + }, + "NODE_129": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.0572239571999997 + ] + }, + "NODE_130": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.0166592984 + ] + }, + "NODE_131": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.0747844335999999 + ] + }, + "NODE_132": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.0570586827000001 + ] + }, + "NODE_133": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.40679797 + ] + }, + "NODE_134": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 0.8766842853000001 + ] + }, + "NODE_135": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.3258199751 + ] + }, + "NODE_136": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.1573665992 + ] + }, + "NODE_137": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.4148236092 + ] + }, + "NODE_138": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.4894639660999998 + ] + }, + "NODE_139": { + "SHAPE_CHANGE_X": [ + 0.0 + ], + "SHAPE_CHANGE_Y": [ + 0.0 + ], + "SHAPE_CHANGE_Z": [ + 1.2908312311 + ] + } +} \ No newline at end of file