https://kratos-wiki.cimne.upc.edu/index.php?title=Special:Contributions&feed=atom&deletedOnly=&limit=100&target=Gcasas&topOnly=&year=&month=KratosWiki - User contributions [en]2022-05-16T09:14:56ZFrom KratosWikiMediaWiki 1.17.0https://kratos-wiki.cimne.upc.edu/index.php/VtuneVtune2017-04-23T11:38:40Z<p>Gcasas: </p>
<hr />
<div>Once vtune has been installed, go to the installation folder, e.g.,<br />
cd /opt/intel<br />
<br />
Then the GUI can be launched by writing the following lines in the terminal:<br />
source vtune_amplifier_xe_2017/amplxe-vars.sh<br />
amplxe-gui&</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/VtuneVtune2017-04-23T11:37:04Z<p>Gcasas: </p>
<hr />
<div>Once vtune has been installed, its GUI can be launched by writing the following lines in the terminal:<br />
<br />
source /opt/intel/vtune_amplifier_xe_2017/amplxe-vars.sh<br />
amplxe-gui&</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/VtuneVtune2017-04-23T11:36:13Z<p>Gcasas: Created page with "Once vtune has been installed, its GUI can be launched by writing the following lines in the terminal: source /opt/intel/vtune_amplifier_xe_2017/amplxe-vars.sh amplxe-gui&"</p>
<hr />
<div>Once vtune has been installed, its GUI can be launched by writing the following lines in the terminal:<br />
<br />
source /opt/intel/vtune_amplifier_xe_2017/amplxe-vars.sh<br />
amplxe-gui&</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/User:GcasasUser:Gcasas2017-04-23T11:33:06Z<p>Gcasas: </p>
<hr />
<div>[[Ubuntu]]<br />
<br />
[[RapidSVN]]<br />
<br />
[[GidHacks]]<br />
<br />
[[Vtune]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Add_a_New_ConditionHow to Add a New Condition2016-12-11T17:57:29Z<p>Gcasas: Adding a link to the relevant info, since it was empty before</p>
<hr />
<div>See [[How to Add a New Element (Condition)]].</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Access_Neighbor_Nodes_and_ElementsHow to Access Neighbor Nodes and Elements2016-04-06T08:09:56Z<p>Gcasas: /* Generating Neighbour Lists -- C++ */</p>
<hr />
<div>In a finite element context it is often useful to have a fast access ( O(N) time with N number of nodes ) to the objects which are "around" a certain node. This can be achieved by storing on each node the list of all of the nodes and elements which are close to it.<br />
<br />
== Definition ==<br />
Before proceeding it is useful to clarify the meaning of the word "around". To do so, let's consider the mesh<br />
<br />
[[Image:Neighbours.jpg]]<br />
<br />
we will define as "NEIGHBOUR NODES" of node 910 which share at least one element with node 910. To make an example<br />
* Node 910 has the nodes {968 , 940 , 986 , 850 , 876 , 931 } as neighbours nodes<br />
* Node 876 has the nodes {910, 850, 820, 843, 898, 931} as neighbours nodes <br />
<br />
and the elements <br />
* {2873, 2960, 2961, 2876, 2875, 2874} as NEIGHBOUR ELEMENTS of node 910<br />
* {2876, 2877, 2849, 2848, 2865, 2875} as NEIGHBOUR ELEMENTS of node 876<br />
<br />
note that the order is NOT guaranteed.<br />
<br />
== Creation of the Neighbour Lists ==<br />
The creation of a list of neighbour nodes and elements implies a non neglibible price in terms of memory occupation. <br />
A "Process" is provided in order to ease the generation of the lists when needed. The procedure for the search of the neighbours is as follows:<br />
<br />
=== Generating Neighbour Lists -- C++ ===<br />
a minimal file to perform the calculation of neighbours inside C++ is as follows:<br />
#include processes/find_nodal_neighbours_process.h<br />
...<br />
//Generate the process<br />
Process& neighbour_finder = FindNodalNeighboursProcess(model_part,avg_elems, avg_nodes);<br />
...<br />
//regenerate the lists ... this should be done every time the connectivity changes<br />
neighbour_finder.Execute();<br />
the user should note that two parameters are left to be specified:<br />
* avg_elements -- represents the expected average number of elements around one node<br />
* avg_nodes -- expected average number of nodes arouind one node<br />
this is just for optimization. A wrong (or too little) guess will not lead to wrong results (just the search will be a little slower)<br />
<br />
=== Generating Neighbour Lists -- Python ===<br />
The Python interface follows very closely the C++. A minimal script looks like<br />
from Kratos import * ##import all of the Kratos functions<br />
...<br />
neighbour_finder = FindNodalNeighboursProcess(model_part,avg_elems, avg_nodes);<br />
...<br />
neigbour_finder.Execute(); ##at wish ... when it is needed<br />
<br />
<br />
=== Troubleshooting === <br />
The usage of the "neighbour_finder" is pretty straightforward and should not provide major problems. Some care should however be taken when different model parts share the same list of nodes. The problem is that a node contains A SINGLE list of neighbours which is calculated according to the connectivity stored in a given model part. If another model part (having the same nodes but a different connectivity) is used, the list of neighbours should be recalculated on the model part on which we are interested to work and recalculated every time one switches to one model part to the other. <br />
<br />
This problem does not exist if the different model parts have different Nodes, nor if they have the same nodes but also the same connectivity.<br />
<br />
<br />
<br />
== Format and usage of the Containers ==<br />
The list of neighbour elements is stored in Kratos as a list of (Weak) Pointers to the nodes or elements.<br />
Given a pointer "pnode" to a Node, the list of its neighbour nodes can be obtained as <br />
WeakPointerVector< Node<3> >& rneigh = pnode->GetValue(NEIGHBOUR_NODES);<br />
while the neighbour elements list can be accessed by<br />
WeakPointerVector< Element >& rneigh_el = pnode->GetValue(NEIGHBOUR_ELEMENTS);<br />
<br />
The two containers are standard and can be treated as normal stl-style containers. A loop over nodes can thus be done by<br />
WeakPointerVector< Node<3> >& rneigh = pnode->GetValue(NEIGHBOUR_NODES);<br />
for( WeakPointerVector<Node<3> >::iterator inode = rneigh.begin(); inode!=rneigh.end(); inode++)<br />
inode->Id(); //accesses to the Id of the node<br />
<br />
the user should note that "Weak" pointers are used instead of "Pointers". This is done to guarantee that if a node or element is removed from the list, it will be automatically set to null (but will not be preserved in the memory as it would happen using "Pointers").<br />
<br />
<br />
<br />
[[Category:How To]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/GidHacksGidHacks2016-03-17T15:09:42Z<p>Gcasas: </p>
<hr />
<div>Show actual tree as it is at the moment by typing the followig command in the command line:<br />
-np- W [$::KPriv(xml) asXML]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/GidHacksGidHacks2016-03-15T14:33:10Z<p>Gcasas: Created page with "type -np- W [$::KPriv(xml) asXML] in command line"</p>
<hr />
<div>type -np- W [$::KPriv(xml) asXML] in command line</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/User:GcasasUser:Gcasas2016-03-15T14:32:47Z<p>Gcasas: </p>
<hr />
<div>[[Ubuntu]]<br />
<br />
[[RapidSVN]]<br />
<br />
[[GidHacks]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/DEM_ApplicationDEM Application2016-02-07T18:36:03Z<p>Gcasas: /* JKR Cohesive Force */</p>
<hr />
<div>WARNING: This page is still under construction.<br />
<br />
The DEM Kratos Team<br />
<br />
<br />
== Theory ==<br />
<br />
The fundamental theoretical background corresponding to the discontinuous (granular matter) part of the code can be found Casas et al. (2015). The following sections are destined to contain this information though are curently still under construction.<br />
<br />
Nothing numerical<br />
<br />
=== Integration Schemes ===<br />
Forward Euler Scheme<br />
<br />
=== Contact Laws ===<br />
Concept of indentation<br />
HMD, LSD<br />
===== Normal Force Laws =====<br />
<br />
====== Linear Repulsive Force ======<br />
<br />
The most simple representation of a repulsive contact force between a sphere and a wall is given by a linear law, where the force acting on the sphere when contacting a plane is a linear function of the indentation, which in turn would bring a quadratic dependence with the contact radius. The next figure shows this simple law:<br />
<br />
[[File:Jkr cohesion linear force.png]]<br />
<br />
<br />
====== Hertzian Repulsive Force ======<br />
<br />
On the other hand, Hertz solved in 1882 the non-cohesive normal contact between a sphere and a plane. In 1971 Johnson, Kendall and Roberts presented the solution (JKR-Theory) for the same problem in this case adding cohesive behaviour. Not much later, Derjaguin, Müller and Toporov published similar results (DMT-Theory).<br />
<br />
Both theories are very close and correct, the main difference being that the JKR theory is oriented to the study of flexible, large spheres, while the DMT theory is specially suited to represent the behaviour of rigid, small ones.<br />
<br />
[[File:Jkr cohesion hertz.jpeg]]<br />
<br />
The previous figure shows the standard representation of a Linear or Hertzian contact between a sphere and a wall. The distribution of contact pressures between both bodies follow a parabolic law.<br />
<br />
====== JKR Cohesive Force ======<br />
<br />
[[File:Jkr cohesion jkr.jpeg]]<br />
<br />
The preceding capture shows the a representation of a JKR contact between a sphere and a wall. In this case, the distribution of pressures between both bodies is more complex due to the formation of a neck at the boundaries of the contact. A later figure shows a detailed view of the pressures involved.<br />
<br />
[[Image:Jkr cohesion forces.png]]<br />
<br />
In the previous graphic, it is very interesting to note the existence of two singular values of contact radius: one for which the total forces acting on the contacting sphere is zero, and another for which the maximum value of adhesion is achieved.<br />
<br />
<br />
[[File:Jkr cohesion pressures.png]]<br />
<br />
In the previous figure, the blue area represents the distribution of pressures acting on the sphere when contacting a wall if a Hertzian Law is followed. On the other hand, the sum of both green and blue areas represents the JKR distribution. Note the larger values and the existence of adhesive behaviour at both sides of the pressures distribution. <br />
<br />
<br />
An example of granular simulation without cohesive forces:<br />
<br />
[[File:JKR no cohesion 1 33.png]]<br />
[[File:JKR no cohesion 2 33.png]]<br />
[[File:JKR no cohesion 3 33.png]]<br />
[[File:JKR no cohesion 4 33.png]]<br />
<br />
The same simulation as before, this time with cohesive forces in both sphere-sphere and sphere-plane contacts.<br />
<br />
[[File:JKR cohesion 1 33.png]]<br />
[[File:JKR cohesion 2 33.png]]<br />
[[File:JKR cohesion 3 33.png]]<br />
[[File:JKR cohesion 4 33.png]]<br />
<br />
<br />
References:<br />
<br />
V. L. Popov. "Contact Mechanics and Friction" (2010). <br><br />
G. Casas et al. "A modular, partitioned, discrete element framework for industrial grain distribution systems with rotating machinery." Computational Particle Mechanics (2015): 1-18.<br />
<br />
===== Tangential Force Laws =====<br />
<br />
===== Damping Force Laws =====<br />
<br />
(restit. coef)<br />
<br />
== Numerical approach ==<br />
<br />
This section is dedicated to describe the numerical methods used to solve.<br />
<br />
<br />
[[File:Dem manual main scheme 66 discontinuum.png]]<br />
<br />
<br />
=== DEM elements ===<br />
<br />
===== Spheric Particle =====<br />
===== Spheric Continuum Particle =====<br />
===== Spheric Swimming Particle =====<br />
<br />
=== DEM walls (Kratos Conditions) ===<br />
<br />
=== DEM Inlets ===<br />
<br />
A DEM Inlet is a source of new DEM Elements. It is a cloud of Nodes, where each Node is the center of a ''Generator Sphere''. In a random order, the Nodes are chosen to create a new DEM Element (both spherical elements or clusters) whose center coincides with the Node and the whole element is fully included in the ''Generator Sphere''. The newly generated DEM Element has a non-zero imposed velocity which eventually makes the DEM Element get outside the ''Generator Sphere''. Until this moment, the ''Generator Sphere'' is not allowed to generate another DEM-Element. From this moment on, the newly created DEM Element no longer has the velocity imposed, it moves freely and cannot penetrate its ''Generator Sphere'' or any other. Actually, the ''Generator Sphere'' is seen as any other Sphere of the domain by the DEM Element, and the contact between them is calculated as usual. In other words, '''the Generator Spheres reserve the necessary space to create a new DEM Element'''.<br />
<br />
=== DEM strategies ===<br />
<br />
===== Non-cohesive materials Strategy =====<br />
<br />
====== Evaluation of Forces ======<br />
<br />
Once contact between two spheres has been detected (see next figure), the forces occurring at the<br />
contact point are computed. The interaction between the two contacting spheres can be represented<br />
by forces Fij and Fji, which have the same module but opposite directions.<br />
<br />
<br />
[[File:Dem application interaction forces.png]]<br />
<br />
<br />
At the same time, this force Fij can be decomposed into its normal and shear components Fijn and Fijs, respectively. The next figure shows a detailed view of this decomposition.<br />
<br />
Fij = Fijn + Fijs = Fnnij + Fijs<br />
<br />
<br />
<br />
[[File:Dem application forces decomposition.png]]<br />
<br />
<br />
The nij vector represents the unitary normal with respect to the surfaces of both particles at the contact point. This vector lies along the line connecting the centers of the two particles and is directed<br />
outwards from particle i.<br />
<br />
<br />
The contact forces Fn, Fs1 and Fs2 are obtained using a constitutive model formulated for the<br />
contact between two rigid spheres (or discs in 2D). The contact interface for the simplest formulation<br />
is characterized by the normal and tangential stiffness Kn and Ks, respectively, a frictional device<br />
obeying the Couloumb law with a frictional coefficient mu, and a dashpot defined by the contact damping<br />
coefficient cn as shown in the next figure.<br />
<br />
<br />
[[File:Dem application forces rheological.png]]<br />
<br />
====== Rolling Friction ======<br />
<br />
In order to represent irregular particles with spheres, a numerical correction is used. This correction is the rolling friction which is about imposing a virtual moment opposite to particle rotation and dependent on its size.<br />
<br />
[[File:Rolling_friction_1.png|300px]]<br />
<br />
Fn represents the normal force, Ft the tangential force, ω the angular velocity, r is the radius and η the rolling friction coefficient.<br />
<br />
In our approach there are two importatn conditions that have to be fulfilled:<br />
<br />
1. The moment due to rolling friction can not change the direction of the particle spin.<br />
<br />
2. If the contact occurs between two spheres the rolling friction is calculated with the radius of the smallest spheres.<br />
<br />
[[File:Rolling_friction_surfaces.png|150px]]<br />
<br />
The model used is model A described in the following article: C.M. Wensrich, A. Katterfeld. Rolling friction as a technique for modelling particle shape in DEM. Powder Technology 217 (2012) 409–417.<br />
<br />
===== Continuum Strategy =====<br />
<br />
=== DEM schemes ===<br />
<br />
==== Integration of Motion ====<br />
<br />
<br />
The standard translational and rotational equations for the motion of rigid bodies are used to compute the dynamics of the spheres.<br />
For the i-th particle we have:<br />
<br />
miui = Fi<br />
Ii i = Ti<br />
<br />
<br />
where u represents the element centroid displacement in a xed (inertial) coordinate frame X, ! the angular<br />
velocity, m the element mass, I the moment of inertia, Fi the resultant force, and Ti the<br />
total moment arount the central axes.<br />
<br />
<br />
Vectors Fi and Ti are the sum of all forces and moments applied to the i-th particle due<br />
to external loads, Fexti and Texti , respectively, the contact interactions with neighbour spheres Fc<br />
i, j = 1;; nc, where nci is the total number of elements in contact with the i-th discrete<br />
element, and the forces and moments resulting from external damping, Fdampi and Tdampi , respectively,<br />
which can be written as:<br />
<br />
<br />
Fi = Fexti +nciXj=1Fci + Fdampi<br />
<br />
Ti = Texti +nciXj=1(rci Fci + qci ) + Tdampi<br />
<br />
<br />
[[File:Dem application motion.png]]<br />
<br />
<br />
where rci is the vector connecting the centre of mass of the i th particle with the contact point c<br />
(see next figure) and qci are the torques due to rolling and/or torsion (not related to the tangential forces).<br />
The next figure shows the motion of a rigid particle. Note that the form of the rotational equation is only valid for spheres and cylinders (in<br />
2D). It is simplified with respect to a general form for an arbitrary rigid body with the rotational<br />
inertial properties represented by a second order tensor. In the general case it is more convenient to<br />
describe the rotational motion with respect to a co-rotational frame x which is embedded at each<br />
element since in this frame the tensor of inertia is constant.<br />
<br />
<br />
The previous set of equations are integrated in time using a simple central difference scheme. The<br />
time integration operator for the translational motion at the n-th time step is as follows:<br />
<br />
<br />
The first two steps in the integration scheme for the rotational motion are identical to those given<br />
by the previous equations:<br />
<br />
<br />
On the other hand, the vector of incremental rotation is computed as<br />
<br />
<br />
Knowledge of the incremental rotation is enough to update the tangential contact forces. It is also<br />
possible to track the rotational position of particles when necessary. If that is the case, the rotation matrices between<br />
the moving frames embedded in the particles and the fixed global frame must be updated<br />
incrementally by using an adequate multiplicative scheme. <br />
Explicit integration in time yields high computational efficiency and it enables the solution of large<br />
models. The main disadvantage of this explicit integration scheme is its conditional numerical stability<br />
which imposes a limitation on the time step delta_t.<br />
<br />
=== Search Strategies ===<br />
<br />
The contact search is a very important part of the method in terms of computational cost (range 60%-90% of simulation time in simulations with large number of particles) and it is possibly the most difficult part to treat when dealing with particles that have no spherical/circular shape.<br />
<br />
The contact detection basically consists in determining, for every target object, what other objects are in contact with, and then, judge for the correspondent interaction. It is usually not needed to perform a search every time step, which is generally limited for the stability of the explicit integration of the equations of motion. Due to the fact that the search is an expensive step a lower search frequency can be selected without much loss of accuracy.<br />
<br />
The most naïve method of search that can be set is the brute search; for every element, the method does a loop for any other element checking for the contact. The order of the number of operations needed is quadratic: n2. Normally, the strategy to avoid such an expensive scheme is to divide the contact search in two basic stages, a global search and a subsequent local resolution of the contact; in this case the computation time of the contact search is proportional to n log n. In the DEMApplication a Grid/Cell based algorithm is used in this purpose.<br />
<br />
==== Global Search ====<br />
<br />
In a generic way, there are two types of elements: searcher elements (particles or finite elements) and target elements (particles or finite elements). Hereafter<br />
searcher elements will be called S.E. and target elements T.E.<br />
<br />
[[File:Global_search1.png]]<br />
<br />
The steps needed to perform contact search are:<br />
<br />
a) Build bounding box of S.E. (Figure 2(a)).<br />
<br />
b) Build bins cells based on size and position of S.E. (Figure 2(b)).<br />
<br />
c) Collocate S.E. in bins and construct hash table with relates coordinates with cells which point to the contacting S.E. (Figure 2(c)).<br />
<br />
d) Build bounding box of T.E. (Figure 2(d)).<br />
<br />
e) Loop over T.E., detect the intersecting cells to each T.E., check the intersection with the possible found cells and add the entire S.E. contained in the cells intersected by each T.E. (Figure 2(e)).<br />
<br />
f) Solve the contact with local resolution (Figure 2(f)).<br />
<br />
Note: In the case of FE and DE the FE are selected as the S.E. to construct the Bins and the Spheres are T.E. to be found in that bins. <br />
<br />
[[File:Global_search2_bigger.png]]<br />
<br />
==== Local Search ====<br />
<br />
Once the possible neighbours are detected, the local resolution check takes place. For the case of two spherical particles, the check is easy; only the sum of the radius has to be compared against the distance between centres. Other geometries may demand a much complicated check. The followed strategy is to mesh all the geometries with a discretization of triangles. In 3D, surface meshes are used for contact detection. Now, the contact detection should be performed between particles and triangles; if no contact is found, particle contact against lines is searched for; and if contact is still not found, contact against points is performed. Figure 3 shows how the local search is performed. Particle i searches contact against element j, then against lines k, l and m and finally against points n, o and p.<br />
<br />
This is known as a hierarchical algorithm. For further explanation please refer to the paper: "3D contact algorithms for particle DEM with rigid and deformable solids" - M.Santasusana, J.Irazábal, E.Oñate, J.M.Carbonell. Where the advantges and the drawback of this method and other proposed algorithms are detailed and analysed in complicated situations like multicontact.<br />
<br />
[[File:Local_search1.png]]<br />
<br />
Fig. 3 Particle-Face contact detection.<br />
<br />
<br />
<br />
== Programming Implementation ==<br />
Structure of the code (Strategy, Scheme, Element, Node, Utilities, functions frequently used like FastGet,...)<br />
<br />
The source code is accessible through [https://kratos.cimne.upc.es/projects/kratos/repository/show/kratos this site].<br />
<br />
[[File:Dem manual code scheme 66.png]]<br />
<br />
A main Python script is used to trigger the calculation of a problem. It communicates with the C++ code. The main advantage of using a compiled language like python for the main script is that it allows to make quick adjustments or modifications to the main strategy without having to recompile the whole code avery time. It also allows for very fast testing.<br />
<br />
=== Main components of the code ===<br />
<br />
<br />
==== Elements ====<br />
<br />
Discontinuum, continuum, cohesive, swimming, cluster of spheres.<br />
<br />
==== Conditions ====<br />
<br />
Main the FEM elements: lines and triangles.<br />
<br />
==== Constitutive Laws ====<br />
<br />
Different interactions laws: linear, Hertzian and continuum behaviour.<br />
<br />
==== Python elements ====<br />
<br />
These files translate the strategies and the<br />
utilities defined in C++ language to be able<br />
to call them from python scripts.<br />
<br />
==== Strategies====<br />
<br />
The main script that calls the necessary schemes and the functions on the<br />
elements during the global loop.<br />
<br />
==== Schemes====<br />
<br />
Explicit integration schemes available<br />
<br />
==== Utilities====<br />
<br />
Here geometric functions are defined, the<br />
neighbours search function, configuration of the particle, amongst others.<br />
<br />
==== Python scripts====<br />
<br />
It contains, amongst other things, the python interface where the main function<br />
of the strategy such as Initialize or Solve<br />
are called. Other operations are done like adding the necessary nodal variables.<br />
<br />
==== Test examples====<br />
<br />
They contain the Benchmarks<br />
<br />
==== DEM_application====<br />
<br />
It contains all the variables that will be used so they are<br />
created and registered in python and C++.<br />
<br />
<br />
=== Main elements in the global python script ===<br />
<br />
<br />
The first step consist of importing all libraries and files necessary for the application.<br />
It is followed by the creation of the necessary input files of the problem at hand to be<br />
processed by the code. The solver strategy is also chosen, and some important operations are done, as for example the<br />
addition of the necessary variables to the solver.<br />
<br />
At this step the buffer size value is also set: this number determines the<br />
historical database size of the problem. The values for the different options and<br />
variables that Kratos will use in the computation are also imported here, like the<br />
integration scheme, the type of solution, damping specifications, time step, output<br />
time step of results, et cetera.<br />
<br />
After the definition of the previous settings, several initializing functions are called which are responsible<br />
of initializing, among other objects, the mesh, the inlet of particles or the solver. Also some operations related to parallel<br />
computing are done.<br />
<br />
Afterwards the time step is determined and the main computing loop is entered. At each time step Solve function is executed.<br />
Finally, at user-defined time steps the results are exported to GiD so they become ready for visualization and postprocessing.<br />
<br />
In fact, this main python file imports another script written in the same language, the ''sphere_strategy.py''. This file is very important<br />
because it contains the core structure that the program will follow when computing. On the other hand, it is the last door to the kernel of the program, which<br />
is written in C++. Some of the most important functions and classes in this file are the following:<br />
<br />
AddVariables: This function sets which variables will be<br />
stored in the nodes at each time step and also the value of those variables as a function of time also<br />
depending on the buffer size previously selected. See the next figure for details.<br />
<br />
<br />
[[File:Dem application add variables 66.png]]<br />
<br />
<br />
AddDofs: The desired degrees of freedom are set here, as can be seen in the design of the function in the figure that follows.<br />
<br />
<br />
[[File:Dem application add dofs 66.png]]<br />
<br />
<br />
The global structure of the explicit strategy the code uses is shown in the next figure. It consists of three main parts: a constructor of the class of the solver,<br />
which creates some default variables for the class, an initializer, which defines the values of several standard parameters, and finally, the ''Solve()'' function<br />
which takes command of almost the entire computing process. The next figure shows and schematic view of this structure.<br />
<br />
When using the ''Initialize()'' function to set the original values of a group of variables, they automatically become accessible in the different C++ files<br />
of the application by means of the ProcessInfo container (see more at ). This function also calls the Initialize function in the solver.<br />
<br />
<br />
[[File:Dem application explicit strategy 66.png]]<br />
<br />
<br />
C++ Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve cpp scheme 80.png]]<br />
<br />
<br />
<br />
DEM CONTINUUM<br />
<br />
<br />
Initialize() function scheme:<br />
<br />
<br />
[[File:Dem application initialize scheme 75.png]]<br />
<br />
<br />
Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve scheme 75.png]]<br />
<br />
<br />
Search() function scheme:<br />
<br />
<br />
[[File:Dem application search scheme 66.png]]<br />
<br />
== Benchmarks ==<br />
<br />
The DEM Benchmarks consist of a set of 9 simple tests which are run every night and whose object is to make sure both that the application performs correctly and that the code did not break after the daily changes. They are the following:<br />
<br />
===Test1: Elastic normal impact of two identical spheres===<br />
<br />
Check the evolution of the elastic normal contact force between two spheres with time.<br />
<br />
<br />
<br />
[[File:Benchmark1_1.png]]<br />
[[File:Benchmark1 graph 66.png]]<br />
<br />
If the coefficient of restitution is 1, the module of the initial and final velocities should be the same. Also, by symmetry, velocities should be equal for both spheres.<br />
<br />
_<br />
<br />
===Test2: Elastic normal impact of a sphere against a rigid plane===<br />
<br />
Check the evolution of the elastic normal contact force between a sphere and a plane.<br />
<br />
[[File:Benchmark2 66.png]]<br />
[[File:Benchmark2 graph 66.png]]<br />
<br />
If the coefficient of restitution is equal to 1, the module of the initial and final velocity should remain unchanged.<br />
<br />
_<br />
<br />
===Test3: Impact of a sphere against a rigid plane with different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark3 66.png]]<br />
[[File:Benchmark3 graph 66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
_<br />
<br />
===Test4: Oblique impact of a sphere with a rigid plane with constant velocity module and variable incident angles===<br />
<br />
Check the tangential restitution coefficient, final angular velocity and rebound angle of the sphere.<br />
<br />
[[File:Benchmark4_66.png]]<br />
[[File:Benchmark4 graph1_66.png]]<br />
[[File:Benchmark4 graph2_66.png]]<br />
<br />
_<br />
<br />
===Test5: Oblique impact of a sphere with a rigid plane with constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark5_66.png]]<br />
[[File:Benchmark5 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test6: Impact of a sphere with a rigid plane with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark6_66.png]]<br />
[[File:Benchmark6 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test7: Impact of two identical spheres with a constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark7_66.png]]<br />
[[File:Benchmark7 graph1_66.png]]<br />
<br />
By symmetry, the tangential final velocity of both spheres should be zero. Additionally, for a coefficient of restitution of 1, there should be no changes in the modules of both linear and angular velocities and their values should conserve symmetry. <br />
<br />
_<br />
<br />
===Test8: Impact of two differently sized spheres with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark8_66.png]]<br />
[[File:Benchmark8_graph1_66.png]]<br />
<br />
In this case, it is interesting to note that, the bigger and/or denser sphere 2 is, the more this test resembles the sphere versus plane simulation.<br />
<br />
_<br />
<br />
===Test9: Impact of two identical spheres with a constant normal velocity and different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark9_66.png]]<br />
[[File:Benchmark9_graph1_66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
<br />
References:<br />
<br />
Y.C.Chung, J.Y.Ooi. ''Benchmark tests for verifying discrete element modelling codes at particle impact level'' (2011).<br />
<br />
== How to analyse using the current application ==<br />
<br />
=== Pre-Process ===<br />
<br />
GUI's & GiD<br />
<br />
===== D-DEMPack =====<br />
<br />
D-DEMPack is the package that allows a user to create, run and analyze results of a DEM simulation for discontinuum / granular / little-cohesive materials. It is written for GiD. So in order to use this package, you should install GiD first.<br />
<br />
You can read the [[D-DEMPack manual]] or follow the [[D-DEMPack Tutorials]] for fast learning on how to use the GUI.<br />
<br />
===== C-DEMPack =====<br />
<br />
Continuum / Cohesive <br />
<br />
===== F-DEMPack =====<br />
<br />
Fluid coupling<br />
<br />
=== Post-Process ===<br />
<br />
== Application Dependencies ==<br />
<br />
The Swimming DEM Application depends on the DEM application<br />
<br />
=== Other Kratos Applications used in current Application ===<br />
<br />
FEM-DEM<br />
<br />
<br />
== Problems! ==<br />
<br />
==== What to do if the Discrete Elements behave strangely ====<br />
<br />
In the case you notice that some discrete elements cross walls, penetrate in them or simply fly away of the domain at high velocity, check the following points:<br />
<br />
<br />
In the case of excessive penetration:<br />
<br />
*'''Check that the Young Modulus is big enough'''. A small Young Modulus makes the Elements and the walls behave in a very smooth way. Sometimes they are so soft that total penetration and trespass is possible.<br />
<br />
*'''Check the Density of the material'''. An excessive density means a big weight and inertia that cannot be stopped by the walls.<br />
*'''Check the Time Step'''. If the time step is too big, the Elements can go from one side of the wall to the other with no appearence of a reaction.<br />
*'''Check the frequency of neighbour search'''. If the search is not done frequently enough, the new contacts with the walls may not be detected soon enough.<br />
<br />
<br />
In the case of excessive bounce:<br />
<br />
*'''Check that the Young Modulus is not extremely big'''. An exaggerated Young Modulus yields extremely large reactions that can make the Elements bounce too fast in just one time step. Also take into account that the stability of explicit methods depends on the Young Modulus (the higher the modulus, the closer to instability).<br />
<br />
*'''Check the Density of the material'''. A very low density means a very small weight and inertia, so any force exerted by other elements or the walls can induce big accelerations on the element.<br />
*'''Check the Time Step'''. If the time step is too big, the method gains more energy, and gets closer to instability.<br />
*'''Check the restitution coefficient of the material'''. Explicit integration schemes gain energy noticeably, unless you use a really small time step. In case the time step is chosen to be big (but still stable), use the restitution coefficient to compensate the gain of energy and get more realistic results.<br />
<br />
== Contact ==<br />
<br />
Contact us for any question regarding this application:<br />
<br />
<br />
-Miguel Angel Celigueta: [mailto:maceli@cimne.upc.edu maceli@cimne.upc.edu]<br />
<br />
-Guillermo Casas: [mailto:gcasas@cimne.upc.edu gcasas@cimne.upc.edu]<br />
<br />
-Salva Latorre: [mailto:latorre@cimne.upc.edu latorre@cimne.upc.edu]<br />
<br />
-Miquel Santasusana: [mailto:msantasusana@cimne.upc.edu msantasusana@cimne.upc.edu]<br />
<br />
-Ferran Arrufat: [mailto:farrufat@cimne.upc.edu farrufat@cimne.upc.edu]<br />
<br />
<br />
[[Category: Applications]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/DEM_ApplicationDEM Application2016-02-07T18:35:45Z<p>Gcasas: /* JKR Cohesive Force */</p>
<hr />
<div>WARNING: This page is still under construction.<br />
<br />
The DEM Kratos Team<br />
<br />
<br />
== Theory ==<br />
<br />
The fundamental theoretical background corresponding to the discontinuous (granular matter) part of the code can be found Casas et al. (2015). The following sections are destined to contain this information though are curently still under construction.<br />
<br />
Nothing numerical<br />
<br />
=== Integration Schemes ===<br />
Forward Euler Scheme<br />
<br />
=== Contact Laws ===<br />
Concept of indentation<br />
HMD, LSD<br />
===== Normal Force Laws =====<br />
<br />
====== Linear Repulsive Force ======<br />
<br />
The most simple representation of a repulsive contact force between a sphere and a wall is given by a linear law, where the force acting on the sphere when contacting a plane is a linear function of the indentation, which in turn would bring a quadratic dependence with the contact radius. The next figure shows this simple law:<br />
<br />
[[File:Jkr cohesion linear force.png]]<br />
<br />
<br />
====== Hertzian Repulsive Force ======<br />
<br />
On the other hand, Hertz solved in 1882 the non-cohesive normal contact between a sphere and a plane. In 1971 Johnson, Kendall and Roberts presented the solution (JKR-Theory) for the same problem in this case adding cohesive behaviour. Not much later, Derjaguin, Müller and Toporov published similar results (DMT-Theory).<br />
<br />
Both theories are very close and correct, the main difference being that the JKR theory is oriented to the study of flexible, large spheres, while the DMT theory is specially suited to represent the behaviour of rigid, small ones.<br />
<br />
[[File:Jkr cohesion hertz.jpeg]]<br />
<br />
The previous figure shows the standard representation of a Linear or Hertzian contact between a sphere and a wall. The distribution of contact pressures between both bodies follow a parabolic law.<br />
<br />
====== JKR Cohesive Force ======<br />
<br />
[[File:Jkr cohesion jkr.jpeg]]<br />
<br />
The preceding capture shows the a representation of a JKR contact between a sphere and a wall. In this case, the distribution of pressures between both bodies is more complex due to the formation of a neck at the boundaries of the contact. A later figure shows a detailed view of the pressures involved.<br />
<br />
[[Image:Jkr cohesion forces.png]]<br />
<br />
In the previous graphic, it is very interesting to note the existence of two singular values of contact radius: one for which the total forces acting on the contacting sphere is zero, and another for which the maximum value of adhesion is achieved.<br />
<br />
<br />
[[File:Jkr cohesion pressures.png]]<br />
<br />
In the previous figure, the blue area represents the distribution of pressures acting on the sphere when contacting a wall if a Hertzian Law is followed. On the other hand, the sum of both green and blue areas represents the JKR distribution. Note the larger values and the existence of adhesive behaviour at both sides of the pressures distribution. <br />
<br />
<br />
An example of granular simulation without cohesive forces:<br />
<br />
[[File:JKR no cohesion 1 33.png]]<br />
[[File:JKR no cohesion 2 33.png]]<br />
[[File:JKR no cohesion 3 33.png]]<br />
[[File:JKR no cohesion 4 33.png]]<br />
<br />
The same simulation as before, this time with cohesive forces in both sphere-sphere and sphere-plane contacts.<br />
<br />
[[File:JKR cohesion 1 33.png]]<br />
[[File:JKR cohesion 2 33.png]]<br />
[[File:JKR cohesion 3 33.png]]<br />
[[File:JKR cohesion 4 33.png]]<br />
<br />
<br />
References:<br />
<br />
V. L. Popov. "Contact Mechanics and Friction" (2010). <br><br />
G. Casaset al. "A modular, partitioned, discrete element framework for industrial grain distribution systems with rotating machinery." Computational Particle Mechanics (2015): 1-18.<br />
<br />
===== Tangential Force Laws =====<br />
<br />
===== Damping Force Laws =====<br />
<br />
(restit. coef)<br />
<br />
== Numerical approach ==<br />
<br />
This section is dedicated to describe the numerical methods used to solve.<br />
<br />
<br />
[[File:Dem manual main scheme 66 discontinuum.png]]<br />
<br />
<br />
=== DEM elements ===<br />
<br />
===== Spheric Particle =====<br />
===== Spheric Continuum Particle =====<br />
===== Spheric Swimming Particle =====<br />
<br />
=== DEM walls (Kratos Conditions) ===<br />
<br />
=== DEM Inlets ===<br />
<br />
A DEM Inlet is a source of new DEM Elements. It is a cloud of Nodes, where each Node is the center of a ''Generator Sphere''. In a random order, the Nodes are chosen to create a new DEM Element (both spherical elements or clusters) whose center coincides with the Node and the whole element is fully included in the ''Generator Sphere''. The newly generated DEM Element has a non-zero imposed velocity which eventually makes the DEM Element get outside the ''Generator Sphere''. Until this moment, the ''Generator Sphere'' is not allowed to generate another DEM-Element. From this moment on, the newly created DEM Element no longer has the velocity imposed, it moves freely and cannot penetrate its ''Generator Sphere'' or any other. Actually, the ''Generator Sphere'' is seen as any other Sphere of the domain by the DEM Element, and the contact between them is calculated as usual. In other words, '''the Generator Spheres reserve the necessary space to create a new DEM Element'''.<br />
<br />
=== DEM strategies ===<br />
<br />
===== Non-cohesive materials Strategy =====<br />
<br />
====== Evaluation of Forces ======<br />
<br />
Once contact between two spheres has been detected (see next figure), the forces occurring at the<br />
contact point are computed. The interaction between the two contacting spheres can be represented<br />
by forces Fij and Fji, which have the same module but opposite directions.<br />
<br />
<br />
[[File:Dem application interaction forces.png]]<br />
<br />
<br />
At the same time, this force Fij can be decomposed into its normal and shear components Fijn and Fijs, respectively. The next figure shows a detailed view of this decomposition.<br />
<br />
Fij = Fijn + Fijs = Fnnij + Fijs<br />
<br />
<br />
<br />
[[File:Dem application forces decomposition.png]]<br />
<br />
<br />
The nij vector represents the unitary normal with respect to the surfaces of both particles at the contact point. This vector lies along the line connecting the centers of the two particles and is directed<br />
outwards from particle i.<br />
<br />
<br />
The contact forces Fn, Fs1 and Fs2 are obtained using a constitutive model formulated for the<br />
contact between two rigid spheres (or discs in 2D). The contact interface for the simplest formulation<br />
is characterized by the normal and tangential stiffness Kn and Ks, respectively, a frictional device<br />
obeying the Couloumb law with a frictional coefficient mu, and a dashpot defined by the contact damping<br />
coefficient cn as shown in the next figure.<br />
<br />
<br />
[[File:Dem application forces rheological.png]]<br />
<br />
====== Rolling Friction ======<br />
<br />
In order to represent irregular particles with spheres, a numerical correction is used. This correction is the rolling friction which is about imposing a virtual moment opposite to particle rotation and dependent on its size.<br />
<br />
[[File:Rolling_friction_1.png|300px]]<br />
<br />
Fn represents the normal force, Ft the tangential force, ω the angular velocity, r is the radius and η the rolling friction coefficient.<br />
<br />
In our approach there are two importatn conditions that have to be fulfilled:<br />
<br />
1. The moment due to rolling friction can not change the direction of the particle spin.<br />
<br />
2. If the contact occurs between two spheres the rolling friction is calculated with the radius of the smallest spheres.<br />
<br />
[[File:Rolling_friction_surfaces.png|150px]]<br />
<br />
The model used is model A described in the following article: C.M. Wensrich, A. Katterfeld. Rolling friction as a technique for modelling particle shape in DEM. Powder Technology 217 (2012) 409–417.<br />
<br />
===== Continuum Strategy =====<br />
<br />
=== DEM schemes ===<br />
<br />
==== Integration of Motion ====<br />
<br />
<br />
The standard translational and rotational equations for the motion of rigid bodies are used to compute the dynamics of the spheres.<br />
For the i-th particle we have:<br />
<br />
miui = Fi<br />
Ii i = Ti<br />
<br />
<br />
where u represents the element centroid displacement in a xed (inertial) coordinate frame X, ! the angular<br />
velocity, m the element mass, I the moment of inertia, Fi the resultant force, and Ti the<br />
total moment arount the central axes.<br />
<br />
<br />
Vectors Fi and Ti are the sum of all forces and moments applied to the i-th particle due<br />
to external loads, Fexti and Texti , respectively, the contact interactions with neighbour spheres Fc<br />
i, j = 1;; nc, where nci is the total number of elements in contact with the i-th discrete<br />
element, and the forces and moments resulting from external damping, Fdampi and Tdampi , respectively,<br />
which can be written as:<br />
<br />
<br />
Fi = Fexti +nciXj=1Fci + Fdampi<br />
<br />
Ti = Texti +nciXj=1(rci Fci + qci ) + Tdampi<br />
<br />
<br />
[[File:Dem application motion.png]]<br />
<br />
<br />
where rci is the vector connecting the centre of mass of the i th particle with the contact point c<br />
(see next figure) and qci are the torques due to rolling and/or torsion (not related to the tangential forces).<br />
The next figure shows the motion of a rigid particle. Note that the form of the rotational equation is only valid for spheres and cylinders (in<br />
2D). It is simplified with respect to a general form for an arbitrary rigid body with the rotational<br />
inertial properties represented by a second order tensor. In the general case it is more convenient to<br />
describe the rotational motion with respect to a co-rotational frame x which is embedded at each<br />
element since in this frame the tensor of inertia is constant.<br />
<br />
<br />
The previous set of equations are integrated in time using a simple central difference scheme. The<br />
time integration operator for the translational motion at the n-th time step is as follows:<br />
<br />
<br />
The first two steps in the integration scheme for the rotational motion are identical to those given<br />
by the previous equations:<br />
<br />
<br />
On the other hand, the vector of incremental rotation is computed as<br />
<br />
<br />
Knowledge of the incremental rotation is enough to update the tangential contact forces. It is also<br />
possible to track the rotational position of particles when necessary. If that is the case, the rotation matrices between<br />
the moving frames embedded in the particles and the fixed global frame must be updated<br />
incrementally by using an adequate multiplicative scheme. <br />
Explicit integration in time yields high computational efficiency and it enables the solution of large<br />
models. The main disadvantage of this explicit integration scheme is its conditional numerical stability<br />
which imposes a limitation on the time step delta_t.<br />
<br />
=== Search Strategies ===<br />
<br />
The contact search is a very important part of the method in terms of computational cost (range 60%-90% of simulation time in simulations with large number of particles) and it is possibly the most difficult part to treat when dealing with particles that have no spherical/circular shape.<br />
<br />
The contact detection basically consists in determining, for every target object, what other objects are in contact with, and then, judge for the correspondent interaction. It is usually not needed to perform a search every time step, which is generally limited for the stability of the explicit integration of the equations of motion. Due to the fact that the search is an expensive step a lower search frequency can be selected without much loss of accuracy.<br />
<br />
The most naïve method of search that can be set is the brute search; for every element, the method does a loop for any other element checking for the contact. The order of the number of operations needed is quadratic: n2. Normally, the strategy to avoid such an expensive scheme is to divide the contact search in two basic stages, a global search and a subsequent local resolution of the contact; in this case the computation time of the contact search is proportional to n log n. In the DEMApplication a Grid/Cell based algorithm is used in this purpose.<br />
<br />
==== Global Search ====<br />
<br />
In a generic way, there are two types of elements: searcher elements (particles or finite elements) and target elements (particles or finite elements). Hereafter<br />
searcher elements will be called S.E. and target elements T.E.<br />
<br />
[[File:Global_search1.png]]<br />
<br />
The steps needed to perform contact search are:<br />
<br />
a) Build bounding box of S.E. (Figure 2(a)).<br />
<br />
b) Build bins cells based on size and position of S.E. (Figure 2(b)).<br />
<br />
c) Collocate S.E. in bins and construct hash table with relates coordinates with cells which point to the contacting S.E. (Figure 2(c)).<br />
<br />
d) Build bounding box of T.E. (Figure 2(d)).<br />
<br />
e) Loop over T.E., detect the intersecting cells to each T.E., check the intersection with the possible found cells and add the entire S.E. contained in the cells intersected by each T.E. (Figure 2(e)).<br />
<br />
f) Solve the contact with local resolution (Figure 2(f)).<br />
<br />
Note: In the case of FE and DE the FE are selected as the S.E. to construct the Bins and the Spheres are T.E. to be found in that bins. <br />
<br />
[[File:Global_search2_bigger.png]]<br />
<br />
==== Local Search ====<br />
<br />
Once the possible neighbours are detected, the local resolution check takes place. For the case of two spherical particles, the check is easy; only the sum of the radius has to be compared against the distance between centres. Other geometries may demand a much complicated check. The followed strategy is to mesh all the geometries with a discretization of triangles. In 3D, surface meshes are used for contact detection. Now, the contact detection should be performed between particles and triangles; if no contact is found, particle contact against lines is searched for; and if contact is still not found, contact against points is performed. Figure 3 shows how the local search is performed. Particle i searches contact against element j, then against lines k, l and m and finally against points n, o and p.<br />
<br />
This is known as a hierarchical algorithm. For further explanation please refer to the paper: "3D contact algorithms for particle DEM with rigid and deformable solids" - M.Santasusana, J.Irazábal, E.Oñate, J.M.Carbonell. Where the advantges and the drawback of this method and other proposed algorithms are detailed and analysed in complicated situations like multicontact.<br />
<br />
[[File:Local_search1.png]]<br />
<br />
Fig. 3 Particle-Face contact detection.<br />
<br />
<br />
<br />
== Programming Implementation ==<br />
Structure of the code (Strategy, Scheme, Element, Node, Utilities, functions frequently used like FastGet,...)<br />
<br />
The source code is accessible through [https://kratos.cimne.upc.es/projects/kratos/repository/show/kratos this site].<br />
<br />
[[File:Dem manual code scheme 66.png]]<br />
<br />
A main Python script is used to trigger the calculation of a problem. It communicates with the C++ code. The main advantage of using a compiled language like python for the main script is that it allows to make quick adjustments or modifications to the main strategy without having to recompile the whole code avery time. It also allows for very fast testing.<br />
<br />
=== Main components of the code ===<br />
<br />
<br />
==== Elements ====<br />
<br />
Discontinuum, continuum, cohesive, swimming, cluster of spheres.<br />
<br />
==== Conditions ====<br />
<br />
Main the FEM elements: lines and triangles.<br />
<br />
==== Constitutive Laws ====<br />
<br />
Different interactions laws: linear, Hertzian and continuum behaviour.<br />
<br />
==== Python elements ====<br />
<br />
These files translate the strategies and the<br />
utilities defined in C++ language to be able<br />
to call them from python scripts.<br />
<br />
==== Strategies====<br />
<br />
The main script that calls the necessary schemes and the functions on the<br />
elements during the global loop.<br />
<br />
==== Schemes====<br />
<br />
Explicit integration schemes available<br />
<br />
==== Utilities====<br />
<br />
Here geometric functions are defined, the<br />
neighbours search function, configuration of the particle, amongst others.<br />
<br />
==== Python scripts====<br />
<br />
It contains, amongst other things, the python interface where the main function<br />
of the strategy such as Initialize or Solve<br />
are called. Other operations are done like adding the necessary nodal variables.<br />
<br />
==== Test examples====<br />
<br />
They contain the Benchmarks<br />
<br />
==== DEM_application====<br />
<br />
It contains all the variables that will be used so they are<br />
created and registered in python and C++.<br />
<br />
<br />
=== Main elements in the global python script ===<br />
<br />
<br />
The first step consist of importing all libraries and files necessary for the application.<br />
It is followed by the creation of the necessary input files of the problem at hand to be<br />
processed by the code. The solver strategy is also chosen, and some important operations are done, as for example the<br />
addition of the necessary variables to the solver.<br />
<br />
At this step the buffer size value is also set: this number determines the<br />
historical database size of the problem. The values for the different options and<br />
variables that Kratos will use in the computation are also imported here, like the<br />
integration scheme, the type of solution, damping specifications, time step, output<br />
time step of results, et cetera.<br />
<br />
After the definition of the previous settings, several initializing functions are called which are responsible<br />
of initializing, among other objects, the mesh, the inlet of particles or the solver. Also some operations related to parallel<br />
computing are done.<br />
<br />
Afterwards the time step is determined and the main computing loop is entered. At each time step Solve function is executed.<br />
Finally, at user-defined time steps the results are exported to GiD so they become ready for visualization and postprocessing.<br />
<br />
In fact, this main python file imports another script written in the same language, the ''sphere_strategy.py''. This file is very important<br />
because it contains the core structure that the program will follow when computing. On the other hand, it is the last door to the kernel of the program, which<br />
is written in C++. Some of the most important functions and classes in this file are the following:<br />
<br />
AddVariables: This function sets which variables will be<br />
stored in the nodes at each time step and also the value of those variables as a function of time also<br />
depending on the buffer size previously selected. See the next figure for details.<br />
<br />
<br />
[[File:Dem application add variables 66.png]]<br />
<br />
<br />
AddDofs: The desired degrees of freedom are set here, as can be seen in the design of the function in the figure that follows.<br />
<br />
<br />
[[File:Dem application add dofs 66.png]]<br />
<br />
<br />
The global structure of the explicit strategy the code uses is shown in the next figure. It consists of three main parts: a constructor of the class of the solver,<br />
which creates some default variables for the class, an initializer, which defines the values of several standard parameters, and finally, the ''Solve()'' function<br />
which takes command of almost the entire computing process. The next figure shows and schematic view of this structure.<br />
<br />
When using the ''Initialize()'' function to set the original values of a group of variables, they automatically become accessible in the different C++ files<br />
of the application by means of the ProcessInfo container (see more at ). This function also calls the Initialize function in the solver.<br />
<br />
<br />
[[File:Dem application explicit strategy 66.png]]<br />
<br />
<br />
C++ Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve cpp scheme 80.png]]<br />
<br />
<br />
<br />
DEM CONTINUUM<br />
<br />
<br />
Initialize() function scheme:<br />
<br />
<br />
[[File:Dem application initialize scheme 75.png]]<br />
<br />
<br />
Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve scheme 75.png]]<br />
<br />
<br />
Search() function scheme:<br />
<br />
<br />
[[File:Dem application search scheme 66.png]]<br />
<br />
== Benchmarks ==<br />
<br />
The DEM Benchmarks consist of a set of 9 simple tests which are run every night and whose object is to make sure both that the application performs correctly and that the code did not break after the daily changes. They are the following:<br />
<br />
===Test1: Elastic normal impact of two identical spheres===<br />
<br />
Check the evolution of the elastic normal contact force between two spheres with time.<br />
<br />
<br />
<br />
[[File:Benchmark1_1.png]]<br />
[[File:Benchmark1 graph 66.png]]<br />
<br />
If the coefficient of restitution is 1, the module of the initial and final velocities should be the same. Also, by symmetry, velocities should be equal for both spheres.<br />
<br />
_<br />
<br />
===Test2: Elastic normal impact of a sphere against a rigid plane===<br />
<br />
Check the evolution of the elastic normal contact force between a sphere and a plane.<br />
<br />
[[File:Benchmark2 66.png]]<br />
[[File:Benchmark2 graph 66.png]]<br />
<br />
If the coefficient of restitution is equal to 1, the module of the initial and final velocity should remain unchanged.<br />
<br />
_<br />
<br />
===Test3: Impact of a sphere against a rigid plane with different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark3 66.png]]<br />
[[File:Benchmark3 graph 66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
_<br />
<br />
===Test4: Oblique impact of a sphere with a rigid plane with constant velocity module and variable incident angles===<br />
<br />
Check the tangential restitution coefficient, final angular velocity and rebound angle of the sphere.<br />
<br />
[[File:Benchmark4_66.png]]<br />
[[File:Benchmark4 graph1_66.png]]<br />
[[File:Benchmark4 graph2_66.png]]<br />
<br />
_<br />
<br />
===Test5: Oblique impact of a sphere with a rigid plane with constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark5_66.png]]<br />
[[File:Benchmark5 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test6: Impact of a sphere with a rigid plane with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark6_66.png]]<br />
[[File:Benchmark6 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test7: Impact of two identical spheres with a constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark7_66.png]]<br />
[[File:Benchmark7 graph1_66.png]]<br />
<br />
By symmetry, the tangential final velocity of both spheres should be zero. Additionally, for a coefficient of restitution of 1, there should be no changes in the modules of both linear and angular velocities and their values should conserve symmetry. <br />
<br />
_<br />
<br />
===Test8: Impact of two differently sized spheres with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark8_66.png]]<br />
[[File:Benchmark8_graph1_66.png]]<br />
<br />
In this case, it is interesting to note that, the bigger and/or denser sphere 2 is, the more this test resembles the sphere versus plane simulation.<br />
<br />
_<br />
<br />
===Test9: Impact of two identical spheres with a constant normal velocity and different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark9_66.png]]<br />
[[File:Benchmark9_graph1_66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
<br />
References:<br />
<br />
Y.C.Chung, J.Y.Ooi. ''Benchmark tests for verifying discrete element modelling codes at particle impact level'' (2011).<br />
<br />
== How to analyse using the current application ==<br />
<br />
=== Pre-Process ===<br />
<br />
GUI's & GiD<br />
<br />
===== D-DEMPack =====<br />
<br />
D-DEMPack is the package that allows a user to create, run and analyze results of a DEM simulation for discontinuum / granular / little-cohesive materials. It is written for GiD. So in order to use this package, you should install GiD first.<br />
<br />
You can read the [[D-DEMPack manual]] or follow the [[D-DEMPack Tutorials]] for fast learning on how to use the GUI.<br />
<br />
===== C-DEMPack =====<br />
<br />
Continuum / Cohesive <br />
<br />
===== F-DEMPack =====<br />
<br />
Fluid coupling<br />
<br />
=== Post-Process ===<br />
<br />
== Application Dependencies ==<br />
<br />
The Swimming DEM Application depends on the DEM application<br />
<br />
=== Other Kratos Applications used in current Application ===<br />
<br />
FEM-DEM<br />
<br />
<br />
== Problems! ==<br />
<br />
==== What to do if the Discrete Elements behave strangely ====<br />
<br />
In the case you notice that some discrete elements cross walls, penetrate in them or simply fly away of the domain at high velocity, check the following points:<br />
<br />
<br />
In the case of excessive penetration:<br />
<br />
*'''Check that the Young Modulus is big enough'''. A small Young Modulus makes the Elements and the walls behave in a very smooth way. Sometimes they are so soft that total penetration and trespass is possible.<br />
<br />
*'''Check the Density of the material'''. An excessive density means a big weight and inertia that cannot be stopped by the walls.<br />
*'''Check the Time Step'''. If the time step is too big, the Elements can go from one side of the wall to the other with no appearence of a reaction.<br />
*'''Check the frequency of neighbour search'''. If the search is not done frequently enough, the new contacts with the walls may not be detected soon enough.<br />
<br />
<br />
In the case of excessive bounce:<br />
<br />
*'''Check that the Young Modulus is not extremely big'''. An exaggerated Young Modulus yields extremely large reactions that can make the Elements bounce too fast in just one time step. Also take into account that the stability of explicit methods depends on the Young Modulus (the higher the modulus, the closer to instability).<br />
<br />
*'''Check the Density of the material'''. A very low density means a very small weight and inertia, so any force exerted by other elements or the walls can induce big accelerations on the element.<br />
*'''Check the Time Step'''. If the time step is too big, the method gains more energy, and gets closer to instability.<br />
*'''Check the restitution coefficient of the material'''. Explicit integration schemes gain energy noticeably, unless you use a really small time step. In case the time step is chosen to be big (but still stable), use the restitution coefficient to compensate the gain of energy and get more realistic results.<br />
<br />
== Contact ==<br />
<br />
Contact us for any question regarding this application:<br />
<br />
<br />
-Miguel Angel Celigueta: [mailto:maceli@cimne.upc.edu maceli@cimne.upc.edu]<br />
<br />
-Guillermo Casas: [mailto:gcasas@cimne.upc.edu gcasas@cimne.upc.edu]<br />
<br />
-Salva Latorre: [mailto:latorre@cimne.upc.edu latorre@cimne.upc.edu]<br />
<br />
-Miquel Santasusana: [mailto:msantasusana@cimne.upc.edu msantasusana@cimne.upc.edu]<br />
<br />
-Ferran Arrufat: [mailto:farrufat@cimne.upc.edu farrufat@cimne.upc.edu]<br />
<br />
<br />
[[Category: Applications]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/DEM_ApplicationDEM Application2016-02-07T18:34:27Z<p>Gcasas: /* JKR Cohesive Force */</p>
<hr />
<div>WARNING: This page is still under construction.<br />
<br />
The DEM Kratos Team<br />
<br />
<br />
== Theory ==<br />
<br />
The fundamental theoretical background corresponding to the discontinuous (granular matter) part of the code can be found Casas et al. (2015). The following sections are destined to contain this information though are curently still under construction.<br />
<br />
Nothing numerical<br />
<br />
=== Integration Schemes ===<br />
Forward Euler Scheme<br />
<br />
=== Contact Laws ===<br />
Concept of indentation<br />
HMD, LSD<br />
===== Normal Force Laws =====<br />
<br />
====== Linear Repulsive Force ======<br />
<br />
The most simple representation of a repulsive contact force between a sphere and a wall is given by a linear law, where the force acting on the sphere when contacting a plane is a linear function of the indentation, which in turn would bring a quadratic dependence with the contact radius. The next figure shows this simple law:<br />
<br />
[[File:Jkr cohesion linear force.png]]<br />
<br />
<br />
====== Hertzian Repulsive Force ======<br />
<br />
On the other hand, Hertz solved in 1882 the non-cohesive normal contact between a sphere and a plane. In 1971 Johnson, Kendall and Roberts presented the solution (JKR-Theory) for the same problem in this case adding cohesive behaviour. Not much later, Derjaguin, Müller and Toporov published similar results (DMT-Theory).<br />
<br />
Both theories are very close and correct, the main difference being that the JKR theory is oriented to the study of flexible, large spheres, while the DMT theory is specially suited to represent the behaviour of rigid, small ones.<br />
<br />
[[File:Jkr cohesion hertz.jpeg]]<br />
<br />
The previous figure shows the standard representation of a Linear or Hertzian contact between a sphere and a wall. The distribution of contact pressures between both bodies follow a parabolic law.<br />
<br />
====== JKR Cohesive Force ======<br />
<br />
[[File:Jkr cohesion jkr.jpeg]]<br />
<br />
The preceding capture shows the a representation of a JKR contact between a sphere and a wall. In this case, the distribution of pressures between both bodies is more complex due to the formation of a neck at the boundaries of the contact. A later figure shows a detailed view of the pressures involved.<br />
<br />
[[Image:Jkr cohesion forces.png]]<br />
<br />
In the previous graphic, it is very interesting to note the existence of two singular values of contact radius: one for which the total forces acting on the contacting sphere is zero, and another for which the maximum value of adhesion is achieved.<br />
<br />
<br />
[[File:Jkr cohesion pressures.png]]<br />
<br />
In the previous figure, the blue area represents the distribution of pressures acting on the sphere when contacting a wall if a Hertzian Law is followed. On the other hand, the sum of both green and blue areas represents the JKR distribution. Note the larger values and the existence of adhesive behaviour at both sides of the pressures distribution. <br />
<br />
<br />
An example of granular simulation without cohesive forces:<br />
<br />
[[File:JKR no cohesion 1 33.png]]<br />
[[File:JKR no cohesion 2 33.png]]<br />
[[File:JKR no cohesion 3 33.png]]<br />
[[File:JKR no cohesion 4 33.png]]<br />
<br />
The same simulation as before, this time with cohesive forces in both sphere-sphere and sphere-plane contacts.<br />
<br />
[[File:JKR cohesion 1 33.png]]<br />
[[File:JKR cohesion 2 33.png]]<br />
[[File:JKR cohesion 3 33.png]]<br />
[[File:JKR cohesion 4 33.png]]<br />
<br />
<br />
References:<br />
<br />
V. L. Popov. "Contact Mechanics and Friction" (2010).<br />
G. Casaset al. "A modular, partitioned, discrete element framework for industrial grain distribution systems with rotating machinery." Computational Particle Mechanics (2015): 1-18.<br />
<br />
===== Tangential Force Laws =====<br />
<br />
===== Damping Force Laws =====<br />
<br />
(restit. coef)<br />
<br />
== Numerical approach ==<br />
<br />
This section is dedicated to describe the numerical methods used to solve.<br />
<br />
<br />
[[File:Dem manual main scheme 66 discontinuum.png]]<br />
<br />
<br />
=== DEM elements ===<br />
<br />
===== Spheric Particle =====<br />
===== Spheric Continuum Particle =====<br />
===== Spheric Swimming Particle =====<br />
<br />
=== DEM walls (Kratos Conditions) ===<br />
<br />
=== DEM Inlets ===<br />
<br />
A DEM Inlet is a source of new DEM Elements. It is a cloud of Nodes, where each Node is the center of a ''Generator Sphere''. In a random order, the Nodes are chosen to create a new DEM Element (both spherical elements or clusters) whose center coincides with the Node and the whole element is fully included in the ''Generator Sphere''. The newly generated DEM Element has a non-zero imposed velocity which eventually makes the DEM Element get outside the ''Generator Sphere''. Until this moment, the ''Generator Sphere'' is not allowed to generate another DEM-Element. From this moment on, the newly created DEM Element no longer has the velocity imposed, it moves freely and cannot penetrate its ''Generator Sphere'' or any other. Actually, the ''Generator Sphere'' is seen as any other Sphere of the domain by the DEM Element, and the contact between them is calculated as usual. In other words, '''the Generator Spheres reserve the necessary space to create a new DEM Element'''.<br />
<br />
=== DEM strategies ===<br />
<br />
===== Non-cohesive materials Strategy =====<br />
<br />
====== Evaluation of Forces ======<br />
<br />
Once contact between two spheres has been detected (see next figure), the forces occurring at the<br />
contact point are computed. The interaction between the two contacting spheres can be represented<br />
by forces Fij and Fji, which have the same module but opposite directions.<br />
<br />
<br />
[[File:Dem application interaction forces.png]]<br />
<br />
<br />
At the same time, this force Fij can be decomposed into its normal and shear components Fijn and Fijs, respectively. The next figure shows a detailed view of this decomposition.<br />
<br />
Fij = Fijn + Fijs = Fnnij + Fijs<br />
<br />
<br />
<br />
[[File:Dem application forces decomposition.png]]<br />
<br />
<br />
The nij vector represents the unitary normal with respect to the surfaces of both particles at the contact point. This vector lies along the line connecting the centers of the two particles and is directed<br />
outwards from particle i.<br />
<br />
<br />
The contact forces Fn, Fs1 and Fs2 are obtained using a constitutive model formulated for the<br />
contact between two rigid spheres (or discs in 2D). The contact interface for the simplest formulation<br />
is characterized by the normal and tangential stiffness Kn and Ks, respectively, a frictional device<br />
obeying the Couloumb law with a frictional coefficient mu, and a dashpot defined by the contact damping<br />
coefficient cn as shown in the next figure.<br />
<br />
<br />
[[File:Dem application forces rheological.png]]<br />
<br />
====== Rolling Friction ======<br />
<br />
In order to represent irregular particles with spheres, a numerical correction is used. This correction is the rolling friction which is about imposing a virtual moment opposite to particle rotation and dependent on its size.<br />
<br />
[[File:Rolling_friction_1.png|300px]]<br />
<br />
Fn represents the normal force, Ft the tangential force, ω the angular velocity, r is the radius and η the rolling friction coefficient.<br />
<br />
In our approach there are two importatn conditions that have to be fulfilled:<br />
<br />
1. The moment due to rolling friction can not change the direction of the particle spin.<br />
<br />
2. If the contact occurs between two spheres the rolling friction is calculated with the radius of the smallest spheres.<br />
<br />
[[File:Rolling_friction_surfaces.png|150px]]<br />
<br />
The model used is model A described in the following article: C.M. Wensrich, A. Katterfeld. Rolling friction as a technique for modelling particle shape in DEM. Powder Technology 217 (2012) 409–417.<br />
<br />
===== Continuum Strategy =====<br />
<br />
=== DEM schemes ===<br />
<br />
==== Integration of Motion ====<br />
<br />
<br />
The standard translational and rotational equations for the motion of rigid bodies are used to compute the dynamics of the spheres.<br />
For the i-th particle we have:<br />
<br />
miui = Fi<br />
Ii i = Ti<br />
<br />
<br />
where u represents the element centroid displacement in a xed (inertial) coordinate frame X, ! the angular<br />
velocity, m the element mass, I the moment of inertia, Fi the resultant force, and Ti the<br />
total moment arount the central axes.<br />
<br />
<br />
Vectors Fi and Ti are the sum of all forces and moments applied to the i-th particle due<br />
to external loads, Fexti and Texti , respectively, the contact interactions with neighbour spheres Fc<br />
i, j = 1;; nc, where nci is the total number of elements in contact with the i-th discrete<br />
element, and the forces and moments resulting from external damping, Fdampi and Tdampi , respectively,<br />
which can be written as:<br />
<br />
<br />
Fi = Fexti +nciXj=1Fci + Fdampi<br />
<br />
Ti = Texti +nciXj=1(rci Fci + qci ) + Tdampi<br />
<br />
<br />
[[File:Dem application motion.png]]<br />
<br />
<br />
where rci is the vector connecting the centre of mass of the i th particle with the contact point c<br />
(see next figure) and qci are the torques due to rolling and/or torsion (not related to the tangential forces).<br />
The next figure shows the motion of a rigid particle. Note that the form of the rotational equation is only valid for spheres and cylinders (in<br />
2D). It is simplified with respect to a general form for an arbitrary rigid body with the rotational<br />
inertial properties represented by a second order tensor. In the general case it is more convenient to<br />
describe the rotational motion with respect to a co-rotational frame x which is embedded at each<br />
element since in this frame the tensor of inertia is constant.<br />
<br />
<br />
The previous set of equations are integrated in time using a simple central difference scheme. The<br />
time integration operator for the translational motion at the n-th time step is as follows:<br />
<br />
<br />
The first two steps in the integration scheme for the rotational motion are identical to those given<br />
by the previous equations:<br />
<br />
<br />
On the other hand, the vector of incremental rotation is computed as<br />
<br />
<br />
Knowledge of the incremental rotation is enough to update the tangential contact forces. It is also<br />
possible to track the rotational position of particles when necessary. If that is the case, the rotation matrices between<br />
the moving frames embedded in the particles and the fixed global frame must be updated<br />
incrementally by using an adequate multiplicative scheme. <br />
Explicit integration in time yields high computational efficiency and it enables the solution of large<br />
models. The main disadvantage of this explicit integration scheme is its conditional numerical stability<br />
which imposes a limitation on the time step delta_t.<br />
<br />
=== Search Strategies ===<br />
<br />
The contact search is a very important part of the method in terms of computational cost (range 60%-90% of simulation time in simulations with large number of particles) and it is possibly the most difficult part to treat when dealing with particles that have no spherical/circular shape.<br />
<br />
The contact detection basically consists in determining, for every target object, what other objects are in contact with, and then, judge for the correspondent interaction. It is usually not needed to perform a search every time step, which is generally limited for the stability of the explicit integration of the equations of motion. Due to the fact that the search is an expensive step a lower search frequency can be selected without much loss of accuracy.<br />
<br />
The most naïve method of search that can be set is the brute search; for every element, the method does a loop for any other element checking for the contact. The order of the number of operations needed is quadratic: n2. Normally, the strategy to avoid such an expensive scheme is to divide the contact search in two basic stages, a global search and a subsequent local resolution of the contact; in this case the computation time of the contact search is proportional to n log n. In the DEMApplication a Grid/Cell based algorithm is used in this purpose.<br />
<br />
==== Global Search ====<br />
<br />
In a generic way, there are two types of elements: searcher elements (particles or finite elements) and target elements (particles or finite elements). Hereafter<br />
searcher elements will be called S.E. and target elements T.E.<br />
<br />
[[File:Global_search1.png]]<br />
<br />
The steps needed to perform contact search are:<br />
<br />
a) Build bounding box of S.E. (Figure 2(a)).<br />
<br />
b) Build bins cells based on size and position of S.E. (Figure 2(b)).<br />
<br />
c) Collocate S.E. in bins and construct hash table with relates coordinates with cells which point to the contacting S.E. (Figure 2(c)).<br />
<br />
d) Build bounding box of T.E. (Figure 2(d)).<br />
<br />
e) Loop over T.E., detect the intersecting cells to each T.E., check the intersection with the possible found cells and add the entire S.E. contained in the cells intersected by each T.E. (Figure 2(e)).<br />
<br />
f) Solve the contact with local resolution (Figure 2(f)).<br />
<br />
Note: In the case of FE and DE the FE are selected as the S.E. to construct the Bins and the Spheres are T.E. to be found in that bins. <br />
<br />
[[File:Global_search2_bigger.png]]<br />
<br />
==== Local Search ====<br />
<br />
Once the possible neighbours are detected, the local resolution check takes place. For the case of two spherical particles, the check is easy; only the sum of the radius has to be compared against the distance between centres. Other geometries may demand a much complicated check. The followed strategy is to mesh all the geometries with a discretization of triangles. In 3D, surface meshes are used for contact detection. Now, the contact detection should be performed between particles and triangles; if no contact is found, particle contact against lines is searched for; and if contact is still not found, contact against points is performed. Figure 3 shows how the local search is performed. Particle i searches contact against element j, then against lines k, l and m and finally against points n, o and p.<br />
<br />
This is known as a hierarchical algorithm. For further explanation please refer to the paper: "3D contact algorithms for particle DEM with rigid and deformable solids" - M.Santasusana, J.Irazábal, E.Oñate, J.M.Carbonell. Where the advantges and the drawback of this method and other proposed algorithms are detailed and analysed in complicated situations like multicontact.<br />
<br />
[[File:Local_search1.png]]<br />
<br />
Fig. 3 Particle-Face contact detection.<br />
<br />
<br />
<br />
== Programming Implementation ==<br />
Structure of the code (Strategy, Scheme, Element, Node, Utilities, functions frequently used like FastGet,...)<br />
<br />
The source code is accessible through [https://kratos.cimne.upc.es/projects/kratos/repository/show/kratos this site].<br />
<br />
[[File:Dem manual code scheme 66.png]]<br />
<br />
A main Python script is used to trigger the calculation of a problem. It communicates with the C++ code. The main advantage of using a compiled language like python for the main script is that it allows to make quick adjustments or modifications to the main strategy without having to recompile the whole code avery time. It also allows for very fast testing.<br />
<br />
=== Main components of the code ===<br />
<br />
<br />
==== Elements ====<br />
<br />
Discontinuum, continuum, cohesive, swimming, cluster of spheres.<br />
<br />
==== Conditions ====<br />
<br />
Main the FEM elements: lines and triangles.<br />
<br />
==== Constitutive Laws ====<br />
<br />
Different interactions laws: linear, Hertzian and continuum behaviour.<br />
<br />
==== Python elements ====<br />
<br />
These files translate the strategies and the<br />
utilities defined in C++ language to be able<br />
to call them from python scripts.<br />
<br />
==== Strategies====<br />
<br />
The main script that calls the necessary schemes and the functions on the<br />
elements during the global loop.<br />
<br />
==== Schemes====<br />
<br />
Explicit integration schemes available<br />
<br />
==== Utilities====<br />
<br />
Here geometric functions are defined, the<br />
neighbours search function, configuration of the particle, amongst others.<br />
<br />
==== Python scripts====<br />
<br />
It contains, amongst other things, the python interface where the main function<br />
of the strategy such as Initialize or Solve<br />
are called. Other operations are done like adding the necessary nodal variables.<br />
<br />
==== Test examples====<br />
<br />
They contain the Benchmarks<br />
<br />
==== DEM_application====<br />
<br />
It contains all the variables that will be used so they are<br />
created and registered in python and C++.<br />
<br />
<br />
=== Main elements in the global python script ===<br />
<br />
<br />
The first step consist of importing all libraries and files necessary for the application.<br />
It is followed by the creation of the necessary input files of the problem at hand to be<br />
processed by the code. The solver strategy is also chosen, and some important operations are done, as for example the<br />
addition of the necessary variables to the solver.<br />
<br />
At this step the buffer size value is also set: this number determines the<br />
historical database size of the problem. The values for the different options and<br />
variables that Kratos will use in the computation are also imported here, like the<br />
integration scheme, the type of solution, damping specifications, time step, output<br />
time step of results, et cetera.<br />
<br />
After the definition of the previous settings, several initializing functions are called which are responsible<br />
of initializing, among other objects, the mesh, the inlet of particles or the solver. Also some operations related to parallel<br />
computing are done.<br />
<br />
Afterwards the time step is determined and the main computing loop is entered. At each time step Solve function is executed.<br />
Finally, at user-defined time steps the results are exported to GiD so they become ready for visualization and postprocessing.<br />
<br />
In fact, this main python file imports another script written in the same language, the ''sphere_strategy.py''. This file is very important<br />
because it contains the core structure that the program will follow when computing. On the other hand, it is the last door to the kernel of the program, which<br />
is written in C++. Some of the most important functions and classes in this file are the following:<br />
<br />
AddVariables: This function sets which variables will be<br />
stored in the nodes at each time step and also the value of those variables as a function of time also<br />
depending on the buffer size previously selected. See the next figure for details.<br />
<br />
<br />
[[File:Dem application add variables 66.png]]<br />
<br />
<br />
AddDofs: The desired degrees of freedom are set here, as can be seen in the design of the function in the figure that follows.<br />
<br />
<br />
[[File:Dem application add dofs 66.png]]<br />
<br />
<br />
The global structure of the explicit strategy the code uses is shown in the next figure. It consists of three main parts: a constructor of the class of the solver,<br />
which creates some default variables for the class, an initializer, which defines the values of several standard parameters, and finally, the ''Solve()'' function<br />
which takes command of almost the entire computing process. The next figure shows and schematic view of this structure.<br />
<br />
When using the ''Initialize()'' function to set the original values of a group of variables, they automatically become accessible in the different C++ files<br />
of the application by means of the ProcessInfo container (see more at ). This function also calls the Initialize function in the solver.<br />
<br />
<br />
[[File:Dem application explicit strategy 66.png]]<br />
<br />
<br />
C++ Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve cpp scheme 80.png]]<br />
<br />
<br />
<br />
DEM CONTINUUM<br />
<br />
<br />
Initialize() function scheme:<br />
<br />
<br />
[[File:Dem application initialize scheme 75.png]]<br />
<br />
<br />
Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve scheme 75.png]]<br />
<br />
<br />
Search() function scheme:<br />
<br />
<br />
[[File:Dem application search scheme 66.png]]<br />
<br />
== Benchmarks ==<br />
<br />
The DEM Benchmarks consist of a set of 9 simple tests which are run every night and whose object is to make sure both that the application performs correctly and that the code did not break after the daily changes. They are the following:<br />
<br />
===Test1: Elastic normal impact of two identical spheres===<br />
<br />
Check the evolution of the elastic normal contact force between two spheres with time.<br />
<br />
<br />
<br />
[[File:Benchmark1_1.png]]<br />
[[File:Benchmark1 graph 66.png]]<br />
<br />
If the coefficient of restitution is 1, the module of the initial and final velocities should be the same. Also, by symmetry, velocities should be equal for both spheres.<br />
<br />
_<br />
<br />
===Test2: Elastic normal impact of a sphere against a rigid plane===<br />
<br />
Check the evolution of the elastic normal contact force between a sphere and a plane.<br />
<br />
[[File:Benchmark2 66.png]]<br />
[[File:Benchmark2 graph 66.png]]<br />
<br />
If the coefficient of restitution is equal to 1, the module of the initial and final velocity should remain unchanged.<br />
<br />
_<br />
<br />
===Test3: Impact of a sphere against a rigid plane with different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark3 66.png]]<br />
[[File:Benchmark3 graph 66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
_<br />
<br />
===Test4: Oblique impact of a sphere with a rigid plane with constant velocity module and variable incident angles===<br />
<br />
Check the tangential restitution coefficient, final angular velocity and rebound angle of the sphere.<br />
<br />
[[File:Benchmark4_66.png]]<br />
[[File:Benchmark4 graph1_66.png]]<br />
[[File:Benchmark4 graph2_66.png]]<br />
<br />
_<br />
<br />
===Test5: Oblique impact of a sphere with a rigid plane with constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark5_66.png]]<br />
[[File:Benchmark5 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test6: Impact of a sphere with a rigid plane with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark6_66.png]]<br />
[[File:Benchmark6 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test7: Impact of two identical spheres with a constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark7_66.png]]<br />
[[File:Benchmark7 graph1_66.png]]<br />
<br />
By symmetry, the tangential final velocity of both spheres should be zero. Additionally, for a coefficient of restitution of 1, there should be no changes in the modules of both linear and angular velocities and their values should conserve symmetry. <br />
<br />
_<br />
<br />
===Test8: Impact of two differently sized spheres with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark8_66.png]]<br />
[[File:Benchmark8_graph1_66.png]]<br />
<br />
In this case, it is interesting to note that, the bigger and/or denser sphere 2 is, the more this test resembles the sphere versus plane simulation.<br />
<br />
_<br />
<br />
===Test9: Impact of two identical spheres with a constant normal velocity and different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark9_66.png]]<br />
[[File:Benchmark9_graph1_66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
<br />
References:<br />
<br />
Y.C.Chung, J.Y.Ooi. ''Benchmark tests for verifying discrete element modelling codes at particle impact level'' (2011).<br />
<br />
== How to analyse using the current application ==<br />
<br />
=== Pre-Process ===<br />
<br />
GUI's & GiD<br />
<br />
===== D-DEMPack =====<br />
<br />
D-DEMPack is the package that allows a user to create, run and analyze results of a DEM simulation for discontinuum / granular / little-cohesive materials. It is written for GiD. So in order to use this package, you should install GiD first.<br />
<br />
You can read the [[D-DEMPack manual]] or follow the [[D-DEMPack Tutorials]] for fast learning on how to use the GUI.<br />
<br />
===== C-DEMPack =====<br />
<br />
Continuum / Cohesive <br />
<br />
===== F-DEMPack =====<br />
<br />
Fluid coupling<br />
<br />
=== Post-Process ===<br />
<br />
== Application Dependencies ==<br />
<br />
The Swimming DEM Application depends on the DEM application<br />
<br />
=== Other Kratos Applications used in current Application ===<br />
<br />
FEM-DEM<br />
<br />
<br />
== Problems! ==<br />
<br />
==== What to do if the Discrete Elements behave strangely ====<br />
<br />
In the case you notice that some discrete elements cross walls, penetrate in them or simply fly away of the domain at high velocity, check the following points:<br />
<br />
<br />
In the case of excessive penetration:<br />
<br />
*'''Check that the Young Modulus is big enough'''. A small Young Modulus makes the Elements and the walls behave in a very smooth way. Sometimes they are so soft that total penetration and trespass is possible.<br />
<br />
*'''Check the Density of the material'''. An excessive density means a big weight and inertia that cannot be stopped by the walls.<br />
*'''Check the Time Step'''. If the time step is too big, the Elements can go from one side of the wall to the other with no appearence of a reaction.<br />
*'''Check the frequency of neighbour search'''. If the search is not done frequently enough, the new contacts with the walls may not be detected soon enough.<br />
<br />
<br />
In the case of excessive bounce:<br />
<br />
*'''Check that the Young Modulus is not extremely big'''. An exaggerated Young Modulus yields extremely large reactions that can make the Elements bounce too fast in just one time step. Also take into account that the stability of explicit methods depends on the Young Modulus (the higher the modulus, the closer to instability).<br />
<br />
*'''Check the Density of the material'''. A very low density means a very small weight and inertia, so any force exerted by other elements or the walls can induce big accelerations on the element.<br />
*'''Check the Time Step'''. If the time step is too big, the method gains more energy, and gets closer to instability.<br />
*'''Check the restitution coefficient of the material'''. Explicit integration schemes gain energy noticeably, unless you use a really small time step. In case the time step is chosen to be big (but still stable), use the restitution coefficient to compensate the gain of energy and get more realistic results.<br />
<br />
== Contact ==<br />
<br />
Contact us for any question regarding this application:<br />
<br />
<br />
-Miguel Angel Celigueta: [mailto:maceli@cimne.upc.edu maceli@cimne.upc.edu]<br />
<br />
-Guillermo Casas: [mailto:gcasas@cimne.upc.edu gcasas@cimne.upc.edu]<br />
<br />
-Salva Latorre: [mailto:latorre@cimne.upc.edu latorre@cimne.upc.edu]<br />
<br />
-Miquel Santasusana: [mailto:msantasusana@cimne.upc.edu msantasusana@cimne.upc.edu]<br />
<br />
-Ferran Arrufat: [mailto:farrufat@cimne.upc.edu farrufat@cimne.upc.edu]<br />
<br />
<br />
[[Category: Applications]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/DEM_ApplicationDEM Application2016-02-07T18:31:53Z<p>Gcasas: /* Theory */</p>
<hr />
<div>WARNING: This page is still under construction.<br />
<br />
The DEM Kratos Team<br />
<br />
<br />
== Theory ==<br />
<br />
The fundamental theoretical background corresponding to the discontinuous (granular matter) part of the code can be found Casas et al. (2015). The following sections are destined to contain this information though are curently still under construction.<br />
<br />
Nothing numerical<br />
<br />
=== Integration Schemes ===<br />
Forward Euler Scheme<br />
<br />
=== Contact Laws ===<br />
Concept of indentation<br />
HMD, LSD<br />
===== Normal Force Laws =====<br />
<br />
====== Linear Repulsive Force ======<br />
<br />
The most simple representation of a repulsive contact force between a sphere and a wall is given by a linear law, where the force acting on the sphere when contacting a plane is a linear function of the indentation, which in turn would bring a quadratic dependence with the contact radius. The next figure shows this simple law:<br />
<br />
[[File:Jkr cohesion linear force.png]]<br />
<br />
<br />
====== Hertzian Repulsive Force ======<br />
<br />
On the other hand, Hertz solved in 1882 the non-cohesive normal contact between a sphere and a plane. In 1971 Johnson, Kendall and Roberts presented the solution (JKR-Theory) for the same problem in this case adding cohesive behaviour. Not much later, Derjaguin, Müller and Toporov published similar results (DMT-Theory).<br />
<br />
Both theories are very close and correct, the main difference being that the JKR theory is oriented to the study of flexible, large spheres, while the DMT theory is specially suited to represent the behaviour of rigid, small ones.<br />
<br />
[[File:Jkr cohesion hertz.jpeg]]<br />
<br />
The previous figure shows the standard representation of a Linear or Hertzian contact between a sphere and a wall. The distribution of contact pressures between both bodies follow a parabolic law.<br />
<br />
====== JKR Cohesive Force ======<br />
<br />
[[File:Jkr cohesion jkr.jpeg]]<br />
<br />
The preceding capture shows the a representation of a JKR contact between a sphere and a wall. In this case, the distribution of pressures between both bodies is more complex due to the formation of a neck at the boundaries of the contact. A later figure shows a detailed view of the pressures involved.<br />
<br />
[[Image:Jkr cohesion forces.png]]<br />
<br />
In the previous graphic, it is very interesting to note the existence of two singular values of contact radius: one for which the total forces acting on the contacting sphere is zero, and another for which the maximum value of adhesion is achieved.<br />
<br />
<br />
[[File:Jkr cohesion pressures.png]]<br />
<br />
In the previous figure, the blue area represents the distribution of pressures acting on the sphere when contacting a wall if a Hertzian Law is followed. On the other hand, the sum of both green and blue areas represents the JKR distribution. Note the larger values and the existence of adhesive behaviour at both sides of the pressures distribution. <br />
<br />
<br />
An example of granular simulation without cohesive forces:<br />
<br />
[[File:JKR no cohesion 1 33.png]]<br />
[[File:JKR no cohesion 2 33.png]]<br />
[[File:JKR no cohesion 3 33.png]]<br />
[[File:JKR no cohesion 4 33.png]]<br />
<br />
The same simulation as before, this time with cohesive forces in both sphere-sphere and sphere-plane contacts.<br />
<br />
[[File:JKR cohesion 1 33.png]]<br />
[[File:JKR cohesion 2 33.png]]<br />
[[File:JKR cohesion 3 33.png]]<br />
[[File:JKR cohesion 4 33.png]]<br />
<br />
<br />
References:<br />
<br />
V. L. Popov. ''Contact Mechanics and Friction'' (2010).<br />
<br />
===== Tangential Force Laws =====<br />
<br />
===== Damping Force Laws =====<br />
<br />
(restit. coef)<br />
<br />
== Numerical approach ==<br />
<br />
This section is dedicated to describe the numerical methods used to solve.<br />
<br />
<br />
[[File:Dem manual main scheme 66 discontinuum.png]]<br />
<br />
<br />
=== DEM elements ===<br />
<br />
===== Spheric Particle =====<br />
===== Spheric Continuum Particle =====<br />
===== Spheric Swimming Particle =====<br />
<br />
=== DEM walls (Kratos Conditions) ===<br />
<br />
=== DEM Inlets ===<br />
<br />
A DEM Inlet is a source of new DEM Elements. It is a cloud of Nodes, where each Node is the center of a ''Generator Sphere''. In a random order, the Nodes are chosen to create a new DEM Element (both spherical elements or clusters) whose center coincides with the Node and the whole element is fully included in the ''Generator Sphere''. The newly generated DEM Element has a non-zero imposed velocity which eventually makes the DEM Element get outside the ''Generator Sphere''. Until this moment, the ''Generator Sphere'' is not allowed to generate another DEM-Element. From this moment on, the newly created DEM Element no longer has the velocity imposed, it moves freely and cannot penetrate its ''Generator Sphere'' or any other. Actually, the ''Generator Sphere'' is seen as any other Sphere of the domain by the DEM Element, and the contact between them is calculated as usual. In other words, '''the Generator Spheres reserve the necessary space to create a new DEM Element'''.<br />
<br />
=== DEM strategies ===<br />
<br />
===== Non-cohesive materials Strategy =====<br />
<br />
====== Evaluation of Forces ======<br />
<br />
Once contact between two spheres has been detected (see next figure), the forces occurring at the<br />
contact point are computed. The interaction between the two contacting spheres can be represented<br />
by forces Fij and Fji, which have the same module but opposite directions.<br />
<br />
<br />
[[File:Dem application interaction forces.png]]<br />
<br />
<br />
At the same time, this force Fij can be decomposed into its normal and shear components Fijn and Fijs, respectively. The next figure shows a detailed view of this decomposition.<br />
<br />
Fij = Fijn + Fijs = Fnnij + Fijs<br />
<br />
<br />
<br />
[[File:Dem application forces decomposition.png]]<br />
<br />
<br />
The nij vector represents the unitary normal with respect to the surfaces of both particles at the contact point. This vector lies along the line connecting the centers of the two particles and is directed<br />
outwards from particle i.<br />
<br />
<br />
The contact forces Fn, Fs1 and Fs2 are obtained using a constitutive model formulated for the<br />
contact between two rigid spheres (or discs in 2D). The contact interface for the simplest formulation<br />
is characterized by the normal and tangential stiffness Kn and Ks, respectively, a frictional device<br />
obeying the Couloumb law with a frictional coefficient mu, and a dashpot defined by the contact damping<br />
coefficient cn as shown in the next figure.<br />
<br />
<br />
[[File:Dem application forces rheological.png]]<br />
<br />
====== Rolling Friction ======<br />
<br />
In order to represent irregular particles with spheres, a numerical correction is used. This correction is the rolling friction which is about imposing a virtual moment opposite to particle rotation and dependent on its size.<br />
<br />
[[File:Rolling_friction_1.png|300px]]<br />
<br />
Fn represents the normal force, Ft the tangential force, ω the angular velocity, r is the radius and η the rolling friction coefficient.<br />
<br />
In our approach there are two importatn conditions that have to be fulfilled:<br />
<br />
1. The moment due to rolling friction can not change the direction of the particle spin.<br />
<br />
2. If the contact occurs between two spheres the rolling friction is calculated with the radius of the smallest spheres.<br />
<br />
[[File:Rolling_friction_surfaces.png|150px]]<br />
<br />
The model used is model A described in the following article: C.M. Wensrich, A. Katterfeld. Rolling friction as a technique for modelling particle shape in DEM. Powder Technology 217 (2012) 409–417.<br />
<br />
===== Continuum Strategy =====<br />
<br />
=== DEM schemes ===<br />
<br />
==== Integration of Motion ====<br />
<br />
<br />
The standard translational and rotational equations for the motion of rigid bodies are used to compute the dynamics of the spheres.<br />
For the i-th particle we have:<br />
<br />
miui = Fi<br />
Ii i = Ti<br />
<br />
<br />
where u represents the element centroid displacement in a xed (inertial) coordinate frame X, ! the angular<br />
velocity, m the element mass, I the moment of inertia, Fi the resultant force, and Ti the<br />
total moment arount the central axes.<br />
<br />
<br />
Vectors Fi and Ti are the sum of all forces and moments applied to the i-th particle due<br />
to external loads, Fexti and Texti , respectively, the contact interactions with neighbour spheres Fc<br />
i, j = 1;; nc, where nci is the total number of elements in contact with the i-th discrete<br />
element, and the forces and moments resulting from external damping, Fdampi and Tdampi , respectively,<br />
which can be written as:<br />
<br />
<br />
Fi = Fexti +nciXj=1Fci + Fdampi<br />
<br />
Ti = Texti +nciXj=1(rci Fci + qci ) + Tdampi<br />
<br />
<br />
[[File:Dem application motion.png]]<br />
<br />
<br />
where rci is the vector connecting the centre of mass of the i th particle with the contact point c<br />
(see next figure) and qci are the torques due to rolling and/or torsion (not related to the tangential forces).<br />
The next figure shows the motion of a rigid particle. Note that the form of the rotational equation is only valid for spheres and cylinders (in<br />
2D). It is simplified with respect to a general form for an arbitrary rigid body with the rotational<br />
inertial properties represented by a second order tensor. In the general case it is more convenient to<br />
describe the rotational motion with respect to a co-rotational frame x which is embedded at each<br />
element since in this frame the tensor of inertia is constant.<br />
<br />
<br />
The previous set of equations are integrated in time using a simple central difference scheme. The<br />
time integration operator for the translational motion at the n-th time step is as follows:<br />
<br />
<br />
The first two steps in the integration scheme for the rotational motion are identical to those given<br />
by the previous equations:<br />
<br />
<br />
On the other hand, the vector of incremental rotation is computed as<br />
<br />
<br />
Knowledge of the incremental rotation is enough to update the tangential contact forces. It is also<br />
possible to track the rotational position of particles when necessary. If that is the case, the rotation matrices between<br />
the moving frames embedded in the particles and the fixed global frame must be updated<br />
incrementally by using an adequate multiplicative scheme. <br />
Explicit integration in time yields high computational efficiency and it enables the solution of large<br />
models. The main disadvantage of this explicit integration scheme is its conditional numerical stability<br />
which imposes a limitation on the time step delta_t.<br />
<br />
=== Search Strategies ===<br />
<br />
The contact search is a very important part of the method in terms of computational cost (range 60%-90% of simulation time in simulations with large number of particles) and it is possibly the most difficult part to treat when dealing with particles that have no spherical/circular shape.<br />
<br />
The contact detection basically consists in determining, for every target object, what other objects are in contact with, and then, judge for the correspondent interaction. It is usually not needed to perform a search every time step, which is generally limited for the stability of the explicit integration of the equations of motion. Due to the fact that the search is an expensive step a lower search frequency can be selected without much loss of accuracy.<br />
<br />
The most naïve method of search that can be set is the brute search; for every element, the method does a loop for any other element checking for the contact. The order of the number of operations needed is quadratic: n2. Normally, the strategy to avoid such an expensive scheme is to divide the contact search in two basic stages, a global search and a subsequent local resolution of the contact; in this case the computation time of the contact search is proportional to n log n. In the DEMApplication a Grid/Cell based algorithm is used in this purpose.<br />
<br />
==== Global Search ====<br />
<br />
In a generic way, there are two types of elements: searcher elements (particles or finite elements) and target elements (particles or finite elements). Hereafter<br />
searcher elements will be called S.E. and target elements T.E.<br />
<br />
[[File:Global_search1.png]]<br />
<br />
The steps needed to perform contact search are:<br />
<br />
a) Build bounding box of S.E. (Figure 2(a)).<br />
<br />
b) Build bins cells based on size and position of S.E. (Figure 2(b)).<br />
<br />
c) Collocate S.E. in bins and construct hash table with relates coordinates with cells which point to the contacting S.E. (Figure 2(c)).<br />
<br />
d) Build bounding box of T.E. (Figure 2(d)).<br />
<br />
e) Loop over T.E., detect the intersecting cells to each T.E., check the intersection with the possible found cells and add the entire S.E. contained in the cells intersected by each T.E. (Figure 2(e)).<br />
<br />
f) Solve the contact with local resolution (Figure 2(f)).<br />
<br />
Note: In the case of FE and DE the FE are selected as the S.E. to construct the Bins and the Spheres are T.E. to be found in that bins. <br />
<br />
[[File:Global_search2_bigger.png]]<br />
<br />
==== Local Search ====<br />
<br />
Once the possible neighbours are detected, the local resolution check takes place. For the case of two spherical particles, the check is easy; only the sum of the radius has to be compared against the distance between centres. Other geometries may demand a much complicated check. The followed strategy is to mesh all the geometries with a discretization of triangles. In 3D, surface meshes are used for contact detection. Now, the contact detection should be performed between particles and triangles; if no contact is found, particle contact against lines is searched for; and if contact is still not found, contact against points is performed. Figure 3 shows how the local search is performed. Particle i searches contact against element j, then against lines k, l and m and finally against points n, o and p.<br />
<br />
This is known as a hierarchical algorithm. For further explanation please refer to the paper: "3D contact algorithms for particle DEM with rigid and deformable solids" - M.Santasusana, J.Irazábal, E.Oñate, J.M.Carbonell. Where the advantges and the drawback of this method and other proposed algorithms are detailed and analysed in complicated situations like multicontact.<br />
<br />
[[File:Local_search1.png]]<br />
<br />
Fig. 3 Particle-Face contact detection.<br />
<br />
<br />
<br />
== Programming Implementation ==<br />
Structure of the code (Strategy, Scheme, Element, Node, Utilities, functions frequently used like FastGet,...)<br />
<br />
The source code is accessible through [https://kratos.cimne.upc.es/projects/kratos/repository/show/kratos this site].<br />
<br />
[[File:Dem manual code scheme 66.png]]<br />
<br />
A main Python script is used to trigger the calculation of a problem. It communicates with the C++ code. The main advantage of using a compiled language like python for the main script is that it allows to make quick adjustments or modifications to the main strategy without having to recompile the whole code avery time. It also allows for very fast testing.<br />
<br />
=== Main components of the code ===<br />
<br />
<br />
==== Elements ====<br />
<br />
Discontinuum, continuum, cohesive, swimming, cluster of spheres.<br />
<br />
==== Conditions ====<br />
<br />
Main the FEM elements: lines and triangles.<br />
<br />
==== Constitutive Laws ====<br />
<br />
Different interactions laws: linear, Hertzian and continuum behaviour.<br />
<br />
==== Python elements ====<br />
<br />
These files translate the strategies and the<br />
utilities defined in C++ language to be able<br />
to call them from python scripts.<br />
<br />
==== Strategies====<br />
<br />
The main script that calls the necessary schemes and the functions on the<br />
elements during the global loop.<br />
<br />
==== Schemes====<br />
<br />
Explicit integration schemes available<br />
<br />
==== Utilities====<br />
<br />
Here geometric functions are defined, the<br />
neighbours search function, configuration of the particle, amongst others.<br />
<br />
==== Python scripts====<br />
<br />
It contains, amongst other things, the python interface where the main function<br />
of the strategy such as Initialize or Solve<br />
are called. Other operations are done like adding the necessary nodal variables.<br />
<br />
==== Test examples====<br />
<br />
They contain the Benchmarks<br />
<br />
==== DEM_application====<br />
<br />
It contains all the variables that will be used so they are<br />
created and registered in python and C++.<br />
<br />
<br />
=== Main elements in the global python script ===<br />
<br />
<br />
The first step consist of importing all libraries and files necessary for the application.<br />
It is followed by the creation of the necessary input files of the problem at hand to be<br />
processed by the code. The solver strategy is also chosen, and some important operations are done, as for example the<br />
addition of the necessary variables to the solver.<br />
<br />
At this step the buffer size value is also set: this number determines the<br />
historical database size of the problem. The values for the different options and<br />
variables that Kratos will use in the computation are also imported here, like the<br />
integration scheme, the type of solution, damping specifications, time step, output<br />
time step of results, et cetera.<br />
<br />
After the definition of the previous settings, several initializing functions are called which are responsible<br />
of initializing, among other objects, the mesh, the inlet of particles or the solver. Also some operations related to parallel<br />
computing are done.<br />
<br />
Afterwards the time step is determined and the main computing loop is entered. At each time step Solve function is executed.<br />
Finally, at user-defined time steps the results are exported to GiD so they become ready for visualization and postprocessing.<br />
<br />
In fact, this main python file imports another script written in the same language, the ''sphere_strategy.py''. This file is very important<br />
because it contains the core structure that the program will follow when computing. On the other hand, it is the last door to the kernel of the program, which<br />
is written in C++. Some of the most important functions and classes in this file are the following:<br />
<br />
AddVariables: This function sets which variables will be<br />
stored in the nodes at each time step and also the value of those variables as a function of time also<br />
depending on the buffer size previously selected. See the next figure for details.<br />
<br />
<br />
[[File:Dem application add variables 66.png]]<br />
<br />
<br />
AddDofs: The desired degrees of freedom are set here, as can be seen in the design of the function in the figure that follows.<br />
<br />
<br />
[[File:Dem application add dofs 66.png]]<br />
<br />
<br />
The global structure of the explicit strategy the code uses is shown in the next figure. It consists of three main parts: a constructor of the class of the solver,<br />
which creates some default variables for the class, an initializer, which defines the values of several standard parameters, and finally, the ''Solve()'' function<br />
which takes command of almost the entire computing process. The next figure shows and schematic view of this structure.<br />
<br />
When using the ''Initialize()'' function to set the original values of a group of variables, they automatically become accessible in the different C++ files<br />
of the application by means of the ProcessInfo container (see more at ). This function also calls the Initialize function in the solver.<br />
<br />
<br />
[[File:Dem application explicit strategy 66.png]]<br />
<br />
<br />
C++ Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve cpp scheme 80.png]]<br />
<br />
<br />
<br />
DEM CONTINUUM<br />
<br />
<br />
Initialize() function scheme:<br />
<br />
<br />
[[File:Dem application initialize scheme 75.png]]<br />
<br />
<br />
Solve() function scheme:<br />
<br />
<br />
[[File:Dem application solve scheme 75.png]]<br />
<br />
<br />
Search() function scheme:<br />
<br />
<br />
[[File:Dem application search scheme 66.png]]<br />
<br />
== Benchmarks ==<br />
<br />
The DEM Benchmarks consist of a set of 9 simple tests which are run every night and whose object is to make sure both that the application performs correctly and that the code did not break after the daily changes. They are the following:<br />
<br />
===Test1: Elastic normal impact of two identical spheres===<br />
<br />
Check the evolution of the elastic normal contact force between two spheres with time.<br />
<br />
<br />
<br />
[[File:Benchmark1_1.png]]<br />
[[File:Benchmark1 graph 66.png]]<br />
<br />
If the coefficient of restitution is 1, the module of the initial and final velocities should be the same. Also, by symmetry, velocities should be equal for both spheres.<br />
<br />
_<br />
<br />
===Test2: Elastic normal impact of a sphere against a rigid plane===<br />
<br />
Check the evolution of the elastic normal contact force between a sphere and a plane.<br />
<br />
[[File:Benchmark2 66.png]]<br />
[[File:Benchmark2 graph 66.png]]<br />
<br />
If the coefficient of restitution is equal to 1, the module of the initial and final velocity should remain unchanged.<br />
<br />
_<br />
<br />
===Test3: Impact of a sphere against a rigid plane with different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark3 66.png]]<br />
[[File:Benchmark3 graph 66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
_<br />
<br />
===Test4: Oblique impact of a sphere with a rigid plane with constant velocity module and variable incident angles===<br />
<br />
Check the tangential restitution coefficient, final angular velocity and rebound angle of the sphere.<br />
<br />
[[File:Benchmark4_66.png]]<br />
[[File:Benchmark4 graph1_66.png]]<br />
[[File:Benchmark4 graph2_66.png]]<br />
<br />
_<br />
<br />
===Test5: Oblique impact of a sphere with a rigid plane with constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark5_66.png]]<br />
[[File:Benchmark5 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test6: Impact of a sphere with a rigid plane with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark6_66.png]]<br />
[[File:Benchmark6 graph1_66.png]]<br />
<br />
_<br />
<br />
===Test7: Impact of two identical spheres with a constant normal velocity and different angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark7_66.png]]<br />
[[File:Benchmark7 graph1_66.png]]<br />
<br />
By symmetry, the tangential final velocity of both spheres should be zero. Additionally, for a coefficient of restitution of 1, there should be no changes in the modules of both linear and angular velocities and their values should conserve symmetry. <br />
<br />
_<br />
<br />
===Test8: Impact of two differently sized spheres with a constant normal velocity and variable angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark8_66.png]]<br />
[[File:Benchmark8_graph1_66.png]]<br />
<br />
In this case, it is interesting to note that, the bigger and/or denser sphere 2 is, the more this test resembles the sphere versus plane simulation.<br />
<br />
_<br />
<br />
===Test9: Impact of two identical spheres with a constant normal velocity and different coefficients of restitution===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark9_66.png]]<br />
[[File:Benchmark9_graph1_66.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
<br />
References:<br />
<br />
Y.C.Chung, J.Y.Ooi. ''Benchmark tests for verifying discrete element modelling codes at particle impact level'' (2011).<br />
<br />
== How to analyse using the current application ==<br />
<br />
=== Pre-Process ===<br />
<br />
GUI's & GiD<br />
<br />
===== D-DEMPack =====<br />
<br />
D-DEMPack is the package that allows a user to create, run and analyze results of a DEM simulation for discontinuum / granular / little-cohesive materials. It is written for GiD. So in order to use this package, you should install GiD first.<br />
<br />
You can read the [[D-DEMPack manual]] or follow the [[D-DEMPack Tutorials]] for fast learning on how to use the GUI.<br />
<br />
===== C-DEMPack =====<br />
<br />
Continuum / Cohesive <br />
<br />
===== F-DEMPack =====<br />
<br />
Fluid coupling<br />
<br />
=== Post-Process ===<br />
<br />
== Application Dependencies ==<br />
<br />
The Swimming DEM Application depends on the DEM application<br />
<br />
=== Other Kratos Applications used in current Application ===<br />
<br />
FEM-DEM<br />
<br />
<br />
== Problems! ==<br />
<br />
==== What to do if the Discrete Elements behave strangely ====<br />
<br />
In the case you notice that some discrete elements cross walls, penetrate in them or simply fly away of the domain at high velocity, check the following points:<br />
<br />
<br />
In the case of excessive penetration:<br />
<br />
*'''Check that the Young Modulus is big enough'''. A small Young Modulus makes the Elements and the walls behave in a very smooth way. Sometimes they are so soft that total penetration and trespass is possible.<br />
<br />
*'''Check the Density of the material'''. An excessive density means a big weight and inertia that cannot be stopped by the walls.<br />
*'''Check the Time Step'''. If the time step is too big, the Elements can go from one side of the wall to the other with no appearence of a reaction.<br />
*'''Check the frequency of neighbour search'''. If the search is not done frequently enough, the new contacts with the walls may not be detected soon enough.<br />
<br />
<br />
In the case of excessive bounce:<br />
<br />
*'''Check that the Young Modulus is not extremely big'''. An exaggerated Young Modulus yields extremely large reactions that can make the Elements bounce too fast in just one time step. Also take into account that the stability of explicit methods depends on the Young Modulus (the higher the modulus, the closer to instability).<br />
<br />
*'''Check the Density of the material'''. A very low density means a very small weight and inertia, so any force exerted by other elements or the walls can induce big accelerations on the element.<br />
*'''Check the Time Step'''. If the time step is too big, the method gains more energy, and gets closer to instability.<br />
*'''Check the restitution coefficient of the material'''. Explicit integration schemes gain energy noticeably, unless you use a really small time step. In case the time step is chosen to be big (but still stable), use the restitution coefficient to compensate the gain of energy and get more realistic results.<br />
<br />
== Contact ==<br />
<br />
Contact us for any question regarding this application:<br />
<br />
<br />
-Miguel Angel Celigueta: [mailto:maceli@cimne.upc.edu maceli@cimne.upc.edu]<br />
<br />
-Guillermo Casas: [mailto:gcasas@cimne.upc.edu gcasas@cimne.upc.edu]<br />
<br />
-Salva Latorre: [mailto:latorre@cimne.upc.edu latorre@cimne.upc.edu]<br />
<br />
-Miquel Santasusana: [mailto:msantasusana@cimne.upc.edu msantasusana@cimne.upc.edu]<br />
<br />
-Ferran Arrufat: [mailto:farrufat@cimne.upc.edu farrufat@cimne.upc.edu]<br />
<br />
<br />
[[Category: Applications]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/DEM_ApplicationDEM Application2015-09-10T10:02:22Z<p>Gcasas: /* What to do if the Discrete Elements behave strangely */</p>
<hr />
<div>WARNING: This page is still under construction.<br />
<br />
The DEM Kratos Team<br />
<br />
<br />
== Theory ==<br />
<br />
This application solve the the equations....<br />
Mathematical approach to the problems. <br />
<br />
Nothing numerical<br />
<br />
=== Integration Schemes ===<br />
Forward Euler Scheme<br />
<br />
=== Contact Laws ===<br />
Concept of indentation<br />
HMD, LSD<br />
===== Normal Force Laws =====<br />
<br />
====== Linear Repulsive Force ======<br />
<br />
The most simple representation of a repulsive contact force between a sphere and a wall is given by a linear law, where the force acting on the sphere when contacting a plane is a linear function of the indentation, which in turn would bring a quadratic dependence with the contact radius. The next figure shows this simple law:<br />
<br />
[[File:Jkr cohesion linear force.png]]<br />
<br />
<br />
====== Hertzian Repulsive Force ======<br />
<br />
On the other hand, Hertz solved in 1882 the non-cohesive normal contact between a sphere and a plane. In 1971 Johnson, Kendall and Roberts presented the solution (JKR-Theory) for the same problem in this case adding cohesive behaviour. Not much later, Derjaguin, Müller and Toporov published similar results (DMT-Theory).<br />
<br />
Both theories are very close and correct, the main difference being that the JKR theory is oriented to the study of flexible, large spheres, while the DMT theory is specially suited to represent the behaviour of rigid, small ones.<br />
<br />
[[File:Jkr cohesion hertz.jpeg]]<br />
<br />
The previous figure shows the standard representation of a Linear or Hertzian contact between a sphere and a wall. The distribution of contact pressures between both bodies follow a parabolic law.<br />
<br />
====== JKR Cohesive Force ======<br />
<br />
[[File:Jkr cohesion jkr.jpeg]]<br />
<br />
The preceding capture shows the a representation of a JKR contact between a sphere and a wall. In this case, the distribution of pressures between both bodies is more complex due to the formation of a neck at the boundaries of the contact. A later figure shows a detailed view of the pressures involved.<br />
<br />
[[Image:Jkr cohesion forces.png]]<br />
<br />
In the previous graphic, it is very interesting to note the existence of two singular values of contact radius: one for which the total forces acting on the contacting sphere is zero, and another for which the maximum value of adhesion is achieved.<br />
<br />
<br />
[[File:Jkr cohesion pressures.png]]<br />
<br />
In the previous figure, the blue area represents the distribution of pressures acting on the sphere when contacting a wall if a Hertzian Law is followed. On the other hand, the green area represents the JKR distribution. Note the larger values and the existence of adhesive behaviour at both sides of the pressures distribution. <br />
<br />
<br />
An example of granular simulation without cohesive forces:<br />
<br />
[[File:JKR no cohesion 1 33.png]]<br />
[[File:JKR no cohesion 2 33.png]]<br />
[[File:JKR no cohesion 3 33.png]]<br />
[[File:JKR no cohesion 4 33.png]]<br />
<br />
The same simulation as before, this time with cohesive forces in both sphere-sphere and sphere-plane contacts.<br />
<br />
[[File:JKR cohesion 1 33.png]]<br />
[[File:JKR cohesion 2 33.png]]<br />
[[File:JKR cohesion 3 33.png]]<br />
[[File:JKR cohesion 4 33.png]]<br />
<br />
<br />
References:<br />
<br />
V. L. Popov. ''Contact Mechanics and Friction'' (2010).<br />
<br />
<br />
===== Tangential Force Laws =====<br />
<br />
===== Damping Force Laws =====<br />
<br />
(restit. coef)<br />
<br />
== Numerical approach (implementation) ==<br />
<br />
Structure of the code (Strategy, Scheme, Element, Node, Utilities, functions frequently used like FastGet,...)<br />
<br />
<br />
=== DEM strategies ===<br />
<br />
===== Non-cohesive materials Strategy =====<br />
<br />
===== Continuum Strategy =====<br />
<br />
=== DEM schemes ===<br />
<br />
=== DEM elements ===<br />
<br />
===== Spheric Particle =====<br />
===== Spheric Continuum Particle =====<br />
===== Spheric Swimming Particle =====<br />
<br />
== Benchmarks ==<br />
<br />
The DEM Benchmarks consist of a set of 9 simple tests which are run every night and whose object is to make sure both that the application performs correctly and that the code did not break after the daily changes. They are the following:<br />
<br />
===Test1: Elastic normal impact of two identical spheres===<br />
<br />
Check the evolution of the elastic normal contact force between two spheres with time.<br />
<br />
[[File:Benchmark1.jpeg]]<br />
[[File:Benchmark1 graph.png]]<br />
<br />
If the coefficient of restitution is 1, the module of the initial and final velocities should be the same. Also, by symmetry, velocities should be equal for both spheres.<br />
<br />
_<br />
<br />
===Test2: Elastic normal impact of a sphere with a rigid plane===<br />
<br />
Check the evolution of the elastic normal contact force between a sphere and a plane.<br />
<br />
[[File:Benchmark2.jpeg]]<br />
[[File:Benchmark2 graph.png]]<br />
<br />
If the coefficient of restitution is equal to 1, the module of the initial and final velocity should remain unchanged.<br />
<br />
_<br />
<br />
===Test3: Normal contact with different restitution coefficients===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark3.jpeg]]<br />
[[File:Benchmark3 graph.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
_<br />
<br />
===Test4: Oblique impact of a sphere with a rigid plane with a constant resultant velocity but at different incident angles===<br />
<br />
Check the tangential restitution coefficient, final angular velocity and rebound angle of the sphere.<br />
<br />
[[File:Benchmark4.jpeg]]<br />
[[File:Benchmark4 graph1.png]]<br />
[[File:Benchmark4 graph2.png]]<br />
<br />
_<br />
<br />
===Test5: Oblique impact of a sphere with a rigid plane with a constant normal velocity but at different tangential velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark5.jpeg]]<br />
[[File:Benchmark5 graph1.png]]<br />
_<br />
<br />
===Test6: Impact of a sphere with a rigid plane with a constant normal velocity but at different angular velocities===<br />
<br />
Check the final linear and angular velocities of the sphere.<br />
<br />
[[File:Benchmark6.jpeg]]<br />
[[File:Benchmark6 graph1.png]]<br />
_<br />
<br />
===Test7: Impact of two identical spheres with a constant normal velocity and varying angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark7.jpeg]]<br />
[[File:Benchmark7 graph1.png]]<br />
<br />
By symmetry, the tangential final velocity of both spheres should be zero. Additionally, for a coefficient of restitution of 1, there should be no changes in the modules of both linear and angular velocities and their values should conserve symmetry. <br />
<br />
_<br />
<br />
===Test8: Impact of two differently sized spheres with a constant normal velocity and varying angular velocities===<br />
<br />
Check the final linear and angular velocities of both spheres.<br />
<br />
[[File:Benchmark8.jpeg]]<br />
[[File:Benchmark8_graph1.png]]<br />
<br />
In this case, it is interesting to note that, the bigger and/or denser sphere 2 is, the more this test resembles the sphere versus plane simulation.<br />
<br />
_<br />
<br />
===Test9: Impact of two identical spheres with a constant normal velocity and different restitution coefficients===<br />
<br />
Check the effect of different restitution coefficients on the damping ratio.<br />
<br />
[[File:Benchmark9.jpeg]]<br />
[[File:Benchmark9_graph1.png]]<br />
<br />
If total energy is conserved, the restitution coefficient and the damping ratio values should be identical.<br />
<br />
<br />
References:<br />
<br />
Y.C.Chung, J.Y.Ooi. ''Benchmark tests for verifying discrete element modelling codes at particle impact level'' (2011).<br />
<br />
== How to analyse using the current application ==<br />
<br />
=== Pre-Process ===<br />
<br />
GUI's & GiD<br />
<br />
===== D-DEMPack =====<br />
<br />
D-DEMPack is the package that allows a user to create, run and analyze results of a DEM simulation for discontinuum / granular / little-cohesive materials. It is written for GiD. So in order to use this package, you should install GiD first.<br />
<br />
You can read the [[D-DEMPack manual]] or follow the [[D-DEMPack Tutorials]] for fast learning on how to use the GUI.<br />
<br />
===== C-DEMPack =====<br />
<br />
Continuum / Cohesive <br />
<br />
===== F-DEMPack =====<br />
<br />
Fluid coupling<br />
<br />
=== Post-Process ===<br />
<br />
== Application Dependencies ==<br />
<br />
The Swimming DEM Application depends on the DEM application<br />
<br />
=== Other Kratos Applications used in current Application ===<br />
<br />
FEM-DEM<br />
<br />
<br />
== Programming Documentation ==<br />
<br />
The source code is accessible through [https://kratos.cimne.upc.es/projects/kratos/repository/show/kratos this site].<br />
<br />
== Problems! ==<br />
<br />
==== What to do if the Discrete Elements behave strangely ====<br />
<br />
In the case you notice that some discrete elements cross walls, penetrate in them or simply fly away of the domain at high velocity, check the following points:<br />
<br />
<br />
In the case of excessive penetration:<br />
<br />
*'''Check that the Young Modulus is big enough'''. A small Young Modulus makes the Elements and the walls behave in a very smooth way. Sometimes they are so soft that total penetration and trespass is possible.<br />
<br />
*'''Check the Density of the material'''. An excessive density means a big weight and inertia that cannot be stopped by the walls.<br />
*'''Check the Time Step'''. If the time step is too big, the Elements can go from one side of the wall to the other with no appearence of a reaction.<br />
*'''Check the frequency of neighbour search'''. If the search is not done frequently enough, the new contacts with the walls may not be detected soon enough.<br />
<br />
<br />
In the case of excessive bounce:<br />
<br />
*'''Check that the Young Modulus is not extremely big'''. An exaggerated Young Modulus yields extremely large reactions that can make the Elements bounce too fast in just one time step. Also take into account that the stability of explicit methods depends on the Young Modulus (the higher the modulus, the closer to instability).<br />
<br />
*'''Check the Density of the material'''. A very low density means a very small weight and inertia, so any force exerted by other elements or the walls can induce big accelerations on the element.<br />
*'''Check the Time Step'''. If the time step is too big, the method gains more energy, and gets closer to instability.<br />
*'''Check the restitution coefficient of the material'''. Explicit integration schemes gain energy noticeably, unless you use a really small time step. In case the time step is chosen to be big (but still stable), use the restitution coefficient to compensate the gain of energy and get more realistic results.<br />
<br />
== Contact ==<br />
<br />
Contact us for any question regarding this application:<br />
<br />
<br />
-Miguel Angel Celigueta: [mailto:maceli@cimne.upc.edu maceli@cimne.upc.edu]<br />
<br />
-Guillermo Casas: [mailto:gcasas@cimne.upc.edu gcasas@cimne.upc.edu]<br />
<br />
-Salva Latorre: [mailto:latorre@cimne.upc.edu latorre@cimne.upc.edu]<br />
<br />
-Miquel Santasusana: [mailto:msantasusana@cimne.upc.edu msantasusana@cimne.upc.edu]<br />
<br />
-Ferran Arrufat: [mailto:farrufat@cimne.upc.edu farrufat@cimne.upc.edu]<br />
<br />
<br />
[[Category: Applications]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2014-12-29T16:05:47Z<p>Gcasas: /* Introduction */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy are described in the following sections:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as nonlinear problems, of which they are then only a particular case. The reason for this lies in code design, since in this way it is easier to obtain a natural generalization of the SolvingStrategy class. Therefore, also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), solving it and updating the nodal values. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) <br />
#The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears noting that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Add_a_New_VariableHow to Add a New Variable2014-12-22T18:25:46Z<p>Gcasas: </p>
<hr />
<div>If you need to add a new variable NEW_VARIABLE to Kratos which does not exist 4 steps are needed:<br />
<br />
<br />
'''1. Define''' the variable in ''.../kratos/includes/variables.h'' (if you want to share the variable with different applications) or in ''.../applications/your_application/your_application.h'' (if the variable will be used by your application only)<br />
<br />
KRATOS_DEFINE_VARIABLE(double, NEW_VARIABLE)<br />
<br />
or for a vectorial variable:<br />
<br />
KRATOS_DEFINE_3D_VARIABLE_WITH_COMPONENTS(NEW_VARIABLE)<br />
<br />
<br />
'''2. Create''' & '''Register''' in ''.../kratos/sources/variables.cpp'' (if you want to share the variable with different applications) or in ''.../applications/your_application/your_application.cpp'' (if the variable will be used by your application only)<br />
<br />
KRATOS_CREATE_VARIABLE(double, NEW_VARIABLE)<br />
<br />
KRATOS_REGISTER_VARIABLE( NEW_VARIABLE)<br />
<br />
or for a vectorial variable:<br />
<br />
KRATOS_CREATE_3D_VARIABLE_WITH_COMPONENTS(NEW_VARIABLE)<br />
<br />
KRATOS_REGISTER_3D_VARIABLE_WITH_COMPONENTS(NEW_VARIABLE)<br />
<br />
<br />
'''3.''' Now, it is also necessary to make python aware of its existence by<br />
inserting in ''.../kratos/kratos/python/add_containers_to_python.cpp'' (if you want to share the variable with different applications) or in ''.../applications/your_application/custom_python/your_application_python_application.cpp'' (if the variable will be used by your application only):<br />
<br />
KRATOS_REGISTER_IN_PYTHON_VARIABLE( NEW_VARIABLE)<br />
<br />
or for a vectorial variable:<br />
<br />
KRATOS_REGISTER_IN_PYTHON_3D_VARIABLE_WITH_COMPONENTS( NEW_VARIABLE)<br />
<br />
<br />
'''4.''' In your python script, remember to '''allocate memory''' for your new variable:<br />
<br />
AddVariables(model_part)<br />
model_part.AddNodalSolutionStepVariable( NEW_VARIABLE)<br />
<br />
Steps 3 and 4 are not necessary if you are going to use this variable by the order 'GetValue'.<br />
[[Category:How To]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_compile_the_Boost_if_you_want_to_use_MPIHow to compile the Boost if you want to use MPI2014-11-18T15:20:15Z<p>Gcasas: /* installing boost-build in the system */</p>
<hr />
<div>=== BOOST with MPI SUPPORT ===<br />
ATTENTION: Boost distro NEEDS to be installed manually to allow mpi support. DO NOT USE the boost that comes in the ubuntu repositories. <br />
<br />
ATTENTION: MPI modules are only compatible with python3 with version 1.55 or later.<br />
<br />
download boost 1.55.0 from [http://www.boost.org boost web page] and decompress it on a "tempdir" directory.<br />
From such "tempdir" run<br />
./boostrap.sh<br />
once the configure succeds edit the file "user-config.jam" and add the following line (take care to insert the space between "mpi" and ";")<br />
using mpi ;<br />
at the end of the file<br />
<br />
and after success<br />
<br />
./b2 --target=shared,static<br />
<br />
if your machine have multiple cores you can specify the flag -jN<br />
<br />
./b2 -j2 -target=shared,static<br />
<br />
sudo ./b2 install<br />
<br />
to "inform" the system of the installation path edit the file ".bashrc" and add the two lines<br />
#path for boost mpi<br />
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH<br />
export PYTHONPATH=/usr/local/lib:$PYTHONPATH<br />
<br />
<br />
=== SuperLU_DIST install ===<br />
Download the latest release of superlu_dist from [http://crd.lbl.gov/~xiaoye/SuperLU/#superlu_dist]<br />
<br />
from your home folder create a directory<br />
mkdir compiled_libraries<br />
now extract the superlu_dist in there to obtain<br />
~/compiled_libraries/SuperLU_DIST_2.3<br />
<br />
the following make.inc file has to be put in such directory and works for a 32bit ubuntu with the packages installed in the standard directories.<br />
<br />
[[Media:Make.inc.zip]] <br />
<br />
Please be noted that for the version other than 2.3 (2.4 for example), the following two lines must be amended to reflect the version changes:<br />
<br />
DSuperLUroot = ~/compiled_libraries/SuperLU_DIST_2.4<br />
<br />
DSUPERLULIB = $(DSuperLUroot)/lib/libsuperlu_dist_2.4.a<br />
<br />
launch the compilation using the command<br />
make<br />
sudo make install<br />
which installs the library in<br />
~/compiled_libraries/SuperLU_DIST_2.3/lib<br />
<br />
=== Trilinos install ===<br />
<br />
We recommend installing the latest Trilinos version (right now the Trilinos 10 series) but you will also find instructions for Trilinos 9 version.<br />
<br />
==== Trilinos 9 ====<br />
<br />
As a first step connect to the site<br />
<br />
and download the latest Trilinos Package. (At the moment of writing a tutorial 9.0.2)<br />
extract the downloaded file in <br />
scratch/trilinos9<br />
<br />
from such path type<br />
mkdir LINUX_MPI<br />
cd LINUX_MPI<br />
save in there the file "StandardTrilinosCompilation.sh" <br />
<br />
[[media:StandardTrilinosCompilation.sh.zip]]<br />
<br />
and run it as<br />
sh StandardTrilinosCompilation.sh<br />
make<br />
make and execute the tests <br />
make tests<br />
make runtests-mpi<br />
<br />
and finally install if everything is correct (installation will be in /usr/local/trilinos-9.0.2<br />
sudo make install<br />
<br />
====Trilinos 10====<br />
<br />
The installation of the newer versions of Trilinos is different and requires CMake, which can be obtained from http://www.cmake.org or from the Ubuntu repositories. Note that you will need at least version 2.8, so when installing from repository use the Synaptic package manager and check which version you are getting. <br />
<br />
If you intend to use PyTrillinos, or if you you are new to Kratos and are just following the instructions on this page, you'll need swig version 1.3.38 or later. Unfortunately, the swig version that you can obtain from the repositories for Ubuntu 9.10 is 1.3.36 (Ubuntu 10.4 has 1.3.40, which should be enough). You can get the current swig release from http://www.swig.org/. Extract it and go to the extracted folder. From there, run<br />
./configure<br />
make<br />
sudo make install<br />
Now you can download Trilinos from http://trilinos.sandia.gov/index.html (they will probably ask you to fill a form). Extract the contents of the downloaded file to a temporary folder. Create another folder to temporarily store the Trilinos installation files using<br />
mkdir trilinos-build<br />
cd trilinos-bulid<br />
Copy and extract this file [[Media:do-configure.zip]] to that folder and open it using a text editor. Its first line reads<br />
TRILINOS_HOME="~/Downloads/trilinos-10.2.0-Source"<br />
Change it to point to the folder where you extracted the Trilinos files. You may also want to change the path where Trilinos will be installed -- do this by modifying the line<br />
-D CMAKE_INSTALL_PREFIX:PATH=/usr/local/trilinos-10.2.0 \<br />
to suit your tastes. Now run<br />
./do-configure<br />
make<br />
You can run some tests to check that everything works fine using<br />
ctest<br />
but be warned that these can take a long time. Then install Trilinos using<br />
sudo make install<br />
Finally, add the trilinos path to your .basrc file. From your home folder (go there by typing 'cd' without arguments) type<br />
gedit .bashrc<br />
scroll to the end of the file and paste the following<br />
#path for trilinos<br />
export LD_LIBRARY_PATH=/usr/local/trilinos-10.2.0/lib:$LD_LIBRARY_PATH<br />
Or, if you installed trilinos somewhere else, change the path accordingly.<br />
<br />
<br />
<br />
Go back to [[Installing kratos in Ubuntu Linux#IF YOU NEED MPI|Installing kratos in Ubuntu Linux]] and follow the last steps to install kratos.</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Installing_kratos_in_Ubuntu_LinuxInstalling kratos in Ubuntu Linux2014-11-17T11:32:26Z<p>Gcasas: /* SYSTEM LIBRARIES INSTALL */</p>
<hr />
<div>The objective of current tutorial is to "standardize" the installation of Kratos on an Ubuntu system.<br />
Please note that 90% of the operations described are necessary for the installation of libraries in the system rather than for the compilation of the Kratos itself.<br />
<br />
== SYSTEM LIBRARIES INSTALL ==<br />
Set the download server to "main server". This can be done operning the ''Synaptic Package Manager'' and going to '''Settings-Repository''' : in the section ''Ubuntu Software'' change the option ''Download from: Main server''<br />
<br />
we detail here the installation of some basic packages. Note that not all of the packages are strictly needed however our advice is to install all of them if you are not sure of what you are doing<br />
<br />
essential packages:<br />
sudo apt-get install -y build-essential<br />
sudo apt-get install -y subversion<br />
sudo apt-get install -y rapidsvn<br />
sudo apt-get install -y libblas3gf<br />
sudo apt-get install -y liblapack3gf<br />
sudo apt-get install -y libblas-dev<br />
sudo apt-get install -y liblapack-dev<br />
sudo apt-get install -y python-dev<br />
sudo apt-get install -y gfortran<br />
sudo apt-get install -y csh<br />
sudo apt-get install -y swig<br />
sudo apt-get install -y python-matplotlib<br />
<br />
useful packages (not strictly needed for the kratos):<br />
sudo apt-get install -y emacs<br />
sudo apt-get install -y kate<br />
sudo apt-get install -y python-scipy<br />
sudo apt-get install -y python-numpy<br />
sudo apt-get install -y python-scientific<br />
<br />
<br />
sudo apt-get install -y g++<br />
sudo apt-get install -y python<br />
<br />
== IF YOU DON'T NEED [http://en.wikipedia.org/wiki/Message_Passing_Interface MPI] ==<br />
BOOST library:<br />
<br />
You can download the late release of boost libraries from [http://www.boost.org boost web page] or you can use directly the boost libraries that you find in the repositories by typing<br />
sudo apt-get install libboost-all-dev<br />
sudo apt-get install libboost-dbg<br />
<br />
== IF YOU NEED [http://en.wikipedia.org/wiki/Message_Passing_Interface MPI] ==<br />
install the packages<br />
sudo apt-get install -y openmpi-bin<br />
sudo apt-get install -y openmpi-dev<br />
sudo apt-get install -y libparmetis3.1<br />
sudo apt-get install -y libparmetis-dev <br />
<br />
then install the boost_mpi packages:<br />
sudo apt-get install libboost-mpi-python-dev<br />
sudo apt-get install libboost-mpi-pythonX.XX.XX<br />
note that the X.XX.X is to be replaced with the version of the lib that comes with the system<br />
<br />
ALTERNATIVELY, and just if you have a very good reason to do it, you can compile manually the <br />
boost library including mpi, which can be done by following the instructions at [[How to compile the Boost if you want to use MPI]]<br />
<br />
== GET KRATOS ==<br />
<br />
Follow the instructions of [[How to get Kratos]] to create your local kratos copy.<br />
<br />
== Kratos Libraries ==<br />
<br />
The installer now performs automatically the compilation of the libraries needed<br />
<br />
== COMPILE THE KRATOS ==<br />
The compilation of the Kratos is now cmake-based. <br />
Cmake can be installed in the system by simply typing <br />
sudo apt-get install cmake<br />
<br />
HOWEVER please note that cmake is improving very fast and the "cmake-experience" improves with every release, as a consequence we <br />
advise to download and install the latest cmake release<br />
<br />
once cmake is installed in the system go to the directory <br />
cd cmake_build <br />
then copy<br />
cp example_configure.sh.do_not_touch configure.sh<br />
edit "configure.sh" to activate libs as you wish (or leave it as it is for the defaults), and <br />
finally run<br />
sh configure.sh<br />
and the kratos should compile and install<br />
<br />
Detailed README instructions are available in the directory "cmake_build"<br />
<br />
== CREATE IN DEBUG ==<br />
Debug compilation can be obtained by simply substituting "Release" --> "Debug" within your "configure.sh".<br />
Alternative options are "RelWithDebInfo" and "MinSizeRel"<br />
<br />
If you want to have available both a release build and a debug build, just create another directory (say cmake_debug_build), copy into it<br />
your configure file and run it from that directorye <br />
<br />
== FINAL STEP ==<br />
you have to let the system know where the kratos is installed<br />
<br />
this can be done by adding the line (assuming kratos is installed in "${HOME}/kratos/libs"<br />
export PYTHONPATH=${HOME}/kratos/libs:$PYTHONPATH<br />
export PYTHONPATH=${HOME}/kratos:$PYTHONPATH<br />
to the ".bashrc" (a hidden configuration file in the home directory)<br />
<br />
Additionally you may have to add your "${HOME}/kratos/libs" directory to LD_LIBRARY_PATH. This can be done adding these lines:<br />
export LD_LIBRARY_PATH=${HOME}/kratos/libs:$LD_LIBRARY_PATH<br />
to the ".bashrc"<br />
<br />
<br />
<br />
<br />
[[Category:Installation]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_%27locked_file%27Problem: 'locked file'2014-04-07T10:45:21Z<p>Gcasas: </p>
<hr />
<div>{{db-g7}}</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_%27locked_file%27Problem: 'locked file'2014-04-07T10:43:54Z<p>Gcasas: Blanked the page</p>
<hr />
<div></div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_%27locked%27_or_%27bloqueado%27_files_and_foldersProblem: 'locked' or 'bloqueado' files and folders2014-04-07T10:43:32Z<p>Gcasas: Created page with "In some cases, just by going to "Extras"->"Cleanup" the problem will be solved"</p>
<hr />
<div>In some cases, just by going to<br />
<br />
"Extras"->"Cleanup"<br />
<br />
the problem will be solved</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/RapidSVNRapidSVN2014-04-07T10:41:54Z<p>Gcasas: </p>
<hr />
<div>[[problem: 'locked' or 'bloqueado' files and folders]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_%27locked_file%27Problem: 'locked file'2014-04-07T10:40:56Z<p>Gcasas: Created page with "In some cases, just by going to "Extras"->"Cleanup" the problem will be solved"</p>
<hr />
<div>In some cases, just by going to<br />
<br />
"Extras"->"Cleanup"<br />
<br />
the problem will be solved</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/RapidSVNRapidSVN2014-04-07T10:39:58Z<p>Gcasas: </p>
<hr />
<div>[[problem: 'locked file']]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/RapidSVNRapidSVN2014-04-07T10:39:47Z<p>Gcasas: Created page with "problem: 'locked file'"</p>
<hr />
<div>problem: 'locked file'</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/User:GcasasUser:Gcasas2014-04-07T10:37:34Z<p>Gcasas: </p>
<hr />
<div>[[Ubuntu]]<br />
<br />
[[RapidSVN]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/User:GcasasUser:Gcasas2014-04-07T10:37:14Z<p>Gcasas: </p>
<hr />
<div>[[Ubuntu]]<br />
[[RapidSVN]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_menus_not_expandingProblem: menus not expanding2014-03-04T15:24:29Z<p>Gcasas: </p>
<hr />
<div>The solution is restarting Ubuntu's user interface (Unity) by typing in a terminal:<br />
sudo unity --replace</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_menus_not_expandingProblem: menus not expanding2014-03-04T15:23:40Z<p>Gcasas: </p>
<hr />
<div>The solution is restarting Ubuntu's user interface (Unity) by typing:<br />
unity --replace</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/Problem:_menus_not_expandingProblem: menus not expanding2014-03-04T15:22:44Z<p>Gcasas: Created page with "the solution is restarting Ubuntu's graphic manager (Unity) by typing: unity --replace"</p>
<hr />
<div>the solution is restarting Ubuntu's graphic manager (Unity) by typing:<br />
unity --replace</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/UbuntuUbuntu2014-03-04T15:20:37Z<p>Gcasas: Created page with "problem: menus not expanding"</p>
<hr />
<div>[[problem: menus not expanding]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/User:GcasasUser:Gcasas2014-03-04T15:19:16Z<p>Gcasas: /* Ubuntu */</p>
<hr />
<div>[[Ubuntu]]</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/User:GcasasUser:Gcasas2014-03-04T15:16:45Z<p>Gcasas: Created page with "== Ubuntu =="</p>
<hr />
<div>== Ubuntu ==</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-12-19T16:58:40Z<p>Gcasas: /* Public Methods */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as nonlinear problems, of which they are then only a particular case. The reason for this lies in code design, since in this way it is easier to obtain a natural generalization of the SolvingStrategy class. Therefore, also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), solving it and updating the nodal values. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) <br />
#The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears noting that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-12-19T16:54:20Z<p>Gcasas: /* Linear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as nonlinear problems, of which they are then only a particular case. The reason for this lies in code design, since in this way it is easier to obtain a natural generalization of the SolvingStrategy class. Therefore, also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) <br />
#The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears noting that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-12-19T16:51:56Z<p>Gcasas: /* Constructor */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as nonlinear problems, of which they are then only a particular case. The reason for this lies in code design, for this way it is easier to obtain a natural generalization of the SolvingStrategy class. Therefore also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) <br />
#The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears noting that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-12-19T16:50:01Z<p>Gcasas: /* Constructor */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as nonlinear problems, of which they are then only a particular case. The reason for this lies in code design, for this way it is easier to obtain a natural generalization of the SolvingStrategy class. Therefore also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) <br />
#The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T19:12:12Z<p>Gcasas: /* Linear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as nonlinear problems, of which they are then only a particular case. The reason for this lies in code design, for this way it is easier to obtain a natural generalization of the SolvingStrategy class. Therefore also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T19:01:05Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> is an approximation of u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. Therefore also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:48:50Z<p>Gcasas: /* Linear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. Therefore also in this context, the increment &Delta;u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:48:38Z<p>Gcasas: /* Linear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. Therefore also in this context, the increment ""&Delta;""u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:48:20Z<p>Gcasas: /* Linear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. Therefore also in this context, the increment ""&Delta""u is solved for. Then taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:47:19Z<p>Gcasas: /* Linear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. Therefore also in this context, the increment <br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:43:46Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the context of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:42:43Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In the constext of Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:41:34Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. <br />
<br />
A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:41:10Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). These criteria are implemented in a particular instance of the ConvergenceCriteria class. A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:40:48Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos each system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen and then solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). This criteria are implemented in a particular instance of the ConvergenceCriteria class. A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:39:20Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos this system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen. In Kratos, each system is solved by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance. A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). This criteria are implemented in a particular instance of the ConvergenceCriteria class. A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:35:21Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the 'stiffness matrix', and f the force vector, both K and f possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos this system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen. Each system is then solved (In Kratos this can be done by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance). A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). This criteria are implemented in a particular instance of the ConvergenceCriteria class. A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>. In Kratos, A<sub>n</sub> is regarded as the LHS and b<sub>n</sub> as the RHS.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasashttps://kratos-wiki.cimne.upc.edu/index.php/How_to_Use_Solving_StrategyHow to Use Solving Strategy2013-08-02T18:29:32Z<p>Gcasas: /* Nonlinear Strategies */</p>
<hr />
<div><!-- =Dudas=<br />
<br />
Qué hace exactamente MoveMeshFlag? Es que se puede remallar dentro de la estrategia?<br />
<br />
<br />
--><br />
<br />
=Introduction=<br />
<br />
The SolvingStrategy class has been conceived as an abstraction of the outermost structure of the numerical algorithm's operations in stationary problems or, in the context of transient problems, those involved in the complete evolution of the system to the next time step. These operations are typically a sequence of build-and-solve steps, each consisting in:<br />
<br />
#Building a system<br />
#Solving it approximately within a certain tolerance<br />
<br />
Incidentally, a SolvingStrategy instance combines simpler structures that in turn are abstractions of component (sub)algorithms. These structures can belong to any of the following classes: [[Kratos Structure: Strategies and Processes|Scheme, LinearSolver, BuilderAndSolver, ConvergenceCriteria]] and even [[Kratos Structure: Strategies and Processes|SolvingStrategy]]. The role of each of these should be clarified in the following sections. Nonetheless it is important to understand all of these complex structures to be able to fully grasp the more complex SolvingStrategy, so further reading is recommended (see the '[[HOW TOs]]' list). With a reasonable degree of generality, the sequence of operations that are (sometimes trivially) performed by a SolvingStrategy instance can be summarized as follows:<br />
<br />
==Nonlinear Strategies==<br />
<br />
They are employed in problems in which, having applied the particular time discretization (which is implemented in the [[Kratos Structure: Strategies and Processes|Scheme]] class instance) and introduced the prescribed boundary conditions, produce systems of nonlinear equations of the form:<br />
<br />
<math>K(u)u = f(u)</math><br />
<br />
Where u represents the vector of unknowns, K the LHS matrix and f(u) the RHS vector, both possibly depending on the solution u.<br />
<br />
Because u is unknown, this system is then approximately solved by some iterative algorithm that, in [[Kratos]], takes the following form:<br />
<br />
<math>A_n\Delta u_n = b_n</math><br />
<br />
<math>u_n = u_{n-1} + \Delta u_{n-1}</math><br />
<br />
So that u<sub>n</sub> approximates u and A<sub>n</sub> and b<sub>n</sub> can be calculated from previous solutions. In Kratos this system is built according to the design of the specific [[Element]] and [[Kratos Structure: Strategies and Processes|Scheme]] classes that have been chosen. Each system is then solved (In Kratos this can be done by means of a [[Kratos Structure: Strategies and Processes|LinearSolver]] instance). A certain convergence criterion is often placed on the norm of ''&Delta;''u<sub>n</sub> (that should tend to 0), although other criteria are possible (e.g. energy norm). This criteria are implemented in a particular instance of the ConvergenceCriteria class. A fixed point strategy is generally applied to create the sequence of systems. For example, in the Newton-Raphson method, b<sub>n</sub> is equal to the minus the residual (f-Ku)<br />
and A<sub>n</sub> to the tangent matrix (d(f-Ku)/du), both evaluated at u<sub>n-1</sub>.<br />
<br />
==Linear Strategies==<br />
<br />
They are used for problems that produce systems of linear equations of the form:<br />
<br />
<math>Ku = f</math><br />
<br />
where neither K or f depend on the unknown. In [[Kratos]] these problems are formulated as in nonlinear problems, for which they are only a particular case. The reason for this lays in the code design, for it allows for a natural generalization of the SolvingStrategy. The same type of reformulation yieds:<br />
<br />
<math>K\Delta u = f - Ku_0</math><br />
<br />
Taking u<sub>0</sub> = 0, A<sub>0</sub> = K and b<sub>0</sub> = f, the approximate system coincides with the original system and the solution is reached in one iteration.<br />
<br />
=Object Description=<br />
<br />
In this section we interpret the SolvingStrategy in a more concise way by referring to its actual implementation in the code. Therefore, here we will discuss the SolvingStrategy C++ defined class and some of its children to explain how to effectively use them and maybe facilitate the task of programming a new one in [[Kratos]]. <br />
<br />
The strategy pattern is designed to allow users to implement a new SolvingStrategy and add it to [[Kratos]] easily, which increases the extendibility of [[Kratos]]. It also allows them to easily select a particular strategy and use it instead of another in order to change the solving algorithm in a straight forward way, which increases the flexibility of the code. <br />
<br />
On the other hand, a composite pattern is used to let users combine different strategies in one. For example, a fractional step strategy can be implemented by combining different strategies used for each step in one composite strategy. As in the case of the Process class (see [[General Structure]]), the interface for changing the children of the composite strategy is considered to be too sophisticated and is removed from the SolverStrategy. Therefore a composite structure can be constructed by giving all its components at the constructing time and then it can be used but without changing its sub algorithms. In the same spirit, all the system matrices and vectors in the systems to be solved will be stored in the strategy. This permits dealing with multiple LHS and RHS. <br />
<br />
==Structure of the base class==<br />
<br />
===Constructor===<br />
<br />
Let us look at the constructors definition:<br />
<br />
template<class TSparseSpace,<br />
class TDenseSpace,<br />
class TLinearSolver //= LinearSolver<TSparseSpace,TDenseSpace><br />
><br />
SolvingStrategy(<br />
ModelPart& model_part, bool MoveMeshFlag = false<br />
)<br />
: mr_model_part(model_part)<br />
{<br />
SetMoveMeshFlag(MoveMeshFlag);<br />
}<br />
<br />
TSparseSpace, TDenseSpace and TLinearSolver are classes that define particular sparse matrix container types, dense matrix container types and the associated LinearSolver. This allows for different linear system solving algorithms to be used without changing the strategy. By looking at the constructor's parameters it is seen that any SolvingStrategy instance will take up a <br />
#A [[How to Use the ModelPart|ModelPart]] instance, containing the mesh data and the boundary conditions information. It contains a set of elements that discretize a domain which corresponds to a certain part of the whole model and in which a finite element discretization is to be performed (e.g. there could be more than one model parts as parameters, like a structure_model_part and a fluid_model_part in a FSI application)<br />
#The flag 'MoveMeshFlag', which indicates if the mesh nodes are to be moved with the calculated solution (e.g. if nodal displacements are computed) to be modified from inside the SolverStrategy or not. Note that both parameters are stored as member variables, thus linking a SolverStrategy to a particular ModelPart instance.<br />
<br />
===Public Methods===<br />
<br />
These methods are typically accessed through the [[How to use Python|Python]] strategy interface or from inside of a bigger containing SolverStrategy instance. The most important ones are next listed below. They are meant to be rewritten in a children strategy:<br />
<br />
virtual void Predict()<br />
<br />
It is empty by default. It is used to produce a guess for the solution. If it is not called a trivial predictor is used in which the values of the solution step of interest are assumed equal to the old values.<br />
<br />
virtual double Solve()<br />
<br />
It only returns the value of the norm of the solution correction (0.0 by default). This method typically encapsulates the greatest amount of computations of all the methods in the SolverStrategy. It contains the iterative loop that implements the sequence of approximate solutions by building the system by assembling local components (by means of a BuilderAndSolver instance, maybe not at each step), Solving it, updating the nodal values a method. <br />
<br />
virtual void Clear()<br />
<br />
It is empty by default. It can be used to clear internal storage.<br />
<br />
virtual bool IsConverged()<br />
<br />
It only returns true by default. It should be considered as a "post solution" convergence check which is useful for coupled analysis. The convergence criteria that is used is the one used inside the 'Solve()' step.<br />
<br />
virtual void CalculateOutputData()<br />
<br />
This method is used when nontrivial results (e.g. stresses) need to be calculated from the solution. This mothod should be called only when needed (e.g. before printing), as it can involve a non negligible cost.<br />
<br />
void MoveMesh()<br />
<br />
This method is not a virtual function, so it is not meant to be rewritten in derived classes. It simply changes the meshes coordinates with the calculated DISPLACEMENTS (raising an error if the variable DISPLACEMENT is not being solved for) if MoveMeshFlag is set to true.<br />
<br />
virtual int Check()<br />
<br />
This method is meant to perform expensive checks. It is designed to be called once to verify that the input is correct. By default, it checks weather the DISPLACEMENT variable is needed and raises an error in case it is but the variable is not [[How to Add a New Variable|added]] to the node and it loops over the [[Kratos Structure: Elements and Conditions|elements and conditions]] of the model part, calling their respective Check methods:<br />
<br />
it->Check(GetModelPart().GetProcessInfo());<br />
<br />
The return integer is to be interpreted as a flag used to inform the user. It is 0 by default.<br />
<br />
==Example: ResidualBasedNewtonRaphsonStrategy==<br />
<br />
In this section ResidualBasedNewtonRaphsonStrategy strategy is analysed in some detail as an example of a SolverStrategy derived class.<br />
<br />
===Constructor===<br />
<br />
ResidualBasedNewtonRaphsonStrategy(<br />
ModelPart& model_part, <br />
typename TSchemeType::Pointer pScheme,<br />
typename TLinearSolver::Pointer pNewLinearSolver,<br />
typename TConvergenceCriteriaType::Pointer pNewConvergenceCriteria,<br />
int MaxIterations = 30,<br />
bool CalculateReactions = false,<br />
bool ReformDofSetAtEachStep = false,<br />
bool MoveMeshFlag = false<br />
)<br />
: SolvingStrategy<TSparseSpace, TDenseSpace, TLinearSolver>(model_part, MoveMeshFlag)<br />
<br />
Let us look at the different arguments:<br />
#The first argument is the model_part, used as explained in the previous section.<br />
#The second argument is a pointer to a Scheme instance. It defines the time integration scheme. (e.g. Newmark) #The next argument is a pointer to a LinearSolver instance, which defines the linear system solver (e.g. a Conjugate Gradient solver). In this particular case it is used for the solution of the linear system arising at every iteration of Newton-Raphson.<br />
#The next argument is a pointer to a ConvergenceCriteria instance. It defines the convergence criterion for the Newton-Raphson procedure. It can be the norm of the residual or something else (e.g. the energy norm)<br />
#The next argument is MaxIterations. It is the cut of criterion for the iterative procedure. If the convergence is not achieved within the allowed number of iterations, the solution terminates and the value of variable of interest achieved at the last iteration is taken as the result, though a message appears that the solution did not converge.<br />
#The next parameter is CalculateReactions, wich activates the CalculateOutputData method when set to true.<br />
#ReformDofSetAtEachStep should be set to true if nodes or elements are erased or added during the solution of the problem.<br />
#MoveMeshFlag should be set to true if use a non-Eulerian approach (the mesh is moved). <br />
The last two flags are therefore important when choosing between Eulerian and Lagrangian frameworks<br />
<br />
===Member Variables===<br />
<br />
Let us look at the member variables of the ResidualBasedNewtonRaphsonStrategy class:<br />
<br />
typename TSchemeType::Pointer mpScheme;<br />
typename TLinearSolver::Pointer mpLinearSolver;<br />
typename TBuilderAndSolverType::Pointer mpBuilderAndSolver;<br />
typename TConvergenceCriteriaType::Pointer mpConvergenceCriteria;<br />
<br />
TSystemVectorPointerType mpDx;<br />
TSystemVectorPointerType mpb;<br />
TSystemMatrixPointerType mpA;<br />
<br />
bool mSolutionStepIsInitialized;<br />
bool mInitializeWasPerformed;<br />
bool mCalculateReactionsFlag;<br />
<br />
bool mKeepSystemConstantDuringIterations;<br />
bool mReformDofSetAtEachStep;<br />
unsigned int mMaxIterationNumber;<br />
<br />
The first four variables are pointers to structures that carry out a great part of the computations. These are instances of classes [[Scheme]], [[LinearSolver]], [[BuilderAndSolver]] and [[ConvergenceCriteria]], the role of which has been briefly outlined in the previous section.<br />
<br />
The next three variables correspond to pointers to the system matrices K (mpA), ''&Delta;''u (mpDx) and f (mpb). Their respective types are defined in the base class template argument classes TSparseSpace and TDenseSpace that have been described in the description of the base class' constructor, providing the desired flexibility to the selection of a corresponding LinearSolver.<br />
<br />
The next three variables are flags indicative the status of the resolution process. They are used to control the internal workflow.<br />
<br />
The rest of the variables are customization flags:<br />
*mKeepSystemConstantDuringIterations It indicates weather or not the system matrices are to be modified at each iteration (e.g. as in the complete Newton-Raphson method). Setting it to true will drop the convergence rate but could result in an efficient method in some applications.<br />
*mReformDofSetAtEachStep It is set to true only when the connectivity changes in each time step (e.g. there is remeshing at each step). This operation involves requiring the DOF set to each element and rebuilding the system matrices at each time step, which is expensive. Therefore, it should be used only when strictly necessary. Otherwise it is only called at the begining of the calculation.<br />
*mMaxIterationNumber Its meaning has already been explained in the description of the class' constructor.<br />
<br />
===Public Methods===<br />
<br />
Here we discuss in some detail the specific implementation of this derived class' public methods.<br />
<br />
====Predict====<br />
<br />
void Predict()<br />
<br />
It calls the scheme's 'Predict' method ([[see How to Use Scheme]]), moving the mesh if needed:<br />
<br />
GetScheme()->Predict(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
if (this->MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
====Solve====<br />
<br />
double Solve()<br />
<br />
It contains the iterative loop of the Newton-Raphson method. The needed elemental matrices are calculated by a Scheme instance and the system matrices are assembled by the BuilderAndSolver that takes it as a parameter and can deal with the particular container structures and linear solver of the SolverStrategy because it too takes them as template arguments. The flow of operations is as follows:<br />
<br />
=====Step 1=====<br />
<br />
A first iteration is initiated by checking if convergence is already achieved by the actual state:<br />
<br />
is_converged = mpConvergenceCriteria->PreCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 2=====<br />
<br />
If the base type member variable mRebuldLevel has been set to 0, just the RHS is rebuild after each time step:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false)<br />
pBuilderAndSolver->BuildAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
which performes one iteration, that is, it builds the system and solves for mDx.<br />
<br />
For most applications, though, a higher level is set and the following method is called instead:<br />
<br />
else<br />
pBuilderAndSolver->BuildRHSAndSolve(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
=====Step 3=====<br />
<br />
Next the problem variables are updated with the obtained results. This is performed by the scheme:<br />
<br />
pScheme->FinalizeNonLinIteration(BaseType::GetModelPart(), mA, mDx, mb);<br />
<br />
Additinally, the mesh is moved if needed:<br />
<br />
if (BaseType::MoveMeshFlag() == true) BaseType::MoveMesh();<br />
<br />
=====Step 4=====<br />
Now the 'PostCriteria' convergence check is performed only if the 'PreCriteria' method in step 1 had returned 'true'. Otherwise the algorithm simply continues. This method may require updating the RHS:<br />
<br />
if (mpConvergenceCriteria->GetActualizeRHSflag() == true)<br />
{<br />
TSparseSpace::SetToZero(mb);<br />
<br />
pBuilderAndSolver->BuildRHS(pScheme, BaseType::GetModelPart(), mb);<br />
}<br />
<br />
is_converged = mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=====Step 5=====<br />
<br />
The iterative loop is initiatied:<br />
<br />
while (is_converged == false && iteration_number++ < mMaxIterationNumber)<br />
<br />
======Step 5.1======<br />
<br />
Just like in Step 1. 'pre' convergence criteria are assessed.<br />
<br />
======Step 5.2======<br />
<br />
Only if needed, Step 2 is repeated:<br />
<br />
if (BaseType::mRebuildLevel > 1 || BaseType::mStiffnessMatrixIsBuilt == false ):<br />
//Step 2 is performed<br />
<br />
======Step 5.3======<br />
<br />
Step 3 is repeated<br />
<br />
======Step 5.4======<br />
Step 4 is repeated<br />
<br />
=====Step 6=====<br />
Once the loop is finished, reactions are calculated if required:<br />
<br />
if (mCalculateReactionsFlag == true)<br />
{<br />
pBuilderAndSolver->CalculateReactions(pScheme, BaseType::GetModelPart(), mA, mDx, mb);<br />
}<br />
<br />
=====Step 7=====<br />
Finally the scheme's and builder and solver's 'FinalizeSolutionStep' method are called as well as some other clearing methods if required.<br />
<br />
====Clear====<br />
<br />
void Clear()<br />
<br />
It calls special methods defined in the base class template argument classes TSparseSpace and TDenseSpace to clear and resize the system matrices (mpA, mpDx and mpb) to 0. It also calls the builder and solver's and the scheme's respective 'Clear' methods, since they in turn also contain matrices. In order to make sure that the DOFs are recalculated, DofSetIsInitializedFlag is set to false.<br />
<br />
====IsConverged====<br />
<br />
bool IsConverged()<br />
<br />
It calls the builder and solver's method 'BuildRHS' (see [[How to use Builder And Solver]]) if an actualized RHS vector is needed for the particular ConvergenceCriteria class that is used. <br />
<br />
if (mpConvergenceCriteria->mActualizeRHSIsNeeded == true)<br />
{<br />
GetBuilderAndSolver()->BuildRHS(GetScheme(), BaseType::GetModelPart(), mb);<br />
}<br />
<br />
Then it calls ConvergenceCriteria's 'PostCriteria' method, which applies the particular criteria to its input and returns its output (true or false):<br />
<br />
return mpConvergenceCriteria->PostCriteria(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
====CalculateOutputData====<br />
<br />
void CalculateOutputData()<br />
<br />
It calls the scheme's corresponding method:<br />
<br />
GetScheme()->CalculateOutputData(BaseType::GetModelPart(), rDofSet, mA, mDx, mb);<br />
<br />
=Python interface=<br />
Kratos [[applications]] are usually designed to be used through a Python interface. Therefore, the objects described in this page are often created in a python script that we refer to as [[Strategy python]] (see [[How to construct the "solving strategies"]]). Similarly, the public methods of SolverStrategy will typically be called from the main script, which is usually also python based (see [[Python Script Tutorial: Using Kratos Solvers]])</div>Gcasas