1 /** @file unittest_UAT.cpp
2 
3     @brief Unit tests performs Uniaxial Tension Test for Neo-Hookean, Mooney-Rivlin and Ogden material models
4 
5     This file tests the following classes and functions:
6     - gsMaterialMatrix          (dim=2, mat=1,3,4, impl=1,2,3)
7     - gsMaterialMatrixIntegrate (dim=2)
8     - gsMaterialMatrixBase
9     - gsThinShellAssembler      (dim=2)
10         - assemble(), assembleMatrix(), assembleVector()
11         - boundaryFoceVector(), getArea()
12         - constructSolution(), computePrincipalStretches()
13 
14     This file is part of the G+Smo library.
15 
16     This Source Code Form is subject to the terms of the Mozilla Public
17     License, v. 2.0. If a copy of the MPL was not distributed with this
18     file, You can obtain one at http://mozilla.org/MPL/2.0/.
19 
20     Author(s): H.M.Verhelst (2019 - ..., TU Delft)
21 */
22 
23 #include <gismo.h>
24 
25 #include <gsKLShell/gsThinShellAssembler.h>
26 #include <gsKLShell/getMaterialMatrix.h>
27 
28 using namespace gismo;
29 
numerical(index_t material,index_t impl,bool Compressibility)30 std::pair<real_t,real_t> numerical(index_t material, index_t impl, bool Compressibility)
31 {
32     //! [Parse command line]
33     index_t numRefine  = 1;
34     index_t numElevate = 1;
35 
36     real_t E_modulus = 1.0;
37     real_t PoissonRatio;
38     real_t Density = 1.0;
39     real_t Ratio = 7.0;
40 
41     real_t mu = 1.5e6;
42     real_t thickness = 0.001;
43 
44     real_t alpha1,alpha2,alpha3,mu1,mu2,mu3;
45     alpha1 = 1.3;
46     mu1    = 6.3e5/4.225e5*mu;
47     alpha2 = 5.0;
48     mu2    = 0.012e5/4.225e5*mu;
49     alpha3 = -2.0;
50     mu3    = -0.1e5/4.225e5*mu;
51 
52     if (!Compressibility)
53       PoissonRatio = 0.5;
54     else
55       PoissonRatio = 0.45;
56 
57     E_modulus = 2*mu*(1+PoissonRatio);
58 
59     //! [Parse command line]
60 
61     //! [Read input file]
62     gsMultiPatch<> mp, mp_def;
63 
64     mp.addPatch( gsNurbsCreator<>::BSplineSquare(1) ); // degree
65 
66     if (numElevate!=0)
67         mp.degreeElevate(numElevate);
68 
69     // h-refine
70     for (int r =0; r < numRefine; ++r)
71         mp.uniformRefine();
72 
73     mp_def = mp;
74 
75     //! [Refinement]
76     gsMultiBasis<> dbasis(mp);
77 
78     gsBoundaryConditions<> bc;
79     bc.setGeoMap(mp);
80 
81     gsPointLoads<real_t> pLoads = gsPointLoads<real_t>();
82 
83     real_t lambda = 2.0;
84     gsConstantFunction<> displx(lambda-1.0,2);
85 
86     GISMO_ASSERT(mp.targetDim()==2,"Geometry must be planar (targetDim=2)!");
87     bc.addCondition(boundary::west, condition_type::dirichlet, 0, 0, false, 0 );
88 
89     bc.addCondition(boundary::east, condition_type::dirichlet, &displx, 0, false, 0 );
90 
91     bc.addCondition(boundary::south, condition_type::dirichlet, 0, 0, false, 1 );
92 
93     //! [Refinement]
94 
95     // Linear isotropic material model
96     gsVector<> tmp(2);
97     tmp.setZero();
98     gsConstantFunction<> force(tmp,2);
99     gsFunctionExpr<> t(std::to_string(thickness),2);
100     gsFunctionExpr<> E(std::to_string(E_modulus),2);
101     gsFunctionExpr<> nu(std::to_string(PoissonRatio),2);
102     gsFunctionExpr<> rho(std::to_string(Density),2);
103     gsConstantFunction<> ratio(Ratio,2);
104 
105     gsConstantFunction<> alpha1fun(alpha1,2);
106     gsConstantFunction<> mu1fun(mu1,2);
107     gsConstantFunction<> alpha2fun(alpha2,2);
108     gsConstantFunction<> mu2fun(mu2,2);
109     gsConstantFunction<> alpha3fun(alpha3,2);
110     gsConstantFunction<> mu3fun(mu3,2);
111 
112     std::vector<gsFunction<>*> parameters(3);
113     parameters[0] = &E;
114     parameters[1] = &nu;
115     parameters[2] = &ratio;
116     gsMaterialMatrixBase<real_t>* materialMatrix;
117 
118     if (material==4)
119     {
120         parameters.resize(8);
121         parameters[0] = &E;
122         parameters[1] = &nu;
123         parameters[2] = &mu1fun;
124         parameters[3] = &alpha1fun;
125         parameters[4] = &mu2fun;
126         parameters[5] = &alpha2fun;
127         parameters[6] = &mu3fun;
128         parameters[7] = &alpha3fun;
129     }
130 
131     gsOptionList options;
132     if      (material==0)
133     {
134         GISMO_ERROR("This test is not available for SvK models");
135     }
136     else
137     {
138         options.addInt("Material","Material model: (0): SvK | (1): NH | (2): NH_ext | (3): MR | (4): Ogden",material);
139         options.addSwitch("Compressibility","Compressibility: (false): Imcompressible | (true): Compressible",Compressibility);
140         options.addInt("Implementation","Implementation: (0): Composites | (1): Analytical | (2): Generalized | (3): Spectral",impl);
141         materialMatrix = getMaterialMatrix<2,real_t>(mp,t,parameters,rho,options);
142     }
143 
144     gsThinShellAssemblerBase<real_t>* assembler;
145     assembler = new gsThinShellAssembler<2, real_t, false >(mp,dbasis,bc,force,materialMatrix);
146 
147     assembler->setPointLoads(pLoads);
148 
149     // Function for the Jacobian
150     typedef std::function<gsSparseMatrix<real_t> (gsVector<real_t> const &)>    Jacobian_t;
151     typedef std::function<gsVector<real_t> (gsVector<real_t> const &) >         Residual_t;
152     Jacobian_t Jacobian = [&assembler,&mp_def](gsVector<real_t> const &x)
153     {
154       assembler->constructSolution(x,mp_def);
155       assembler->assembleMatrix(mp_def);
156       gsSparseMatrix<real_t> m = assembler->matrix();
157       return m;
158     };
159     // Function for the Residual
160     Residual_t Residual = [&assembler,&mp_def](gsVector<real_t> const &x)
161     {
162       assembler->constructSolution(x,mp_def);
163       assembler->assembleVector(mp_def);
164       return assembler->rhs();
165     };
166 
167     // Define Matrices
168     assembler->assemble();
169 
170     gsSparseMatrix<> matrix = assembler->matrix();
171     gsVector<> vector = assembler->rhs();
172 
173     // Solve linear problem
174     gsVector<> solVector;
175     gsSparseSolver<>::CGDiagonal solver;
176     solver.compute( matrix );
177     solVector = solver.solve(vector);
178 
179     gsVector<real_t> updateVector = solVector;
180     gsVector<real_t> resVec = Residual(solVector);
181     gsSparseMatrix<real_t> jacMat;
182     for (index_t it = 0; it != 100; ++it)
183     {
184         jacMat = Jacobian(solVector);
185         solver.compute(jacMat);
186         updateVector = solver.solve(resVec); // this is the UPDATE
187         solVector += updateVector;
188 
189         resVec = Residual(solVector);
190 
191         if (updateVector.norm() < 1e-6)
192             break;
193         else if (it+1 == it)
194             gsWarn<<"Maximum iterations reached!\n";
195     }
196 
197     mp_def = assembler->constructSolution(solVector);
198 
199     gsMultiPatch<> deformation = mp_def;
200     for (size_t k = 0; k != mp_def.nPatches(); ++k)
201         deformation.patch(k).coefs() -= mp.patch(k).coefs();
202 
203     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
204     // Check solutions
205     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
206     // NOTE: all analytical solutions for compressible materials are fixed for displ=1; (lambda=2)
207 
208     // Compute stretches (should be the same everywhere)
209     // Ordering: lambda(0) < lambda(1); lambda(2) is ALWAYS the through-thickness stretch
210     gsVector<> pt(2);
211     pt<<1,0;
212     gsMatrix<> lambdas = assembler->computePrincipalStretches(pt,mp_def,0);
213 
214     // Get the total force on the tension boundary
215     patchSide ps(0,boundary::east);
216     gsMatrix<> forceVector = assembler->boundaryForceVector(mp_def,ps,0);
217     real_t sideForce = forceVector.sum();
218     real_t S   = -sideForce / (thickness*lambdas(0)*lambdas(2));
219     real_t L   = lambdas(0);
220 
221     std::pair<real_t,real_t> result;
222     result.first = L;
223     result.second = S;
224 
225     delete materialMatrix;
226     delete assembler;
227 
228     return result;
229 }
230 
analytical(index_t material,index_t impl,bool Compressibility)231 std::pair<real_t,real_t> analytical(index_t material, index_t impl, bool Compressibility)
232 {
233     real_t PoissonRatio;
234     real_t Ratio = 7.0;
235 
236     real_t mu = 1.5e6;
237 
238     real_t alpha1,alpha2,alpha3,mu1,mu2,mu3;
239     alpha1 = 1.3;
240     mu1    = 6.3e5/4.225e5*mu;
241     alpha2 = 5.0;
242     mu2    = 0.012e5/4.225e5*mu;
243     alpha3 = -2.0;
244     mu3    = -0.1e5/4.225e5*mu;
245 
246     if (!Compressibility)
247       PoissonRatio = 0.5;
248     else
249       PoissonRatio = 0.45;
250 
251     real_t lambda = 2.0;
252 
253     real_t San,J,K,Lan;
254     if      (material==1 && Compressibility)
255     {
256         K = 2*mu*(1+PoissonRatio)/(3-6*PoissonRatio);
257         J = 1.105598565;// specific for lambda==2!!
258         San = lambda*(0.5*mu*(-(2*(math::pow(lambda,2)+2*J/lambda))/(3*math::pow(J,2./3.)*lambda)+2*lambda/math::pow(J,2./3.))+0.25*K*(2*math::pow(J,2)/lambda-2./lambda))/J;
259         Lan = math::pow(J/lambda,0.5);
260     }
261     else if (material==1 && !Compressibility)
262     {
263         San = mu * (lambda*lambda - 1/lambda);
264         Lan = math::pow(1./lambda,0.5);
265     }
266     else if (material==3 && Compressibility)
267     {
268         real_t c2 = 1.0 / (Ratio+1);
269         real_t c1 = 1.0 - c2;
270         K = 2*mu*(1+PoissonRatio)/(3-6*PoissonRatio);
271         J = 1.099905842;// specific for lambda==2!!
272         San = lambda*(0.5*c1*mu*(-(2*(math::pow(lambda,2)+2*J/lambda))/(3*math::pow(J,2./3.)*lambda)+2*lambda/math::pow(J,2./3.))+0.5*c2*mu*(-(4*(2*lambda*J+math::pow(J,2)/math::pow(lambda,2)))/(3*math::pow(J,4./3.)*lambda)+4/math::pow(J,1./3.))+0.25*K*(2*math::pow(J,2)/lambda-2/lambda))/J;
273         Lan = math::pow(J/lambda,0.5);
274     }
275     else if (material==3 && !Compressibility)
276     {
277         real_t c2 = 1.0 / (Ratio+1);
278         real_t c1 = 1.0 - c2;
279         San =-mu*(c2*lambda*lambda+c2/lambda+c1)/lambda+lambda*(c1*lambda*mu+2*c2*mu);
280         Lan = math::pow(1./lambda,0.5);
281     }
282     else if (material==4 && Compressibility)
283     {
284         K = 2*mu*(1+PoissonRatio)/(3-6*PoissonRatio);
285         J = 1.088778638;// specific for lambda==2!!
286         San = 1./J* (lambda *( mu1*(2*math::pow(lambda/math::pow(J,1./3.),alpha1)*alpha1/(3*lambda)-2*math::pow(math::pow(J/lambda,0.5)/math::pow(J,1./3.),alpha1)*alpha1/(3*lambda))/alpha1+mu2*(2*math::pow(lambda/math::pow(J,1./3.),alpha2)*alpha2/(3*lambda)-2*math::pow(math::pow(J/lambda,0.5)/math::pow(J,1./3.),alpha2)*alpha2/(3*lambda))/alpha2+mu3*(2*math::pow(lambda/math::pow(J,1./3.),alpha3)*alpha3/(3*lambda)-2*math::pow(math::pow(J/lambda,0.5)/math::pow(J,1./3.),alpha3)*alpha3/(3*lambda))/alpha3+0.25*K*(2*math::pow(J,2)/lambda-2/lambda) ) );
287         Lan = math::pow(J/lambda,0.5);
288     }
289     else if (material==4 && !Compressibility)
290     {
291         San =-mu1*math::pow((1./lambda),0.5*alpha1)-mu2*math::pow((1./lambda),0.5*alpha2)-mu3*math::pow((1./lambda),0.5*alpha3)+mu1*math::pow(lambda,alpha1)+mu2*math::pow(lambda,alpha2)+mu3*math::pow(lambda,alpha3);
292         Lan = math::pow(1./lambda,0.5);
293     }
294     else
295         GISMO_ERROR("Material not treated");
296 
297     std::pair<real_t,real_t> result;
298     result.first = Lan;
299     result.second = San;
300     return result;
301 }
302 
303 // Choose among various shell examples, default = Thin Plate
main(int argc,char * argv[])304 int main(int argc, char *argv[])
305 {
306 
307 real_t S, San,L,Lan;
308 
309 std::vector<index_t> materials{ 1,3,4 };
310 std::vector<index_t> implementations{ 1,2,3 };
311 std::vector<bool> compressibility{ true,false };
312 
313 std::pair<real_t,real_t> num, an;
314 real_t tol = 1e-9;
315 for (std::vector<index_t>::iterator mat = materials.begin(); mat!=materials.end(); mat++)
316 {
317     for (std::vector<index_t>::iterator impl = implementations.begin(); impl!=implementations.end(); impl++)
318     {
319         for (std::vector<bool>::iterator comp = compressibility.begin(); comp!=compressibility.end(); comp++)
320         {
321             if (*mat==4 && *impl!=3) continue;
322             num = numerical(*mat,*impl,*comp);
323             L = num.first;
324             S = num.second;
325 
326             an = analytical(*mat,*impl,*comp);
327             Lan = an.first;
328             San = an.second;
329 
330 
331             if ( (std::abs(L-Lan)/Lan < tol) && (std::abs(S-San)/San < tol) )
332                 gsInfo<<"Passed\n";
333             else
334                 gsInfo<<"Failed; L error = "<<std::abs(L-Lan)/Lan<<"\t S error = "<<std::abs(S-San)/San<<"\n";
335         }
336     }
337 }
338 
339 return EXIT_SUCCESS;
340 
341 }// end main