File: anisotropic_adaptation.py

package info (click to toggle)
petsc4py 3.23.1-1exp2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,448 kB
  • sloc: python: 12,503; ansic: 1,697; makefile: 343; f90: 313; sh: 14
file content (104 lines) | stat: -rw-r--r-- 3,234 bytes parent folder | download | duplicates (3)
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
import sys,petsc4py
petsc4py.init(sys.argv)
from petsc4py import PETSc
import numpy as np


def sensor(x, y):
    """
    Classic hyperbolic sensor function for testing
    multi-scale anisotropic mesh adaptation:

    f:[-1, 1]² → R,
        f(x, y) = sin(50xy)/100 if |xy| > 2π/50 else sin(50xy)

    (mapped to have domain [0,1]² in this case).
    """
    xy = (2*x - 1)*(2*y - 1)
    ret = np.sin(50*xy)
    if np.abs(xy) > 2*np.pi/50:
        ret *= 0.01
    return ret


# Set metric parameters
h_min = 1.0e-10             # Minimum tolerated metric magnitude ~ cell size
h_max = 1.0e-01             # Maximum tolerated metric magnitude ~ cell size
a_max = 1.0e+05             # Maximum tolerated anisotropy
targetComplexity = 10000.0  # Analogous to number of vertices in adapted mesh
p = 1.0                     # Lᵖ normalization order

# Create a uniform mesh
OptDB = PETSc.Options()
dim = OptDB.getInt('dim', 2)
numEdges = 10
simplex = True
plex = PETSc.DMPlex().createBoxMesh([numEdges]*dim, simplex=simplex)
plex.distribute()
plex.view()
viewer = PETSc.Viewer().createVTK("anisotropic_mesh_0.vtk", "w")
viewer(plex)

# Do four mesh adaptation iterations
for i in range(1, 5):
    vStart, vEnd = plex.getDepthStratum(0)

    # Create a P1 sensor function
    comm = plex.getComm()
    fe = PETSc.FE().createLagrange(dim, 1, simplex, 1, -1, comm=comm)
    plex.setField(0, fe)
    plex.createDS()
    f = plex.createLocalVector()
    csec = plex.getCoordinateSection()
    coords = plex.getCoordinatesLocal()
    pf = f.getArray()
    pcoords = coords.getArray()
    for v in range(vStart, vEnd):
        off = csec.getOffset(v)
        x = pcoords[off]
        y = pcoords[off+1]
        pf[off//dim] = sensor(x, y)
    f.setName("Sensor")
    viewer = PETSc.Viewer().createVTK(f"sensor_{i}.vtk", "w")
    viewer(f)

    # Recover the gradient of the sensor function
    dmGrad = plex.clone()
    fe = PETSc.FE().createLagrange(dim, dim, simplex, 1, -1, comm=comm)
    dmGrad.setField(0, fe)
    dmGrad.createDS()
    g = dmGrad.createLocalVector()
    plex.computeGradientClementInterpolant(f, g)
    g.setName("Gradient")
    viewer = PETSc.Viewer().createVTK(f"gradient_{i}.vtk", "w")
    viewer(g)

    # Recover the Hessian of the sensor function
    dmHess = plex.clone()
    H = dmHess.metricCreate()
    dmGrad.computeGradientClementInterpolant(g, H)
    H.setName("Hessian")
    viewer = PETSc.Viewer().createVTK(f"hessian_{i}.vtk", "w")
    viewer(H)

    # Obtain a metric by Lᵖ normalization
    dmHess.metricSetMinimumMagnitude(h_min)
    dmHess.metricSetMaximumMagnitude(h_max)
    dmHess.metricSetMaximumAnisotropy(a_max)
    dmHess.metricSetNormalizationOrder(p)
    dmHess.metricSetTargetComplexity(targetComplexity)
    metric = dmHess.metricCreate()
    det = dmHess.metricDeterminantCreate()
    dmHess.metricNormalize(H, metric, det)
    metric.setName("Metric")
    viewer = PETSc.Viewer().createVTK(f"metric_{i}.vtk", "w")
    viewer(metric)

    # Call adapt routine - boundary label None by default
    plex = plex.adaptMetric(metric)
    plex.distribute()
    plex.view()

    # Write to VTK file
    viewer = PETSc.Viewer().createVTK(f"anisotropic_mesh_{i}.vtk", "w")
    viewer(plex)