Previous lecture we started talking about non-linear finite element model development. We started by discussing sources of nonlinearity and we saw that the main source of nonlinearity could be due to strain displacement relations being nonlinear that is called geometric nonlinearity and the stress strain relations could be nonlinear that is material nonlinearity. There could be nonlinearity associated with boundary conditions or energy dissipation mechanisms. So various schemes of classification are possible all these nonlinearities can coexist in a problem. So in this case what is shown here is small displacement and small strains but there is material nonlinearity here whereas in this case there is material could be linear or nonlinear but there are large rotations but small strains. Here material again could be linear or nonlinear there is large rotation and large strains and this is schematic of a situation where there could be nonlinearity associated with boundary conditions. So this spring will come into action only when this displacement here exceeds this threshold in which case the stiffness of the system increases. Here the loading and unloading path will be tracing each other whereas in material nonlinearity the unloading path will be different from the loading path. After reviewing few details about qualitative feature of nonlinear system response and how it differs from response of linear system we started talking about elements of continuum mechanics and I briefly started talking about kinematics that is study of motion and deformation without concerning the causes of motion and deformation. So we have a configuration at time equal to 0 it is here and this could be the reference configuration and this is a Cartesian coordinate system in which the position of particles in the material points in this body are described. So position vector of P is X and after deformation this point P with position vector X gets mapped to point P with position vector lower X through this transformation and P is the displacement vector defined as U of X minus X. So in Lagrangian descriptions we treat the coordinates of capital P that is capital X1, X2, X3 as the independent variables whereas in Eulerian system we treat the current position the position of particle P in the current configuration that is lower case X1, X2, X3 as the independent variables that is Eulerian system. So in solid mechanics problem often Lagrangian coordinate system is used. Now let us consider a line segment PQ in the body B at in the reference configuration and due to this deformation this point PQ moves to this position PQ as shown here and the initial length is D capital X this is D lower case X. So various position vectors of PQ in the T equal to 0 and at some time T are shown here. Now the position vector in the current configuration is a function of the position in the original configuration and I can write X1, X2, X3 in the long hand in this form and from this I deduce DX1 is dou X1 by dou X1 into DX1 etcetera that is shown here. So this set of equations where I get DX1 DX2 DX3 which are components of position vector in the deformed configuration which are related to the components of position vector in undeformed configuration through this matrix and this matrix is known as deformation gradient tensor. So we get this equation in matrix notation it is DX is equal to FDX in the initial notation DXI is FIJ DXJ or in tensor notation it is F dot DX. The determinant of this matrix F is known as Jacobian by inverting this relation I can also hear the components of line segment in the deformed configuration are related to component of position vector in the original configuration this can be inverted and I can get this relation and DX is F inverse DX. We have displacement vector as U is X-X from which I get DU is DX-DX and DU I can write therefore as for DX I will write FDX so this is F-IDX and this matrix F-I I call it as capital G and this is a displacement gradient with respect to reference configuration. Alternatively I can express D capital X in terms of D lowercase X through this relation and I get this is I-F inverse into DX and this matrix is known as J naught. So this is a displacement gradient with respect to current configuration. So this G and J naught matrices are shown here this is with respect to displacements whereas this is with respect to the coordinates of the position vector. So G is this, J naught is this but for small deformation G and J naught are related through this relation so in small deformation these relations apply. We can consider few examples suppose I have the displacement field given by lambda E1 X1, lambda E2 X2, lambda E3 X3 so that X1 X2 X3 are respectively lambda X1, lambda X2 and lambda X3. The displacement U1 U2 U3 will be lambda minus X1 X1 and lambda minus 1 X2 lambda minus 1 X3. From this I get the deformation this matrix F as this and this type of deformation is called pure dilatation.  So this deformation is called pure extension. Now I can consider X to be A into X plus C from this it follows U is A minus I X plus C and F is simply A. When the matrix F is independent of X we say that the deformation is homogeneous. A slightly more involved another example here X1 is X1 plus gamma X2, X2 is lambda X2, X3 is lambda X3 so that the F matrix will be given by this and this deformation is called pure shear. Now here I have a displacement field where there are non-linear terms X1 into X2 and so on and so forth. So U1 will be gamma 1 X1 X2, U2 X2 is gamma 2 X1 X2 and X3 is gamma 3 X2 X3 from which I get F to be this which is a function of now X1 X2 X3 and such deformation are called non-homogeneous. So they are simple illustrations. Now this phi of X, T which maps the position vector X to lowercase X it has to satisfy certain conditions this function is continuously differentiable and this is 1 to 1 and that means F matrix can be inverted so DX is F DX and J is a Jacobian determinant of F. We impose a condition J to be greater than 0. You can see that the determinant can be expressed in terms of displacements as shown here if all displacements are 0 then I get J equal to 1. So for F to be invertible J must not be equal to 0 because it is determinant of F, for F inverse to exist determinant of F must not be 0. So upon deformation J cannot become negative without crossing J equal to 0. So hence we impose a condition that J must be greater than or equal to 0 and these are such motions are admissible motions. Now an example of a motion which is not admissible is shown here you can verify that this is not admissible. I mentioned in the previous lecture that rotations play a crucial role in analysis of nonlinear systems. So let's consider a displacement field where there is a rigid body translation and a rotation. This R matrix is a rotation matrix so this relation is expressed in matrix form here, indicial form here and a tensorial form as shown here. This R of T is a rotation matrix, XT of T is a rigid body translation. Now from this equation if I find DX it will be RDX plus DXT of T, DXT of T is 0 because rigid body translation implies no change in length therefore this is RDX. Now if you find the length DXT, square of the length DXT DX and for DX if I write this equation I get DX transpose, R transpose, R DX.  So this length remains unchanged therefore I mean I have to verify that R transpose R is I therefore the argument is the length in a rigid body rotation and translation length doesn't change therefore DXT transpose DX must be equal to DXT DX therefore this is true for any DX consequently I get the relation that R transpose R is I or in other words R transpose is R inverse that is R is orthogonal. Now how does pictorially it looks like suppose you would consider a triangular domain upon this transformation RX plus XT this R effect of R is to rotate this as shown here and XT the effect of XT is to translate, so this is how the element looks upon undergoing this deformation. Now if we consider two coordinate systems XJ and XJ prime such that a position vector in XJ system is XJ, EJ and position vector in XJ prime coordinate system is XJ prime EJ prime where EI are the unit vectors in XJ system and EJ prime are the unit vectors in XJ prime system. Now repeated indices imply summation here. Now clearly this dot product of EI EJ is delta EJ and since this is equal I get this relation I will dot product with EI and I get this and from this I denote EJ prime dot EI as RJI and this gives me the relationship between XI and XJ prime and similarly I consider this equation and again do another dot product this time with EI prime I get this relation. So from this analysis we get that X is all transpose X prime and X prime is RX, so this is how a vector undergoes transformation due to coordinate transformation. By using similar arguments we can show that a tensor like stress undergoes transformation following this rule. We introduce another quantity known as angular velocity so we consider again the rigid body translation and the rotation as shown here and I differentiate this with respect to time I get X dot is R dot X plus X dot T T. Now for X I will write using this relation it is X minus XT of T into R inverse of that and R inverse is RT and I get this relation, so from this I get this equation I rewrite in this form by denoting R dot R transpose by capital omega and this quantity is known as angular velocity tensor. Now we can show that this angular velocity tensor is a skew symmetric matrix to show that we consider D by DT of RR transpose since RR transpose is identity matrix this must be equal to 0, so that means R dot R transpose plus RR dot transpose must be 0 from which I get the relation omega must be equal to minus omega transpose therefore omega is skew symmetric and this will have this form. A simple example I consider X equal to 2X plus 3YT and Y is 2 XT plus 3Y, so the question is examine the admissibility of the motion sketch the configuration of the element at T equal to 0.5 second and determine the displacement velocity and acceleration fields, so the domain is OAB is described here this length is 1 this length is also 1, so deformation gradient you can easily evaluate it to be this and the Jacobian will be 6 minus 60 square and this has to for motion to be admissible this has to be greater than 0 therefore this motion is admissible only for T less than 1, at T equal to half I will put for T half I get this displacement field and I can map the points the 3 points here OAB and I will plot them in the transform coordinate and find out the displacement field and velocity field and acceleration field and this triangle is the OA prime B prime is the how this is how the triangle looks upon deformation at T equal to half, okay. Now there are other few things that I am stating without proof some of this we have done but other couple of things I am not doing upon deformation DX becomes FDX this we have shown similarly area element and volume element undergo transformation as shown here DA is JF transpose inverse into DA and volume is DV is JDFIJ as we already discussed it DOXPY DOXJ and inverse of this is this and J is the Jacobian and so is the determinant of F called the Jacobian, so these two results I am stating without proof you can with some effort you will be able to show that. There is an important concept known as polar decomposition theorem and that leads to notion of stretch and rotation tensors the main idea is this the motion of a line segment can be expressed as a pure deformation followed by a rigid body rotation or a rigid body rotation followed by a pure deformation that is F can be written as R into U or V into R where R is a rotation matrix and U and V are symmetric positive definite matrices. So for pure rigid body rotations F is R and consequently U will be I and V will be I.  So from this I get U is to be square root of F transpose R. Similarly if I consider the second relation F equal to VR I get post multiplying by F transpose I get FF transposes VRF F transpose and again by making the substitutions we show that V is square root of FF transpose. So how to find R from a given F?  Now this quantity F transpose F is known as denoted by CR and it is known as right Cauchy Green's stretch tensor and CL which is FF transpose is left Cauchy Green stretch tensor. Clearly they are symmetric and positive definite so that can be verified by inspection here. Now how do you find a square root of a symmetric positive definite matrix? Quickly we can recall let A be a N by N symmetric positive definite matrix let us consider the eigenvalue problem A phi equal to lambda phi. Let phi be the matrix of eigenvectors such that it is phi transpose A phi is capital lambda and phi transpose phi is I. So this lambda is a diagonal matrix of eigenvalues obtained by solving this problem and we know that these lambdas are positive and phi is real valued. So from this I can write A as phi lambda phi transpose. Now let B be square root of A that's what I wish to find out. So and I consider B equal to phi D phi transpose I don't know what is D. So from this I get B square is phi D phi transpose into phi D phi transpose but phi transpose phi is I that is how we have normalized this will therefore be phi D square phi transpose. This is equal to A because B square is A and consequently by comparing these two relations I get lambda to be D square you will be equal to lambda capital lambda and therefore D is square root lambda. So B is therefore obtained as phi square root lambda phi transpose. So this gives interpretation of what is a square root of a matrix. Now we will now talk about measures of strain. Now there are two requirements that we need to satisfy. First is the strains must vanish when body undergoes rigid body motion. Secondly the strains coincide with the infinitesimal strains in the limit of strains becoming small. So any measure of strain that we develop should conform to this and we can ask begin by asking the question why do we need new measures of strain? Why not be content with linear measures of strain? That is infinitesimal strains. Now the problem with infinitesimal strains is that linear measure of strain does not lead to zero strains for structures undergoing rigid body rotations. How do we see that? We can consider a small example. You consider a two dimensional example of an element which is rotated by angle theta it's a rigid body rotation. So the deformation is given by X is equal to X1 X2 is cos theta minus sin theta sin theta cos theta X1 X2. So for this deformation for any value of theta we expect that there won't be any, the strains would be zero. Now we can find the displacement field U1 is X minus X1 this and U2 is this and from which I compute the strain infinitesimal strain component dou U1 by dou X1 is cos theta minus 1, epsilon YY is dou U2 by dou X2 is cos theta minus 1 and shear strain is zero. Now you look at epsilon XX and epsilon YY they are not zero, but of course as theta goes to zero this goes to zero that is okay, but for large theta epsilon XX and epsilon YY do not vanish. This is why we need to redefine strain when you are considering large problems with large displacements. Now to examine this in a slightly greater detail we can consider cos theta minus 1 this can be shown to be equal to minus theta square by 2 plus order of theta to the power of 4 and approximately we can take it as theta square by 2. So based on this we will be able to judge when to abandon linear strain measures. So when theta is large we need to abandon. So just to quickly see that suppose the strain being measured is about 0.01 and acceptable accuracy is about 1% of that so this is accuracy. Now the term that we are ignoring in linear strain approximation is of the order theta square by 2. We assume theta to be small so that theta square by 2 can be ignored. So for that approximation to be acceptable theta has to be less than this which is about 0.01 radians. So if you are dealing with strains of about 0.01 and you want to characterize with 1% accuracy the rotation should not cross this. Similarly if the strain being measured is about 10 to the power of minus 4 and acceptable accuracy is 10 to the power of minus 6 that is again 1% error. The linear measure of strain is acceptable if theta is less than 0.001 radians. Now obviously if theta exceeds this the infinitesimal strain measures are not acceptable. Now it is important to note that if the structure is on the verge of losing stability small strains can cause large rotations. So we cannot use linear measures of strain in buckling analysis that is why we you know if you recall we use non-linear strain displacement relations when we did buckling analysis. Now equip with this we can introduce the first strain measure that is green Lagrange strain measure. We have seen this earlier but in a slightly different notation DX is F into DX and therefore length of an element DS square is DX transpose DX. This is in the deformed configuration and in the original configuration it is this. So the change in square of the length is DS square minus DS square and this I can write in this form.  So I will write this quantity in this form DX transpose, F transpose F minus I into DX. This quantity in the parenthesis I call it as 2 into tensor E defined as half of F transpose F minus I. This quantity is known as green Lagrange strain measure. Now we can relate this to displacement, gradients of the displacement. So we have U is X minus X that is dou U by dou X is dou X by dou X minus I and we have G is equal to F minus I and for F if I write now I plus G I will be able to get that and upon slight simplification I get E as this. Now one of the requirements that we stipulated is under rigid body motions the strain measure should go to 0. So we can verify whether that is true here. So I again consider rigid body motions as R into X plus XT of T. So this is translation this is rotation, R is a rotation matrix. So F is R of T in this case and if I substitute that into this I get F transpose is R transpose, here it is R transpose R minus I and R transpose R is I therefore this is 0. So unlike the infinitesimal strain measures this is 0 for any rotation, any rigid body translation and rotation. Now how about the other requirement that when strains are small this strain we should recover the infinitesimal strain components. So to be able to do that we expand this and write in terms of all the terms we write in long hand and quantities that are shown in the red are the nonlinear terms and the quantities in black are the infinitesimal strains. So for small strains we can clearly see that quadratic terms can be ignored so we recover back all the terms in the red vanish and we recover the infinitesimal strain components. So therefore this definition is acceptable by the 2 L sticks that we stipulated. Now we can also show that the magnification of a line segment that is if there is a line segment PQ with direction cosines N alpha upon deformation if I define a quantity known as magnification factor as dS by dS whole square minus 1 that you can see that this is nothing but dS minus dS whole square divided by dS square and this is defined as a magnification factor of a line element. We can show that in terms of the green like a grand strain tensor this is this magnification factor is given by this. So clearly here if the line segment is such that it aligns along with X1 axis this is a repeated index implies summation so the line segment that is lying along X axis is magnified by the quantity E11 and a line segment which is aligned with X2 axis is magnified by E22 and a line segment along this is magnified by E33. Now similarly if you take two line segments which bear an angle theta before deformation and they deform to this configuration we can show that a measure of shearing strain this again we have discussed in the previous one of the previous lectures is given by this and the strain E appears here. So here again if theta is pi by 2 and a line segment is aligned with X axis and Y axis here there are two direction cosines N alpha is direction cosines of PA and M alpha direction cosines of PB. So if PA aligns along one of these axis and PB aligns along one of these axis then for example E12 will be the shearing strain as per this definition between these two lines. So the green like a grand strain has this interpretation there is another strain measure known as Almancy-Hamel or Eulerian strain here instead of you know eliminating capital DX here in this case if you see here we obtained the difference in square of the length in terms of DX in the original configuration a similar equation can be derived by using DX in the current configuration. So that definition takes us to this Almancy-Hamel Eulerian or the Eulerian strain and this is defined with notation small E and here DS square minus DS square is written in terms of lower case DX and we get in this form and this quantity I minus F inverse transpose F inverse is defined as E this is the Almancy-Hamel strain measure. Here again if you take a rigid body rotation we can show that we can first derive the strain components in terms of displacements and we get in terms of J naught matrix E is expressed in terms of J naught matrix as shown here this can be verified. If you consider now rigid body motions X as RX plus XT of T again we can show that E becomes 0 and by expanding the terms we can again show that it can be verified that for small strain the strain measure agree with the measures agree with the results from infinitesimal strains. Two definitions of strain measure we also talk about what is known as rate of deformation we call capital L as velocity gradient where LIJ is defined as dou VI by dou XJ so that means DVI is LIJ DXJ this L matrix furthermore we write it as sum of a symmetric matrix and an anti-symmetric matrix and this symmetric component of that is known as rate of deformation tensor and W is given by this. Now if you consider the rate of change of the line segment DS square rate of change of square of the length of infinitesimal line element if you consider this you can begin by noting that DS square is DX1 square plus DX2 square plus DX3 square and by writing this in this form we will be able to see that it is 2 DXT DX transpose DX by DT and we can rearrange these terms and use this identity and we can actually show that the F matrix and D matrix are related through this. So this F dot matrix so this is some discussion on rate of deformation the relationship between D and derivative of the Green Lagrange tensor can also be derived I have indicated the steps here and we can show that E dot is F transpose DF so I leave it as an exercise for you to verify this. Now how about measures of stress we have talked about measures of strains while defining stress there are two alternative perspective in the first perspective we think of an internal force and an area over which this force acts in Cauchy stress tensor with which we are all familiar the force is a deformed, force is reckoned with respect to deformed configuration and area is also reckoned with respect to deformed configuration. Now this Cauchy stress tensor is difficult to use because beforehand we will not know the properties of the deformed configuration so that is what makes us to think of alternative measures of stress. So in first Piola-Kirchhoff stress tensor the force is measured with respect to a deformed configuration but the area is transformed back to the undeformed configuration, so it is force internal force reckoned with respect to deformed configuration and expressed with respect to area that is stress is expressed with respect to area in the undeformed configuration. In the second Piola-Kirchhoff stress tensor the force is also transformed back to the undeformed configuration and area is also transformed back to the undeformed configuration. So this is one way of looking at stress but the other alternative is to look at stress and strain measures as conjugate pairs which combine together to produce an expression for internal work done. So in terms of a virtual work concept we have seen that strain energy stored in a body is expressed as product of integral of a product of stress and strain. So we can think of stress as something that is a conjugate of a strain measure so that along with the associated with strain measure it leads to a proper definition of internal work done due to deformation. Now let's quickly recall the Cauchy stress, definition of Cauchy stress so we have an object in the initial configuration body B I call it as B naught and this is a coordinate system and this is acted upon by body forces that is forces which are proportional to the volume and surface tractions which are forces which are proportional to the area and upon application of these forces the body deforms and the process of deformation is opposed by an internal set of forces set up in the body and that internal set of forces is what creates stress in the body. To characterize that what we do is we consider the body in the current configuration that is after the application of these body forces and surface tractions the body has deformed and internal force system has been developed. So what I do is I consider an imaginary region C and I cut this out from this configuration, so this picture represents the current configuration with inner part of C removed and this configuration this figure represents the current configuration with outer part removed. So what I do is at a point P I consider an area element say delta A and that element has in this figure it has an outer unit normal N and the internal force acting on this delta A produces a force vector and that is delta F it need not coincide with N nor it should be need to be parallel to the surface area. This force system when imagined for this part there is a hole here and N is a unit outward normal and delta F is the force acting on the elementary area delta A at P. Now the fact that such internal force system exist is the Cauchy Euler hypothesis. So what it says is material occupying the interior of C exerts a force field on the material exterior to C. Similarly material exterior to C exerts a force field on material interior to C. These two force fields are equivalent opposite. The interaction is free of any moment there are no couples okay it's only the delta F is only a force there is no moment there okay there is an assumption that we make and under these conditions we define stress at P I call it as T tilde N there is N is a unit outward normal tilde denotes that is a vector this is limit of delta A going to 0 delta F by delta A. T tilde A is defined in the current configuration and T tilde N is a vector normal stress is components of T tilde N along N and shear stress components of T tilde N perpendicular to N. T tilde N clearly depends on N that means passing through this point P I can select so many area segment that means this the way I have cut this not the only way I can cut it in many ways. So the direction of unit outward normal can vary so passing through point P I can draw an infinity of you know planes with unit outward normal N and we need to if you want to define state of stress at point P I need I should be able to specify what is T tilde N for any choice of the orientation of N. So complete specification of state of stress at P requires all these T tilde N to be specified for any choice of N. Now stress analysis is determination of stress analysis consist of to determine state of stress at all points in B. So this looks like a tall order at any one point I need some infinity of vectors and there are infinite points in B so how do we proceed? So here what we do is we select a cardinal coordinate system and erect three planes which are mutually perpendicular passing through P and define the stress vector on these three planes and knowing that we will be able to specify stress on any plane that is inclined to this cardinal plane. So according to Kashi's stress formula the sigma is the stress tensor this is T tilde N is the stress vector with unit outward normal N and this is given by this. So the sigma is a second order tensor and this is symmetric because there are no interacting moments and if you change coordinate system sigma prime is given by C sigma C transport where C is the transformation matrix and this I am quickly recalling I expected you have this is not the first time you are hearing about all this. So this leads to the concept of principal stresses and principal axis then stress invariance and we will be able to find out maximum normal and shear stresses and the planes over which they act and when writing stress in finite element formulations as you have seen stress can be written either as a 3 by 3 matrix which is symmetric or as a column vector by using what is known as white convention. So we select elements in this order these diagonals this and this and I have sigma 1 1 2 2 3 3 then 2 3 1 3 and 1 2 so this is how we arrange the column vector. Now Kashi's stress is the most natural measure of stress because it finds out it considers the body in the deformed configuration when the internal force system has been set up and it describes that state of force per unit area in some sense that area is also record with respect to deformed configuration, but that itself leads to certain difficulties that is it is defined with respect to deformed geometry which would not be known during the solution process. So in Lagrangian description equations are written with respect to the known reference configuration, so this there is a contradiction between these two and consequently it reads the requirement that we need alternative definitions of stress measures. So this leads to a couple of definitions for stress the first is known as first Piola Kirchhoff stress, so here what we do is this is the description is quite similar to what I talked about. Now this stress is defined what we do is this is the unit outward normal here and this is the force vector and I consider this area delta A I map it back to what it would be in the undeformed configuration. So I will consider this delta F and this area in the undeformed configuration and set up a definition for stress, how do I do that? So we have seen that the rule for transformation of areas is DA is J F inverse transpose DA and we have DF is T tilde N DA and T tilde N itself is sigma N, these results are known now. Now what I do is we introduce capital T tilde N I call it as stress vector acting on element DA that is this in this, okay. It is introduced in such a way that it produces the force DF that force is this DF, so I have DF as T tilde N DA and this must be equal to capital T tilde N DA, so this and consistent with this definition T tilde N is sigma N I introduce another matrix P such that T tilde N is P into N where capital N is the vector of unit outward normals, so this quantity capital P is known as first Piola Kirchhoff stress tensor, this is a current force per unit undeformed area, so we need not know the deformed configuration to work with the first Piola Kirchhoff stress, there is a problem here and you can relate the first Piola Kirchhoff stress to the Cauchy stress through this relation using the relation between you know DA and capital DA as shown here. If you observe this matrix carefully we see that P is not symmetric and it has nine independent components and when working with you know constitutive laws with symmetric strain matrices this becomes inconvenient, so this is not going to be convenient for our modeling purposes, so that leads us to introduction of another stress measure known as second Piola Kirchhoff stress. Now here what we do is we introduce a pseudo force vector fashioned after the relation DX is F inverse DX, I define DP cap as F inverse DF, see I have here this DF and I define with respect to the undeformed configuration another force vector see a line segment which is again a vector gets transformed through this relation, so using this is a vector and force is also a vector using the same transformation I define a force vector DP cap as F inverse DF, so this DP cap is F inverse DF and using our relations of definition of DF I can write this as F inverse for DF I will write T tilde N DA and again for T tilde N if I write sigma N DA I can rearrange the terms and I get a quantity known as SN DA where S is given by J F inverse sigma F inverse transpose, this quantity is known as second Piola Kirchhoff stress transfer and as you can see S will be symmetric here, sigma is symmetric and there is a F inverse and F inverse transpose coming here so if you find S transpose of S it will be same as S, so this is the second Piola Kirchhoff stress, so the relationship between second Piola Kirchhoff stress, Cauchy stress and the first Piola Kirchhoff stress is through these three relations, okay. Now to proceed further we need to set up the physical laws which are expressed as you know what are known as balance laws that is principle of conservation of mass, principle of conservation of linear momentum, principle of conservation of angular momentum and principle of conservation of energy, so these form the backbone of our mathematical formulation of problems of continuum mechanics and these basically relate the field variables like displacement, velocities, accelerations, stresses and strains and to the body geometry applied surface tractions and body forces, boundary conditions etc. and they lead to the governing equations to be solved. Now we need to elaborate on that but before that we can make some observations, when introducing the notion of stress I talked about conjugate pairs of stress and strain, we can show that the second Piola Kirchhoff stress tensor and the green Lagrange strain tensor form conjugate pairs so that we can compute the strain energy stored due to deformation using this relation, this is S double dot E dot DV where V is the volume, so W is the internal work done per unit time, per unit volume in the reference configuration. So we start with this expression such as this and use either principle of virtual work or variational approaches and we will be able to express S and E in terms of the displacement fields and those displacement fields will be interpolated within an element and we derive the governing structural matrices and vectors. So that we need to do, to begin our discussion what we will do is we will focus on linear relationship between conjugate stress and strain measures. So for example we will take that the PK2 that is Piola Kirchhoff second stress tensor and the green Lagrange strain tensor are linearly related, it is like Hooke's law between stress and strain but the stress is now not the Cauchy stress and strain is not the you know green Lagrange, this is green Lagrange but stress is second Piola Kirchhoff. So this is what we will start doing and if there is material nonlinearity of course this will be more involved. Now in our development of finite element formulations there are three alternative kinematic descriptions that are possible, so they are known as total Lagrangian approach, updated Lagrangian approach and co-rotational formulation. So I will just explain what these are then we will consider the more details in the following lectures. In the total Lagrangian approach the base and reference configurations coincide and it is taken to remain fixed and the current configuration is this, so we describe the reference configuration to be the undeformed or the initial configuration, this is total Lagrangian approach. In updated Lagrangian approach the reference configuration is updated at each increment of loading while solving the equilibrium equations. So this is a current configuration and this is the reference configuration this is the undeformed configuration, so this reference state gets updated at every time as the load is incremented. In co-rotational formulation we start with the base configuration and it is used as a reference to measure rotations and co-rotated configuration is used as a reference to measure current stress and stress, so all these figures are very exaggerated, here the co-rotated configuration undergoes rigid motion for example the CG of these two configurations will coincide, so it is a rigid body motion and then from this rotated configuration we characterize the current configuration. So what we will do is in the following lectures we will elaborate on this and try to develop finite element formulations with whatever time that is left probably we will deal with total Lagrangian formulation for simple line elements like bars and beams and I will also outline how to proceed for two dimensional and other problems, so that we will take up in the following lectures, so at this stage we will close this lecture.


Okay. Hello. It seems we're live. So welcome to my talk in these strange times. My name is Bartosz Golaszewski, and I'll be talking about GPIOs in Linux user space. While I'll try to keep this presentation fairly accessible to beginners, I won't be going into much detail about the things I covered during previous presentations when I was already talking about GPIOs in Linux. I'll just post some links to my previous talks and the slides. With that out of the way, I'll start with some background. So I'm with Pelebre. We are a base in southern France. Plus we have some people scattered all over the world, and we are right now around 40 mostly senior engineers, and we do a bit of everything. So we do upstream kernel development and maintenance. We help clients launch projects from concept through design and implementation up to manufacturing. We're also the founding members of Kernel CI and we regularly contribute to many open source projects. Personally, I've been working in the field for 10 years now. I work throughout the stacks from bootloaders through the Linux kernel app to low-level user space and build tools. What's important for this talk is I wrote and maintain LibGPIOD and co-maintain the GPIO subsystem in Linux and also contribute to many other projects like Uboot, Yocto, and so on. So this presentation will actually be much different than what I imagined when writing the abstract a couple months ago. It's not only because I thought I'd be physically present in Austin, but also the subject matter of this presentation change significantly. So in one line of the abstract, I said something like this, the GPIO character device has been extended with new features in Linux 5.5, and final new addition before declaring it feature complete our plan for 5.6 and 5.7. So this is all wrong. Basically, we got a serious reality check when we started receiving some bug reports on the mailing list and some feature request that were sensible by all means, but which we just couldn't implement without breaking the ABI. Breaking the ABI of the current interface, and all in all this line in the abstract should be changed to something like this. The GPIO character device has been extended with new features in Linux 5.5, but due to shortcomings, the first version of the ABI, the existing IOTL calls are being retired and V2 of the ABI is being introduced aiming at the release as part of Linux 5.9, hopefully. So there you have it. With this in mind, let's maybe dynamically change the title of the presentation to Linux GPIO, a lesson in user API design. So welcome again to my talk. I'll be talking about what to keep in mind when designing user-facing interfaces in the Linux kernel in the context of Linux GPIOs. So the agenda for today is as follows. First, I'll discuss the current state of the GPIO user API. I'll talk about the deprecated CISFS interface. I'll briefly describe the character device basics, and then go to the recently added new features, including but not limited to the GPIO aggregator. Next, we'll talk about the work happening right now on the new version of the GPIO API. I'll talk about what's wrong in the current ABI, what new features are being worked on, and I'll also talk on what to pay attention to when designing user-facing interfaces. Lastly, I'll talk a bit about the user-spaced part of the GPIO interface, which is the GPIOD. Again, I'll talk mostly about new features and the upcoming development, because most of the features already existing have been covered previously. So let's start with the current state of GPIO user API in Linux. What you normally do with GPIOs in the Linux kernel is to try to use them from the kernel space. That is, you should aim at writing drivers for your hardware and use the internal GPIO interface, but that's not always possible or maybe even desirable. There are many cases where you'd want to control GPIOs from user space. Examples of such use cases are power switches and relays, various devices that are controlled in big part from user space, for example, GPS. Also, certain users simply seem to prefer to control elements of their hardware from high-level user space applications. This is prevailing in robotics and home automation. In 2008, the GPIO subsystem was quite different from what we have today. There were no GPIO descriptors. There was only the global GPIO number space, and also there was no GPIO maintainer. It's during that time that the GPIO SysFS interface was merged. Today, it's really become apparent that it's outdated and has many drawbacks which were the reason to implement the character device in the first place. First, the state of the GPIOs is not tied to any process. It's global in the system. For instance, if a process modifies the state of certain lines or just exports some lines and then dies the settings aren't reset. Of course, that also makes it possible to access the lines concurrently from many processes, and no process can really own the lines. The API is quite cumbersome with many attributes and different SysFS files. Things like looking up lines by name are complicated. By far the worst part of the interface is the fact that it represents a concept that is a two-level hierarchy as a flat number space. Precisely what we did in the all-deprecated kernel interface. But this is especially wrong since the global GPIO numbers are dynamic. Unless a driver explicitly makes it static, it can change depending on the ordering of modules being loaded. There are even bizarre caveats with GPIOs and SysFS like the fact that if you export a line by writing a number to the export attribute, it normally shows up as a new directory called GPIO number. But if the line is named, it will show up as a new directory with that line's name, which is some way of looking up lines by name, but it's not really intuitive and makes the SysFS behave in a non-consistent way, let's call it. So I'd really like to stress the need to move away from this interface because even last year in Lyon during the ELCE, it was brought to my attention that there was a talk presenting among others, the SysFS GPIO interface. So we're working hard to remove the global number space from the kernel, but SysFS is one of those things that will block us for the foreseeable future. So yeah, please don't use it anymore. It's not disabled by default anyway. So since Linux 4.8, we have a new interface for GPIOs in user space and that is the character device. So now for every GPIO chip, we export a device file in the dev slash dev directory. It can be accessed and acted on using standard Linux system calls, Open, Paul, Reed, IOPL. This time, the state is tied to the process owning the file descriptor. It reverts to default when the process closes the relevant file descriptor manually or closes by simply dying. We now can manipulate multiple lines at once. We can easily look up chips and lines by name. We have several config options unsupported by SysFS, like the drive flags, bias flags. We have visible consumer names, new event notifications, and finally we have something interesting, which is reliable polling. Because actually previously in SysFS, many people are not aware of that, polling for line events was possible, but it was quite unreliable in cumbersome. So in SysFS, when you export a GPIO line, you can open the value attribute and poll it for input events. And when you get an event, when Paul reports an event, it means that there has been a change in line value. Now, what you need to do to read the current value is to either close and reopen the value attribute, or you have to LC to its beginning and read the ASCII value from the attribute. And as you can tell, this is quite racy because there could have been multiple changes, multiple line events during that time. And so with the character device, this works much better because now the events are queued in the buffer in the kernel. Every time an interrupt is handled, a new event is put into the kernel FIFO, and yeah, unless this buffer overflows, it can be read one by one from user space. The poll system call will return an input event as long as there are events queued. And yeah, this allows to implement simple bit banging from user space. This will be actually made even more reliable in V2, but I'll be talking about it in a minute. And so I don't want to get into much detail about the character device, about all the features. I will focus on the new features, but this is a link for my talk that happened in St. Petersburg during the Linux Peter in 2018, I think, which covers basically all features of the kernel UAPI and the GPIOD. So what I want to talk about are the new features. So recently we've had some development happening. The first interesting new feature is the new Set Config IOPO. So one of the drawbacks we identified in the character device was the fact that you cannot change the configuration of a requested line without releasing it and requesting it again. And this is obviously racy as someone may simply grab the line when it's released and we cannot even change the direction from user space while maintaining the ownership of the line. So the answer to that is the Set Config IOPO. Basically, it allows to change the configuration flags for a set of requested lines. Unfortunately, for now, this doesn't work with lines requested for events, but this will be addressed in version two of the API. Basically, you have to use a very simple structure which contains the field for the flags, which is analogous to the one we have in the context structure used when we call line-handled request IOPO. And we also have optional default values used when changing the direction to output. This is also a counter part of the field used in the context structure. And the next feature is something that has been requested for a long time, but we simply somehow could not really agree on a proper approach, which turned out to be relatively simple in the end. It's the bias configuration. So many GPIO controllers have internal configurable resistors. In the kernel, this is represented as bias pull up or pull down options in the pin control and GPIO subsystems. And now we can finally change these options from user space using the three new flags. There's the pull up, pull down, or bias disable flag. And these work both when requesting lines as well as with the set counting IOPO. So I know that this feature in particular made a couple folks very happy. And then we have the line watch IOPO. So this is something that I personally really wanted to see because I'm interested in the user space part of the GPIO interface. So the background for this is the fact that GPIO character device doesn't hold a global state, and this is by design. But some people are used to having some central authority having control over all the GPIO lines. And in this case, what we're working on is a GPIO demon that will, basically it would be responsible for controlling the global state of the GPIO. And in this case, it's useful to always be up to date regarding the current status of lines you control. Otherwise, so you want to know if their status, when their status changes, when they are being requested, released, or their config changes. And otherwise you'd have to constantly pull the lines for a config change at certain intervals. So the line watch IOPTO allows you to set up a watch on the line state changes. And when calling this IOPTO, the current line config is returned first. So this is how it is. We call the line watch IOPTO. We get the current configuration as it is at the moment. And then we pull the GPIO, the file descriptor associated with the GPIO chip. So not the file descriptor returned, not the file descriptor associated with certain lines, but with the chip itself. And then when any of the lines that are being watched changes, it's requests requested, released, or it's config changes, we can read the strike GPIO line info changed from the file descriptor, and it contains the new configuration. So this is the line info, the timestamp, which is the approximation of when the change happened and the event type, which tells you if the line will happen to the line. And I've also noted that the structure contains padding because it's gonna be important for when I'm gonna be talking about what's wrong in the current API. So right, so by far the biggest and probably my favorite feature, new feature is the GPIO aggregator. It's my favorite to the point where it got its own title slide. So one of the problems with the character device is that it's a device file in the Unix sense, which means it has modifiable permissions, but they affect all the lines of a chip. They basically affect the chip file, the device file, but not single lines. So you cannot have just certain lines accessible to a user and not others. And this of course may be undesired, but worry not, because now you can aggregate lines in virtual GPIO chips of which permissions can be different than those of real chips. So this works like this. You have, let's say you have two real GPIO chips, only readable and writable by root. So you have chip A and chip B both with four lines. So let's say that you can, you want now to make lines zero and two of the first chip and one of the second chip accessible to a user with different permissions. So you can now with the GPIO aggregator, create a virtual GPIO chip that will have, that will basically represent the line zero, one and over the first chip and two of the second chip. And you can adjust the permissions as required. And this chip, this new chip will be visible in the system, just like any other GPIO chip. And the lines in the real chips will be visible in the system as requested by the GPIO aggregator. So the GPIO aggregator will become the consumer of those lines. So here's a little example on how it works.  So this is the new device and delete device. So let's say you call GPIO detect, which is, I'm gonna talking about it in a second, it lists the existing GPIO chips in the system. So you see we have two chips, the zero and one with their appropriate labels. This is just the testing module. So now this is documented in the documentation, the new source. If you type the GPIO mockup A and GPIO mockup B followed by the list comma separated or dash separated, if you want ranges of lines that you want to make part of the new virtual chip. If you put that line into a new device, unless there's some error, it's gonna create a new virtual file, virtual GPIO chip. And now when you, unless again, the file sits here, GPIO aggregator dot two, because the two is the index of the chip. And when we call GPIO detect again, we can see that we have a new GPIO chip, which is labeled as GPIO aggregator. And then if we print its info, I'm sorry, if we print the info of the first chip, we can see that now GPIO aggregator is the consumer of line zero and two of the real chip GPIO mockup A. So that's it, very simple to use, really recommend it. So now that we have all these new features merged recently, why do we need a whole new version of the API? So the main problem with this user API with every kernel user API is the fact that it's stable and its stability is enforced. So once released, if anyone in the user space uses it, it must be maintained. If you get it wrong and only notice once this interface has been adopted, then either you left some margin for error in the way you implemented it or you're stuck with a bad API. Unfortunately, some problems were identified with the GPIO character device and some of them simply can't be fixed. So first one is a rather uncommon use case that is running a 64-bit kernel with 32-bit user space, which is perfectly valid, but in GPIO, this has been broken for a couple of years ever since the character device has been merged, but it's been a couple of years until someone spotted it. So basically what happens is that we have this struct GPIO event data, which is read from the file descriptor created when requesting lines for event monitoring, and this structure looks differently from the 64-bit kernel and 32-bit user space point of view. On 64-bits, the structure has an implicit 32-bit padding at the end so that it's 46-bit aligned. So if both the kernel and user space have the same alignment, it's fine. But in the case of a 64-bit kernel and 32-bit user space, suddenly the kernel is sending a structure to the user space, which the user space isn't expecting because what the user space is expecting is a 96-byte structure in NAND 128. So this of course cannot work. I think it was Andi Shevchenko tried to fix it at some point, but basically we would end up with a lot of ugly code and eventually it's been decided to just fix it properly in V2. So another issue is what I mentioned before, it's the padding. So as I said before, you may, when designing a user ABI, you may leave some margin for error. Unfortunately, we didn't do that. So if the structure you're using for IOTL parameters has some unused fields, you can use them to extend given IOTL with new features. But we don't have any such fields at the end, appendage, basically no padding. Or rather, only recently with the set-config IOTL, we started to add padding for future use. We probably just didn't know any better at the time, but it turned out that only adding new flags sometimes isn't enough as a new feature. Because when it turned out that the debounce period is something that the user space should be able to configure and monitor in line events, we unfortunately didn't have any place to put that value in. So yeah, padding is important if you think that you're gonna extend your API in the future. And another thing is using the right timers. So the GPIO event timestamps, for a long time we're using the real-time clock. But the thing with the real-time clock is that it's not guaranteed to always advance. It can go backwards in certain circumstances. So the right clock to use in this case is the monotonic clock. This has been actually fixed in the following commit, GPIO switch timestamps to K-time get NS. But this technically is ADI breakage. We just decided to do it and see if anyone complains and nobody did, so it stayed like this. And V2 will of course use the monotonic clock right away. So then we had several requests for sensible new features. So we tried initially to limit the GPIO character device functionalities to not encourage people to overuse it more than for prototyping or some limited scope of use cases. However, some requests make sense. So we're okay with adding them, but it's simply impossible in V1. I mentioned before the debound period for events, bias settings is something that we already have. But then something like event sequence numbering. This is another one that's useful because if the kernel buffer overflows or the order somehow gets lost between the hired IRQ and threat IRQ handlers, it's good to keep the right sequencing in user space. Right now we can't add it because it's another field for which we don't have space and the structures we have now. And so the work has started on the mailing list to implement the second version of the GPIO character device. So what's going on? The first version of the V2 API has been proposed on the LKMA, LKML, sorry. And it's being right now discussed. The V1, of course, has to stay in place so it will be maintained. The 64-bit kernel to 32-bit user space problems are fixed. So with the missing padding, we now have the possibility to extend the structures even further if needed. And also all the flags have been reworked. And the line event and line handle requests have been merged into one because there is no good reason to keep them separate. So just to give you some examples on what we did. So the merging of the two request types is for a good reason because right now you can request multiple lines for values for doing the get or set operations, but you can only request a single line at a time for events. And every time you request a line to monitor its events, you get a new file descriptor. And you can, of course, pull all these file descriptors at once. There is no issue with that, but you have to use a single file descriptor per monitored line. And there's actually no good reason to do that. So yeah, so we may be, even in LibGP IOD, we actually wrap it in a single object. So the user actually in using the GPID, the user doesn't see that we actually have multiple line descriptors or one file descriptor per line. So what will happen in V2, we will merge these two requests together, or rather we will still have two request types, but we will always use sets of lines and operate on them. And the edge detection or edge event detection will simply become another flag. And we're talking about flags. So right now we have a single 32-bit field for all the flags. And they all, a lot of them conflict with one another logically. And that makes parsing these options in the kernel and nightmare and it's also easy to get it wrong. So in V2, they will actually be Anom's and they will be enumerated types. And yeah, we will have a separate type for direction, a separate type for drive, separate type for bias and a separate type for edge detection. And this way it will be impossible to get it wrong because you will simply only choose one from each set. Yeah, so there's a lot of small implementation details in this which I won't be covering, but we now seem to have something much better and thoroughly designed. So right now I want to just talk a bit about LibGP IOD, which is the user space, wrapper around the row, IOQL calls, the goal of which is to make them easier to use, provide lots of convenience helpers and set of command line tools and also bindings to higher level languages. So yeah, just a bit of history in the beginning. So the reason for me to write LibGP IOD was the support for power switches on Belibra ACME probes. We have this power consumption measurement device. So these power switches were basically GPIOs exposed by GPIO expanders, but I tried to model them somehow initially. First as IO, industrial IO and in this kernel attributes, but this was shut down because and rightfully so because these GPIOs were not really part of any driver conceptually. So then I switched to the regulator framework and I tried to model them as regulators controlled from user space, but this was shut down by Mark Brown, the maintainer of the regulator subsystem. And again, this has been discussed in the past that regulators should not be controllable from user space. And finally, it turned out that using the GPIO character device is the way to go. So first version was released in January, 2017. This was at the time still a work in progress effort, but a year later, the 1.0 stable API was released. Right now we have a current stable version is 1.5.1. Actually the slide says it's the two, but I'm going to make this release this week. And this release needs kernel 5.5. I'm still supporting the 1.4 series because it's the version that works with Linux with Linux 5.4 long-term support version. And as soon as we get the version two IO sub stream, we'll start working on a new major version of libGPIOD because certain things will need to change in the API in order to adapt the new kernel interface. So these changes will not be backward compatible, hence the new major release that we're planning. So what's inside? So we have a C API of the core library. It's fully documented in Doxygen. We have a set of command line tools. So we have GPIO detect that lists the chips present in the system. We have GPIO info that prints information about chips and lines. We have GPIO set and GPIO get. These programs allow to set and read current values of GPIO lines. And finally we have GPIO find, which allows you to look up lines by name. And also we have GPIO mon, which allows you to monitor line events from command line. We have a custom test suite. This works together with the GPIO mock up kernel module and interrupt simulator in the kernel. And we have, again, fully documented bindings to C++ and Python. As far as I know, at least the Python bindings are pretty widely used. And new features. So in 1.5, we've added support for the bias flags and the set config IOctl. We also switched to using proper testing frameworks. So what we do now, we use the Jilip unit testing for the core library. We use catch two for C++ bindings. We use Python unit tests for Python bindings. And then for the command line tools, we use the bus, which is the bash automated testing suite, I think. What will change in the 2.0.x series? So we're gonna have a completely new API that will work with the new kernel interface. We will introduce Jilip bindings and a debas demon, which will be the first demon to hold the global state of GPIO lines. We'll implement GPIO watch, which will be a command line tool for watching state changes of GPIO lines. So basically, this will use the line watch IOctl. And also something that the slide doesn't mention is changes in how we look up lines by name, because it turned out that a significant thing that we missed in the GPIO is the fact that line names are not unique neither for a chip. Neither globally as well as for a chip. So basically multiple lines in the chip and have the same name, nothing stops us from doing that. Unfortunately, the current version of the GPIO is not aware of that. So we're gonna have to work on this as well. And yeah, so that's actually it. It went faster than I thought.  I'd like to say thank you to Kent Gibson who wrote the V2, so who's working mostly on the V2 of GPIO UAPI. And I'll also like to say thank you to Herth Eiter Huffe, who's implemented the GPIO aggregator, which is my really, really, just really nice tool. And that's it. Okay, some open questions. So let me just skim through the questions that are already here. So Christopher is asking, I like the CISFS interface since it was possible to easily modify it from a script. Is this still possible with the new GPIO character device? Of course, it's not possible to do it with Iockel calls, but LibGPIOD as I said has a set of command line tools which makes it quite convenient, I think. I use it in script, so definitely you should check it out. You can even monitor events and adjust the format in which the events are printed to be easily parsable by script. So I have used Epo to jade, I think, it's a name. I have used Epo to attempt to cut GPIO edges in specific hardware, IMX6. Is that reliable and does it suffer from race conditions that you have solved with your current work? Unfortunately, I'm not the race conditions. You mean the ones that we fixed with the character device in regards to the CISFS? We should probably move that to Slack. I can be of help with that, but I need to understand the question better. So what is the overhead of using the GPIO aggregator as compared to direct access to the actual hardware GPIO chips? So I haven't measured it, but basically what happens is that the kernel GPIO aggregator module requests the lines from the real module. There is a bit of overhead, but not much. I will check it out and see you on Slack. And answer you on Slack. So Robert is asking, when a line is added to a virtual GPIO chip with GPIO aggregator, is it possible to access the line through both the real chip and the virtual chip? Or is the line only accessible through the virtual chip now? So you can read line info from the real chip, but you cannot request the line. You can only now request it from the GPIO aggregator because there can only be one user of a line and conceptually the GPIO aggregator becomes the user of this line of the line on the real chip. Drew is asking, is it a good idea to declare in device tree that I want to use certain GPIO lines from user space? It's a good question, I don't know. First, I don't think we have any means to do that. We probably can use the name for that that it's somehow used, marked to be used from user space, but right now, no, it's not a good idea because device tree is describing the hardware. I think that every time something like this pops up, Rob Herring is against it and says that device tree should focus on describing the hardware and not describing the implementation details. So I modify my answer, it's no. And Drew, again, on BigoBone, I can set the GPIO max mode and pin control device tree properties, but how can I specify that I want to use the corresponding GPIO lines with the GPIOD? Is it useful to specify? Yeah, so this is the same, this is related to that question above. In DT, this shouldn't be specified, basically. And Drew, one complaint I've gotten from users is that the GPIOD user API has no way to keep a line set after the process ends. They said this makes SysFS better for them. Any solution for that scenario? Yes. So this is something I mentioned. We, many users want something to store the global state of GPIO lines. I have, this has been in my to-do for far too long, but I will at some point implement a very simple demon written in C together with a client that will hold the global state and expose it to the client. And basically, as long as the demon will be active, you will be able to set a state of a line and keep it that way. Also something that is already, that I've been already working on for some time now is the divas demon and then set of divas bindings. This of course will keep the global state of lines, like the state of lines will be kept even after the client program exists, if it's so wishes. And yeah, otherwise this is how character devices work in Linux and this is by design. So yeah, the most we can do is implement a demon that will stay alive and be an intermediate between the GPIO character device and the client programs. So I think for now there are no more questions. I will stay on Slack of course, to answer any more questions. So if we have time, I can go back to the questions that we had before, answer it live. So I have used EPOL to attempt to catch the edges in specific. So this was the question from Jade. I will read it again. I have used EPOL to attempt to catch GPIO edges in specific hardware, is that reliable and does it suffer from race conditions that you have solved with your current work? So if you've done it using CISFS, yes, this is racy and it's not reliable. If you switch to the character device, it's now going to be more reliable. It's not 100% reliable because the buffer in the kernel can overflow, but it's gonna be much, much more reliable than CISFS. Another question from Drew. Any other bindings in C++ and Python plant? Yes, so Kent Gibson who wrote, who implemented the set conflict IOQL and the bias then support for bias flags works on his Go library. It's not binding, basically it's not related to the GPAD, it's a standalone library written in Go, but I know it's functional and Kent is actively supporting it. Google for the name because I don't even know. It's called GPIO simply. It's a native Go library for Raspberry Pi GPIO. Oh, actually it's for Resbia. Okay, so it's called GPIOD. It's a native Go library for Linux GPIO and uses the character device, yeah. Okay, so if there are no more questions. Okay, so there are no more questions. I think this is it and I'll see you on Slack then. Thank you very much.