Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kratos Master-Slave Constraints to Neighbours Process and Utility #11129

Merged
merged 34 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9f907b5
Working.
SADPR May 11, 2023
945f8ff
Redefined some names.
SADPR May 11, 2023
38dbd7e
Working without Kratos components inside the loop.
SADPR May 12, 2023
af86d4d
Cleaned.
SADPR May 12, 2023
6de6410
Style corrections.
SADPR May 12, 2023
680851c
Checkpoint, of Ruben's suggestions. Added MPCs warnings.
SADPR May 12, 2023
69cddb6
Correcting WARNING. Next step is to create TLS.
SADPR May 12, 2023
4a16d26
Working with some TODOs.
SADPR May 12, 2023
22dbe16
Changed names from MPCs to MSCs
SADPR May 15, 2023
f68a314
Working with new approach.
SADPR May 15, 2023
fad4218
First clean.
SADPR May 15, 2023
32070ee
Erasing previous utilities and processes.
SADPR May 15, 2023
889d4d0
Naming and style guide.
SADPR May 15, 2023
701a837
Erasing unused functions.
SADPR May 15, 2023
7fe3d95
Some comments and self explanatory variables and functions names.
SADPR May 15, 2023
2becbda
Added comments to main function
SADPR May 15, 2023
003819b
Some comments
SADPR May 15, 2023
4ebb566
Updated descriptions
SADPR May 15, 2023
37a6e55
Small style corrections.
SADPR May 15, 2023
4d3af65
Typo
SADPR May 15, 2023
4093562
Test working with assertAlmostEqual
SADPR May 15, 2023
f738f82
Working with assertVectorAlmostEqual
SADPR May 15, 2023
97e736c
Style guide
SADPR May 15, 2023
530b241
Merge branch 'master' into Kratos_MPCs_suggestions
SADPR May 15, 2023
b7a8838
Node template update (there was an update when merging)
SADPR May 15, 2023
0cd3765
Destructor was failing in the CI (clang). Locally it wasn't.
SADPR May 15, 2023
43415eb
K&R_style
SADPR May 16, 2023
16ac146
Merged with assign mpcs. Please erase this files.
SADPR Jun 16, 2023
45b47ce
Erase this, it is case dependent.
SADPR Jul 17, 2023
22276be
Riccardo's suggestion (from another discussion).
SADPR Aug 22, 2023
53830ab
Going back to original PR.
SADPR Aug 22, 2023
eb603b1
Pass local_results as argument for optimized memory in parallel loops
SADPR Aug 22, 2023
a9c1265
Minor style
loumalouomega Sep 11, 2023
01f185c
Add backward compatibility for renamed MPC process
SADPR Sep 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion applications/FluidDynamicsApplication/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,4 @@ endif((${USE_MPI} MATCHES ON) AND (${TRILINOS_FOUND}))

# Define custom targets
set(KRATOS_KERNEL "${KRATOS_KERNEL};KratosFluidDynamicsCore" PARENT_SCOPE)
set(KRATOS_PYTHON_INTERFACE "${KRATOS_PYTHON_INTERFACE};KratosFluidDynamicsApplication" PARENT_SCOPE)
set(KRATOS_PYTHON_INTERFACE "${KRATOS_PYTHON_INTERFACE};KratosFluidDynamicsApplication" PARENT_SCOPE)
18 changes: 7 additions & 11 deletions kratos/python/add_geometrical_utilities_to_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include "utilities/mls_shape_functions_utility.h"
#include "utilities/tessellation_utilities/delaunator_utilities.h"
#include "utilities/rbf_shape_functions_utility.h"
#include "utilities/assign_mpcs_to_neighbours_utility.h"
#include "utilities/assign_master_slave_constraints_to_neighbours_utility.h"

namespace Kratos::Python {

Expand Down Expand Up @@ -432,17 +432,13 @@ void AddGeometricalUtilitiesToPython(pybind11::module &m)
.def_static("CalculateShapeFunctions", [](const Matrix& rPoints, const array_1d<double,3>& rX, const double h, Vector& rN, DenseQRPointerType pDenseQR){
return RBFShapeFunctionsUtility::CalculateShapeFunctions(rPoints, rX, h, rN, pDenseQR);})
;
// Radial Node Search and MPCs Assignation Utility
using NodesContainerType = typename AssignMPCsToNeighboursUtility::NodesContainerType;
py::class_<AssignMPCsToNeighboursUtility>(m, "AssignMPCsToNeighboursUtility")

// Radial Node Search and MasterSlaveConstraints Assignation Utility
using NodesContainerType = typename AssignMasterSlaveConstraintsToNeighboursUtility::NodesContainerType;
py::class_<AssignMasterSlaveConstraintsToNeighboursUtility>(m, "AssignMasterSlaveConstraintsToNeighboursUtility")
.def(py::init<ModelPart::NodesContainerType&>())
.def("AssignRotationToNodes", &AssignMPCsToNeighboursUtility::AssignRotationToNodes)
// .def("AssignMPCsToNodes", &AssignMPCsToNeighboursUtility::AssignMPCsToNodes)
.def("AssignMPCsToNodes", [](AssignMPCsToNeighboursUtility& rAssignMPCsToNeighboursUtility, NodesContainerType pNodes, double const Radius, ModelPart& rComputingModelPart, const Variable<double>& rVariable, double const MinNumOfNeighNodes){
return rAssignMPCsToNeighboursUtility.AssignMPCsToNodes(pNodes, Radius, rComputingModelPart, rVariable, MinNumOfNeighNodes);})
.def("AssignMPCsToNodes", [](AssignMPCsToNeighboursUtility& rAssignMPCsToNeighboursUtility, NodesContainerType pNodes, double const Radius, ModelPart& rComputingModelPart, const Variable<array_1d<double, 3>>& rVariable, double const MinNumOfNeighNodes){
return rAssignMPCsToNeighboursUtility.AssignMPCsToNodes(pNodes, Radius, rComputingModelPart, rVariable, MinNumOfNeighNodes);})
.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);})
;
}

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

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

## All the processes python should be derived from "Process"
class AssignMasterSlaveConstraintsToNeighboursProcess(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


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

Keyword arguments:
self -- It signifies an instance of a class.
"""
# 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)
183 changes: 12 additions & 171 deletions kratos/python_scripts/assign_mpcs_to_neighbours_process.py
Original file line number Diff line number Diff line change
@@ -1,178 +1,19 @@
import KratosMultiphysics as KM
import numpy as np
import KratosMultiphysics
from KratosMultiphysics.assign_master_slave_constraints_to_neighbours_process import AssignMasterSlaveConstraintsToNeighboursProcess

def Factory(settings, model):
if not isinstance(settings, KM.Parameters):
raise Exception("expected input shall be a Parameters object, encapsulating a json string")
KratosMultiphysics.Logger.PrintWarning("assign_mpcs_to_neighbours_process", "This process is deprecated. Please use 'assign_master_slave_constraints_to_neighbours_process' instead.")
return AssignMPCsToNeighboursProcess(model, settings["Parameters"])

## All the processes python should be derived from "Process"
class AssignMPCsToNeighboursProcess(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 multipoint constraint (MPC)
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.
"""

class AssignMPCsToNeighboursProcess(AssignMasterSlaveConstraintsToNeighboursProcess):
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("""{
"computing_model_part_name": "",
"slave_model_part_name": "",
"master_model_part_name": "",
"variable_names": [],
"search_radius": 1.0,
"minimum_number_of_neighbouring_nodes": 3,
"assign_mpcs_every_time_step": false
}""")

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

#Assign settings
self.model = model

# Get the model part on which the mpcs are going to be applied
if not settings["computing_model_part_name"].GetString():
raise Exception("\'computing_model_part_name\' not provided. Please specify the model part to apply to MPCs to.")
self.computing_model_part_name = settings["computing_model_part_name"].GetString() #mpcs are applied to computing model part
# Check for deprecated settings and update them

# 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.")
self.slave_model_part_name = settings["slave_model_part_name"].GetString()
# The setting 'assign_mpcs_every_time_step' was renamed to 'reform_constraints_at_each_step'
# This block ensures backward compatibility by updating the old setting to the new one and issuing a warning.
if settings.Has("assign_mpcs_every_time_step"):
KratosMultiphysics.Logger.PrintWarning("AssignMPCsToNeighboursProcess", "'assign_mpcs_every_time_step' is deprecated. Please use 'reform_constraints_at_each_step' instead.")
settings.AddValue("reform_constraints_at_each_step", settings["assign_mpcs_every_time_step"])
settings.RemoveValue("assign_mpcs_every_time_step")

# 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.")
self.master_model_part_name = settings["master_model_part_name"].GetString()

# Search radius for the mpcs
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 mpcs at each time step (True) or only once (False)
self.assign_mpcs_every_time_step = settings["assign_mpcs_every_time_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 = []
for var_name in variable_names:
if not KM.KratosGlobals.HasVariable(var_name):
err_msg = "\'{}\' variable in \'variable_names\' is not in KratosGlobals. Please check provided value.".format(var_name)
if not KM.KratosGlobals.GetVariableType(var_name):
err_msg = "\'{}\' variable in \'variable_names\' is not double type. Please check provide double type variables (e.g. [\"DISPLACEMENT_X\",\"DISPLACEMENT_Y\"]).".format(var_name)
self.variables_list.append(KM.KratosGlobals.GetVariable(var_name))
debug = True

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

Keyword arguments:
self -- It signifies an instance of a class.
"""
## Assign master and slave model parts
self.computing_model_part = self.model.GetModelPart(self.computing_model_part_name)
self.slave_model_part = self.model.GetModelPart(self.slave_model_part_name)
self.master_model_part = self.model.GetModelPart(self.master_model_part_name)

# Initialize MPCs to neighbours utility
self.assign_mpcs_utility = KM.AssignMPCsToNeighboursUtility(self.master_model_part.Nodes)

# The user may only need to set up the mpcs only once
if not self.assign_mpcs_every_time_step:
for variable in self.variables_list:
self.assign_mpcs_utility.AssignMPCsToNodes(self.slave_model_part.Nodes,self.search_radius,self.computing_model_part, variable, self.minimum_number_of_neighbouring_nodes)

def Check(self):
""" This method verifies that the input is correct

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

def ExecuteBeforeSolutionLoop(self):
""" This method is executed just before the solution-loop

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

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 mpcs to be updated at each time step, this is usefull for moving meshes.
if self.assign_mpcs_every_time_step:
for variable in self.variables_list:
self.assign_mpcs_utility.AssignMPCsToNodes(self.slave_model_part.Nodes,self.search_radius,self.computing_model_part, variable, self.minimum_number_of_neighbouring_nodes)

def ExecuteBeforeOutputStep(self):
""" This method is executed before writing the output (if output
is being written in this step)

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

def ExecuteAfterOutputStep(self):
""" This method is executed after writing the output (if output
is being written in this step)

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

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 mpcs are updated every time step, these are to me removed before being re-assigned.
if self.assign_mpcs_every_time_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 ExecuteFinalize(self):
""" This method is executed after the computations, at the end of the solution-loop

Keyword arguments:
self -- It signifies an instance of a class.
"""
pass
super().__init__(model, settings)
Loading