from __future__ import print_function, absolute_import, division  # makes KratosMultiphysics backward compatible with python 2.6 and 2.7

# Importing the Kratos Library
import KratosMultiphysics

# Check that applications were imported in the main script
KratosMultiphysics.CheckRegisteredApplications("StructuralMechanicsApplication")

# Import applications
import KratosMultiphysics.StructuralMechanicsApplication as StructuralMechanicsApplication

# Other imports
import os


def CreateSolver(main_model_part, custom_settings):
    return MechanicalSolver(main_model_part, custom_settings)


class MechanicalSolver(object):
    """The base class for structural mechanics solvers.

    This class provides functions for importing and exporting models,
    adding nodal variables and dofs and solving each solution step.

    Derived classes must override the function _create_solution_scheme which
    constructs and returns a solution scheme. Depending on the type of
    solver, derived classes may also need to override the following functions:

    _create_solution_scheme
    _create_convergence_criterion
    _create_linear_solver
    _create_builder_and_solver
    _create_mechanical_solution_strategy
    _create_restart_utility

    The mechanical_solution_strategy, builder_and_solver, etc. should alway be retrieved
    using the getter functions get_mechanical_solution_strategy, get_builder_and_solver,
    etc. from this base class.

    Only the member variables listed below should be accessed directly.

    Public member variables:
    settings -- Kratos parameters containing solver settings.
    main_model_part -- the model part used to construct the solver.
    """
    def __init__(self, main_model_part, custom_settings):
        default_settings = KratosMultiphysics.Parameters("""
        {
            "echo_level": 0,
            "buffer_size": 2,
            "analysis_type": "non_linear",
            "model_import_settings": {
                "input_type": "mdpa",
                "input_filename": "unknown_name"
            },
            "restart_settings" : {
                "load_restart"  : false,
                "save_restart"  : false
            },
            "computing_model_part_name" : "computing_domain",
            "material_import_settings" :{
                "materials_filename": ""
            },
            "rotation_dofs": false,
            "pressure_dofs": false,
            "reform_dofs_at_each_step": false,
            "line_search": false,
            "compute_reactions": true,
            "block_builder": true,
            "clear_storage": false,
            "move_mesh_flag": true,
            "multi_point_constraints_used": false,
            "convergence_criterion": "residual_criterion",
            "displacement_relative_tolerance": 1.0e-4,
            "displacement_absolute_tolerance": 1.0e-9,
            "residual_relative_tolerance": 1.0e-4,
            "residual_absolute_tolerance": 1.0e-9,
            "max_iteration": 10,
            "linear_solver_settings":{
                "solver_type": "SuperLUSolver",
                "max_iteration": 500,
                "tolerance": 1e-9,
                "scaling": false,
                "verbosity": 1
            },
            "problem_domain_sub_model_part_list": ["solid"],
            "processes_sub_model_part_list": [""],
            "auxiliary_variables_list" : []
        }
        """)

        # temporary warnings, to be removed
        if custom_settings.Has("bodies_list"):
            custom_settings.RemoveValue("bodies_list")
            warning = '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "bodies_list", '
            warning += 'which is deprecated and will be removed soon. \nPlease remove it from the "solver settings"!\n'
            self.print_warning_on_rank_zero("Bodies list", warning)
        if custom_settings.Has("solver_type"):
            custom_settings.RemoveValue("solver_type")
            warning = '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "solver_type", '
            warning += 'which is only needed if you use the "python_solvers_wrapper_structural". \nPlease remove it '
            warning += 'from the "solver settings" if you dont use this wrapper, this check will be removed soon!\n'
            self.print_warning_on_rank_zero("Solver type", warning)
        if custom_settings.Has("time_integration_method"):
            custom_settings.RemoveValue("time_integration_method")
            warning = '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "time_integration_method", '
            warning += 'which is only needed if you use the "python_solvers_wrapper_structural". \nPlease remove it '
            warning += 'from the "solver settings" if you dont use this wrapper, this check will be removed soon!\n'
            self.print_warning_on_rank_zero("Time integration method", warning)

        # Overwrite the default settings with user-provided parameters.
        self.settings = custom_settings
        self.settings.ValidateAndAssignDefaults(default_settings)

        #TODO: shall obtain the computing_model_part from the MODEL once the object is implemented
        self.main_model_part = main_model_part
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Construction finished")

        # Set if the analysis is restarted
        if self.settings["restart_settings"].Has("load_restart"):
            load_restart = self.settings["restart_settings"]["load_restart"].GetBool()
            self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED] = load_restart
        else:
            self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED] = False

    def AddVariables(self):
        # this can safely be called also for restarts, it is internally checked if the variables exist already
        # Add displacements.
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.DISPLACEMENT)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.REACTION)
        # Add specific variables for the problem conditions.
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.POSITIVE_FACE_PRESSURE)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.NEGATIVE_FACE_PRESSURE)
        self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.POINT_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.LINE_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.SURFACE_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.VOLUME_ACCELERATION)
        if self.settings["rotation_dofs"].GetBool():
            # Add specific variables for the problem (rotation dofs).
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ROTATION)
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.REACTION_MOMENT)
            self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.POINT_MOMENT)
        if self.settings["pressure_dofs"].GetBool(): # TODO: The creation of UP and USigma elements is pending
            # Add specific variables for the problem (pressure dofs).
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.PRESSURE)
            self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.PRESSURE_REACTION)
        # Add variables that the user defined in the ProjectParameters
        for i in range(self.settings["auxiliary_variables_list"].size()):
            variable_name = self.settings["auxiliary_variables_list"][i].GetString()
            variable = KratosMultiphysics.KratosGlobals.GetVariable(variable_name)
            self.main_model_part.AddNodalSolutionStepVariable(variable)
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Variables ADDED")

    def GetMinimumBufferSize(self):
        return 2

    def AddDofs(self):
        # this can safely be called also for restarts, it is internally checked if the dofs exist already
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.DISPLACEMENT_X, KratosMultiphysics.REACTION_X,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.DISPLACEMENT_Y, KratosMultiphysics.REACTION_Y,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.DISPLACEMENT_Z, KratosMultiphysics.REACTION_Z,self.main_model_part)
        if self.settings["rotation_dofs"].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ROTATION_X, KratosMultiphysics.REACTION_MOMENT_X,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ROTATION_Y, KratosMultiphysics.REACTION_MOMENT_Y,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ROTATION_Z, KratosMultiphysics.REACTION_MOMENT_Z,self.main_model_part)
        if self.settings["pressure_dofs"].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.PRESSURE, KratosMultiphysics.PRESSURE_REACTION,self.main_model_part)
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "DOF's ADDED")

    def ImportModelPart(self):
        """ Legacy function, use ReadModelPart and PrepareModelPartForSolver instead """
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Importing model part.")
        problem_path = os.getcwd()
        input_filename = self.settings["model_import_settings"]["input_filename"].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif(self.settings["model_import_settings"]["input_type"].GetString() == "mdpa"):
            # Import model part from mdpa file.
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Reading model part from file: " + os.path.join(problem_path, input_filename) + ".mdpa")
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self.main_model_part)
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Finished reading model part from mdpa file.")
            self.PrepareModelPartForSolver()
        else:
            raise Exception("Other model part input options are not yet implemented.")
        KratosMultiphysics.Logger.PrintInfo("ModelPart", self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]:: ", "Finished importing model part.")

    def ReadModelPart(self):
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Reading model part.")
        problem_path = os.getcwd()
        input_filename = self.settings["model_import_settings"]["input_filename"].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif(self.settings["model_import_settings"]["input_type"].GetString() == "mdpa"):
            # Import model part from mdpa file.
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Reading model part from file: " + os.path.join(problem_path, input_filename) + ".mdpa")
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self.main_model_part)
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Finished reading model part from mdpa file.")
        else:
            raise Exception("Other model part input options are not yet implemented.")
        KratosMultiphysics.Logger.PrintInfo("ModelPart", self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]:: ", "Finished reading model part.")

    def PrepareModelPartForSolver(self):
        if not self.is_restarted():
            # Check and prepare computing model part and import constitutive laws.
            self._execute_after_reading()
            self._set_and_fill_buffer()
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "ModelPart prepared for Solver.")

    def ExportModelPart(self):
        name_out_file = self.settings["model_import_settings"]["input_filename"].GetString()+".out"
        file = open(name_out_file + ".mdpa","w")
        file.close()
        KratosMultiphysics.ModelPartIO(name_out_file, KratosMultiphysics.IO.WRITE).WriteModelPart(self.main_model_part)

    def Initialize(self):
        """Perform initialization after adding nodal variables and dofs to the main model part. """
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Initializing ...")
        # The mechanical solution strategy is created here if it does not already exist.
        if self.settings["clear_storage"].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.SetEchoLevel(self.settings["echo_level"].GetInt())
        if not self.is_restarted():
            mechanical_solution_strategy.Initialize()
        else:
            # SetInitializePerformedFlag is not a member of SolvingStrategy but
            # is used by ResidualBasedNewtonRaphsonStrategy.
            try:
                mechanical_solution_strategy.SetInitializePerformedFlag(True)
            except AttributeError:
                pass
        self.Check()
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Finished initialization.")

    def GetComputingModelPart(self):
        return self.main_model_part.GetSubModelPart(self.settings["computing_model_part_name"].GetString())

    def GetOutputVariables(self):
        pass

    def SaveRestart(self):
        # Check could be integrated in the utility
        # It is here intentionally, this way the utility is only created if it is actually needed!
        if self.settings["restart_settings"].Has("save_restart"):
            if (self.settings["restart_settings"]["save_restart"].GetBool() == True):
                # the check if this step is a restart-output step is done internally
                self.get_restart_utility().SaveRestart()

    def Solve(self):
        if self.settings["clear_storage"].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.Solve()

    def InitializeSolutionStep(self):
        self.get_mechanical_solution_strategy().InitializeSolutionStep()

    def Predict(self):
        self.get_mechanical_solution_strategy().Predict()

    def SolveSolutionStep(self):
        is_converged = self.get_mechanical_solution_strategy().SolveSolutionStep()
        return is_converged

    def FinalizeSolutionStep(self):
        self.get_mechanical_solution_strategy().FinalizeSolutionStep()

    def AdvanceInTime(self, current_time):
        dt = self.ComputeDeltaTime()
        new_time = current_time + dt
        self.main_model_part.ProcessInfo[KratosMultiphysics.STEP] += 1
        self.main_model_part.CloneTimeStep(new_time)

        return new_time

    def ComputeDeltaTime(self):
        return self.delta_time

    def SetDeltaTime(self, dt):
        # This is a TEMPORARY function until the solver can compute dt!
        self.delta_time = dt

    def SetEchoLevel(self, level):
        self.get_mechanical_solution_strategy().SetEchoLevel(level)

    def Clear(self):
        self.get_mechanical_solution_strategy().Clear()

    def Check(self):
        self.get_mechanical_solution_strategy().Check()

    #### Specific internal functions ####

    def get_solution_scheme(self):
        if not hasattr(self, '_solution_scheme'):
            self._solution_scheme = self._create_solution_scheme()
        return self._solution_scheme

    def get_convergence_criterion(self):
        if not hasattr(self, '_convergence_criterion'):
            self._convergence_criterion = self._create_convergence_criterion()
        return self._convergence_criterion

    def get_linear_solver(self):
        if not hasattr(self, '_linear_solver'):
            self._linear_solver = self._create_linear_solver()
        return self._linear_solver

    def get_builder_and_solver(self):
        if not hasattr(self, '_builder_and_solver'):
            self._builder_and_solver = self._create_builder_and_solver()
        return self._builder_and_solver

    def get_mechanical_solution_strategy(self):
        if not hasattr(self, '_mechanical_solution_strategy'):
            self._mechanical_solution_strategy = self._create_mechanical_solution_strategy()
        return self._mechanical_solution_strategy

    def get_restart_utility(self):
        if not hasattr(self, '_restart_utility'):
            self._restart_utility = self._create_restart_utility()
        return self._restart_utility

    def import_constitutive_laws(self):
        materials_filename = self.settings["material_import_settings"]["materials_filename"].GetString()
        if (materials_filename != ""):
            import read_materials_process
            # Create a dictionary of model parts.
            Model = KratosMultiphysics.Model()
            Model.AddModelPart(self.main_model_part)
            # Add constitutive laws and material properties from json file to model parts.
            read_materials_process.ReadMaterialsProcess(Model, self.settings["material_import_settings"])
            materials_imported = True
        else:
            materials_imported = False
        return materials_imported

    def validate_and_transfer_matching_settings(self, origin_settings, destination_settings):
        """Transfer matching settings from origin to destination.

        If a name in origin matches a name in destination, then the setting is
        validated against the destination.

        The typical use is for validating and extracting settings in derived classes:

        class A:
            def __init__(self, model_part, a_settings):
                default_a_settings = Parameters('''{
                    ...
                }''')
                a_settings.ValidateAndAssignDefaults(default_a_settings)
        class B(A):
            def __init__(self, model_part, custom_settings):
                b_settings = Parameters('''{
                    ...
                }''') # Here the settings contain default values.
                self.validate_and_transfer_matching_settings(custom_settings, b_settings)
                super().__init__(model_part, custom_settings)
        """
        for name, dest_value in destination_settings.items():
            if origin_settings.Has(name): # Validate and transfer value.
                orig_value = origin_settings[name]
                if dest_value.IsDouble() and orig_value.IsDouble():
                    destination_settings[name].SetDouble(origin_settings[name].GetDouble())
                elif dest_value.IsInt() and orig_value.IsInt():
                    destination_settings[name].SetInt(origin_settings[name].GetInt())
                elif dest_value.IsBool() and orig_value.IsBool():
                    destination_settings[name].SetBool(origin_settings[name].GetBool())
                elif dest_value.IsString() and orig_value.IsString():
                    destination_settings[name].SetString(origin_settings[name].GetString())
                elif dest_value.IsArray() and orig_value.IsArray():
                    if dest_value.size() != orig_value.size():
                        raise Exception('len("' + name + '") != ' + str(dest_value.size()))
                    for i in range(dest_value.size()):
                        if dest_value[i].IsDouble() and orig_value[i].IsDouble():
                            dest_value[i].SetDouble(orig_value[i].GetDouble())
                        elif dest_value[i].IsInt() and orig_value[i].IsInt():
                            dest_value[i].SetInt(orig_value[i].GetInt())
                        elif dest_value[i].IsBool() and orig_value[i].IsBool():
                            dest_value[i].SetBool(orig_value[i].GetBool())
                        elif dest_value[i].IsString() and orig_value[i].IsString():
                            dest_value[i].SetString(orig_value[i].GetString())
                        elif dest_value[i].IsSubParameter() and orig_value[i].IsSubParameter():
                            self.validate_and_transfer_matching_settings(orig_value[i], dest_value[i])
                            if len(orig_value[i].items()) != 0:
                                raise Exception('Json settings not found in default settings: ' + orig_value[i].PrettyPrintJsonString())
                        else:
                            raise Exception('Unsupported parameter type.')
                elif dest_value.IsSubParameter() and orig_value.IsSubParameter():
                    self.validate_and_transfer_matching_settings(orig_value, dest_value)
                    if len(orig_value.items()) != 0:
                        raise Exception('Json settings not found in default settings: ' + orig_value.PrettyPrintJsonString())
                else:
                    raise Exception('Unsupported parameter type.')
                origin_settings.RemoveValue(name)

    def is_restarted(self):
        # this function avoids the long call to ProcessInfo and is also safer
        # in case the detection of a restart is changed later
        return self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED]

    def print_on_rank_zero(self, *args):
        # This function will be overridden in the trilinos-solvers
        KratosMultiphysics.Logger.PrintInfo(" ".join(map(str,args)))

    def print_warning_on_rank_zero(self, *args):
        # This function will be overridden in the trilinos-solvers
        KratosMultiphysics.Logger.PrintWarning(" ".join(map(str,args)))

    #### Private functions ####

    def _execute_after_reading(self):
        """Prepare computing model part and import constitutive laws. """
        # Auxiliary parameters object for the CheckAndPepareModelProcess
        params = KratosMultiphysics.Parameters("{}")
        params.AddValue("computing_model_part_name",self.settings["computing_model_part_name"])
        params.AddValue("problem_domain_sub_model_part_list",self.settings["problem_domain_sub_model_part_list"])
        params.AddValue("processes_sub_model_part_list",self.settings["processes_sub_model_part_list"])
        # Assign mesh entities from domain and process sub model parts to the computing model part.
        import check_and_prepare_model_process_structural
        check_and_prepare_model_process_structural.CheckAndPrepareModelProcess(self.main_model_part, params).Execute()

        # Import constitutive laws.
        materials_imported = self.import_constitutive_laws()
        if materials_imported:
            self.print_on_rank_zero("::[MechanicalSolver]:: ", "Constitutive law was successfully imported.")
        else:
            self.print_on_rank_zero("::[MechanicalSolver]:: ", "Constitutive law was not imported.")

    def _set_and_fill_buffer(self):
        """Prepare nodal solution step data containers and time step information. """
        # Set the buffer size for the nodal solution steps data. Existing nodal
        # solution step data may be lost.
        required_buffer_size = self.settings["buffer_size"].GetInt()
        if required_buffer_size < self.GetMinimumBufferSize():
            required_buffer_size = self.GetMinimumBufferSize()
        current_buffer_size = self.main_model_part.GetBufferSize()
        buffer_size = max(current_buffer_size, required_buffer_size)
        self.main_model_part.SetBufferSize(buffer_size)
        # Cycle the buffer. This sets all historical nodal solution step data to
        # the current value and initializes the time stepping in the process info.
        delta_time = self.main_model_part.ProcessInfo[KratosMultiphysics.DELTA_TIME]
        time = self.main_model_part.ProcessInfo[KratosMultiphysics.TIME]
        step =-buffer_size
        time = time - delta_time * buffer_size
        self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.TIME, time)
        for i in range(0, buffer_size):
            step = step + 1
            time = time + delta_time
            self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.STEP, step)
            self.main_model_part.CloneTimeStep(time)

    def _add_dynamic_variables(self):
        # For being consistent for Serial and Trilinos
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.VELOCITY)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ACCELERATION)
        if self.settings["rotation_dofs"].GetBool():
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ANGULAR_VELOCITY)
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ANGULAR_ACCELERATION)

    def _add_dynamic_dofs(self):
        # For being consistent for Serial and Trilinos
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.VELOCITY_X,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.VELOCITY_Y,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.VELOCITY_Z,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ACCELERATION_X,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ACCELERATION_Y,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ACCELERATION_Z,self.main_model_part)
        if(self.settings["rotation_dofs"].GetBool()):
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_VELOCITY_X,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_VELOCITY_Y,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_VELOCITY_Z,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_ACCELERATION_X,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_ACCELERATION_Y,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_ACCELERATION_Z,self.main_model_part)

    def _get_restart_settings(self):
        restart_settings = self.settings["restart_settings"].Clone()
        restart_settings.AddValue("input_filename", self.settings["model_import_settings"]["input_filename"])
        restart_settings.AddValue("echo_level", self.settings["echo_level"])
        restart_settings.RemoveValue("load_restart")
        restart_settings.RemoveValue("save_restart")

        return restart_settings

    def _get_convergence_criterion_settings(self):
        # Create an auxiliary Kratos parameters object to store the convergence settings.
        conv_params = KratosMultiphysics.Parameters("{}")
        conv_params.AddValue("convergence_criterion",self.settings["convergence_criterion"])
        conv_params.AddValue("rotation_dofs",self.settings["rotation_dofs"])
        conv_params.AddValue("echo_level",self.settings["echo_level"])
        conv_params.AddValue("displacement_relative_tolerance",self.settings["displacement_relative_tolerance"])
        conv_params.AddValue("displacement_absolute_tolerance",self.settings["displacement_absolute_tolerance"])
        conv_params.AddValue("residual_relative_tolerance",self.settings["residual_relative_tolerance"])
        conv_params.AddValue("residual_absolute_tolerance",self.settings["residual_absolute_tolerance"])

        return conv_params

    def _create_convergence_criterion(self):
        import convergence_criteria_factory
        convergence_criterion = convergence_criteria_factory.convergence_criterion(self._get_convergence_criterion_settings())
        return convergence_criterion.mechanical_convergence_criterion

    def _create_linear_solver(self):
        import linear_solver_factory
        linear_solver = linear_solver_factory.ConstructSolver(self.settings["linear_solver_settings"])
        return linear_solver

    def _create_builder_and_solver(self):
        linear_solver = self.get_linear_solver()
        if self.settings["block_builder"].GetBool():
            if self.settings["multi_point_constraints_used"].GetBool():
                builder_and_solver = KratosMultiphysics.StructuralMechanicsApplication.ResidualBasedBlockBuilderAndSolverWithMpc(linear_solver)
            else:
                builder_and_solver = KratosMultiphysics.ResidualBasedBlockBuilderAndSolver(linear_solver)
        else:
            if self.settings["multi_point_constraints_used"].GetBool():
                raise Exception("To use MPCs you also have to set \"block_builder\" to \"true\"")
            builder_and_solver = KratosMultiphysics.ResidualBasedEliminationBuilderAndSolver(linear_solver)
        return builder_and_solver

    def _create_solution_scheme(self):
        """Create the solution scheme for the structural problem.
        """
        raise Exception("Solution Scheme creation must be implemented in the derived class.")

    def _create_mechanical_solution_strategy(self):
        analysis_type = self.settings["analysis_type"].GetString()
        if analysis_type == "linear":
            mechanical_solution_strategy = self._create_linear_strategy()
        elif analysis_type == "non_linear":
            if(self.settings["line_search"].GetBool() == False):
                mechanical_solution_strategy = self._create_newton_raphson_strategy()
            else:
                mechanical_solution_strategy = self._create_line_search_strategy()
        else:
            err_msg =  "The requested analysis type \"" + analysis_type + "\" is not available!\n"
            err_msg += "Available options are: \"linear\", \"non_linear\""
            raise Exception(err_msg)
        return mechanical_solution_strategy

    def _create_linear_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedLinearStrategy(computing_model_part,
                                                              mechanical_scheme,
                                                              linear_solver,
                                                              builder_and_solver,
                                                              self.settings["compute_reactions"].GetBool(),
                                                              self.settings["reform_dofs_at_each_step"].GetBool(),
                                                              False,
                                                              self.settings["move_mesh_flag"].GetBool())

    def _create_newton_raphson_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedNewtonRaphsonStrategy(computing_model_part,
                                                                     mechanical_scheme,
                                                                     linear_solver,
                                                                     mechanical_convergence_criterion,
                                                                     builder_and_solver,
                                                                     self.settings["max_iteration"].GetInt(),
                                                                     self.settings["compute_reactions"].GetBool(),
                                                                     self.settings["reform_dofs_at_each_step"].GetBool(),
                                                                     self.settings["move_mesh_flag"].GetBool())

    def _create_line_search_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.LineSearchStrategy(computing_model_part,
                                                     mechanical_scheme,
                                                     linear_solver,
                                                     mechanical_convergence_criterion,
                                                     builder_and_solver,
                                                     self.settings["max_iteration"].GetInt(),
                                                     self.settings["compute_reactions"].GetBool(),
                                                     self.settings["reform_dofs_at_each_step"].GetBool(),
                                                     self.settings["move_mesh_flag"].GetBool())

    def _create_restart_utility(self):
        """Create the restart utility. Has to be overridden for MPI/trilinos-solvers"""
        import restart_utility
        rest_utility = restart_utility.RestartUtility(self.main_model_part,
                                                      self._get_restart_settings())
        return rest_utility

214783ca919608c1fa5359f5a526b2b61c6f0cb7
<mask token>


class MechanicalSolver(object):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def ReadModelPart(self):
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'Reading model part.')
        problem_path = os.getcwd()
        input_filename = self.settings['model_import_settings'][
            'input_filename'].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif self.settings['model_import_settings']['input_type'].GetString(
            ) == 'mdpa':
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::', 
                'Reading model part from file: ' + os.path.join(
                problem_path, input_filename) + '.mdpa')
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self
                .main_model_part)
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
                'Finished reading model part from mdpa file.')
        else:
            raise Exception(
                'Other model part input options are not yet implemented.')
        KratosMultiphysics.Logger.PrintInfo('ModelPart', self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]:: ',
            'Finished reading model part.')

    def PrepareModelPartForSolver(self):
        if not self.is_restarted():
            self._execute_after_reading()
            self._set_and_fill_buffer()
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'ModelPart prepared for Solver.')
    <mask token>
    <mask token>

    def GetComputingModelPart(self):
        return self.main_model_part.GetSubModelPart(self.settings[
            'computing_model_part_name'].GetString())

    def GetOutputVariables(self):
        pass
    <mask token>

    def Solve(self):
        if self.settings['clear_storage'].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.Solve()
    <mask token>
    <mask token>

    def SolveSolutionStep(self):
        is_converged = self.get_mechanical_solution_strategy(
            ).SolveSolutionStep()
        return is_converged
    <mask token>

    def AdvanceInTime(self, current_time):
        dt = self.ComputeDeltaTime()
        new_time = current_time + dt
        self.main_model_part.ProcessInfo[KratosMultiphysics.STEP] += 1
        self.main_model_part.CloneTimeStep(new_time)
        return new_time

    def ComputeDeltaTime(self):
        return self.delta_time

    def SetDeltaTime(self, dt):
        self.delta_time = dt
    <mask token>

    def Clear(self):
        self.get_mechanical_solution_strategy().Clear()

    def Check(self):
        self.get_mechanical_solution_strategy().Check()
    <mask token>
    <mask token>

    def get_linear_solver(self):
        if not hasattr(self, '_linear_solver'):
            self._linear_solver = self._create_linear_solver()
        return self._linear_solver
    <mask token>
    <mask token>
    <mask token>

    def import_constitutive_laws(self):
        materials_filename = self.settings['material_import_settings'][
            'materials_filename'].GetString()
        if materials_filename != '':
            import read_materials_process
            Model = KratosMultiphysics.Model()
            Model.AddModelPart(self.main_model_part)
            read_materials_process.ReadMaterialsProcess(Model, self.
                settings['material_import_settings'])
            materials_imported = True
        else:
            materials_imported = False
        return materials_imported

    def validate_and_transfer_matching_settings(self, origin_settings,
        destination_settings):
        """Transfer matching settings from origin to destination.

        If a name in origin matches a name in destination, then the setting is
        validated against the destination.

        The typical use is for validating and extracting settings in derived classes:

        class A:
            def __init__(self, model_part, a_settings):
                default_a_settings = Parameters('''{
                    ...
                }''')
                a_settings.ValidateAndAssignDefaults(default_a_settings)
        class B(A):
            def __init__(self, model_part, custom_settings):
                b_settings = Parameters('''{
                    ...
                }''') # Here the settings contain default values.
                self.validate_and_transfer_matching_settings(custom_settings, b_settings)
                super().__init__(model_part, custom_settings)
        """
        for name, dest_value in destination_settings.items():
            if origin_settings.Has(name):
                orig_value = origin_settings[name]
                if dest_value.IsDouble() and orig_value.IsDouble():
                    destination_settings[name].SetDouble(origin_settings[
                        name].GetDouble())
                elif dest_value.IsInt() and orig_value.IsInt():
                    destination_settings[name].SetInt(origin_settings[name]
                        .GetInt())
                elif dest_value.IsBool() and orig_value.IsBool():
                    destination_settings[name].SetBool(origin_settings[name
                        ].GetBool())
                elif dest_value.IsString() and orig_value.IsString():
                    destination_settings[name].SetString(origin_settings[
                        name].GetString())
                elif dest_value.IsArray() and orig_value.IsArray():
                    if dest_value.size() != orig_value.size():
                        raise Exception('len("' + name + '") != ' + str(
                            dest_value.size()))
                    for i in range(dest_value.size()):
                        if dest_value[i].IsDouble() and orig_value[i].IsDouble(
                            ):
                            dest_value[i].SetDouble(orig_value[i].GetDouble())
                        elif dest_value[i].IsInt() and orig_value[i].IsInt():
                            dest_value[i].SetInt(orig_value[i].GetInt())
                        elif dest_value[i].IsBool() and orig_value[i].IsBool():
                            dest_value[i].SetBool(orig_value[i].GetBool())
                        elif dest_value[i].IsString() and orig_value[i
                            ].IsString():
                            dest_value[i].SetString(orig_value[i].GetString())
                        elif dest_value[i].IsSubParameter() and orig_value[i
                            ].IsSubParameter():
                            self.validate_and_transfer_matching_settings(
                                orig_value[i], dest_value[i])
                            if len(orig_value[i].items()) != 0:
                                raise Exception(
                                    'Json settings not found in default settings: '
                                     + orig_value[i].PrettyPrintJsonString())
                        else:
                            raise Exception('Unsupported parameter type.')
                elif dest_value.IsSubParameter() and orig_value.IsSubParameter(
                    ):
                    self.validate_and_transfer_matching_settings(orig_value,
                        dest_value)
                    if len(orig_value.items()) != 0:
                        raise Exception(
                            'Json settings not found in default settings: ' +
                            orig_value.PrettyPrintJsonString())
                else:
                    raise Exception('Unsupported parameter type.')
                origin_settings.RemoveValue(name)
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def _set_and_fill_buffer(self):
        """Prepare nodal solution step data containers and time step information. """
        required_buffer_size = self.settings['buffer_size'].GetInt()
        if required_buffer_size < self.GetMinimumBufferSize():
            required_buffer_size = self.GetMinimumBufferSize()
        current_buffer_size = self.main_model_part.GetBufferSize()
        buffer_size = max(current_buffer_size, required_buffer_size)
        self.main_model_part.SetBufferSize(buffer_size)
        delta_time = self.main_model_part.ProcessInfo[KratosMultiphysics.
            DELTA_TIME]
        time = self.main_model_part.ProcessInfo[KratosMultiphysics.TIME]
        step = -buffer_size
        time = time - delta_time * buffer_size
        self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.TIME, time
            )
        for i in range(0, buffer_size):
            step = step + 1
            time = time + delta_time
            self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.
                STEP, step)
            self.main_model_part.CloneTimeStep(time)
    <mask token>

    def _add_dynamic_dofs(self):
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Z, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Z, self.main_model_part)
        if self.settings['rotation_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Z, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Z, self.main_model_part)

    def _get_restart_settings(self):
        restart_settings = self.settings['restart_settings'].Clone()
        restart_settings.AddValue('input_filename', self.settings[
            'model_import_settings']['input_filename'])
        restart_settings.AddValue('echo_level', self.settings['echo_level'])
        restart_settings.RemoveValue('load_restart')
        restart_settings.RemoveValue('save_restart')
        return restart_settings

    def _get_convergence_criterion_settings(self):
        conv_params = KratosMultiphysics.Parameters('{}')
        conv_params.AddValue('convergence_criterion', self.settings[
            'convergence_criterion'])
        conv_params.AddValue('rotation_dofs', self.settings['rotation_dofs'])
        conv_params.AddValue('echo_level', self.settings['echo_level'])
        conv_params.AddValue('displacement_relative_tolerance', self.
            settings['displacement_relative_tolerance'])
        conv_params.AddValue('displacement_absolute_tolerance', self.
            settings['displacement_absolute_tolerance'])
        conv_params.AddValue('residual_relative_tolerance', self.settings[
            'residual_relative_tolerance'])
        conv_params.AddValue('residual_absolute_tolerance', self.settings[
            'residual_absolute_tolerance'])
        return conv_params

    def _create_convergence_criterion(self):
        import convergence_criteria_factory
        convergence_criterion = (convergence_criteria_factory.
            convergence_criterion(self._get_convergence_criterion_settings()))
        return convergence_criterion.mechanical_convergence_criterion

    def _create_linear_solver(self):
        import linear_solver_factory
        linear_solver = linear_solver_factory.ConstructSolver(self.settings
            ['linear_solver_settings'])
        return linear_solver

    def _create_builder_and_solver(self):
        linear_solver = self.get_linear_solver()
        if self.settings['block_builder'].GetBool():
            if self.settings['multi_point_constraints_used'].GetBool():
                builder_and_solver = (KratosMultiphysics.
                    StructuralMechanicsApplication.
                    ResidualBasedBlockBuilderAndSolverWithMpc(linear_solver))
            else:
                builder_and_solver = (KratosMultiphysics.
                    ResidualBasedBlockBuilderAndSolver(linear_solver))
        else:
            if self.settings['multi_point_constraints_used'].GetBool():
                raise Exception(
                    'To use MPCs you also have to set "block_builder" to "true"'
                    )
            builder_and_solver = (KratosMultiphysics.
                ResidualBasedEliminationBuilderAndSolver(linear_solver))
        return builder_and_solver

    def _create_solution_scheme(self):
        """Create the solution scheme for the structural problem.
        """
        raise Exception(
            'Solution Scheme creation must be implemented in the derived class.'
            )

    def _create_mechanical_solution_strategy(self):
        analysis_type = self.settings['analysis_type'].GetString()
        if analysis_type == 'linear':
            mechanical_solution_strategy = self._create_linear_strategy()
        elif analysis_type == 'non_linear':
            if self.settings['line_search'].GetBool() == False:
                mechanical_solution_strategy = (self.
                    _create_newton_raphson_strategy())
            else:
                mechanical_solution_strategy = (self.
                    _create_line_search_strategy())
        else:
            err_msg = ('The requested analysis type "' + analysis_type +
                '" is not available!\n')
            err_msg += 'Available options are: "linear", "non_linear"'
            raise Exception(err_msg)
        return mechanical_solution_strategy

    def _create_linear_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedLinearStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            builder_and_solver, self.settings['compute_reactions'].GetBool(
            ), self.settings['reform_dofs_at_each_step'].GetBool(), False,
            self.settings['move_mesh_flag'].GetBool())

    def _create_newton_raphson_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedNewtonRaphsonStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            mechanical_convergence_criterion, builder_and_solver, self.
            settings['max_iteration'].GetInt(), self.settings[
            'compute_reactions'].GetBool(), self.settings[
            'reform_dofs_at_each_step'].GetBool(), self.settings[
            'move_mesh_flag'].GetBool())
    <mask token>
    <mask token>

<mask token>


class MechanicalSolver(object):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def ReadModelPart(self):
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'Reading model part.')
        problem_path = os.getcwd()
        input_filename = self.settings['model_import_settings'][
            'input_filename'].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif self.settings['model_import_settings']['input_type'].GetString(
            ) == 'mdpa':
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::', 
                'Reading model part from file: ' + os.path.join(
                problem_path, input_filename) + '.mdpa')
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self
                .main_model_part)
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
                'Finished reading model part from mdpa file.')
        else:
            raise Exception(
                'Other model part input options are not yet implemented.')
        KratosMultiphysics.Logger.PrintInfo('ModelPart', self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]:: ',
            'Finished reading model part.')

    def PrepareModelPartForSolver(self):
        if not self.is_restarted():
            self._execute_after_reading()
            self._set_and_fill_buffer()
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'ModelPart prepared for Solver.')
    <mask token>
    <mask token>

    def GetComputingModelPart(self):
        return self.main_model_part.GetSubModelPart(self.settings[
            'computing_model_part_name'].GetString())

    def GetOutputVariables(self):
        pass
    <mask token>

    def Solve(self):
        if self.settings['clear_storage'].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.Solve()
    <mask token>
    <mask token>

    def SolveSolutionStep(self):
        is_converged = self.get_mechanical_solution_strategy(
            ).SolveSolutionStep()
        return is_converged
    <mask token>

    def AdvanceInTime(self, current_time):
        dt = self.ComputeDeltaTime()
        new_time = current_time + dt
        self.main_model_part.ProcessInfo[KratosMultiphysics.STEP] += 1
        self.main_model_part.CloneTimeStep(new_time)
        return new_time

    def ComputeDeltaTime(self):
        return self.delta_time

    def SetDeltaTime(self, dt):
        self.delta_time = dt
    <mask token>

    def Clear(self):
        self.get_mechanical_solution_strategy().Clear()

    def Check(self):
        self.get_mechanical_solution_strategy().Check()
    <mask token>
    <mask token>

    def get_linear_solver(self):
        if not hasattr(self, '_linear_solver'):
            self._linear_solver = self._create_linear_solver()
        return self._linear_solver
    <mask token>
    <mask token>
    <mask token>

    def import_constitutive_laws(self):
        materials_filename = self.settings['material_import_settings'][
            'materials_filename'].GetString()
        if materials_filename != '':
            import read_materials_process
            Model = KratosMultiphysics.Model()
            Model.AddModelPart(self.main_model_part)
            read_materials_process.ReadMaterialsProcess(Model, self.
                settings['material_import_settings'])
            materials_imported = True
        else:
            materials_imported = False
        return materials_imported

    def validate_and_transfer_matching_settings(self, origin_settings,
        destination_settings):
        """Transfer matching settings from origin to destination.

        If a name in origin matches a name in destination, then the setting is
        validated against the destination.

        The typical use is for validating and extracting settings in derived classes:

        class A:
            def __init__(self, model_part, a_settings):
                default_a_settings = Parameters('''{
                    ...
                }''')
                a_settings.ValidateAndAssignDefaults(default_a_settings)
        class B(A):
            def __init__(self, model_part, custom_settings):
                b_settings = Parameters('''{
                    ...
                }''') # Here the settings contain default values.
                self.validate_and_transfer_matching_settings(custom_settings, b_settings)
                super().__init__(model_part, custom_settings)
        """
        for name, dest_value in destination_settings.items():
            if origin_settings.Has(name):
                orig_value = origin_settings[name]
                if dest_value.IsDouble() and orig_value.IsDouble():
                    destination_settings[name].SetDouble(origin_settings[
                        name].GetDouble())
                elif dest_value.IsInt() and orig_value.IsInt():
                    destination_settings[name].SetInt(origin_settings[name]
                        .GetInt())
                elif dest_value.IsBool() and orig_value.IsBool():
                    destination_settings[name].SetBool(origin_settings[name
                        ].GetBool())
                elif dest_value.IsString() and orig_value.IsString():
                    destination_settings[name].SetString(origin_settings[
                        name].GetString())
                elif dest_value.IsArray() and orig_value.IsArray():
                    if dest_value.size() != orig_value.size():
                        raise Exception('len("' + name + '") != ' + str(
                            dest_value.size()))
                    for i in range(dest_value.size()):
                        if dest_value[i].IsDouble() and orig_value[i].IsDouble(
                            ):
                            dest_value[i].SetDouble(orig_value[i].GetDouble())
                        elif dest_value[i].IsInt() and orig_value[i].IsInt():
                            dest_value[i].SetInt(orig_value[i].GetInt())
                        elif dest_value[i].IsBool() and orig_value[i].IsBool():
                            dest_value[i].SetBool(orig_value[i].GetBool())
                        elif dest_value[i].IsString() and orig_value[i
                            ].IsString():
                            dest_value[i].SetString(orig_value[i].GetString())
                        elif dest_value[i].IsSubParameter() and orig_value[i
                            ].IsSubParameter():
                            self.validate_and_transfer_matching_settings(
                                orig_value[i], dest_value[i])
                            if len(orig_value[i].items()) != 0:
                                raise Exception(
                                    'Json settings not found in default settings: '
                                     + orig_value[i].PrettyPrintJsonString())
                        else:
                            raise Exception('Unsupported parameter type.')
                elif dest_value.IsSubParameter() and orig_value.IsSubParameter(
                    ):
                    self.validate_and_transfer_matching_settings(orig_value,
                        dest_value)
                    if len(orig_value.items()) != 0:
                        raise Exception(
                            'Json settings not found in default settings: ' +
                            orig_value.PrettyPrintJsonString())
                else:
                    raise Exception('Unsupported parameter type.')
                origin_settings.RemoveValue(name)
    <mask token>
    <mask token>
    <mask token>

    def _execute_after_reading(self):
        """Prepare computing model part and import constitutive laws. """
        params = KratosMultiphysics.Parameters('{}')
        params.AddValue('computing_model_part_name', self.settings[
            'computing_model_part_name'])
        params.AddValue('problem_domain_sub_model_part_list', self.settings
            ['problem_domain_sub_model_part_list'])
        params.AddValue('processes_sub_model_part_list', self.settings[
            'processes_sub_model_part_list'])
        import check_and_prepare_model_process_structural
        check_and_prepare_model_process_structural.CheckAndPrepareModelProcess(
            self.main_model_part, params).Execute()
        materials_imported = self.import_constitutive_laws()
        if materials_imported:
            self.print_on_rank_zero('::[MechanicalSolver]:: ',
                'Constitutive law was successfully imported.')
        else:
            self.print_on_rank_zero('::[MechanicalSolver]:: ',
                'Constitutive law was not imported.')

    def _set_and_fill_buffer(self):
        """Prepare nodal solution step data containers and time step information. """
        required_buffer_size = self.settings['buffer_size'].GetInt()
        if required_buffer_size < self.GetMinimumBufferSize():
            required_buffer_size = self.GetMinimumBufferSize()
        current_buffer_size = self.main_model_part.GetBufferSize()
        buffer_size = max(current_buffer_size, required_buffer_size)
        self.main_model_part.SetBufferSize(buffer_size)
        delta_time = self.main_model_part.ProcessInfo[KratosMultiphysics.
            DELTA_TIME]
        time = self.main_model_part.ProcessInfo[KratosMultiphysics.TIME]
        step = -buffer_size
        time = time - delta_time * buffer_size
        self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.TIME, time
            )
        for i in range(0, buffer_size):
            step = step + 1
            time = time + delta_time
            self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.
                STEP, step)
            self.main_model_part.CloneTimeStep(time)
    <mask token>

    def _add_dynamic_dofs(self):
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Z, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Z, self.main_model_part)
        if self.settings['rotation_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Z, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Z, self.main_model_part)

    def _get_restart_settings(self):
        restart_settings = self.settings['restart_settings'].Clone()
        restart_settings.AddValue('input_filename', self.settings[
            'model_import_settings']['input_filename'])
        restart_settings.AddValue('echo_level', self.settings['echo_level'])
        restart_settings.RemoveValue('load_restart')
        restart_settings.RemoveValue('save_restart')
        return restart_settings

    def _get_convergence_criterion_settings(self):
        conv_params = KratosMultiphysics.Parameters('{}')
        conv_params.AddValue('convergence_criterion', self.settings[
            'convergence_criterion'])
        conv_params.AddValue('rotation_dofs', self.settings['rotation_dofs'])
        conv_params.AddValue('echo_level', self.settings['echo_level'])
        conv_params.AddValue('displacement_relative_tolerance', self.
            settings['displacement_relative_tolerance'])
        conv_params.AddValue('displacement_absolute_tolerance', self.
            settings['displacement_absolute_tolerance'])
        conv_params.AddValue('residual_relative_tolerance', self.settings[
            'residual_relative_tolerance'])
        conv_params.AddValue('residual_absolute_tolerance', self.settings[
            'residual_absolute_tolerance'])
        return conv_params

    def _create_convergence_criterion(self):
        import convergence_criteria_factory
        convergence_criterion = (convergence_criteria_factory.
            convergence_criterion(self._get_convergence_criterion_settings()))
        return convergence_criterion.mechanical_convergence_criterion

    def _create_linear_solver(self):
        import linear_solver_factory
        linear_solver = linear_solver_factory.ConstructSolver(self.settings
            ['linear_solver_settings'])
        return linear_solver

    def _create_builder_and_solver(self):
        linear_solver = self.get_linear_solver()
        if self.settings['block_builder'].GetBool():
            if self.settings['multi_point_constraints_used'].GetBool():
                builder_and_solver = (KratosMultiphysics.
                    StructuralMechanicsApplication.
                    ResidualBasedBlockBuilderAndSolverWithMpc(linear_solver))
            else:
                builder_and_solver = (KratosMultiphysics.
                    ResidualBasedBlockBuilderAndSolver(linear_solver))
        else:
            if self.settings['multi_point_constraints_used'].GetBool():
                raise Exception(
                    'To use MPCs you also have to set "block_builder" to "true"'
                    )
            builder_and_solver = (KratosMultiphysics.
                ResidualBasedEliminationBuilderAndSolver(linear_solver))
        return builder_and_solver

    def _create_solution_scheme(self):
        """Create the solution scheme for the structural problem.
        """
        raise Exception(
            'Solution Scheme creation must be implemented in the derived class.'
            )

    def _create_mechanical_solution_strategy(self):
        analysis_type = self.settings['analysis_type'].GetString()
        if analysis_type == 'linear':
            mechanical_solution_strategy = self._create_linear_strategy()
        elif analysis_type == 'non_linear':
            if self.settings['line_search'].GetBool() == False:
                mechanical_solution_strategy = (self.
                    _create_newton_raphson_strategy())
            else:
                mechanical_solution_strategy = (self.
                    _create_line_search_strategy())
        else:
            err_msg = ('The requested analysis type "' + analysis_type +
                '" is not available!\n')
            err_msg += 'Available options are: "linear", "non_linear"'
            raise Exception(err_msg)
        return mechanical_solution_strategy

    def _create_linear_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedLinearStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            builder_and_solver, self.settings['compute_reactions'].GetBool(
            ), self.settings['reform_dofs_at_each_step'].GetBool(), False,
            self.settings['move_mesh_flag'].GetBool())

    def _create_newton_raphson_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedNewtonRaphsonStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            mechanical_convergence_criterion, builder_and_solver, self.
            settings['max_iteration'].GetInt(), self.settings[
            'compute_reactions'].GetBool(), self.settings[
            'reform_dofs_at_each_step'].GetBool(), self.settings[
            'move_mesh_flag'].GetBool())
    <mask token>
    <mask token>

<mask token>


class MechanicalSolver(object):
    <mask token>
    <mask token>
    <mask token>

    def GetMinimumBufferSize(self):
        return 2

    def AddDofs(self):
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            DISPLACEMENT_X, KratosMultiphysics.REACTION_X, self.main_model_part
            )
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            DISPLACEMENT_Y, KratosMultiphysics.REACTION_Y, self.main_model_part
            )
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            DISPLACEMENT_Z, KratosMultiphysics.REACTION_Z, self.main_model_part
            )
        if self.settings['rotation_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ROTATION_X, KratosMultiphysics.REACTION_MOMENT_X, self.
                main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ROTATION_Y, KratosMultiphysics.REACTION_MOMENT_Y, self.
                main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ROTATION_Z, KratosMultiphysics.REACTION_MOMENT_Z, self.
                main_model_part)
        if self.settings['pressure_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                PRESSURE, KratosMultiphysics.PRESSURE_REACTION, self.
                main_model_part)
        self.print_on_rank_zero('::[MechanicalSolver]:: ', "DOF's ADDED")

    def ImportModelPart(self):
        """ Legacy function, use ReadModelPart and PrepareModelPartForSolver instead """
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'Importing model part.')
        problem_path = os.getcwd()
        input_filename = self.settings['model_import_settings'][
            'input_filename'].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif self.settings['model_import_settings']['input_type'].GetString(
            ) == 'mdpa':
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::', 
                'Reading model part from file: ' + os.path.join(
                problem_path, input_filename) + '.mdpa')
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self
                .main_model_part)
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
                'Finished reading model part from mdpa file.')
            self.PrepareModelPartForSolver()
        else:
            raise Exception(
                'Other model part input options are not yet implemented.')
        KratosMultiphysics.Logger.PrintInfo('ModelPart', self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]:: ',
            'Finished importing model part.')

    def ReadModelPart(self):
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'Reading model part.')
        problem_path = os.getcwd()
        input_filename = self.settings['model_import_settings'][
            'input_filename'].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif self.settings['model_import_settings']['input_type'].GetString(
            ) == 'mdpa':
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::', 
                'Reading model part from file: ' + os.path.join(
                problem_path, input_filename) + '.mdpa')
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self
                .main_model_part)
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
                'Finished reading model part from mdpa file.')
        else:
            raise Exception(
                'Other model part input options are not yet implemented.')
        KratosMultiphysics.Logger.PrintInfo('ModelPart', self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]:: ',
            'Finished reading model part.')

    def PrepareModelPartForSolver(self):
        if not self.is_restarted():
            self._execute_after_reading()
            self._set_and_fill_buffer()
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'ModelPart prepared for Solver.')
    <mask token>

    def Initialize(self):
        """Perform initialization after adding nodal variables and dofs to the main model part. """
        self.print_on_rank_zero('::[MechanicalSolver]:: ', 'Initializing ...')
        if self.settings['clear_storage'].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.SetEchoLevel(self.settings[
            'echo_level'].GetInt())
        if not self.is_restarted():
            mechanical_solution_strategy.Initialize()
        else:
            try:
                mechanical_solution_strategy.SetInitializePerformedFlag(True)
            except AttributeError:
                pass
        self.Check()
        self.print_on_rank_zero('::[MechanicalSolver]:: ',
            'Finished initialization.')

    def GetComputingModelPart(self):
        return self.main_model_part.GetSubModelPart(self.settings[
            'computing_model_part_name'].GetString())

    def GetOutputVariables(self):
        pass
    <mask token>

    def Solve(self):
        if self.settings['clear_storage'].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.Solve()

    def InitializeSolutionStep(self):
        self.get_mechanical_solution_strategy().InitializeSolutionStep()

    def Predict(self):
        self.get_mechanical_solution_strategy().Predict()

    def SolveSolutionStep(self):
        is_converged = self.get_mechanical_solution_strategy(
            ).SolveSolutionStep()
        return is_converged

    def FinalizeSolutionStep(self):
        self.get_mechanical_solution_strategy().FinalizeSolutionStep()

    def AdvanceInTime(self, current_time):
        dt = self.ComputeDeltaTime()
        new_time = current_time + dt
        self.main_model_part.ProcessInfo[KratosMultiphysics.STEP] += 1
        self.main_model_part.CloneTimeStep(new_time)
        return new_time

    def ComputeDeltaTime(self):
        return self.delta_time

    def SetDeltaTime(self, dt):
        self.delta_time = dt

    def SetEchoLevel(self, level):
        self.get_mechanical_solution_strategy().SetEchoLevel(level)

    def Clear(self):
        self.get_mechanical_solution_strategy().Clear()

    def Check(self):
        self.get_mechanical_solution_strategy().Check()

    def get_solution_scheme(self):
        if not hasattr(self, '_solution_scheme'):
            self._solution_scheme = self._create_solution_scheme()
        return self._solution_scheme

    def get_convergence_criterion(self):
        if not hasattr(self, '_convergence_criterion'):
            self._convergence_criterion = self._create_convergence_criterion()
        return self._convergence_criterion

    def get_linear_solver(self):
        if not hasattr(self, '_linear_solver'):
            self._linear_solver = self._create_linear_solver()
        return self._linear_solver
    <mask token>
    <mask token>

    def get_restart_utility(self):
        if not hasattr(self, '_restart_utility'):
            self._restart_utility = self._create_restart_utility()
        return self._restart_utility

    def import_constitutive_laws(self):
        materials_filename = self.settings['material_import_settings'][
            'materials_filename'].GetString()
        if materials_filename != '':
            import read_materials_process
            Model = KratosMultiphysics.Model()
            Model.AddModelPart(self.main_model_part)
            read_materials_process.ReadMaterialsProcess(Model, self.
                settings['material_import_settings'])
            materials_imported = True
        else:
            materials_imported = False
        return materials_imported

    def validate_and_transfer_matching_settings(self, origin_settings,
        destination_settings):
        """Transfer matching settings from origin to destination.

        If a name in origin matches a name in destination, then the setting is
        validated against the destination.

        The typical use is for validating and extracting settings in derived classes:

        class A:
            def __init__(self, model_part, a_settings):
                default_a_settings = Parameters('''{
                    ...
                }''')
                a_settings.ValidateAndAssignDefaults(default_a_settings)
        class B(A):
            def __init__(self, model_part, custom_settings):
                b_settings = Parameters('''{
                    ...
                }''') # Here the settings contain default values.
                self.validate_and_transfer_matching_settings(custom_settings, b_settings)
                super().__init__(model_part, custom_settings)
        """
        for name, dest_value in destination_settings.items():
            if origin_settings.Has(name):
                orig_value = origin_settings[name]
                if dest_value.IsDouble() and orig_value.IsDouble():
                    destination_settings[name].SetDouble(origin_settings[
                        name].GetDouble())
                elif dest_value.IsInt() and orig_value.IsInt():
                    destination_settings[name].SetInt(origin_settings[name]
                        .GetInt())
                elif dest_value.IsBool() and orig_value.IsBool():
                    destination_settings[name].SetBool(origin_settings[name
                        ].GetBool())
                elif dest_value.IsString() and orig_value.IsString():
                    destination_settings[name].SetString(origin_settings[
                        name].GetString())
                elif dest_value.IsArray() and orig_value.IsArray():
                    if dest_value.size() != orig_value.size():
                        raise Exception('len("' + name + '") != ' + str(
                            dest_value.size()))
                    for i in range(dest_value.size()):
                        if dest_value[i].IsDouble() and orig_value[i].IsDouble(
                            ):
                            dest_value[i].SetDouble(orig_value[i].GetDouble())
                        elif dest_value[i].IsInt() and orig_value[i].IsInt():
                            dest_value[i].SetInt(orig_value[i].GetInt())
                        elif dest_value[i].IsBool() and orig_value[i].IsBool():
                            dest_value[i].SetBool(orig_value[i].GetBool())
                        elif dest_value[i].IsString() and orig_value[i
                            ].IsString():
                            dest_value[i].SetString(orig_value[i].GetString())
                        elif dest_value[i].IsSubParameter() and orig_value[i
                            ].IsSubParameter():
                            self.validate_and_transfer_matching_settings(
                                orig_value[i], dest_value[i])
                            if len(orig_value[i].items()) != 0:
                                raise Exception(
                                    'Json settings not found in default settings: '
                                     + orig_value[i].PrettyPrintJsonString())
                        else:
                            raise Exception('Unsupported parameter type.')
                elif dest_value.IsSubParameter() and orig_value.IsSubParameter(
                    ):
                    self.validate_and_transfer_matching_settings(orig_value,
                        dest_value)
                    if len(orig_value.items()) != 0:
                        raise Exception(
                            'Json settings not found in default settings: ' +
                            orig_value.PrettyPrintJsonString())
                else:
                    raise Exception('Unsupported parameter type.')
                origin_settings.RemoveValue(name)

    def is_restarted(self):
        return self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED
            ]

    def print_on_rank_zero(self, *args):
        KratosMultiphysics.Logger.PrintInfo(' '.join(map(str, args)))
    <mask token>

    def _execute_after_reading(self):
        """Prepare computing model part and import constitutive laws. """
        params = KratosMultiphysics.Parameters('{}')
        params.AddValue('computing_model_part_name', self.settings[
            'computing_model_part_name'])
        params.AddValue('problem_domain_sub_model_part_list', self.settings
            ['problem_domain_sub_model_part_list'])
        params.AddValue('processes_sub_model_part_list', self.settings[
            'processes_sub_model_part_list'])
        import check_and_prepare_model_process_structural
        check_and_prepare_model_process_structural.CheckAndPrepareModelProcess(
            self.main_model_part, params).Execute()
        materials_imported = self.import_constitutive_laws()
        if materials_imported:
            self.print_on_rank_zero('::[MechanicalSolver]:: ',
                'Constitutive law was successfully imported.')
        else:
            self.print_on_rank_zero('::[MechanicalSolver]:: ',
                'Constitutive law was not imported.')

    def _set_and_fill_buffer(self):
        """Prepare nodal solution step data containers and time step information. """
        required_buffer_size = self.settings['buffer_size'].GetInt()
        if required_buffer_size < self.GetMinimumBufferSize():
            required_buffer_size = self.GetMinimumBufferSize()
        current_buffer_size = self.main_model_part.GetBufferSize()
        buffer_size = max(current_buffer_size, required_buffer_size)
        self.main_model_part.SetBufferSize(buffer_size)
        delta_time = self.main_model_part.ProcessInfo[KratosMultiphysics.
            DELTA_TIME]
        time = self.main_model_part.ProcessInfo[KratosMultiphysics.TIME]
        step = -buffer_size
        time = time - delta_time * buffer_size
        self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.TIME, time
            )
        for i in range(0, buffer_size):
            step = step + 1
            time = time + delta_time
            self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.
                STEP, step)
            self.main_model_part.CloneTimeStep(time)

    def _add_dynamic_variables(self):
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .VELOCITY)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .ACCELERATION)
        if self.settings['rotation_dofs'].GetBool():
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.ANGULAR_VELOCITY)
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.ANGULAR_ACCELERATION)

    def _add_dynamic_dofs(self):
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Z, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Z, self.main_model_part)
        if self.settings['rotation_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Z, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Z, self.main_model_part)

    def _get_restart_settings(self):
        restart_settings = self.settings['restart_settings'].Clone()
        restart_settings.AddValue('input_filename', self.settings[
            'model_import_settings']['input_filename'])
        restart_settings.AddValue('echo_level', self.settings['echo_level'])
        restart_settings.RemoveValue('load_restart')
        restart_settings.RemoveValue('save_restart')
        return restart_settings

    def _get_convergence_criterion_settings(self):
        conv_params = KratosMultiphysics.Parameters('{}')
        conv_params.AddValue('convergence_criterion', self.settings[
            'convergence_criterion'])
        conv_params.AddValue('rotation_dofs', self.settings['rotation_dofs'])
        conv_params.AddValue('echo_level', self.settings['echo_level'])
        conv_params.AddValue('displacement_relative_tolerance', self.
            settings['displacement_relative_tolerance'])
        conv_params.AddValue('displacement_absolute_tolerance', self.
            settings['displacement_absolute_tolerance'])
        conv_params.AddValue('residual_relative_tolerance', self.settings[
            'residual_relative_tolerance'])
        conv_params.AddValue('residual_absolute_tolerance', self.settings[
            'residual_absolute_tolerance'])
        return conv_params

    def _create_convergence_criterion(self):
        import convergence_criteria_factory
        convergence_criterion = (convergence_criteria_factory.
            convergence_criterion(self._get_convergence_criterion_settings()))
        return convergence_criterion.mechanical_convergence_criterion

    def _create_linear_solver(self):
        import linear_solver_factory
        linear_solver = linear_solver_factory.ConstructSolver(self.settings
            ['linear_solver_settings'])
        return linear_solver

    def _create_builder_and_solver(self):
        linear_solver = self.get_linear_solver()
        if self.settings['block_builder'].GetBool():
            if self.settings['multi_point_constraints_used'].GetBool():
                builder_and_solver = (KratosMultiphysics.
                    StructuralMechanicsApplication.
                    ResidualBasedBlockBuilderAndSolverWithMpc(linear_solver))
            else:
                builder_and_solver = (KratosMultiphysics.
                    ResidualBasedBlockBuilderAndSolver(linear_solver))
        else:
            if self.settings['multi_point_constraints_used'].GetBool():
                raise Exception(
                    'To use MPCs you also have to set "block_builder" to "true"'
                    )
            builder_and_solver = (KratosMultiphysics.
                ResidualBasedEliminationBuilderAndSolver(linear_solver))
        return builder_and_solver

    def _create_solution_scheme(self):
        """Create the solution scheme for the structural problem.
        """
        raise Exception(
            'Solution Scheme creation must be implemented in the derived class.'
            )

    def _create_mechanical_solution_strategy(self):
        analysis_type = self.settings['analysis_type'].GetString()
        if analysis_type == 'linear':
            mechanical_solution_strategy = self._create_linear_strategy()
        elif analysis_type == 'non_linear':
            if self.settings['line_search'].GetBool() == False:
                mechanical_solution_strategy = (self.
                    _create_newton_raphson_strategy())
            else:
                mechanical_solution_strategy = (self.
                    _create_line_search_strategy())
        else:
            err_msg = ('The requested analysis type "' + analysis_type +
                '" is not available!\n')
            err_msg += 'Available options are: "linear", "non_linear"'
            raise Exception(err_msg)
        return mechanical_solution_strategy

    def _create_linear_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedLinearStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            builder_and_solver, self.settings['compute_reactions'].GetBool(
            ), self.settings['reform_dofs_at_each_step'].GetBool(), False,
            self.settings['move_mesh_flag'].GetBool())

    def _create_newton_raphson_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedNewtonRaphsonStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            mechanical_convergence_criterion, builder_and_solver, self.
            settings['max_iteration'].GetInt(), self.settings[
            'compute_reactions'].GetBool(), self.settings[
            'reform_dofs_at_each_step'].GetBool(), self.settings[
            'move_mesh_flag'].GetBool())
    <mask token>

    def _create_restart_utility(self):
        """Create the restart utility. Has to be overridden for MPI/trilinos-solvers"""
        import restart_utility
        rest_utility = restart_utility.RestartUtility(self.main_model_part,
            self._get_restart_settings())
        return rest_utility

<mask token>


class MechanicalSolver(object):
    <mask token>

    def __init__(self, main_model_part, custom_settings):
        default_settings = KratosMultiphysics.Parameters(
            """
        {
            "echo_level": 0,
            "buffer_size": 2,
            "analysis_type": "non_linear",
            "model_import_settings": {
                "input_type": "mdpa",
                "input_filename": "unknown_name"
            },
            "restart_settings" : {
                "load_restart"  : false,
                "save_restart"  : false
            },
            "computing_model_part_name" : "computing_domain",
            "material_import_settings" :{
                "materials_filename": ""
            },
            "rotation_dofs": false,
            "pressure_dofs": false,
            "reform_dofs_at_each_step": false,
            "line_search": false,
            "compute_reactions": true,
            "block_builder": true,
            "clear_storage": false,
            "move_mesh_flag": true,
            "multi_point_constraints_used": false,
            "convergence_criterion": "residual_criterion",
            "displacement_relative_tolerance": 1.0e-4,
            "displacement_absolute_tolerance": 1.0e-9,
            "residual_relative_tolerance": 1.0e-4,
            "residual_absolute_tolerance": 1.0e-9,
            "max_iteration": 10,
            "linear_solver_settings":{
                "solver_type": "SuperLUSolver",
                "max_iteration": 500,
                "tolerance": 1e-9,
                "scaling": false,
                "verbosity": 1
            },
            "problem_domain_sub_model_part_list": ["solid"],
            "processes_sub_model_part_list": [""],
            "auxiliary_variables_list" : []
        }
        """
            )
        if custom_settings.Has('bodies_list'):
            custom_settings.RemoveValue('bodies_list')
            warning = (
                '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "bodies_list", '
                )
            warning += """which is deprecated and will be removed soon. 
Please remove it from the "solver settings"!
"""
            self.print_warning_on_rank_zero('Bodies list', warning)
        if custom_settings.Has('solver_type'):
            custom_settings.RemoveValue('solver_type')
            warning = (
                '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "solver_type", '
                )
            warning += """which is only needed if you use the "python_solvers_wrapper_structural". 
Please remove it """
            warning += """from the "solver settings" if you dont use this wrapper, this check will be removed soon!
"""
            self.print_warning_on_rank_zero('Solver type', warning)
        if custom_settings.Has('time_integration_method'):
            custom_settings.RemoveValue('time_integration_method')
            warning = """
::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "time_integration_method", """
            warning += """which is only needed if you use the "python_solvers_wrapper_structural". 
Please remove it """
            warning += """from the "solver settings" if you dont use this wrapper, this check will be removed soon!
"""
            self.print_warning_on_rank_zero('Time integration method', warning)
        self.settings = custom_settings
        self.settings.ValidateAndAssignDefaults(default_settings)
        self.main_model_part = main_model_part
        self.print_on_rank_zero('::[MechanicalSolver]:: ',
            'Construction finished')
        if self.settings['restart_settings'].Has('load_restart'):
            load_restart = self.settings['restart_settings']['load_restart'
                ].GetBool()
            self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED
                ] = load_restart
        else:
            self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED
                ] = False

    def AddVariables(self):
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .DISPLACEMENT)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .REACTION)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .POSITIVE_FACE_PRESSURE)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .NEGATIVE_FACE_PRESSURE)
        self.main_model_part.AddNodalSolutionStepVariable(
            StructuralMechanicsApplication.POINT_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(
            StructuralMechanicsApplication.LINE_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(
            StructuralMechanicsApplication.SURFACE_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .VOLUME_ACCELERATION)
        if self.settings['rotation_dofs'].GetBool():
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.ROTATION)
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.REACTION_MOMENT)
            self.main_model_part.AddNodalSolutionStepVariable(
                StructuralMechanicsApplication.POINT_MOMENT)
        if self.settings['pressure_dofs'].GetBool():
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.PRESSURE)
            self.main_model_part.AddNodalSolutionStepVariable(
                StructuralMechanicsApplication.PRESSURE_REACTION)
        for i in range(self.settings['auxiliary_variables_list'].size()):
            variable_name = self.settings['auxiliary_variables_list'][i
                ].GetString()
            variable = KratosMultiphysics.KratosGlobals.GetVariable(
                variable_name)
            self.main_model_part.AddNodalSolutionStepVariable(variable)
        self.print_on_rank_zero('::[MechanicalSolver]:: ', 'Variables ADDED')

    def GetMinimumBufferSize(self):
        return 2

    def AddDofs(self):
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            DISPLACEMENT_X, KratosMultiphysics.REACTION_X, self.main_model_part
            )
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            DISPLACEMENT_Y, KratosMultiphysics.REACTION_Y, self.main_model_part
            )
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            DISPLACEMENT_Z, KratosMultiphysics.REACTION_Z, self.main_model_part
            )
        if self.settings['rotation_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ROTATION_X, KratosMultiphysics.REACTION_MOMENT_X, self.
                main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ROTATION_Y, KratosMultiphysics.REACTION_MOMENT_Y, self.
                main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ROTATION_Z, KratosMultiphysics.REACTION_MOMENT_Z, self.
                main_model_part)
        if self.settings['pressure_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                PRESSURE, KratosMultiphysics.PRESSURE_REACTION, self.
                main_model_part)
        self.print_on_rank_zero('::[MechanicalSolver]:: ', "DOF's ADDED")

    def ImportModelPart(self):
        """ Legacy function, use ReadModelPart and PrepareModelPartForSolver instead """
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'Importing model part.')
        problem_path = os.getcwd()
        input_filename = self.settings['model_import_settings'][
            'input_filename'].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif self.settings['model_import_settings']['input_type'].GetString(
            ) == 'mdpa':
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::', 
                'Reading model part from file: ' + os.path.join(
                problem_path, input_filename) + '.mdpa')
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self
                .main_model_part)
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
                'Finished reading model part from mdpa file.')
            self.PrepareModelPartForSolver()
        else:
            raise Exception(
                'Other model part input options are not yet implemented.')
        KratosMultiphysics.Logger.PrintInfo('ModelPart', self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]:: ',
            'Finished importing model part.')

    def ReadModelPart(self):
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'Reading model part.')
        problem_path = os.getcwd()
        input_filename = self.settings['model_import_settings'][
            'input_filename'].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif self.settings['model_import_settings']['input_type'].GetString(
            ) == 'mdpa':
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::', 
                'Reading model part from file: ' + os.path.join(
                problem_path, input_filename) + '.mdpa')
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self
                .main_model_part)
            KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
                'Finished reading model part from mdpa file.')
        else:
            raise Exception(
                'Other model part input options are not yet implemented.')
        KratosMultiphysics.Logger.PrintInfo('ModelPart', self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]:: ',
            'Finished reading model part.')

    def PrepareModelPartForSolver(self):
        if not self.is_restarted():
            self._execute_after_reading()
            self._set_and_fill_buffer()
        KratosMultiphysics.Logger.PrintInfo('::[MechanicalSolver]::',
            'ModelPart prepared for Solver.')

    def ExportModelPart(self):
        name_out_file = self.settings['model_import_settings']['input_filename'
            ].GetString() + '.out'
        file = open(name_out_file + '.mdpa', 'w')
        file.close()
        KratosMultiphysics.ModelPartIO(name_out_file, KratosMultiphysics.IO
            .WRITE).WriteModelPart(self.main_model_part)

    def Initialize(self):
        """Perform initialization after adding nodal variables and dofs to the main model part. """
        self.print_on_rank_zero('::[MechanicalSolver]:: ', 'Initializing ...')
        if self.settings['clear_storage'].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.SetEchoLevel(self.settings[
            'echo_level'].GetInt())
        if not self.is_restarted():
            mechanical_solution_strategy.Initialize()
        else:
            try:
                mechanical_solution_strategy.SetInitializePerformedFlag(True)
            except AttributeError:
                pass
        self.Check()
        self.print_on_rank_zero('::[MechanicalSolver]:: ',
            'Finished initialization.')

    def GetComputingModelPart(self):
        return self.main_model_part.GetSubModelPart(self.settings[
            'computing_model_part_name'].GetString())

    def GetOutputVariables(self):
        pass

    def SaveRestart(self):
        if self.settings['restart_settings'].Has('save_restart'):
            if self.settings['restart_settings']['save_restart'].GetBool(
                ) == True:
                self.get_restart_utility().SaveRestart()

    def Solve(self):
        if self.settings['clear_storage'].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.Solve()

    def InitializeSolutionStep(self):
        self.get_mechanical_solution_strategy().InitializeSolutionStep()

    def Predict(self):
        self.get_mechanical_solution_strategy().Predict()

    def SolveSolutionStep(self):
        is_converged = self.get_mechanical_solution_strategy(
            ).SolveSolutionStep()
        return is_converged

    def FinalizeSolutionStep(self):
        self.get_mechanical_solution_strategy().FinalizeSolutionStep()

    def AdvanceInTime(self, current_time):
        dt = self.ComputeDeltaTime()
        new_time = current_time + dt
        self.main_model_part.ProcessInfo[KratosMultiphysics.STEP] += 1
        self.main_model_part.CloneTimeStep(new_time)
        return new_time

    def ComputeDeltaTime(self):
        return self.delta_time

    def SetDeltaTime(self, dt):
        self.delta_time = dt

    def SetEchoLevel(self, level):
        self.get_mechanical_solution_strategy().SetEchoLevel(level)

    def Clear(self):
        self.get_mechanical_solution_strategy().Clear()

    def Check(self):
        self.get_mechanical_solution_strategy().Check()

    def get_solution_scheme(self):
        if not hasattr(self, '_solution_scheme'):
            self._solution_scheme = self._create_solution_scheme()
        return self._solution_scheme

    def get_convergence_criterion(self):
        if not hasattr(self, '_convergence_criterion'):
            self._convergence_criterion = self._create_convergence_criterion()
        return self._convergence_criterion

    def get_linear_solver(self):
        if not hasattr(self, '_linear_solver'):
            self._linear_solver = self._create_linear_solver()
        return self._linear_solver
    <mask token>
    <mask token>

    def get_restart_utility(self):
        if not hasattr(self, '_restart_utility'):
            self._restart_utility = self._create_restart_utility()
        return self._restart_utility

    def import_constitutive_laws(self):
        materials_filename = self.settings['material_import_settings'][
            'materials_filename'].GetString()
        if materials_filename != '':
            import read_materials_process
            Model = KratosMultiphysics.Model()
            Model.AddModelPart(self.main_model_part)
            read_materials_process.ReadMaterialsProcess(Model, self.
                settings['material_import_settings'])
            materials_imported = True
        else:
            materials_imported = False
        return materials_imported

    def validate_and_transfer_matching_settings(self, origin_settings,
        destination_settings):
        """Transfer matching settings from origin to destination.

        If a name in origin matches a name in destination, then the setting is
        validated against the destination.

        The typical use is for validating and extracting settings in derived classes:

        class A:
            def __init__(self, model_part, a_settings):
                default_a_settings = Parameters('''{
                    ...
                }''')
                a_settings.ValidateAndAssignDefaults(default_a_settings)
        class B(A):
            def __init__(self, model_part, custom_settings):
                b_settings = Parameters('''{
                    ...
                }''') # Here the settings contain default values.
                self.validate_and_transfer_matching_settings(custom_settings, b_settings)
                super().__init__(model_part, custom_settings)
        """
        for name, dest_value in destination_settings.items():
            if origin_settings.Has(name):
                orig_value = origin_settings[name]
                if dest_value.IsDouble() and orig_value.IsDouble():
                    destination_settings[name].SetDouble(origin_settings[
                        name].GetDouble())
                elif dest_value.IsInt() and orig_value.IsInt():
                    destination_settings[name].SetInt(origin_settings[name]
                        .GetInt())
                elif dest_value.IsBool() and orig_value.IsBool():
                    destination_settings[name].SetBool(origin_settings[name
                        ].GetBool())
                elif dest_value.IsString() and orig_value.IsString():
                    destination_settings[name].SetString(origin_settings[
                        name].GetString())
                elif dest_value.IsArray() and orig_value.IsArray():
                    if dest_value.size() != orig_value.size():
                        raise Exception('len("' + name + '") != ' + str(
                            dest_value.size()))
                    for i in range(dest_value.size()):
                        if dest_value[i].IsDouble() and orig_value[i].IsDouble(
                            ):
                            dest_value[i].SetDouble(orig_value[i].GetDouble())
                        elif dest_value[i].IsInt() and orig_value[i].IsInt():
                            dest_value[i].SetInt(orig_value[i].GetInt())
                        elif dest_value[i].IsBool() and orig_value[i].IsBool():
                            dest_value[i].SetBool(orig_value[i].GetBool())
                        elif dest_value[i].IsString() and orig_value[i
                            ].IsString():
                            dest_value[i].SetString(orig_value[i].GetString())
                        elif dest_value[i].IsSubParameter() and orig_value[i
                            ].IsSubParameter():
                            self.validate_and_transfer_matching_settings(
                                orig_value[i], dest_value[i])
                            if len(orig_value[i].items()) != 0:
                                raise Exception(
                                    'Json settings not found in default settings: '
                                     + orig_value[i].PrettyPrintJsonString())
                        else:
                            raise Exception('Unsupported parameter type.')
                elif dest_value.IsSubParameter() and orig_value.IsSubParameter(
                    ):
                    self.validate_and_transfer_matching_settings(orig_value,
                        dest_value)
                    if len(orig_value.items()) != 0:
                        raise Exception(
                            'Json settings not found in default settings: ' +
                            orig_value.PrettyPrintJsonString())
                else:
                    raise Exception('Unsupported parameter type.')
                origin_settings.RemoveValue(name)

    def is_restarted(self):
        return self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED
            ]

    def print_on_rank_zero(self, *args):
        KratosMultiphysics.Logger.PrintInfo(' '.join(map(str, args)))

    def print_warning_on_rank_zero(self, *args):
        KratosMultiphysics.Logger.PrintWarning(' '.join(map(str, args)))

    def _execute_after_reading(self):
        """Prepare computing model part and import constitutive laws. """
        params = KratosMultiphysics.Parameters('{}')
        params.AddValue('computing_model_part_name', self.settings[
            'computing_model_part_name'])
        params.AddValue('problem_domain_sub_model_part_list', self.settings
            ['problem_domain_sub_model_part_list'])
        params.AddValue('processes_sub_model_part_list', self.settings[
            'processes_sub_model_part_list'])
        import check_and_prepare_model_process_structural
        check_and_prepare_model_process_structural.CheckAndPrepareModelProcess(
            self.main_model_part, params).Execute()
        materials_imported = self.import_constitutive_laws()
        if materials_imported:
            self.print_on_rank_zero('::[MechanicalSolver]:: ',
                'Constitutive law was successfully imported.')
        else:
            self.print_on_rank_zero('::[MechanicalSolver]:: ',
                'Constitutive law was not imported.')

    def _set_and_fill_buffer(self):
        """Prepare nodal solution step data containers and time step information. """
        required_buffer_size = self.settings['buffer_size'].GetInt()
        if required_buffer_size < self.GetMinimumBufferSize():
            required_buffer_size = self.GetMinimumBufferSize()
        current_buffer_size = self.main_model_part.GetBufferSize()
        buffer_size = max(current_buffer_size, required_buffer_size)
        self.main_model_part.SetBufferSize(buffer_size)
        delta_time = self.main_model_part.ProcessInfo[KratosMultiphysics.
            DELTA_TIME]
        time = self.main_model_part.ProcessInfo[KratosMultiphysics.TIME]
        step = -buffer_size
        time = time - delta_time * buffer_size
        self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.TIME, time
            )
        for i in range(0, buffer_size):
            step = step + 1
            time = time + delta_time
            self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.
                STEP, step)
            self.main_model_part.CloneTimeStep(time)

    def _add_dynamic_variables(self):
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .VELOCITY)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics
            .ACCELERATION)
        if self.settings['rotation_dofs'].GetBool():
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.ANGULAR_VELOCITY)
            self.main_model_part.AddNodalSolutionStepVariable(
                KratosMultiphysics.ANGULAR_ACCELERATION)

    def _add_dynamic_dofs(self):
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            VELOCITY_Z, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_X, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Y, self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
            ACCELERATION_Z, self.main_model_part)
        if self.settings['rotation_dofs'].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_VELOCITY_Z, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_X, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Y, self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.
                ANGULAR_ACCELERATION_Z, self.main_model_part)

    def _get_restart_settings(self):
        restart_settings = self.settings['restart_settings'].Clone()
        restart_settings.AddValue('input_filename', self.settings[
            'model_import_settings']['input_filename'])
        restart_settings.AddValue('echo_level', self.settings['echo_level'])
        restart_settings.RemoveValue('load_restart')
        restart_settings.RemoveValue('save_restart')
        return restart_settings

    def _get_convergence_criterion_settings(self):
        conv_params = KratosMultiphysics.Parameters('{}')
        conv_params.AddValue('convergence_criterion', self.settings[
            'convergence_criterion'])
        conv_params.AddValue('rotation_dofs', self.settings['rotation_dofs'])
        conv_params.AddValue('echo_level', self.settings['echo_level'])
        conv_params.AddValue('displacement_relative_tolerance', self.
            settings['displacement_relative_tolerance'])
        conv_params.AddValue('displacement_absolute_tolerance', self.
            settings['displacement_absolute_tolerance'])
        conv_params.AddValue('residual_relative_tolerance', self.settings[
            'residual_relative_tolerance'])
        conv_params.AddValue('residual_absolute_tolerance', self.settings[
            'residual_absolute_tolerance'])
        return conv_params

    def _create_convergence_criterion(self):
        import convergence_criteria_factory
        convergence_criterion = (convergence_criteria_factory.
            convergence_criterion(self._get_convergence_criterion_settings()))
        return convergence_criterion.mechanical_convergence_criterion

    def _create_linear_solver(self):
        import linear_solver_factory
        linear_solver = linear_solver_factory.ConstructSolver(self.settings
            ['linear_solver_settings'])
        return linear_solver

    def _create_builder_and_solver(self):
        linear_solver = self.get_linear_solver()
        if self.settings['block_builder'].GetBool():
            if self.settings['multi_point_constraints_used'].GetBool():
                builder_and_solver = (KratosMultiphysics.
                    StructuralMechanicsApplication.
                    ResidualBasedBlockBuilderAndSolverWithMpc(linear_solver))
            else:
                builder_and_solver = (KratosMultiphysics.
                    ResidualBasedBlockBuilderAndSolver(linear_solver))
        else:
            if self.settings['multi_point_constraints_used'].GetBool():
                raise Exception(
                    'To use MPCs you also have to set "block_builder" to "true"'
                    )
            builder_and_solver = (KratosMultiphysics.
                ResidualBasedEliminationBuilderAndSolver(linear_solver))
        return builder_and_solver

    def _create_solution_scheme(self):
        """Create the solution scheme for the structural problem.
        """
        raise Exception(
            'Solution Scheme creation must be implemented in the derived class.'
            )

    def _create_mechanical_solution_strategy(self):
        analysis_type = self.settings['analysis_type'].GetString()
        if analysis_type == 'linear':
            mechanical_solution_strategy = self._create_linear_strategy()
        elif analysis_type == 'non_linear':
            if self.settings['line_search'].GetBool() == False:
                mechanical_solution_strategy = (self.
                    _create_newton_raphson_strategy())
            else:
                mechanical_solution_strategy = (self.
                    _create_line_search_strategy())
        else:
            err_msg = ('The requested analysis type "' + analysis_type +
                '" is not available!\n')
            err_msg += 'Available options are: "linear", "non_linear"'
            raise Exception(err_msg)
        return mechanical_solution_strategy

    def _create_linear_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedLinearStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            builder_and_solver, self.settings['compute_reactions'].GetBool(
            ), self.settings['reform_dofs_at_each_step'].GetBool(), False,
            self.settings['move_mesh_flag'].GetBool())

    def _create_newton_raphson_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedNewtonRaphsonStrategy(
            computing_model_part, mechanical_scheme, linear_solver,
            mechanical_convergence_criterion, builder_and_solver, self.
            settings['max_iteration'].GetInt(), self.settings[
            'compute_reactions'].GetBool(), self.settings[
            'reform_dofs_at_each_step'].GetBool(), self.settings[
            'move_mesh_flag'].GetBool())

    def _create_line_search_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.LineSearchStrategy(computing_model_part,
            mechanical_scheme, linear_solver,
            mechanical_convergence_criterion, builder_and_solver, self.
            settings['max_iteration'].GetInt(), self.settings[
            'compute_reactions'].GetBool(), self.settings[
            'reform_dofs_at_each_step'].GetBool(), self.settings[
            'move_mesh_flag'].GetBool())

    def _create_restart_utility(self):
        """Create the restart utility. Has to be overridden for MPI/trilinos-solvers"""
        import restart_utility
        rest_utility = restart_utility.RestartUtility(self.main_model_part,
            self._get_restart_settings())
        return rest_utility

from __future__ import print_function, absolute_import, division  # makes KratosMultiphysics backward compatible with python 2.6 and 2.7

# Importing the Kratos Library
import KratosMultiphysics

# Check that applications were imported in the main script
KratosMultiphysics.CheckRegisteredApplications("StructuralMechanicsApplication")

# Import applications
import KratosMultiphysics.StructuralMechanicsApplication as StructuralMechanicsApplication

# Other imports
import os


def CreateSolver(main_model_part, custom_settings):
    return MechanicalSolver(main_model_part, custom_settings)


class MechanicalSolver(object):
    """The base class for structural mechanics solvers.

    This class provides functions for importing and exporting models,
    adding nodal variables and dofs and solving each solution step.

    Derived classes must override the function _create_solution_scheme which
    constructs and returns a solution scheme. Depending on the type of
    solver, derived classes may also need to override the following functions:

    _create_solution_scheme
    _create_convergence_criterion
    _create_linear_solver
    _create_builder_and_solver
    _create_mechanical_solution_strategy
    _create_restart_utility

    The mechanical_solution_strategy, builder_and_solver, etc. should alway be retrieved
    using the getter functions get_mechanical_solution_strategy, get_builder_and_solver,
    etc. from this base class.

    Only the member variables listed below should be accessed directly.

    Public member variables:
    settings -- Kratos parameters containing solver settings.
    main_model_part -- the model part used to construct the solver.
    """
    def __init__(self, main_model_part, custom_settings):
        default_settings = KratosMultiphysics.Parameters("""
        {
            "echo_level": 0,
            "buffer_size": 2,
            "analysis_type": "non_linear",
            "model_import_settings": {
                "input_type": "mdpa",
                "input_filename": "unknown_name"
            },
            "restart_settings" : {
                "load_restart"  : false,
                "save_restart"  : false
            },
            "computing_model_part_name" : "computing_domain",
            "material_import_settings" :{
                "materials_filename": ""
            },
            "rotation_dofs": false,
            "pressure_dofs": false,
            "reform_dofs_at_each_step": false,
            "line_search": false,
            "compute_reactions": true,
            "block_builder": true,
            "clear_storage": false,
            "move_mesh_flag": true,
            "multi_point_constraints_used": false,
            "convergence_criterion": "residual_criterion",
            "displacement_relative_tolerance": 1.0e-4,
            "displacement_absolute_tolerance": 1.0e-9,
            "residual_relative_tolerance": 1.0e-4,
            "residual_absolute_tolerance": 1.0e-9,
            "max_iteration": 10,
            "linear_solver_settings":{
                "solver_type": "SuperLUSolver",
                "max_iteration": 500,
                "tolerance": 1e-9,
                "scaling": false,
                "verbosity": 1
            },
            "problem_domain_sub_model_part_list": ["solid"],
            "processes_sub_model_part_list": [""],
            "auxiliary_variables_list" : []
        }
        """)

        # temporary warnings, to be removed
        if custom_settings.Has("bodies_list"):
            custom_settings.RemoveValue("bodies_list")
            warning = '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "bodies_list", '
            warning += 'which is deprecated and will be removed soon. \nPlease remove it from the "solver settings"!\n'
            self.print_warning_on_rank_zero("Bodies list", warning)
        if custom_settings.Has("solver_type"):
            custom_settings.RemoveValue("solver_type")
            warning = '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "solver_type", '
            warning += 'which is only needed if you use the "python_solvers_wrapper_structural". \nPlease remove it '
            warning += 'from the "solver settings" if you dont use this wrapper, this check will be removed soon!\n'
            self.print_warning_on_rank_zero("Solver type", warning)
        if custom_settings.Has("time_integration_method"):
            custom_settings.RemoveValue("time_integration_method")
            warning = '\n::[MechanicalSolver]:: W-A-R-N-I-N-G: You have specified "time_integration_method", '
            warning += 'which is only needed if you use the "python_solvers_wrapper_structural". \nPlease remove it '
            warning += 'from the "solver settings" if you dont use this wrapper, this check will be removed soon!\n'
            self.print_warning_on_rank_zero("Time integration method", warning)

        # Overwrite the default settings with user-provided parameters.
        self.settings = custom_settings
        self.settings.ValidateAndAssignDefaults(default_settings)

        #TODO: shall obtain the computing_model_part from the MODEL once the object is implemented
        self.main_model_part = main_model_part
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Construction finished")

        # Set if the analysis is restarted
        if self.settings["restart_settings"].Has("load_restart"):
            load_restart = self.settings["restart_settings"]["load_restart"].GetBool()
            self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED] = load_restart
        else:
            self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED] = False

    def AddVariables(self):
        # this can safely be called also for restarts, it is internally checked if the variables exist already
        # Add displacements.
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.DISPLACEMENT)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.REACTION)
        # Add specific variables for the problem conditions.
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.POSITIVE_FACE_PRESSURE)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.NEGATIVE_FACE_PRESSURE)
        self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.POINT_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.LINE_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.SURFACE_LOAD)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.VOLUME_ACCELERATION)
        if self.settings["rotation_dofs"].GetBool():
            # Add specific variables for the problem (rotation dofs).
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ROTATION)
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.REACTION_MOMENT)
            self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.POINT_MOMENT)
        if self.settings["pressure_dofs"].GetBool(): # TODO: The creation of UP and USigma elements is pending
            # Add specific variables for the problem (pressure dofs).
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.PRESSURE)
            self.main_model_part.AddNodalSolutionStepVariable(StructuralMechanicsApplication.PRESSURE_REACTION)
        # Add variables that the user defined in the ProjectParameters
        for i in range(self.settings["auxiliary_variables_list"].size()):
            variable_name = self.settings["auxiliary_variables_list"][i].GetString()
            variable = KratosMultiphysics.KratosGlobals.GetVariable(variable_name)
            self.main_model_part.AddNodalSolutionStepVariable(variable)
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Variables ADDED")

    def GetMinimumBufferSize(self):
        return 2

    def AddDofs(self):
        # this can safely be called also for restarts, it is internally checked if the dofs exist already
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.DISPLACEMENT_X, KratosMultiphysics.REACTION_X,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.DISPLACEMENT_Y, KratosMultiphysics.REACTION_Y,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.DISPLACEMENT_Z, KratosMultiphysics.REACTION_Z,self.main_model_part)
        if self.settings["rotation_dofs"].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ROTATION_X, KratosMultiphysics.REACTION_MOMENT_X,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ROTATION_Y, KratosMultiphysics.REACTION_MOMENT_Y,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ROTATION_Z, KratosMultiphysics.REACTION_MOMENT_Z,self.main_model_part)
        if self.settings["pressure_dofs"].GetBool():
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.PRESSURE, KratosMultiphysics.PRESSURE_REACTION,self.main_model_part)
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "DOF's ADDED")

    def ImportModelPart(self):
        """ Legacy function, use ReadModelPart and PrepareModelPartForSolver instead """
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Importing model part.")
        problem_path = os.getcwd()
        input_filename = self.settings["model_import_settings"]["input_filename"].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif(self.settings["model_import_settings"]["input_type"].GetString() == "mdpa"):
            # Import model part from mdpa file.
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Reading model part from file: " + os.path.join(problem_path, input_filename) + ".mdpa")
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self.main_model_part)
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Finished reading model part from mdpa file.")
            self.PrepareModelPartForSolver()
        else:
            raise Exception("Other model part input options are not yet implemented.")
        KratosMultiphysics.Logger.PrintInfo("ModelPart", self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]:: ", "Finished importing model part.")

    def ReadModelPart(self):
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Reading model part.")
        problem_path = os.getcwd()
        input_filename = self.settings["model_import_settings"]["input_filename"].GetString()
        if self.is_restarted():
            self.get_restart_utility().LoadRestart()
        elif(self.settings["model_import_settings"]["input_type"].GetString() == "mdpa"):
            # Import model part from mdpa file.
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Reading model part from file: " + os.path.join(problem_path, input_filename) + ".mdpa")
            KratosMultiphysics.ModelPartIO(input_filename).ReadModelPart(self.main_model_part)
            KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "Finished reading model part from mdpa file.")
        else:
            raise Exception("Other model part input options are not yet implemented.")
        KratosMultiphysics.Logger.PrintInfo("ModelPart", self.main_model_part)
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]:: ", "Finished reading model part.")

    def PrepareModelPartForSolver(self):
        if not self.is_restarted():
            # Check and prepare computing model part and import constitutive laws.
            self._execute_after_reading()
            self._set_and_fill_buffer()
        KratosMultiphysics.Logger.PrintInfo("::[MechanicalSolver]::", "ModelPart prepared for Solver.")

    def ExportModelPart(self):
        name_out_file = self.settings["model_import_settings"]["input_filename"].GetString()+".out"
        file = open(name_out_file + ".mdpa","w")
        file.close()
        KratosMultiphysics.ModelPartIO(name_out_file, KratosMultiphysics.IO.WRITE).WriteModelPart(self.main_model_part)

    def Initialize(self):
        """Perform initialization after adding nodal variables and dofs to the main model part. """
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Initializing ...")
        # The mechanical solution strategy is created here if it does not already exist.
        if self.settings["clear_storage"].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.SetEchoLevel(self.settings["echo_level"].GetInt())
        if not self.is_restarted():
            mechanical_solution_strategy.Initialize()
        else:
            # SetInitializePerformedFlag is not a member of SolvingStrategy but
            # is used by ResidualBasedNewtonRaphsonStrategy.
            try:
                mechanical_solution_strategy.SetInitializePerformedFlag(True)
            except AttributeError:
                pass
        self.Check()
        self.print_on_rank_zero("::[MechanicalSolver]:: ", "Finished initialization.")

    def GetComputingModelPart(self):
        return self.main_model_part.GetSubModelPart(self.settings["computing_model_part_name"].GetString())

    def GetOutputVariables(self):
        pass

    def SaveRestart(self):
        # Check could be integrated in the utility
        # It is here intentionally, this way the utility is only created if it is actually needed!
        if self.settings["restart_settings"].Has("save_restart"):
            if (self.settings["restart_settings"]["save_restart"].GetBool() == True):
                # the check if this step is a restart-output step is done internally
                self.get_restart_utility().SaveRestart()

    def Solve(self):
        if self.settings["clear_storage"].GetBool():
            self.Clear()
        mechanical_solution_strategy = self.get_mechanical_solution_strategy()
        mechanical_solution_strategy.Solve()

    def InitializeSolutionStep(self):
        self.get_mechanical_solution_strategy().InitializeSolutionStep()

    def Predict(self):
        self.get_mechanical_solution_strategy().Predict()

    def SolveSolutionStep(self):
        is_converged = self.get_mechanical_solution_strategy().SolveSolutionStep()
        return is_converged

    def FinalizeSolutionStep(self):
        self.get_mechanical_solution_strategy().FinalizeSolutionStep()

    def AdvanceInTime(self, current_time):
        dt = self.ComputeDeltaTime()
        new_time = current_time + dt
        self.main_model_part.ProcessInfo[KratosMultiphysics.STEP] += 1
        self.main_model_part.CloneTimeStep(new_time)

        return new_time

    def ComputeDeltaTime(self):
        return self.delta_time

    def SetDeltaTime(self, dt):
        # This is a TEMPORARY function until the solver can compute dt!
        self.delta_time = dt

    def SetEchoLevel(self, level):
        self.get_mechanical_solution_strategy().SetEchoLevel(level)

    def Clear(self):
        self.get_mechanical_solution_strategy().Clear()

    def Check(self):
        self.get_mechanical_solution_strategy().Check()

    #### Specific internal functions ####

    def get_solution_scheme(self):
        if not hasattr(self, '_solution_scheme'):
            self._solution_scheme = self._create_solution_scheme()
        return self._solution_scheme

    def get_convergence_criterion(self):
        if not hasattr(self, '_convergence_criterion'):
            self._convergence_criterion = self._create_convergence_criterion()
        return self._convergence_criterion

    def get_linear_solver(self):
        if not hasattr(self, '_linear_solver'):
            self._linear_solver = self._create_linear_solver()
        return self._linear_solver

    def get_builder_and_solver(self):
        if not hasattr(self, '_builder_and_solver'):
            self._builder_and_solver = self._create_builder_and_solver()
        return self._builder_and_solver

    def get_mechanical_solution_strategy(self):
        if not hasattr(self, '_mechanical_solution_strategy'):
            self._mechanical_solution_strategy = self._create_mechanical_solution_strategy()
        return self._mechanical_solution_strategy

    def get_restart_utility(self):
        if not hasattr(self, '_restart_utility'):
            self._restart_utility = self._create_restart_utility()
        return self._restart_utility

    def import_constitutive_laws(self):
        materials_filename = self.settings["material_import_settings"]["materials_filename"].GetString()
        if (materials_filename != ""):
            import read_materials_process
            # Create a dictionary of model parts.
            Model = KratosMultiphysics.Model()
            Model.AddModelPart(self.main_model_part)
            # Add constitutive laws and material properties from json file to model parts.
            read_materials_process.ReadMaterialsProcess(Model, self.settings["material_import_settings"])
            materials_imported = True
        else:
            materials_imported = False
        return materials_imported

    def validate_and_transfer_matching_settings(self, origin_settings, destination_settings):
        """Transfer matching settings from origin to destination.

        If a name in origin matches a name in destination, then the setting is
        validated against the destination.

        The typical use is for validating and extracting settings in derived classes:

        class A:
            def __init__(self, model_part, a_settings):
                default_a_settings = Parameters('''{
                    ...
                }''')
                a_settings.ValidateAndAssignDefaults(default_a_settings)
        class B(A):
            def __init__(self, model_part, custom_settings):
                b_settings = Parameters('''{
                    ...
                }''') # Here the settings contain default values.
                self.validate_and_transfer_matching_settings(custom_settings, b_settings)
                super().__init__(model_part, custom_settings)
        """
        for name, dest_value in destination_settings.items():
            if origin_settings.Has(name): # Validate and transfer value.
                orig_value = origin_settings[name]
                if dest_value.IsDouble() and orig_value.IsDouble():
                    destination_settings[name].SetDouble(origin_settings[name].GetDouble())
                elif dest_value.IsInt() and orig_value.IsInt():
                    destination_settings[name].SetInt(origin_settings[name].GetInt())
                elif dest_value.IsBool() and orig_value.IsBool():
                    destination_settings[name].SetBool(origin_settings[name].GetBool())
                elif dest_value.IsString() and orig_value.IsString():
                    destination_settings[name].SetString(origin_settings[name].GetString())
                elif dest_value.IsArray() and orig_value.IsArray():
                    if dest_value.size() != orig_value.size():
                        raise Exception('len("' + name + '") != ' + str(dest_value.size()))
                    for i in range(dest_value.size()):
                        if dest_value[i].IsDouble() and orig_value[i].IsDouble():
                            dest_value[i].SetDouble(orig_value[i].GetDouble())
                        elif dest_value[i].IsInt() and orig_value[i].IsInt():
                            dest_value[i].SetInt(orig_value[i].GetInt())
                        elif dest_value[i].IsBool() and orig_value[i].IsBool():
                            dest_value[i].SetBool(orig_value[i].GetBool())
                        elif dest_value[i].IsString() and orig_value[i].IsString():
                            dest_value[i].SetString(orig_value[i].GetString())
                        elif dest_value[i].IsSubParameter() and orig_value[i].IsSubParameter():
                            self.validate_and_transfer_matching_settings(orig_value[i], dest_value[i])
                            if len(orig_value[i].items()) != 0:
                                raise Exception('Json settings not found in default settings: ' + orig_value[i].PrettyPrintJsonString())
                        else:
                            raise Exception('Unsupported parameter type.')
                elif dest_value.IsSubParameter() and orig_value.IsSubParameter():
                    self.validate_and_transfer_matching_settings(orig_value, dest_value)
                    if len(orig_value.items()) != 0:
                        raise Exception('Json settings not found in default settings: ' + orig_value.PrettyPrintJsonString())
                else:
                    raise Exception('Unsupported parameter type.')
                origin_settings.RemoveValue(name)

    def is_restarted(self):
        # this function avoids the long call to ProcessInfo and is also safer
        # in case the detection of a restart is changed later
        return self.main_model_part.ProcessInfo[KratosMultiphysics.IS_RESTARTED]

    def print_on_rank_zero(self, *args):
        # This function will be overridden in the trilinos-solvers
        KratosMultiphysics.Logger.PrintInfo(" ".join(map(str,args)))

    def print_warning_on_rank_zero(self, *args):
        # This function will be overridden in the trilinos-solvers
        KratosMultiphysics.Logger.PrintWarning(" ".join(map(str,args)))

    #### Private functions ####

    def _execute_after_reading(self):
        """Prepare computing model part and import constitutive laws. """
        # Auxiliary parameters object for the CheckAndPepareModelProcess
        params = KratosMultiphysics.Parameters("{}")
        params.AddValue("computing_model_part_name",self.settings["computing_model_part_name"])
        params.AddValue("problem_domain_sub_model_part_list",self.settings["problem_domain_sub_model_part_list"])
        params.AddValue("processes_sub_model_part_list",self.settings["processes_sub_model_part_list"])
        # Assign mesh entities from domain and process sub model parts to the computing model part.
        import check_and_prepare_model_process_structural
        check_and_prepare_model_process_structural.CheckAndPrepareModelProcess(self.main_model_part, params).Execute()

        # Import constitutive laws.
        materials_imported = self.import_constitutive_laws()
        if materials_imported:
            self.print_on_rank_zero("::[MechanicalSolver]:: ", "Constitutive law was successfully imported.")
        else:
            self.print_on_rank_zero("::[MechanicalSolver]:: ", "Constitutive law was not imported.")

    def _set_and_fill_buffer(self):
        """Prepare nodal solution step data containers and time step information. """
        # Set the buffer size for the nodal solution steps data. Existing nodal
        # solution step data may be lost.
        required_buffer_size = self.settings["buffer_size"].GetInt()
        if required_buffer_size < self.GetMinimumBufferSize():
            required_buffer_size = self.GetMinimumBufferSize()
        current_buffer_size = self.main_model_part.GetBufferSize()
        buffer_size = max(current_buffer_size, required_buffer_size)
        self.main_model_part.SetBufferSize(buffer_size)
        # Cycle the buffer. This sets all historical nodal solution step data to
        # the current value and initializes the time stepping in the process info.
        delta_time = self.main_model_part.ProcessInfo[KratosMultiphysics.DELTA_TIME]
        time = self.main_model_part.ProcessInfo[KratosMultiphysics.TIME]
        step =-buffer_size
        time = time - delta_time * buffer_size
        self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.TIME, time)
        for i in range(0, buffer_size):
            step = step + 1
            time = time + delta_time
            self.main_model_part.ProcessInfo.SetValue(KratosMultiphysics.STEP, step)
            self.main_model_part.CloneTimeStep(time)

    def _add_dynamic_variables(self):
        # For being consistent for Serial and Trilinos
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.VELOCITY)
        self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ACCELERATION)
        if self.settings["rotation_dofs"].GetBool():
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ANGULAR_VELOCITY)
            self.main_model_part.AddNodalSolutionStepVariable(KratosMultiphysics.ANGULAR_ACCELERATION)

    def _add_dynamic_dofs(self):
        # For being consistent for Serial and Trilinos
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.VELOCITY_X,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.VELOCITY_Y,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.VELOCITY_Z,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ACCELERATION_X,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ACCELERATION_Y,self.main_model_part)
        KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ACCELERATION_Z,self.main_model_part)
        if(self.settings["rotation_dofs"].GetBool()):
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_VELOCITY_X,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_VELOCITY_Y,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_VELOCITY_Z,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_ACCELERATION_X,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_ACCELERATION_Y,self.main_model_part)
            KratosMultiphysics.VariableUtils().AddDof(KratosMultiphysics.ANGULAR_ACCELERATION_Z,self.main_model_part)

    def _get_restart_settings(self):
        restart_settings = self.settings["restart_settings"].Clone()
        restart_settings.AddValue("input_filename", self.settings["model_import_settings"]["input_filename"])
        restart_settings.AddValue("echo_level", self.settings["echo_level"])
        restart_settings.RemoveValue("load_restart")
        restart_settings.RemoveValue("save_restart")

        return restart_settings

    def _get_convergence_criterion_settings(self):
        # Create an auxiliary Kratos parameters object to store the convergence settings.
        conv_params = KratosMultiphysics.Parameters("{}")
        conv_params.AddValue("convergence_criterion",self.settings["convergence_criterion"])
        conv_params.AddValue("rotation_dofs",self.settings["rotation_dofs"])
        conv_params.AddValue("echo_level",self.settings["echo_level"])
        conv_params.AddValue("displacement_relative_tolerance",self.settings["displacement_relative_tolerance"])
        conv_params.AddValue("displacement_absolute_tolerance",self.settings["displacement_absolute_tolerance"])
        conv_params.AddValue("residual_relative_tolerance",self.settings["residual_relative_tolerance"])
        conv_params.AddValue("residual_absolute_tolerance",self.settings["residual_absolute_tolerance"])

        return conv_params

    def _create_convergence_criterion(self):
        import convergence_criteria_factory
        convergence_criterion = convergence_criteria_factory.convergence_criterion(self._get_convergence_criterion_settings())
        return convergence_criterion.mechanical_convergence_criterion

    def _create_linear_solver(self):
        import linear_solver_factory
        linear_solver = linear_solver_factory.ConstructSolver(self.settings["linear_solver_settings"])
        return linear_solver

    def _create_builder_and_solver(self):
        linear_solver = self.get_linear_solver()
        if self.settings["block_builder"].GetBool():
            if self.settings["multi_point_constraints_used"].GetBool():
                builder_and_solver = KratosMultiphysics.StructuralMechanicsApplication.ResidualBasedBlockBuilderAndSolverWithMpc(linear_solver)
            else:
                builder_and_solver = KratosMultiphysics.ResidualBasedBlockBuilderAndSolver(linear_solver)
        else:
            if self.settings["multi_point_constraints_used"].GetBool():
                raise Exception("To use MPCs you also have to set \"block_builder\" to \"true\"")
            builder_and_solver = KratosMultiphysics.ResidualBasedEliminationBuilderAndSolver(linear_solver)
        return builder_and_solver

    def _create_solution_scheme(self):
        """Create the solution scheme for the structural problem.
        """
        raise Exception("Solution Scheme creation must be implemented in the derived class.")

    def _create_mechanical_solution_strategy(self):
        analysis_type = self.settings["analysis_type"].GetString()
        if analysis_type == "linear":
            mechanical_solution_strategy = self._create_linear_strategy()
        elif analysis_type == "non_linear":
            if(self.settings["line_search"].GetBool() == False):
                mechanical_solution_strategy = self._create_newton_raphson_strategy()
            else:
                mechanical_solution_strategy = self._create_line_search_strategy()
        else:
            err_msg =  "The requested analysis type \"" + analysis_type + "\" is not available!\n"
            err_msg += "Available options are: \"linear\", \"non_linear\""
            raise Exception(err_msg)
        return mechanical_solution_strategy

    def _create_linear_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedLinearStrategy(computing_model_part,
                                                              mechanical_scheme,
                                                              linear_solver,
                                                              builder_and_solver,
                                                              self.settings["compute_reactions"].GetBool(),
                                                              self.settings["reform_dofs_at_each_step"].GetBool(),
                                                              False,
                                                              self.settings["move_mesh_flag"].GetBool())

    def _create_newton_raphson_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.ResidualBasedNewtonRaphsonStrategy(computing_model_part,
                                                                     mechanical_scheme,
                                                                     linear_solver,
                                                                     mechanical_convergence_criterion,
                                                                     builder_and_solver,
                                                                     self.settings["max_iteration"].GetInt(),
                                                                     self.settings["compute_reactions"].GetBool(),
                                                                     self.settings["reform_dofs_at_each_step"].GetBool(),
                                                                     self.settings["move_mesh_flag"].GetBool())

    def _create_line_search_strategy(self):
        computing_model_part = self.GetComputingModelPart()
        mechanical_scheme = self.get_solution_scheme()
        linear_solver = self.get_linear_solver()
        mechanical_convergence_criterion = self.get_convergence_criterion()
        builder_and_solver = self.get_builder_and_solver()
        return KratosMultiphysics.LineSearchStrategy(computing_model_part,
                                                     mechanical_scheme,
                                                     linear_solver,
                                                     mechanical_convergence_criterion,
                                                     builder_and_solver,
                                                     self.settings["max_iteration"].GetInt(),
                                                     self.settings["compute_reactions"].GetBool(),
                                                     self.settings["reform_dofs_at_each_step"].GetBool(),
                                                     self.settings["move_mesh_flag"].GetBool())

    def _create_restart_utility(self):
        """Create the restart utility. Has to be overridden for MPI/trilinos-solvers"""
        import restart_utility
        rest_utility = restart_utility.RestartUtility(self.main_model_part,
                                                      self._get_restart_settings())
        return rest_utility
