forked from CEED/Laghos
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmesh-optimizer.hpp
497 lines (438 loc) · 14.8 KB
/
mesh-optimizer.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
// MFEM Mesh Optimizer Miniapp - Serial/Parallel Shared Code
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace mfem;
using namespace std;
double size_indicator(const Vector &x)
{
// semi-circle
const double xc = x(0) - 0.0, yc = x(1) - 0.5,
zc = (x.Size() == 3) ? x(2) - 0.5 : 0.0;
const double r = sqrt(xc*xc + yc*yc + zc*zc);
double r1 = 0.45; double r2 = 0.55; double sf=30.0;
double val = 0.5*(1+std::tanh(sf*(r-r1))) - 0.5*(1+std::tanh(sf*(r-r2)));
val = fmax(0.,val);
val = fmin(1.,val);
return val;
}
void calc_mass_volume(const GridFunction &g, double &mass, double &vol)
{
Mesh &mesh = *g.FESpace()->GetMesh();
const int NE = mesh.GetNE();
Vector g_vals;
mass = 0.0, vol = 0.0;
for (int e = 0; e < NE; e++)
{
ElementTransformation &Tr = *mesh.GetElementTransformation(e);
const IntegrationRule &ir = IntRules.Get(mesh.GetElementBaseGeometry(e),
Tr.OrderJ());
g.GetValues(Tr, ir, g_vals);
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
Tr.SetIntPoint(&ip);
mass += g_vals(j) * ip.weight * Tr.Weight();
vol += ip.weight * Tr.Weight();
}
}
#ifdef MFEM_USE_MPI
auto gp = dynamic_cast<const ParGridFunction *>(&g);
if (gp)
{
MPI_Comm comm = gp->ParFESpace()->GetComm();
MPI_Allreduce(MPI_IN_PLACE, &mass, 1, MPI_DOUBLE, MPI_SUM, comm);
MPI_Allreduce(MPI_IN_PLACE, &vol, 1, MPI_DOUBLE, MPI_SUM, comm);
}
#endif
}
void ConstructSizeGF(GridFunction &size)
{
// Indicator for small (value -> 1) or big (value -> 0) elements.
FunctionCoefficient size_ind_coeff(size_indicator);
size.ProjectCoefficient(size_ind_coeff);
// Determine small/big target sizes based on the total number of
// elements and the volume occupied by small elements.
double volume_ind, volume;
calc_mass_volume(size, volume_ind, volume);
Mesh &mesh = *size.FESpace()->GetMesh();
int NE = mesh.GetNE();
#ifdef MFEM_USE_MPI
auto size_p = dynamic_cast<const ParGridFunction *>(&size);
if (size_p) { NE = size_p->ParFESpace()->GetParMesh()->GetGlobalNE(); }
#endif
NCMesh *ncmesh = mesh.ncmesh;
// For parallel NC meshes, all tasks have all root elements.
NE = (ncmesh) ? ncmesh->GetNumRootElements() : NE;
const double size_ratio = (mesh.Dimension() == 2) ? 9 : 27;
const double small_el_size = volume_ind / NE +
(volume - volume_ind) / (size_ratio * NE);
const double big_el_size = size_ratio * small_el_size;
for (int i = 0; i < size.Size(); i++)
{
size(i) = size(i) * small_el_size + (1.0 - size(i)) * big_el_size;
}
}
double material_indicator_2d(const Vector &x)
{
double xc = x(0)-0.5, yc = x(1)-0.5;
double th = 22.5*M_PI/180.;
double xn = cos(th)*xc + sin(th)*yc;
double yn = -sin(th)*xc + cos(th)*yc;
double th2 = (th > 45.*M_PI/180) ? M_PI/2 - th : th;
double stretch = 1/cos(th2);
xc = xn/stretch; yc = yn/stretch;
double tfac = 20;
double s1 = 3;
double s2 = 3;
double wgt = std::tanh((tfac*(yc) + s2*std::sin(s1*M_PI*xc)) + 1);
if (wgt > 1) { wgt = 1; }
if (wgt < 0) { wgt = 0; }
return wgt;
}
double discrete_ori_2d(const Vector &x)
{
return M_PI * x(1) * (1.0 - x(1)) * cos(2 * M_PI * x(0));
}
void discrete_aspr_3d(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim);
double l1, l2, l3;
l1 = 1.;
l2 = 1. + 5*x(1);
l3 = 1. + 10*x(2);
v[0] = l1/pow(l2*l3,0.5);
v[1] = l2/pow(l1*l3,0.5);
v[2] = l3/pow(l2*l1,0.5);
}
class HessianCoefficient : public TMOPMatrixCoefficient
{
private:
int metric;
public:
HessianCoefficient(int dim, int metric_id)
: TMOPMatrixCoefficient(dim), metric(metric_id) { }
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector pos(3);
T.Transform(ip, pos);
if (metric != 14 && metric != 36 && metric != 85)
{
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double eps = 0.5;
const double tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
K(0, 0) = eps + 1.0 * (tan1 - tan2);
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
}
else if (metric == 14 || metric == 36) // Size + Alignment
{
const double xc = pos(0), yc = pos(1);
double theta = M_PI * yc * (1.0 - yc) * cos(2 * M_PI * xc);
double alpha_bar = 0.1;
K(0, 0) = cos(theta);
K(1, 0) = sin(theta);
K(0, 1) = -sin(theta);
K(1, 1) = cos(theta);
K *= alpha_bar;
}
else if (metric == 85) // Shape + Alignment
{
Vector x = pos;
double xc = x(0)-0.5, yc = x(1)-0.5;
double th = 22.5*M_PI/180.;
double xn = cos(th)*xc + sin(th)*yc;
double yn = -sin(th)*xc + cos(th)*yc;
xc = xn; yc=yn;
double tfac = 20;
double s1 = 3;
double s2 = 2;
double wgt = std::tanh((tfac*(yc) + s2*std::sin(s1*M_PI*xc)) + 1)
- std::tanh((tfac*(yc) + s2*std::sin(s1*M_PI*xc)) - 1);
if (wgt > 1) { wgt = 1; }
if (wgt < 0) { wgt = 0; }
xc = pos(0), yc = pos(1);
double theta = M_PI * (yc) * (1.0 - yc) * cos(2 * M_PI * xc);
K(0, 0) = cos(theta);
K(1, 0) = sin(theta);
K(0, 1) = -sin(theta);
K(1, 1) = cos(theta);
double asp_ratio_tar = 0.1 + 1*(1-wgt)*(1-wgt);
K(0, 0) *= 1/pow(asp_ratio_tar,0.5);
K(1, 0) *= 1/pow(asp_ratio_tar,0.5);
K(0, 1) *= pow(asp_ratio_tar,0.5);
K(1, 1) *= pow(asp_ratio_tar,0.5);
}
}
virtual void EvalGrad(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip, int comp)
{
Vector pos(3);
T.Transform(ip, pos);
K = 0.;
if (metric != 14 && metric != 85)
{
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
double tan1d = 0., tan2d = 0.;
if (r > 0.001)
{
tan1d = (1.-tan1*tan1)*(sf)/r,
tan2d = (1.-tan2*tan2)*(sf)/r;
}
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
if (comp == 0) { K(0, 0) = tan1d*xc - tan2d*xc; }
else if (comp == 1) { K(0, 0) = tan1d*yc - tan2d*yc; }
}
}
};
class HRHessianCoefficient : public TMOPMatrixCoefficient
{
private:
int dim;
// 0 - size target in an annular region,
// 1 - size+aspect-ratio in an annular region,
// 2 - size+aspect-ratio target for a rotate sine wave.
int hr_target_type;
public:
HRHessianCoefficient(int dim_, int hr_target_type_ = 0)
: TMOPMatrixCoefficient(dim_), dim(dim_),
hr_target_type(hr_target_type_) { }
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector pos(3);
T.Transform(ip, pos);
if (hr_target_type == 0) // size only circle
{
double small = 0.001, big = 0.01;
if (dim == 3) { small = 0.005, big = 0.1; }
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
double zc;
if (dim == 3) { zc = pos(2) - 0.5; }
double r = sqrt(xc*xc + yc*yc);
if (dim == 3) { r = sqrt(xc*xc + yc*yc + zc*zc); }
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
double ind = (tan1 - tan2);
if (ind > 1.0) {ind = 1.;}
if (ind < 0.0) {ind = 0.;}
double val = ind * small + (1.0 - ind) * big;
K = 0.0;
K(0, 0) = 1.0;
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
K(0, 0) *= pow(val,0.5);
K(1, 1) *= pow(val,0.5);
if (dim == 3) { K(2, 2) = pow(val,0.5); }
}
else if (hr_target_type == 1) // circle with size and AR
{
const double small = 0.001, big = 0.01;
const double xc = pos(0)-0.5, yc = pos(1)-0.5;
const double rv = xc*xc + yc*yc;
double r = 0;
if (rv>0.) {r = sqrt(rv);}
double r1 = 0.2; double r2 = 0.3; double sf=30.0;
const double szfac = 1;
const double asfac = 4;
const double eps2 = szfac/asfac;
const double eps1 = szfac;
double tan1 = std::tanh(sf*(r-r1)+1),
tan2 = std::tanh(sf*(r-r2)-1);
double wgt = 0.5*(tan1-tan2);
tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
double ind = (tan1 - tan2);
if (ind > 1.0) {ind = 1.;}
if (ind < 0.0) {ind = 0.;}
double szval = ind * small + (1.0 - ind) * big;
double th = std::atan2(yc,xc)*180./M_PI;
if (wgt > 1) { wgt = 1; }
if (wgt < 0) { wgt = 0; }
double maxval = eps2 + eps1*(1-wgt)*(1-wgt);
double minval = eps1;
double avgval = 0.5*(maxval+minval);
double ampval = 0.5*(maxval-minval);
double val1 = avgval + ampval*sin(2.*th*M_PI/180.+90*M_PI/180.);
double val2 = avgval + ampval*sin(2.*th*M_PI/180.-90*M_PI/180.);
K(0,1) = 0.0;
K(1,0) = 0.0;
K(0,0) = val1;
K(1,1) = val2;
K(0,0) *= pow(szval,0.5);
K(1,1) *= pow(szval,0.5);
}
else if (hr_target_type == 2) // sharp rotated sine wave
{
double xc = pos(0)-0.5, yc = pos(1)-0.5;
double th = 15.5*M_PI/180.;
double xn = cos(th)*xc + sin(th)*yc;
double yn = -sin(th)*xc + cos(th)*yc;
double th2 = (th > 45.*M_PI/180) ? M_PI/2 - th : th;
double stretch = 1/cos(th2);
xc = xn/stretch;
yc = yn;
double tfac = 20;
double s1 = 3;
double s2 = 2;
double yl1 = -0.025;
double yl2 = 0.025;
double wgt = std::tanh((tfac*(yc-yl1) + s2*std::sin(s1*M_PI*xc)) + 1) -
std::tanh((tfac*(yc-yl2) + s2*std::sin(s1*M_PI*xc)) - 1);
if (wgt > 1) { wgt = 1; }
if (wgt < 0) { wgt = 0; }
const double eps2 = 25;
const double eps1 = 1;
K(1,1) = eps1/eps2 + eps1*(1-wgt)*(1-wgt);
K(0,0) = eps1;
K(0,1) = 0.0;
K(1,0) = 0.0;
}
else { MFEM_ABORT("Unsupported option / wrong input."); }
}
virtual void EvalGrad(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip, int comp)
{
K = 0.;
}
};
// Additional IntegrationRules that can be used with the --quad-type option.
IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto);
IntegrationRules IntRulesCU(0, Quadrature1D::ClosedUniform);
// Defined with respect to the icf mesh.
double weight_fun(const Vector &x)
{
const double r = sqrt(x(0)*x(0) + x(1)*x(1) + 1e-12);
const double den = 0.002;
double l2 = 0.2 + 0.5*std::tanh((r-0.16)/den) - 0.5*std::tanh((r-0.17)/den)
+ 0.5*std::tanh((r-0.23)/den) - 0.5*std::tanh((r-0.24)/den);
return l2;
}
// Used for the adaptive limiting examples.
double adapt_lim_fun(const Vector &x)
{
const double xc = x(0) - 0.1, yc = x(1) - 0.2;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.45; double r2 = 0.55; double sf=30.0;
double val = 0.5*(1+std::tanh(sf*(r-r1))) - 0.5*(1+std::tanh(sf*(r-r2)));
val = std::max(0.,val);
val = std::min(1.,val);
return val;
}
// Used for exact surface alignment
double surface_level_set(const Vector &x)
{
const int type = 1;
const int dim = x.Size();
if (type == 0)
{
const double sine = 0.25 * std::sin(4 * M_PI * x(0));
return (x(1) >= sine + 0.5) ? 1.0 : -1.0;
}
else
{
if (dim == 2)
{
const double xc = x(0) - 0.5, yc = x(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
return r-0.3;
}
else
{
const double xc = x(0) - 0.5, yc = x(1) - 0.5, zc = x(2) - 0.5;
const double r = sqrt(xc*xc + yc*yc + zc*zc);
return r-0.3;
}
}
}
int material_id(int el_id, const GridFunction &g)
{
const FiniteElementSpace *fes = g.FESpace();
const FiniteElement *fe = fes->GetFE(el_id);
Vector g_vals;
const IntegrationRule &ir =
IntRules.Get(fe->GetGeomType(), fes->GetOrder(el_id) + 2);
double integral = 0.0;
g.GetValues(el_id, ir, g_vals);
ElementTransformation *Tr = fes->GetMesh()->GetElementTransformation(el_id);
int approach = 1;
if (approach == 0) // integral based
{
for (int q = 0; q < ir.GetNPoints(); q++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
Tr->SetIntPoint(&ip);
integral += ip.weight * g_vals(q) * Tr->Weight();
}
return (integral > 0.0) ? 1.0 : 0.0;
}
else if (approach == 1) // minimum value based
{
double minval = g_vals.Min();
return minval > 0.0 ? 1.0 : 0.0;
}
return 0.0;
}
void DiffuseField(GridFunction &field, int smooth_steps)
{
// Setup the Laplacian operator
BilinearForm *Lap = new BilinearForm(field.FESpace());
Lap->AddDomainIntegrator(new DiffusionIntegrator());
Lap->Assemble();
Lap->Finalize();
// Setup the smoothing operator
DSmoother *S = new DSmoother(0,1.0,smooth_steps);
S->iterative_mode = true;
S->SetOperator(Lap->SpMat());
Vector tmp(field.Size());
tmp = 0.0;
S->Mult(tmp, field);
delete S;
delete Lap;
}
#ifdef MFEM_USE_MPI
void DiffuseField(ParGridFunction &field, int smooth_steps)
{
// Setup the Laplacian operator
ParBilinearForm *Lap = new ParBilinearForm(field.ParFESpace());
Lap->AddDomainIntegrator(new DiffusionIntegrator());
Lap->Assemble();
Lap->Finalize();
HypreParMatrix *A = Lap->ParallelAssemble();
HypreSmoother *S = new HypreSmoother(*A,0,smooth_steps);
S->iterative_mode = true;
Vector tmp(A->Width());
field.SetTrueVector();
Vector fieldtrue = field.GetTrueVector();
tmp = 0.0;
S->Mult(tmp, fieldtrue);
field.SetFromTrueDofs(fieldtrue);
delete S;
delete A;
delete Lap;
}
#endif