|
| 1 | +/** @file solid-gismo-elasticity.cpp |
| 2 | +
|
| 3 | + @brief Elasticity participant for the PreCICE example "perpendicular-flap". |
| 4 | +
|
| 5 | +
|
| 6 | + This file is part of the G+Smo library. |
| 7 | +
|
| 8 | + This Source Code Form is subject to the terms of the Mozilla Public |
| 9 | + License, v. 2.0. If a copy of the MPL was not distributed with this |
| 10 | + file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 11 | +
|
| 12 | + Author(s): J.Li (2023 - ..., TU Delft), H.M. Verhelst (2019 - 2024, TU Delft) |
| 13 | +*/ |
| 14 | + |
| 15 | +#include <gismo.h> |
| 16 | +#include <gsPreCICE/gsPreCICE.h> |
| 17 | +#include <gsPreCICE/gsPreCICEUtils.h> |
| 18 | +#include <gsPreCICE/gsPreCICEFunction.h> |
| 19 | +// #include <gsPreCICE/gsPreCICEVectorFunction.h> |
| 20 | + |
| 21 | +#include <gsElasticity/gsMassAssembler.h> |
| 22 | +#include <gsElasticity/gsElasticityAssembler.h> |
| 23 | + |
| 24 | +#ifdef gsStructuralAnalysis_ENABLED |
| 25 | +#include <gsStructuralAnalysis/src/gsDynamicSolvers/gsDynamicExplicitEuler.h> |
| 26 | +#include <gsStructuralAnalysis/src/gsDynamicSolvers/gsDynamicImplicitEuler.h> |
| 27 | +#include <gsStructuralAnalysis/src/gsDynamicSolvers/gsDynamicNewmark.h> |
| 28 | +#include <gsStructuralAnalysis/src/gsDynamicSolvers/gsDynamicBathe.h> |
| 29 | +#include <gsStructuralAnalysis/src/gsDynamicSolvers/gsDynamicWilson.h> |
| 30 | +#include <gsStructuralAnalysis/src/gsDynamicSolvers/gsDynamicRK4.h> |
| 31 | +#include <gsStructuralAnalysis/src/gsStructuralAnalysisTools/gsStructuralAnalysisUtils.h> |
| 32 | +#endif |
| 33 | + |
| 34 | +using namespace gismo; |
| 35 | + |
| 36 | +int main(int argc, char *argv[]) |
| 37 | +{ |
| 38 | + //! [Parse command line] |
| 39 | + bool plot = false; |
| 40 | + index_t plotmod = 1; |
| 41 | + index_t numRefine = 0; |
| 42 | + index_t numElevate = 0; |
| 43 | + std::string precice_config; |
| 44 | + int method = 3; // 1: Explicit Euler, 2: Implicit Euler, 3: Newmark, 4: Bathe, 5: Wilson, 6 RK4 |
| 45 | + |
| 46 | + gsCmdLine cmd("Flow over heated plate for PreCICE."); |
| 47 | + cmd.addInt( "e", "degreeElevation", |
| 48 | + "Number of degree elevation steps to perform before solving (0: equalize degree in all directions)", numElevate ); |
| 49 | + cmd.addInt( "r", "uniformRefine", "Number of Uniform h-refinement loops", numRefine ); |
| 50 | + cmd.addString( "c", "config", "PreCICE config file", precice_config ); |
| 51 | + cmd.addSwitch("plot", "Create a ParaView visualization file with the solution", plot); |
| 52 | + cmd.addInt("m","plotmod", "Modulo for plotting, i.e. if plotmod==1, plots every timestep", plotmod); |
| 53 | + cmd.addInt("M", "method","1: Explicit Euler, 2: Implicit Euler, 3: Newmark, 4: Bathe, 5: Wilson, 6: RK4",method); |
| 54 | + try { cmd.getValues(argc,argv); } catch (int rv) { return rv; } |
| 55 | + |
| 56 | + //! [Read input file] |
| 57 | + GISMO_ASSERT(gsFileManager::fileExists(precice_config),"No precice config file has been defined"); |
| 58 | + |
| 59 | + // Generate domain |
| 60 | + gsMultiPatch<> patches; |
| 61 | + patches.addPatch(gsNurbsCreator<>::BSplineRectangle(-0.05,0.0,0.05,1.0)); |
| 62 | + |
| 63 | + // Create bases |
| 64 | + gsMultiBasis<> bases(patches);//true: poly-splines (not NURBS) |
| 65 | + |
| 66 | + // Set degree |
| 67 | + bases.setDegree( bases.maxCwiseDegree() + numElevate); |
| 68 | + |
| 69 | + // h-refine each basis |
| 70 | + for (int r =0; r < numRefine; ++r) |
| 71 | + bases.uniformRefine(); |
| 72 | + numRefine = 0; |
| 73 | + |
| 74 | + gsInfo << "Patches: "<< patches.nPatches() <<", degree: "<< bases.minCwiseDegree() <<"\n"; |
| 75 | + |
| 76 | + real_t rho = 3000; |
| 77 | + real_t E = 4e6; |
| 78 | + real_t nu = 0.3; |
| 79 | + |
| 80 | + // Set the interface for the precice coupling |
| 81 | + std::vector<patchSide> couplingInterfaces(3); |
| 82 | + couplingInterfaces[0] = patchSide(0,boundary::east); |
| 83 | + couplingInterfaces[1] = patchSide(0,boundary::north); |
| 84 | + couplingInterfaces[2] = patchSide(0,boundary::west); |
| 85 | + |
| 86 | + /* |
| 87 | + * Initialize the preCICE participant |
| 88 | + * |
| 89 | + * |
| 90 | + */ |
| 91 | + std::string participantName = "Solid"; |
| 92 | + gsPreCICE<real_t> participant(participantName, precice_config); |
| 93 | + |
| 94 | + /* |
| 95 | + * Data initialization |
| 96 | + * |
| 97 | + * This participant manages the geometry. The follow meshes and data are made available: |
| 98 | + * |
| 99 | + * - Meshes: |
| 100 | + * + KnotMesh This mesh contains the knots as mesh vertices |
| 101 | + * + ControlPointMesh: This mesh contains the control points as mesh vertices |
| 102 | + * + ForceMesh: This mesh contains the integration points as mesh vertices |
| 103 | + * |
| 104 | + * - Data: |
| 105 | + * + ControlPointData: This data is defined on the ControlPointMesh and stores the displacement of the control points |
| 106 | + * + ForceData: This data is defined on the ForceMesh and stores pressure/forces |
| 107 | + */ |
| 108 | + |
| 109 | + std::string SolidMesh = "Solid-Mesh"; |
| 110 | + std::string StressData = "Stress"; |
| 111 | + std::string DisplacementData = "Displacement"; |
| 112 | + |
| 113 | + // Get the quadrature nodes on the coupling interface |
| 114 | + gsOptionList quadOptions = gsExprAssembler<>::defaultOptions(); |
| 115 | + |
| 116 | + // Get the quadrature points |
| 117 | + gsMatrix<> quad_uv = gsQuadrature::getAllNodes(bases.basis(0),quadOptions,couplingInterfaces); // Quadrature points in the parametric domain |
| 118 | + gsMatrix<> quad_xy = patches.patch(0).eval(quad_uv); // Quadrature points in the physical domain |
| 119 | + gsVector<index_t> quad_xyIDs; // needed for writing |
| 120 | + participant.addMesh(SolidMesh,quad_xy,quad_xyIDs); |
| 121 | + |
| 122 | + // Define precice interface |
| 123 | + real_t precice_dt = participant.initialize(); |
| 124 | + |
| 125 | +// ---------------------------------------------------------------------------------------------- |
| 126 | + |
| 127 | + // Define boundary conditions |
| 128 | + gsBoundaryConditions<> bcInfo; |
| 129 | + // Dirichlet side |
| 130 | + gsConstantFunction<> g_D(0,patches.geoDim()); |
| 131 | + // Coupling side |
| 132 | + // gsFunctionExpr<> g_C("1","0",patches.geoDim()); |
| 133 | + gsPreCICEFunction<real_t> g_C(&participant,SolidMesh,StressData,patches,patches.geoDim(),false); |
| 134 | + // Add all BCs |
| 135 | + // Coupling interface |
| 136 | + bcInfo.addCondition(0, boundary::north, condition_type::neumann , &g_C,-1,true); |
| 137 | + bcInfo.addCondition(0, boundary::east, condition_type::neumann , &g_C,-1,true); |
| 138 | + bcInfo.addCondition(0, boundary::west, condition_type::neumann , &g_C,-1,true); |
| 139 | + // Bottom side (prescribed temp) |
| 140 | + bcInfo.addCondition(0, boundary::south, condition_type::dirichlet, &g_D, 0); |
| 141 | + bcInfo.addCondition(0, boundary::south, condition_type::dirichlet, &g_D, 1); |
| 142 | + // Assign geometry map |
| 143 | + bcInfo.setGeoMap(patches); |
| 144 | + |
| 145 | +// ---------------------------------------------------------------------------------------------- |
| 146 | + // source function, rhs |
| 147 | + gsConstantFunction<> g(0.,0.,2); |
| 148 | + |
| 149 | + // source function, rhs |
| 150 | + gsConstantFunction<> gravity(0.,0.,2); |
| 151 | + |
| 152 | + // creating mass assembler |
| 153 | + gsMassAssembler<real_t> massAssembler(patches,bases,bcInfo,gravity); |
| 154 | + massAssembler.options().setReal("Density",rho); |
| 155 | + massAssembler.assemble(); |
| 156 | + |
| 157 | + // creating stiffness assembler. |
| 158 | + gsElasticityAssembler<real_t> assembler(patches,bases,bcInfo,g); |
| 159 | + assembler.options().setReal("YoungsModulus",E); |
| 160 | + assembler.options().setReal("PoissonsRatio",nu); |
| 161 | + assembler.assemble(); |
| 162 | + |
| 163 | + gsMatrix<real_t> Minv; |
| 164 | + gsSparseMatrix<> M = massAssembler.matrix(); |
| 165 | + gsSparseMatrix<> K = assembler.matrix(); |
| 166 | + gsSparseMatrix<> K_T; |
| 167 | + |
| 168 | + // Time step |
| 169 | + real_t dt = precice_dt; |
| 170 | + |
| 171 | + // Project u_wall as initial condition (violates Dirichlet side on precice interface) |
| 172 | + // RHS of the projection |
| 173 | + gsMatrix<> solVector; |
| 174 | + solVector.setZero(assembler.numDofs(),1); |
| 175 | + |
| 176 | + std::vector<gsMatrix<> > fixedDofs = assembler.allFixedDofs(); |
| 177 | + // Assemble the RHS |
| 178 | + gsVector<> F = assembler.rhs(); |
| 179 | + gsVector<> F_checkpoint, U_checkpoint, V_checkpoint, A_checkpoint, U, V, A; |
| 180 | + |
| 181 | + F_checkpoint = F; |
| 182 | + U_checkpoint = U = gsVector<real_t>::Zero(assembler.numDofs(),1); |
| 183 | + V_checkpoint = V = gsVector<real_t>::Zero(assembler.numDofs(),1); |
| 184 | + A_checkpoint = A = gsVector<real_t>::Zero(assembler.numDofs(),1); |
| 185 | + |
| 186 | + // Define the solution collection for Paraview |
| 187 | + gsParaviewCollection collection("./output/solution"); |
| 188 | + |
| 189 | + index_t timestep = 0; |
| 190 | + index_t timestep_checkpoint = 0; |
| 191 | + |
| 192 | + // Function for the Jacobian |
| 193 | + gsStructuralAnalysisOps<real_t>::Jacobian_t Jacobian = [&assembler,&fixedDofs](gsMatrix<real_t> const &x, gsSparseMatrix<real_t> & m) { |
| 194 | + // to do: add time dependency of forcing |
| 195 | + assembler.assemble(x, fixedDofs); |
| 196 | + m = assembler.matrix(); |
| 197 | + return true; |
| 198 | + }; |
| 199 | + |
| 200 | + // Function for the Residual |
| 201 | + gsStructuralAnalysisOps<real_t>::TResidual_t Residual = [&assembler,&fixedDofs](gsMatrix<real_t> const &x, real_t t, gsVector<real_t> & result) |
| 202 | + { |
| 203 | + assembler.assemble(x,fixedDofs); |
| 204 | + result = assembler.rhs(); |
| 205 | + return true; |
| 206 | + }; |
| 207 | + |
| 208 | + |
| 209 | + gsSparseMatrix<> C = gsSparseMatrix<>(assembler.numDofs(),assembler.numDofs()); |
| 210 | + gsStructuralAnalysisOps<real_t>::Damping_t Damping = [&C](const gsVector<real_t> &, gsSparseMatrix<real_t> & m) { m = C; return true; }; |
| 211 | + gsStructuralAnalysisOps<real_t>::Mass_t Mass = [&M]( gsSparseMatrix<real_t> & m) { m = M; return true; }; |
| 212 | + |
| 213 | + gsDynamicBase<real_t> * timeIntegrator; |
| 214 | + if (method==1) |
| 215 | + timeIntegrator = new gsDynamicExplicitEuler<real_t,true>(Mass,Damping,Jacobian,Residual); |
| 216 | + else if (method==2) |
| 217 | + timeIntegrator = new gsDynamicImplicitEuler<real_t,true>(Mass,Damping,Jacobian,Residual); |
| 218 | + else if (method==3) |
| 219 | + timeIntegrator = new gsDynamicNewmark<real_t,true>(Mass,Damping,Jacobian,Residual); |
| 220 | + else if (method==4) |
| 221 | + timeIntegrator = new gsDynamicBathe<real_t,true>(Mass,Damping,Jacobian,Residual); |
| 222 | + else if (method==5) |
| 223 | + { |
| 224 | + timeIntegrator = new gsDynamicWilson<real_t,true>(Mass,Damping,Jacobian,Residual); |
| 225 | + timeIntegrator->options().setReal("gamma",1.4); |
| 226 | + } |
| 227 | + else if (method==6) |
| 228 | + timeIntegrator = new gsDynamicRK4<real_t,true>(Mass,Damping,Jacobian,Residual); |
| 229 | + else |
| 230 | + GISMO_ERROR("Method "<<method<<" not known"); |
| 231 | + |
| 232 | + timeIntegrator->options().setReal("DT",dt); |
| 233 | + timeIntegrator->options().setReal("TolU",1e-3); |
| 234 | + timeIntegrator->options().setSwitch("Verbose",true); |
| 235 | + |
| 236 | + real_t time = 0; |
| 237 | + |
| 238 | + // Plot initial solution |
| 239 | + if (plot) |
| 240 | + { |
| 241 | + gsMultiPatch<> solution; |
| 242 | + assembler.constructSolution(solVector,fixedDofs,solution); |
| 243 | + |
| 244 | + gsField<> solField(patches,solution); |
| 245 | + std::string fileName = "./output/solution" + util::to_string(timestep); |
| 246 | + gsWriteParaview<>(solField, fileName, 500); |
| 247 | + fileName = "solution" + util::to_string(timestep) + "0"; |
| 248 | + collection.addTimestep(fileName,time,".vts"); |
| 249 | + } |
| 250 | + |
| 251 | + gsMatrix<> points(2,1); |
| 252 | + points.col(0)<<0.5,1; |
| 253 | + |
| 254 | + gsStructuralAnalysisOutput<real_t> writer("pointData.csv",points); |
| 255 | + writer.init({"x","y"},{"time"}); // point1 - x, point1 - y, time |
| 256 | + |
| 257 | + gsMatrix<> pointDataMatrix; |
| 258 | + gsMatrix<> otherDataMatrix(1,1); |
| 259 | + |
| 260 | + // Time integration loop |
| 261 | + while (participant.isCouplingOngoing()) |
| 262 | + { |
| 263 | + if (participant.requiresWritingCheckpoint()) |
| 264 | + { |
| 265 | + U_checkpoint = U; |
| 266 | + V_checkpoint = V; |
| 267 | + A_checkpoint = A; |
| 268 | + |
| 269 | + gsInfo<<"Checkpoint written:\n"; |
| 270 | + gsInfo<<"\t ||U|| = "<<U.norm()<<"\n"; |
| 271 | + gsInfo<<"\t ||V|| = "<<V.norm()<<"\n"; |
| 272 | + gsInfo<<"\t ||A|| = "<<A.norm()<<"\n"; |
| 273 | + |
| 274 | + |
| 275 | + timestep_checkpoint = timestep; |
| 276 | + } |
| 277 | + |
| 278 | + assembler.assemble(); |
| 279 | + F = assembler.rhs(); |
| 280 | + |
| 281 | + // solve gismo timestep |
| 282 | + gsInfo << "Solving timestep " << time << "...\n"; |
| 283 | + timeIntegrator->step(time,dt,U,V,A); |
| 284 | + solVector = U; |
| 285 | + gsInfo<<"Finished\n"; |
| 286 | + |
| 287 | + // potentially adjust non-matching timestep sizes |
| 288 | + dt = std::min(dt,precice_dt); |
| 289 | + |
| 290 | + |
| 291 | + gsMultiPatch<> solution; |
| 292 | + assembler.constructSolution(solVector,fixedDofs,solution); |
| 293 | + // write heat fluxes to interface |
| 294 | + gsMatrix<> result(patches.geoDim(),quad_uv.cols()); |
| 295 | + solution.patch(0).eval_into(quad_uv,result); |
| 296 | + participant.writeData(SolidMesh,DisplacementData,quad_xyIDs,result); |
| 297 | + |
| 298 | + // do the coupling |
| 299 | + precice_dt =participant.advance(dt); |
| 300 | + |
| 301 | + if (participant.requiresReadingCheckpoint()) |
| 302 | + { |
| 303 | + U = U_checkpoint; |
| 304 | + V = V_checkpoint; |
| 305 | + A = A_checkpoint; |
| 306 | + timestep = timestep_checkpoint; |
| 307 | + } |
| 308 | + else |
| 309 | + { |
| 310 | + // gsTimeIntegrator advances |
| 311 | + // advance variables |
| 312 | + time += dt; |
| 313 | + timestep++; |
| 314 | + |
| 315 | + gsField<> solField(patches,solution); |
| 316 | + if (timestep % plotmod==0 && plot) // Generate Paraview output for visualization |
| 317 | + { |
| 318 | + // solution.patch(0).coefs() -= patches.patch(0).coefs();// assuming 1 patch here |
| 319 | + std::string fileName = "./output/solution" + util::to_string(timestep); |
| 320 | + gsWriteParaview<>(solField, fileName, 500); |
| 321 | + fileName = "solution" + util::to_string(timestep) + "0"; |
| 322 | + collection.addTimestep(fileName,time,".vts"); |
| 323 | + } |
| 324 | + |
| 325 | + solution.patch(0).eval_into(points,pointDataMatrix); |
| 326 | + otherDataMatrix<<time; |
| 327 | + writer.add(pointDataMatrix,otherDataMatrix); |
| 328 | + } |
| 329 | + } |
| 330 | + |
| 331 | + if (plot) |
| 332 | + { |
| 333 | + collection.save(); |
| 334 | + } |
| 335 | + |
| 336 | + |
| 337 | + return EXIT_SUCCESS; |
| 338 | +} |
0 commit comments