Skip to content

Commit

Permalink
Auto stash before merge of "Kratos_RotatingFrameProcess" and "origin/…
Browse files Browse the repository at this point in the history
…Kratos_RotatingFrameProcess"
  • Loading branch information
SADPR committed Dec 5, 2023
1 parent 283492f commit 5be37fe
Show file tree
Hide file tree
Showing 5 changed files with 819 additions and 5 deletions.
2 changes: 2 additions & 0 deletions kratos/python/add_geometrical_utilities_to_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ void AddGeometricalUtilitiesToPython(pybind11::module &m)
.def(py::init<ModelPart::NodesContainerType&>())
.def("AssignMasterSlaveConstraintsToNodes", [](AssignMasterSlaveConstraintsToNeighboursUtility& rAssignMasterSlaveConstraintsToNeighboursUtility, NodesContainerType pNodes, double const Radius, ModelPart& rComputingModelPart, const std::vector<std::reference_wrapper<const Kratos::Variable<double>>>& rVariableList, double const MinNumOfNeighNodes){
return rAssignMasterSlaveConstraintsToNeighboursUtility.AssignMasterSlaveConstraintsToNodes(pNodes, Radius, rComputingModelPart, rVariableList, MinNumOfNeighNodes);})
.def("AssignRotationalMasterSlaveConstraintsToNodes", [](AssignMasterSlaveConstraintsToNeighboursUtility& rAssignMasterSlaveConstraintsToNeighboursUtility, NodesContainerType pNodes, double const Radius, ModelPart& rComputingModelPart, const std::vector<std::reference_wrapper<const Kratos::Variable<double>>>& rVariableList, double const MinNumOfNeighNodes, const std::unordered_map<IndexType, std::pair<IndexType, double>>& GhostToOriginalMapping){
return rAssignMasterSlaveConstraintsToNeighboursUtility.AssignRotationalMasterSlaveConstraintsToNodes(pNodes, Radius, rComputingModelPart, rVariableList, MinNumOfNeighNodes, GhostToOriginalMapping);})
;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import KratosMultiphysics as KM
import numpy as np
import math

def Factory(settings, model):
if not isinstance(settings, KM.Parameters):
raise Exception("expected input shall be a Parameters object, encapsulating a json string")
return AssignRotationalSymmetryMasterSlaveConstraintsProcess(model, settings["Parameters"])

## All the processes python should be derived from "Process"
class AssignRotationalSymmetryMasterSlaveConstraintsProcess(KM.Process):
"""The process facilitates the discovery of neighboring nodes in a
master model part within a designated radius for each node in the
slave model part. Following this, it establishes a master-slave constraint
and calculates its weights using a spatial function that employs radial basis functions.

Public member variables:
model -- the container of the different model parts.
settings -- Kratos parameters containing process settings.
"""

def __init__(self, model, settings):
""" The default constructor of the class

Keyword arguments:
self -- It signifies an instance of a class.
Model -- the container of the different model parts.
settings -- Kratos parameters containing process settings.
"""
KM.Process.__init__(self) # calling the baseclass constructor

default_settings = KM.Parameters("""{
"model_part_name": "",
"slave_model_part_name": "",
"master_model_part_name": "",
"variable_names": [],
"search_radius": 1.0,
"minimum_number_of_neighbouring_nodes": 3,
"reform_constraints_at_each_step": false
}""")

# Add missing settings that the user did not provide but that
# are necessary for this process
settings.ValidateAndAssignDefaults(default_settings)

# Get the model part on which the MasterSlaveConstraints are going to be applied
if not settings["model_part_name"].GetString():
raise Exception("\'model_part_name\' not provided. Please specify the model part to apply to MasterSlaveConstraints to.")
model_part_name = settings["model_part_name"].GetString() #MasterSlaveConstraints are applied to computing model part
self.computing_model_part = model.GetModelPart(model_part_name)

# Get the slave model part
if not settings["slave_model_part_name"].GetString():
raise Exception("\'slave_model_part_name\' not provided. Please specify the slave model part.")
slave_model_part_name = settings["slave_model_part_name"].GetString()
self.slave_model_part = model.GetModelPart(slave_model_part_name)

# Get the master model part
if not settings["master_model_part_name"].GetString():
raise Exception("\'master_model_part_name\' not provided. Please specify the master model part.")
master_model_part_name = settings["master_model_part_name"].GetString()
self.master_model_part = model.GetModelPart(master_model_part_name)

# Search radius for the MasterSlaveConstraints
self.search_radius = settings["search_radius"].GetDouble()

# Minimum number of neighboring nodes retrieved from the search radius
self.minimum_number_of_neighbouring_nodes = settings["minimum_number_of_neighbouring_nodes"].GetInt()

# Apply MasterSlaveConstraints at each time step (True) or only once (False)
self.reform_constraints_at_each_step = settings["reform_constraints_at_each_step"].GetBool()

# Retrieve and check if variables exist
variable_names = settings["variable_names"].GetStringArray()
if len(variable_names) == 0:
err_msg = "The variable names need to be specified by the user in the \'variable_names\' string array."
raise Exception(err_msg)
if any(variable_names.count(var_name) > 1 for var_name in variable_names):
err_msg = "There are repeated variables in the \'variable_names\' string array."
raise Exception(err_msg)
variable_names.sort()

self.variables_list = [] # Initialize the list of variables

for var_name in variable_names:
# Check if the variable exists in KratosGlobals
if not KM.KratosGlobals.HasVariable(var_name):
err_msg = "\'{}\' variable in \'variable_names\' is not in KratosGlobals. Please check the provided value.".format(var_name)

var_type = KM.KratosGlobals.GetVariableType(var_name) # Get the type of the variable

# Check the variable type and add it to the variables_list accordingly
if var_type == "Array":
domain_size = self.computing_model_part.ProcessInfo[KM.DOMAIN_SIZE] # Get the domain size from the ProcessInfo
component_suffixes = ["_X", "_Y", "_Z"] # Suffixes for the components of the array variable
for i in range(domain_size):
var_name_with_suffix = f"{var_name}{component_suffixes[i]}" # Append the component suffix to the variable name
self.variables_list.append(KM.KratosGlobals.GetVariable(var_name_with_suffix)) # Add the variable to the list
elif var_type == "Double":
self.variables_list.append(KM.KratosGlobals.GetVariable(var_name)) # Add the variable to the list
else:
raise Exception("Variable " + var_name + " not compatible") # Raise an exception for incompatible variable types

# Initialization of ghost nodes related attributes
self.model.CreateModelPart("AuxiliarMasterModelPart")
self.aux_master_model_part = self.model.GetModelPart("AuxiliarMasterModelPart")
self.max_node_id = self.computing_model_part.NumberOfNodes()

def ExecuteInitialize(self):
""" This method is executed at the begining to initialize the process

Keyword arguments:
self -- It signifies an instance of a class.
"""

self.__CreateGhostNodes(self.master_model_part.Nodes)

# Initialize MasterSlaveConstraints to neighbours utility
self.assign_mscs_utility = KM.AssignMasterSlaveConstraintsToNeighboursUtility(self.master_model_part.Nodes)

# The user may only need to set up the MasterSlaveConstraints only once
if not self.reform_constraints_at_each_step:
for variable in self.variables_list:
self.assign_mscs_utility.AssignMasterSlaveConstraintsToNodes(self.slave_model_part.Nodes,self.search_radius,self.computing_model_part, variable, self.minimum_number_of_neighbouring_nodes)


def ExecuteInitializeSolutionStep(self):
""" This method is executed in order to initialize the current step

Keyword arguments:
self -- It signifies an instance of a class.
"""
# If the user want the mscs to be updated at each time step, this is usefull for moving meshes.
if self.reform_constraints_at_each_step:
self.assign_mscs_utility.AssignMasterSlaveConstraintsToNodes(self.slave_model_part.Nodes,self.search_radius,self.computing_model_part, self.variables_list, self.minimum_number_of_neighbouring_nodes)

def ExecuteFinalizeSolutionStep(self):
""" This method is executed in order to finalize the current step

Keyword arguments:
self -- It signifies an instance of a class.
"""
# If MasterSlaveConstraints are updated every time step, these are to me removed before being re-assigned.
if self.reform_constraints_at_each_step:
self.__RemoveConstraints()

def __RemoveConstraints(self):
#Remove master-slave constraints
KM.VariableUtils().SetFlag(KM.TO_ERASE, True, self.computing_model_part.MasterSlaveConstraints)
self.computing_model_part.RemoveMasterSlaveConstraintsFromAllLevels(KM.TO_ERASE)

def __CreateGhostNodes(self, nodes):
"""Create ghost nodes for each node."""
ghost_to_original_mapping = {}
rotation_angles = [0, 60, 120, 180, 240, 300]

self.max_node_id = self.main_model_part.NumberOfNodes()

for angle in rotation_angles:
# Create ghost nodes
for original_node in nodes:
ghost_node = self.__RotateNode(original_node, angle)
ghost_to_original_mapping[ghost_node] = original_node
self.aux_master_model_part.AddNode(ghost_node)

return ghost_to_original_mapping

def __RotateNode(self, original_node, angle_degrees):
"""Rotate a node by a given angle (in degrees) about the origin."""
angle_radians = math.radians(angle_degrees)

# Apply 2D rotation matrix
new_x = original_node.X * math.cos(angle_radians) - original_node.Y * math.sin(angle_radians)
new_y = original_node.X * math.sin(angle_radians) + original_node.Y * math.cos(angle_radians)

# Generate a new node ID based on the maximum node ID
new_node_id = self.max_node_id + 1
self.max_node_id += 1

# Create the new node with the ID and coordinates
return KM.Node(new_node_id, new_x, new_y, 0.0)
Loading

0 comments on commit 5be37fe

Please sign in to comment.