Skip to content

Commit 1af0f6a

Browse files
Perpendicular flap example with OpenFOAM
1 parent 834373e commit 1af0f6a

File tree

21 files changed

+1108
-0
lines changed

21 files changed

+1108
-0
lines changed
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
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+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
title: Perpendicular Flap (with IGA Solid Participant Communicating Stress Data)
3+
keywords: G+Smo, perpendicular flap
4+
summary: This tutorial demonstrates the isogeometric analysis (IGA) solid solver version of the “Perpendicular Flap” tutorial. It focuses on using G+Smo to handle solid-structure interactions by exchanging stress data during simulations.
5+
---
6+
7+
8+
## Overview
9+
10+
This example demostrates how the Geometry + Simulation Modules (G+Smo) can be utilised through a module-type adapter to couple with other codes using preCICE. G+Smo offers a robust framework for isogeometric analysis (IGA), seamlessly integrating geometric representations with numerical solvers to enable advanced simulations and efficient code coupling.
11+
12+
## Setup
13+
This tutorial uses the same setup as the **original** perpendicular flap example, with the key difference being the switch in the communicated information to stress.
14+
15+
## Requirements
16+
17+
To run the tutorial you need to install the following components:
18+
- [preCICE](https://precice.org/quickstart.html)
19+
- [G+Smo and gsPreCICE](https://github.com/gismo/gismo)
20+
- [OpenFOAM](https://openfoam.org/download/)
21+
22+
## Run the tutorial
23+
24+
In the G+Smo build folder, build the tutorial file.
25+
26+
```
27+
make <participant file> -j <#threads>
28+
```
29+
30+
Go to the gismo-executable folder and link the compiled executable to the gismo_executable.
31+
32+
```bash
33+
cd gismo-executable
34+
chmod +x create_symlink.sh
35+
./create_symlink.sh
36+
```
37+
38+
You can find the corresponding `run.sh` script for running the case in the folders corresponding to the participant you want to use:
39+
40+
for the fluid participant with OpenFOAM,
41+
```bash
42+
cd fluid-openfoam
43+
OpenFOAM<version> // Enter the OpenFOAM environment
44+
./run.sh
45+
```
46+
47+
and
48+
49+
```bash
50+
cd solid-gismo
51+
./run.sh
52+
```
53+
54+
## Post-processing
55+
The results of this tutorial are comparable to the simulation results communicated with force under the perpendicular-flap tutorials.
56+
![G+Smo stress](https://github.com/Crazy-Rich-Meghan/tutorials/blob/perpendicular-flap-gismo-elasticity-stress/perpendicular-flap-stress/images/tutorials-perpendicular-flap-displacement-openfoam-gismo-elasticity.png)
57+
58+
Additionally, the mesh convergence study data is available under the `images/data` directory. A sample plot illustrating the convergence of x-displacement over time is shown below:
59+
![G+Smo converfence](https://github.com/Crazy-Rich-Meghan/tutorials/blob/perpendicular-flap-gismo-elasticity-stress/perpendicular-flap-stress/images/x_displacement_vs_time.png)

0 commit comments

Comments
 (0)