├── .gitignore
├── LICENSE.md
├── README.md
├── cache
└── .keep
├── example.py
├── files
├── aust.msh
├── mask.vtk
├── topo.msh
└── us48.msh
├── image
└── voro.jpg
├── setup.cfg
└── tests
├── __init__.py
├── case_1_.py
├── case_2_.py
├── case_3_.py
├── case_4_.py
├── case_5_.py
├── case_6_.py
└── case_7_.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # jigsaw things
2 | cache/*.log
3 | cache/*.jig
4 | cache/*.msh
5 |
6 | # python things
7 | __pycache__/
8 | *.py[cod]
9 |
10 | build/
11 | develop-eggs/
12 | dist/
13 | eggs/
14 | parts/
15 | sdist/
16 | var/
17 | *.egg-info/
18 | .installed.cfg
19 | *.egg
20 |
21 | # data files, etc
22 | *.nc
23 | *.grd
24 | *.vtk
25 | *.off
26 | *.obj
27 | *.stl
28 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | `JIGSAW` is licensed under the following terms:
2 |
3 | This program may be freely redistributed under the condition that the copyright notices (including this entire header) are not removed, and no compensation is received through use of the software. Private, research, and institutional use is free. You may distribute modified versions of this code `UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS`. Distribution of this code as part of a commercial system is permissible `ONLY BY DIRECT ARRANGEMENT WITH THE AUTHOR`. (If you are not directly supplying this code to a customer, and you are instead telling them how they can obtain it for free, then you are not required to make any arrangement with me.)
4 |
5 | `DISCLAIMER`: Neither I nor: Columbia University, the Massachusetts Institute of Technology, the University of Sydney, nor the National Aeronautics and Space Administration warrant this code in any way whatsoever. This code is provided "as-is" to be used at your own risk.
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## `JIGSAW(GEO): Mesh generation for geoscientific modelling`
2 |
3 |
4 |
5 |
6 |
7 | `JIGSAW(GEO)` is a set of algorithms designed to generate unstructured grids for geoscientific modelling. Applications include: large-scale atmospheric simulation and numerical weather prediction, global and coastal ocean-modelling, and ice-sheet dynamics.
8 |
9 | `JIGSAW(GEO)` can be used to produce high-quality 'generalised' Delaunay / Voronoi tessellations for unstructured finite-volume / element type models. Grids can be generated in local two-dimensional domains, and over general spheroidal surfaces. Mesh resolution can be adapted to follow complex user-defined metrics, including: topographic contours, discrete solution profiles or coastal features. This enables the construction of complex, multi-resolution climate process models, with simulation fidelity enhanced in regions of interest.
10 |
11 | `JIGSAW(GEO)` is typically able to produce the very high-quality staggered unstructured grids required by contemporary unstructued general circulation models (i.e. `MPAS`, `COMPAS`, `FESOM`, etc), generating highly optimised, multi-resolution meshes that are `locally-orthogonal`, `mutually-centroidal` and `self-centred`.
12 |
13 | `JIGSAW(GEO)` depends on the `JIGSAW-PYTHON` package; a `Python` interface to the underlying `JIGSAW` meshing library.
14 |
15 | ### `Quickstart`
16 |
17 | `JIGSAW(GEO)` requires the `JIGSAW` meshing package be installed. `JIGSAW`'s `Python` interface is available here. Once installed, the test problems can be run via:
18 |
19 | clone/download + unpack this repository.
20 | python3 example.py --IDnumber=1
21 |
22 | Note: installation of `JIGSAW` requires a `c++` compiler and the `cmake` utility. `JIGSAW` may also be installed as a `conda` package. See here for details.
23 |
24 | ### `Example Problems`
25 |
26 | The following set of example problems are available in `example.py`:
27 |
28 | example: 1; # generate a uniform resolution global grid
29 | example: 2; # generate a regionally-refined global grid
30 | example: 3; # build smooth mesh-spacing functions from noisy input data
31 | example: 4; # generate a complex, variable resolution global grid
32 | example: 5; # generate structured icosahedral and cubedsphere meshes
33 | example: 6; # generate a coastal mesh for the Australasian region
34 | example: 7; # generate a multi-part mesh for the (contiguous) USA
35 |
36 | Run `python3 example.py --IDnumber=N` to call the `N-th` example. `*.vtk` output is saved to `../cache` and can be visualised with, for example, Paraview.
37 |
38 | Additional material and discussion can be found in the worked examples here.
39 |
40 | ### `License`
41 |
42 | This program may be freely redistributed under the condition that the copyright notices (including this entire header) are not removed, and no compensation is received through use of the software. Private, research, and institutional use is free. You may distribute modified versions of this code `UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS`. Distribution of this code as part of a commercial system is permissible `ONLY BY DIRECT ARRANGEMENT WITH THE AUTHOR`. (If you are not directly supplying this code to a customer, and you are instead telling them how they can obtain it for free, then you are not required to make any arrangement with me.)
43 |
44 | `DISCLAIMER`: Neither I nor: Columbia University, the Massachusetts Institute of Technology, the University of Sydney, nor the National Aeronautics and Space Administration warrant this code in any way whatsoever. This code is provided "as-is" to be used at your own risk.
45 |
46 | ### `References`
47 |
48 | There are a number of publications that describe the algorithms used in `JIGSAW(GEO)` in detail. Additional information and references regarding the formulation of the underlying `JIGSAW` mesh-generator can also be found here. If you make use of `JIGSAW` in your work, please consider including a reference to the following:
49 |
50 | `[1]` - Darren Engwirda: Generalised primal-dual grids for unstructured co-volume schemes, J. Comp. Phys., 375, pp. 155-176, https://doi.org/10.1016/j.jcp.2018.07.025, 2018.
51 |
52 | `[2]` - Darren Engwirda: JIGSAW-GEO (1.0): locally orthogonal staggered unstructured grid generation for general circulation modelling on the sphere, Geosci. Model Dev., 10, pp. 2117-2140, https://doi.org/10.5194/gmd-10-2117-2017, 2017.
53 |
54 | `[3]` - Darren Engwirda, David Ivers, Off-centre Steiner points for Delaunay-refinement on curved surfaces, Computer-Aided Design, 72, pp. 157-171, http://dx.doi.org/10.1016/j.cad.2015.10.007, 2016.
55 |
56 | `[4]` - Darren Engwirda: Multi-resolution unstructured grid-generation for geophysical applications on the sphere, Research note, Proceedings of the 24th International Meshing Roundtable, https://arxiv.org/abs/1512.00307, 2015.
57 |
58 | `[5]` - Darren Engwirda, Locally-optimal Delaunay-refinement and optimisation-based mesh generation, Ph.D. Thesis, School of Mathematics and Statistics, The University of Sydney, http://hdl.handle.net/2123/13148, 2014.
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/cache/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengwirda/jigsaw-geo-python/24fc1aaf72d7693ae7155e292c38daff03043a1e/cache/.keep
--------------------------------------------------------------------------------
/example.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import os
4 | import argparse
5 |
6 | from tests.case_1_ import case_1_
7 | from tests.case_2_ import case_2_
8 | from tests.case_3_ import case_3_
9 | from tests.case_4_ import case_4_
10 | from tests.case_5_ import case_5_
11 | from tests.case_6_ import case_6_
12 | from tests.case_7_ import case_7_
13 |
14 |
15 | def example(IDnumber=0):
16 |
17 | #--------------- delegate to the individual example cases...
18 |
19 | src_path = os.path.join(
20 | os.path.abspath(
21 | os.path.dirname(__file__)), "files")
22 |
23 | dst_path = os.path.join(
24 | os.path.abspath(
25 | os.path.dirname(__file__)), "cache")
26 |
27 | if (IDnumber == +1):
28 | case_1_(src_path, dst_path)
29 |
30 | elif (IDnumber == +2):
31 | case_2_(src_path, dst_path)
32 |
33 | elif (IDnumber == +3):
34 | case_3_(src_path, dst_path)
35 |
36 | elif (IDnumber == +4):
37 | case_4_(src_path, dst_path)
38 |
39 | elif (IDnumber == +5):
40 | case_5_(src_path, dst_path)
41 |
42 | elif (IDnumber == +6):
43 | case_6_(src_path, dst_path)
44 |
45 | elif (IDnumber == +7):
46 | case_7_(src_path, dst_path)
47 |
48 | return
49 |
50 |
51 | if __name__ == "__main__":
52 | parser = argparse.ArgumentParser(
53 | description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
54 |
55 | parser.add_argument("--IDnumber", dest="IDnumber", type=int,
56 | required=True, help="Run example with ID = (1-7).")
57 |
58 | args = parser.parse_args()
59 |
60 | example(IDnumber=args.IDnumber)
61 |
--------------------------------------------------------------------------------
/image/voro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengwirda/jigsaw-geo-python/24fc1aaf72d7693ae7155e292c38daff03043a1e/image/voro.jpg
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = W503,W504,E115,E265,E271,E701,E702,E704
3 | exclude = jigsawpy/msh_l.py,
4 | jigsawpy/jig_l.py
5 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengwirda/jigsaw-geo-python/24fc1aaf72d7693ae7155e292c38daff03043a1e/tests/__init__.py
--------------------------------------------------------------------------------
/tests/case_1_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import math
4 | import time
5 | import numpy as np
6 | from scipy import interpolate
7 |
8 | import jigsawpy
9 |
10 |
11 | def case_1_(src_path, dst_path):
12 |
13 | # DEMO-1: generate a uniform resolution (150KM) global grid.
14 |
15 | opts = jigsawpy.jigsaw_jig_t()
16 |
17 | topo = jigsawpy.jigsaw_msh_t()
18 |
19 | geom = jigsawpy.jigsaw_msh_t()
20 | mesh = jigsawpy.jigsaw_msh_t()
21 |
22 | #------------------------------------ setup files for JIGSAW
23 |
24 | opts.geom_file = \
25 | os.path.join(dst_path, "geom.msh")
26 |
27 | opts.jcfg_file = \
28 | os.path.join(dst_path, "opts.jig")
29 |
30 | opts.mesh_file = \
31 | os.path.join(dst_path, "mesh.msh")
32 |
33 | #------------------------------------ define JIGSAW geometry
34 |
35 | geom.mshID = "ellipsoid-mesh"
36 | geom.radii = np.full(
37 | 3, 6.371E+003, dtype=geom.REALS_t)
38 |
39 | jigsawpy.savemsh(opts.geom_file, geom)
40 |
41 | #------------------------------------ make mesh using JIGSAW
42 |
43 | opts.hfun_scal = "absolute"
44 | opts.hfun_hmax = +150. # uniform at 150km
45 |
46 | opts.mesh_dims = +2 # 2-dim. simplexes
47 |
48 | opts.optm_qlim = +9.5E-01 # tighter opt. tol
49 | opts.optm_iter = +32
50 | opts.optm_qtol = +1.0E-05
51 |
52 | # opts.optm_kern = "cvt+dqdx"
53 |
54 | rbar = np.mean(geom.radii) # bisect heuristic
55 |
56 | nlev = round(math.log2(
57 | rbar / math.sin(.4 * math.pi) / opts.hfun_hmax)
58 | )
59 |
60 | ttic = time.time()
61 |
62 | jigsawpy.cmd.tetris(opts, nlev - 1, mesh)
63 |
64 | ttoc = time.time()
65 |
66 | print("CPUSEC =", (ttoc - ttic))
67 |
68 | print("BISECT =", +nlev)
69 |
70 | cost = jigsawpy.triscr2( # quality metrics!
71 | mesh.point["coord"],
72 | mesh.tria3["index"])
73 |
74 | print("TRISCR =", np.min(cost), np.mean(cost))
75 |
76 | cost = jigsawpy.pwrscr2(
77 | mesh.point["coord"],
78 | mesh.power,
79 | mesh.tria3["index"])
80 |
81 | print("PWRSCR =", np.min(cost), np.mean(cost))
82 |
83 | tbad = jigsawpy.centre2(
84 | mesh.point["coord"],
85 | mesh.power,
86 | mesh.tria3["index"])
87 |
88 | print("OBTUSE =",
89 | +np.count_nonzero(np.logical_not(tbad)))
90 |
91 | ndeg = jigsawpy.trideg2(
92 | mesh.point["coord"],
93 | mesh.tria3["index"])
94 |
95 | print("TOPOL. =",
96 | +np.count_nonzero(ndeg==+6) / ndeg.size)
97 |
98 | #------------------------------------ save mesh for Paraview
99 |
100 | jigsawpy.loadmsh(os.path.join(
101 | src_path, "topo.msh"), topo)
102 |
103 | #------------------------------------ a very rough land mask
104 |
105 | apos = jigsawpy.R3toS2(
106 | geom.radii, mesh.point["coord"][:])
107 |
108 | apos = apos * 180. / np.pi
109 |
110 | zfun = interpolate.RectBivariateSpline(
111 | topo.ygrid, topo.xgrid, topo.value)
112 |
113 | mesh.value = zfun(
114 | apos[:, 1], apos[:, 0], grid=False)
115 |
116 | cell = mesh.tria3["index"]
117 |
118 | zmsk = \
119 | mesh.value[cell[:, 0]] + \
120 | mesh.value[cell[:, 1]] + \
121 | mesh.value[cell[:, 2]]
122 | zmsk = zmsk / +3.0
123 |
124 | mesh.tria3 = mesh.tria3[zmsk < +0.]
125 |
126 | print("Saving to ../cache/case_1a.vtk")
127 |
128 | jigsawpy.savevtk(os.path.join(
129 | dst_path, "case_1a.vtk"), mesh)
130 |
131 | return
132 |
--------------------------------------------------------------------------------
/tests/case_2_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import time
4 | import math
5 | import numpy as np
6 | from scipy import interpolate
7 |
8 | import jigsawpy
9 |
10 |
11 | def case_2_(src_path, dst_path):
12 |
13 | # DEMO-2: generate a regionally-refined global grid with a
14 | # high-resolution 37.5km patch embedded in a uniform 150km
15 | # background grid.
16 |
17 | opts = jigsawpy.jigsaw_jig_t()
18 |
19 | topo = jigsawpy.jigsaw_msh_t()
20 |
21 | geom = jigsawpy.jigsaw_msh_t()
22 | hfun = jigsawpy.jigsaw_msh_t()
23 | mesh = jigsawpy.jigsaw_msh_t()
24 |
25 | #------------------------------------ setup files for JIGSAW
26 |
27 | opts.geom_file = \
28 | os.path.join(dst_path, "geom.msh")
29 |
30 | opts.jcfg_file = \
31 | os.path.join(dst_path, "opts.jig")
32 |
33 | opts.mesh_file = \
34 | os.path.join(dst_path, "mesh.msh")
35 |
36 | opts.hfun_file = \
37 | os.path.join(dst_path, "spac.msh")
38 |
39 | #------------------------------------ define JIGSAW geometry
40 |
41 | geom.mshID = "ellipsoid-mesh"
42 | geom.radii = np.full(
43 | 3, 6.371E+003, dtype=geom.REALS_t)
44 |
45 | jigsawpy.savemsh(opts.geom_file, geom)
46 |
47 | #------------------------------------ define spacing pattern
48 |
49 | hfun.mshID = "ellipsoid-grid"
50 | hfun.radii = geom.radii
51 |
52 | hfun.xgrid = np.linspace(
53 | -1. * np.pi, +1. * np.pi, 360)
54 |
55 | hfun.ygrid = np.linspace(
56 | -.5 * np.pi, +.5 * np.pi, 180)
57 |
58 | xmat, ymat = \
59 | np.meshgrid(hfun.xgrid, hfun.ygrid)
60 |
61 | hfun.value = +150. - 112.5 * np.exp(-(
62 | +1.5 * (xmat + 1.0) ** 2 +
63 | +1.5 * (ymat - 0.5) ** 2) ** 4)
64 |
65 | jigsawpy.savemsh(opts.hfun_file, hfun)
66 |
67 | #------------------------------------ make mesh using JIGSAW
68 |
69 | opts.hfun_scal = "absolute"
70 | opts.hfun_hmax = float("inf") # null HFUN limits
71 | opts.hfun_hmin = float(+0.00)
72 |
73 | opts.mesh_dims = +2 # 2-dim. simplexes
74 |
75 | opts.optm_qlim = +9.5E-01 # tighter opt. tol
76 | opts.optm_iter = +32
77 | opts.optm_qtol = +1.0E-05
78 |
79 | # opts.optm_kern = "cvt+dqdx"
80 |
81 | rbar = np.mean(geom.radii) # bisect heuristic
82 | hbar = np.mean(hfun.value)
83 | nlev = round(math.log2(
84 | rbar / math.sin(.4 * math.pi) / hbar)
85 | )
86 |
87 | ttic = time.time()
88 |
89 | jigsawpy.cmd.tetris(opts, nlev - 1, mesh)
90 |
91 | ttoc = time.time()
92 |
93 | print("CPUSEC =", (ttoc - ttic))
94 |
95 | print("BISECT =", +nlev)
96 |
97 | cost = jigsawpy.triscr2( # quality metrics!
98 | mesh.point["coord"],
99 | mesh.tria3["index"])
100 |
101 | print("TRISCR =", np.min(cost), np.mean(cost))
102 |
103 | cost = jigsawpy.pwrscr2(
104 | mesh.point["coord"],
105 | mesh.power,
106 | mesh.tria3["index"])
107 |
108 | print("PWRSCR =", np.min(cost), np.mean(cost))
109 |
110 | tbad = jigsawpy.centre2(
111 | mesh.point["coord"],
112 | mesh.power,
113 | mesh.tria3["index"])
114 |
115 | print("OBTUSE =",
116 | +np.count_nonzero(np.logical_not(tbad)))
117 |
118 | ndeg = jigsawpy.trideg2(
119 | mesh.point["coord"],
120 | mesh.tria3["index"])
121 |
122 | print("TOPOL. =",
123 | +np.count_nonzero(ndeg==+6) / ndeg.size)
124 |
125 | #------------------------------------ save mesh for Paraview
126 |
127 | jigsawpy.loadmsh(os.path.join(
128 | src_path, "topo.msh"), topo)
129 |
130 | #------------------------------------ a very rough land mask
131 |
132 | apos = jigsawpy.R3toS2(
133 | geom.radii, mesh.point["coord"][:])
134 |
135 | apos = apos * 180. / np.pi
136 |
137 | zfun = interpolate.RectBivariateSpline(
138 | topo.ygrid, topo.xgrid, topo.value)
139 |
140 | mesh.value = zfun(
141 | apos[:, 1], apos[:, 0], grid=False)
142 |
143 | cell = mesh.tria3["index"]
144 |
145 | zmsk = \
146 | mesh.value[cell[:, 0]] + \
147 | mesh.value[cell[:, 1]] + \
148 | mesh.value[cell[:, 2]]
149 | zmsk = zmsk / +3.0
150 |
151 | mesh.tria3 = mesh.tria3[zmsk < +0.]
152 |
153 | print("Saving to ../cache/case_2a.vtk")
154 |
155 | jigsawpy.savevtk(os.path.join(
156 | dst_path, "case_2a.vtk"), mesh)
157 |
158 | print("Saving to ../cache/case_2b.vtk")
159 |
160 | jigsawpy.savevtk(os.path.join(
161 | dst_path, "case_2b.vtk"), hfun)
162 |
163 | return
164 |
--------------------------------------------------------------------------------
/tests/case_3_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import copy
4 | import numpy as np
5 |
6 | import jigsawpy
7 |
8 |
9 | def case_3_(src_path, dst_path):
10 |
11 | # DEMO-3: generate multi-resolution spacing, via local refi-
12 | # nement along coastlines and shallow ridges. Global grid
13 | # resolution is 150KM, background resolution is 67KM and the
14 | # min. adaptive resolution is 33KM.
15 |
16 | opts = jigsawpy.jigsaw_jig_t()
17 |
18 | topo = jigsawpy.jigsaw_msh_t()
19 |
20 | geom = jigsawpy.jigsaw_msh_t()
21 |
22 | hraw = jigsawpy.jigsaw_msh_t()
23 | hlim = jigsawpy.jigsaw_msh_t()
24 |
25 | #------------------------------------ setup files for JIGSAW
26 |
27 | opts.geom_file = \
28 | os.path.join(dst_path, "geom.msh")
29 |
30 | opts.jcfg_file = \
31 | os.path.join(dst_path, "opts.jig")
32 |
33 | opts.hfun_file = \
34 | os.path.join(dst_path, "spac.msh")
35 |
36 | #------------------------------------ define JIGSAW geometry
37 |
38 | geom.mshID = "ellipsoid-mesh"
39 | geom.radii = np.full(
40 | 3, 6.371E+003, dtype=geom.REALS_t)
41 |
42 | jigsawpy.savemsh(opts.geom_file, geom)
43 |
44 | #------------------------------------ define spacing pattern
45 |
46 | jigsawpy.loadmsh(os.path.join(
47 | src_path, "topo.msh"), topo)
48 |
49 | hraw.mshID = "ellipsoid-grid"
50 | hraw.radii = geom.radii
51 |
52 | hraw.xgrid = topo.xgrid * np.pi / 180.
53 | hraw.ygrid = topo.ygrid * np.pi / 180.
54 |
55 | hfn0 = +150. # global spacing
56 | hfn2 = +33. # adapt. spacing
57 | hfn3 = +67. # arctic spacing
58 |
59 | hraw.value = np.sqrt(
60 | np.maximum(-topo.value, 0.0))
61 |
62 | hraw.value = \
63 | np.maximum(hraw.value, hfn2)
64 | hraw.value = \
65 | np.minimum(hraw.value, hfn3)
66 |
67 | mask = hraw.ygrid < 40. * np.pi / 180.
68 |
69 | hraw.value[mask] = hfn0
70 |
71 | #------------------------------------ set HFUN grad.-limiter
72 |
73 | hlim = copy.copy(hraw)
74 |
75 | hlim.slope = np.full( # |dH/dx| limits
76 | topo.value.shape,
77 | +0.050, dtype=hlim.REALS_t)
78 |
79 | jigsawpy.savemsh(opts.hfun_file, hlim)
80 |
81 | jigsawpy.cmd.marche(opts, hlim)
82 |
83 | #------------------------------------ save mesh for Paraview
84 |
85 | print("Saving to ../cache/case_3a.vtk")
86 |
87 | jigsawpy.savevtk(os.path.join(
88 | dst_path, "case_3a.vtk"), hraw)
89 |
90 | print("Saving to ../cache/case_3b.vtk")
91 |
92 | jigsawpy.savevtk(os.path.join(
93 | dst_path, "case_3b.vtk"), hlim)
94 |
95 | return
96 |
--------------------------------------------------------------------------------
/tests/case_4_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import time
4 | import math
5 | import numpy as np
6 | from scipy import interpolate
7 |
8 | import jigsawpy
9 |
10 |
11 | def case_4_(src_path, dst_path):
12 |
13 | # DEMO-4: generate a multi-resolution mesh, via local refin-
14 | # ement along coastlines and shallow ridges. Global grid
15 | # resolution is 150KM, background resolution is 67KM and the
16 | # min. adaptive resolution is 33KM.
17 |
18 | opts = jigsawpy.jigsaw_jig_t()
19 |
20 | topo = jigsawpy.jigsaw_msh_t()
21 |
22 | geom = jigsawpy.jigsaw_msh_t()
23 | mesh = jigsawpy.jigsaw_msh_t()
24 | hmat = jigsawpy.jigsaw_msh_t()
25 |
26 | #------------------------------------ setup files for JIGSAW
27 |
28 | opts.geom_file = \
29 | os.path.join(dst_path, "geom.msh")
30 |
31 | opts.jcfg_file = \
32 | os.path.join(dst_path, "opts.jig")
33 |
34 | opts.mesh_file = \
35 | os.path.join(dst_path, "mesh.msh")
36 |
37 | opts.hfun_file = \
38 | os.path.join(dst_path, "spac.msh")
39 |
40 | #------------------------------------ define JIGSAW geometry
41 |
42 | geom.mshID = "ellipsoid-mesh"
43 | geom.radii = np.full(
44 | 3, 6.371E+003, dtype=geom.REALS_t)
45 |
46 | jigsawpy.savemsh(opts.geom_file, geom)
47 |
48 | #------------------------------------ define spacing pattern
49 |
50 | jigsawpy.loadmsh(os.path.join(
51 | src_path, "topo.msh"), topo)
52 |
53 | hmat.mshID = "ellipsoid-grid"
54 | hmat.radii = geom.radii
55 |
56 | hmat.xgrid = topo.xgrid * np.pi / 180.
57 | hmat.ygrid = topo.ygrid * np.pi / 180.
58 |
59 | hfn0 = +150. # global spacing
60 | hfn2 = +33. # adapt. spacing
61 | hfn3 = +67. # arctic spacing
62 |
63 | hmat.value = np.sqrt(
64 | np.maximum(-topo.value, 0.0))
65 |
66 | hmat.value = \
67 | np.maximum(hmat.value, hfn2)
68 | hmat.value = \
69 | np.minimum(hmat.value, hfn3)
70 |
71 | mask = hmat.ygrid < 40. * np.pi / 180.
72 |
73 | hmat.value[mask] = hfn0
74 |
75 | #------------------------------------ set HFUN grad.-limiter
76 |
77 | hmat.slope = np.full( # |dH/dx| limits
78 | topo.value.shape,
79 | +0.050, dtype=hmat.REALS_t)
80 |
81 | jigsawpy.savemsh(opts.hfun_file, hmat)
82 |
83 | jigsawpy.cmd.marche(opts, hmat)
84 |
85 | #------------------------------------ make mesh using JIGSAW
86 |
87 | opts.hfun_scal = "absolute"
88 | opts.hfun_hmax = float("inf") # null HFUN limits
89 | opts.hfun_hmin = float(+0.00)
90 |
91 | opts.mesh_dims = +2 # 2-dim. simplexes
92 |
93 | opts.optm_qlim = +9.5E-01 # tighter opt. tol
94 | opts.optm_iter = +32
95 | opts.optm_qtol = +1.0E-05
96 |
97 | # opts.optm_kern = "cvt+dqdx"
98 |
99 | rbar = np.mean(geom.radii) # bisect heuristic
100 | hbar = np.mean(hmat.value)
101 | nlev = round(math.log2(
102 | rbar / math.sin(.4 * math.pi) / hbar)
103 | )
104 |
105 | ttic = time.time()
106 |
107 | jigsawpy.cmd.tetris(opts, nlev - 1, mesh)
108 |
109 | ttoc = time.time()
110 |
111 | print("CPUSEC =", (ttoc - ttic))
112 |
113 | print("BISECT =", +nlev)
114 |
115 | cost = jigsawpy.triscr2( # quality metrics!
116 | mesh.point["coord"],
117 | mesh.tria3["index"])
118 |
119 | print("TRISCR =", np.min(cost), np.mean(cost))
120 |
121 | cost = jigsawpy.pwrscr2(
122 | mesh.point["coord"],
123 | mesh.power,
124 | mesh.tria3["index"])
125 |
126 | print("PWRSCR =", np.min(cost), np.mean(cost))
127 |
128 | tbad = jigsawpy.centre2(
129 | mesh.point["coord"],
130 | mesh.power,
131 | mesh.tria3["index"])
132 |
133 | print("OBTUSE =",
134 | +np.count_nonzero(np.logical_not(tbad)))
135 |
136 | ndeg = jigsawpy.trideg2(
137 | mesh.point["coord"],
138 | mesh.tria3["index"])
139 |
140 | print("TOPOL. =",
141 | +np.count_nonzero(ndeg==+6) / ndeg.size)
142 |
143 | #------------------------------------ save mesh for Paraview
144 |
145 | apos = jigsawpy.R3toS2(
146 | geom.radii, mesh.point["coord"][:])
147 |
148 | apos = apos * 180. / np.pi
149 |
150 | zfun = interpolate.RectBivariateSpline(
151 | topo.ygrid, topo.xgrid, topo.value)
152 |
153 | mesh.value = zfun(
154 | apos[:, 1], apos[:, 0], grid=False)
155 |
156 | cell = mesh.tria3["index"]
157 |
158 | zmsk = \
159 | mesh.value[cell[:, 0]] + \
160 | mesh.value[cell[:, 1]] + \
161 | mesh.value[cell[:, 2]]
162 | zmsk = zmsk / +3.0
163 |
164 | mesh.tria3 = mesh.tria3[zmsk < +0.]
165 |
166 | print("Saving to ../cache/case_4a.vtk")
167 |
168 | jigsawpy.savevtk(os.path.join(
169 | dst_path, "case_4a.vtk"), mesh)
170 |
171 | print("Saving to ../cache/case_4b.vtk")
172 |
173 | jigsawpy.savevtk(os.path.join(
174 | dst_path, "case_4b.vtk"), hmat)
175 |
176 | return
177 |
--------------------------------------------------------------------------------
/tests/case_5_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import time
4 | import numpy as np
5 | from scipy import interpolate
6 |
7 | import jigsawpy
8 |
9 |
10 | def case_5_(src_path, dst_path):
11 |
12 | # DEMO-5: generate struct. icosahedral and cubedsphere grids
13 |
14 | opts = jigsawpy.jigsaw_jig_t()
15 |
16 | topo = jigsawpy.jigsaw_msh_t()
17 |
18 | geom = jigsawpy.jigsaw_msh_t()
19 | icos = jigsawpy.jigsaw_msh_t()
20 | cube = jigsawpy.jigsaw_msh_t()
21 |
22 | #------------------------------------ setup files for JIGSAW
23 |
24 | opts.geom_file = \
25 | os.path.join(dst_path, "geom.msh")
26 |
27 | opts.jcfg_file = \
28 | os.path.join(dst_path, "opts.jig")
29 |
30 | opts.mesh_file = \
31 | os.path.join(dst_path, "mesh.msh")
32 |
33 | #------------------------------------ define JIGSAW geometry
34 |
35 | geom.mshID = "ellipsoid-mesh"
36 | geom.radii = np.full(
37 | 3, 1.000E+000, dtype=geom.REALS_t)
38 |
39 | jigsawpy.savemsh(opts.geom_file, geom)
40 |
41 | #------------------------------------ make mesh using JIGSAW
42 |
43 | opts.hfun_hmax = +1.
44 | opts.mesh_dims = +2 # 2-dim. simplexes
45 |
46 | opts.optm_iter = +512
47 | opts.optm_qtol = +1.0E-06
48 |
49 | # opts.optm_kern = "cvt+dqdx"
50 |
51 | ttic = time.time()
52 |
53 | jigsawpy.cmd.icosahedron(opts, +6, icos)
54 |
55 | ttoc = time.time()
56 |
57 | print("CPUSEC =", (ttoc - ttic))
58 |
59 | ttic = time.time()
60 |
61 | jigsawpy.cmd.cubedsphere(opts, +6, cube)
62 |
63 | ttoc = time.time()
64 |
65 | print("CPUSEC =", (ttoc - ttic))
66 |
67 | #------------------------------------ save mesh for Paraview
68 |
69 | jigsawpy.loadmsh(os.path.join(
70 | src_path, "topo.msh"), topo)
71 |
72 | #------------------------------------ a very rough land mask
73 |
74 | zfun = interpolate.RectBivariateSpline(
75 | topo.ygrid, topo.xgrid, topo.value)
76 |
77 | apos = jigsawpy.R3toS2(
78 | geom.radii, icos.point["coord"][:])
79 |
80 | apos = apos * 180. / np.pi
81 |
82 | icos.value = zfun(
83 | apos[:, 1], apos[:, 0], grid=False)
84 |
85 | cell = icos.tria3["index"]
86 |
87 | zmsk = \
88 | icos.value[cell[:, 0]] + \
89 | icos.value[cell[:, 1]] + \
90 | icos.value[cell[:, 2]]
91 | zmsk = zmsk / +3.0
92 |
93 | icos.tria3 = icos.tria3[zmsk < +0.]
94 |
95 | print("Saving to ../cache/case_5a.vtk")
96 |
97 | jigsawpy.savevtk(os.path.join(
98 | dst_path, "case_5a.vtk"), icos)
99 |
100 | #------------------------------------ a very rough land mask
101 |
102 | apos = jigsawpy.R3toS2(
103 | geom.radii, cube.point["coord"][:])
104 |
105 | apos = apos * 180. / np.pi
106 |
107 | cube.value = zfun(
108 | apos[:, 1], apos[:, 0], grid=False)
109 |
110 | cell = cube.quad4["index"]
111 |
112 | zmsk = \
113 | cube.value[cell[:, 0]] + \
114 | cube.value[cell[:, 1]] + \
115 | cube.value[cell[:, 2]] + \
116 | cube.value[cell[:, 3]]
117 | zmsk = zmsk / +4.0
118 |
119 | cube.quad4 = cube.quad4[zmsk < +0.]
120 |
121 | print("Saving to ../cache/case_5b.vtk")
122 |
123 | jigsawpy.savevtk(os.path.join(
124 | dst_path, "case_5b.vtk"), cube)
125 |
126 | return
127 |
--------------------------------------------------------------------------------
/tests/case_6_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import time
4 | import numpy as np
5 |
6 | import jigsawpy
7 |
8 |
9 | def case_6_(src_path, dst_path):
10 |
11 | # DEMO-6: generate a 2-dim. grid for the Australian coastal
12 | # region, using scaled ocean-depth as a mesh-resolution
13 | # heuristic. A local stereographic projection is employed.
14 |
15 | opts = jigsawpy.jigsaw_jig_t()
16 |
17 | topo = jigsawpy.jigsaw_msh_t()
18 |
19 | geom = jigsawpy.jigsaw_msh_t()
20 | mesh = jigsawpy.jigsaw_msh_t()
21 | hmat = jigsawpy.jigsaw_msh_t()
22 |
23 | proj = jigsawpy.jigsaw_prj_t()
24 |
25 | #------------------------------------ setup files for JIGSAW
26 |
27 | opts.geom_file = \
28 | os.path.join(dst_path, "geom.msh")
29 |
30 | opts.jcfg_file = \
31 | os.path.join(dst_path, "aust.jig")
32 |
33 | opts.mesh_file = \
34 | os.path.join(dst_path, "mesh.msh")
35 |
36 | opts.hfun_file = \
37 | os.path.join(dst_path, "spac.msh")
38 |
39 | #------------------------------------ define JIGSAW geometry
40 |
41 | jigsawpy.loadmsh(os.path.join(
42 | src_path, "aust.msh"), geom)
43 |
44 | jigsawpy.loadmsh(os.path.join(
45 | src_path, "topo.msh"), topo)
46 |
47 | xmin = np.min(
48 | geom.point["coord"][:, 0])
49 | ymin = np.min(
50 | geom.point["coord"][:, 1])
51 | xmax = np.max(
52 | geom.point["coord"][:, 0])
53 | ymax = np.max(
54 | geom.point["coord"][:, 1])
55 |
56 | zlev = topo.value
57 |
58 | xmsk = np.logical_and(topo.xgrid > xmin,
59 | topo.xgrid < xmax)
60 | ymsk = np.logical_and(topo.ygrid > ymin,
61 | topo.ygrid < ymax)
62 |
63 | zlev = zlev[:, xmsk]
64 | zlev = zlev[ymsk, :]
65 |
66 | #------------------------------------ define spacing pattern
67 |
68 | hmat.mshID = "ellipsoid-grid"
69 | hmat.radii = np.full(
70 | +3, +6371.0,
71 | dtype=jigsawpy.jigsaw_msh_t.REALS_t)
72 |
73 | hmat.xgrid = \
74 | topo.xgrid[xmsk] * np.pi / 180.
75 | hmat.ygrid = \
76 | topo.ygrid[ymsk] * np.pi / 180.
77 |
78 | hmin = +1.0E+01; hmax = +1.0E+02
79 |
80 | hmat.value = \
81 | np.sqrt(np.maximum(-zlev, 0.)) / 0.5
82 |
83 | hmat.value = \
84 | np.maximum(hmat.value, hmin)
85 | hmat.value = \
86 | np.minimum(hmat.value, hmax)
87 |
88 | hmat.slope = np.full(
89 | hmat.value.shape, +0.1500,
90 | dtype=jigsawpy.jigsaw_msh_t.REALS_t)
91 |
92 | #------------------------------------ do stereographic proj.
93 |
94 | geom.point["coord"][:, :] *= np.pi / 180.
95 |
96 | proj.prjID = 'stereographic'
97 | proj.radii = +6.371E+003
98 | proj.xbase = \
99 | +0.500 * (xmin + xmax) * np.pi / 180.
100 | proj.ybase = \
101 | +0.500 * (ymin + ymax) * np.pi / 180.
102 |
103 | jigsawpy.project(geom, proj, "fwd")
104 | jigsawpy.project(hmat, proj, "fwd")
105 |
106 | jigsawpy.savemsh(opts.geom_file, geom)
107 | jigsawpy.savemsh(opts.hfun_file, hmat)
108 |
109 | #------------------------------------ set HFUN grad.-limiter
110 |
111 | jigsawpy.cmd.marche(opts, hmat)
112 |
113 | #------------------------------------ make mesh using JIGSAW
114 |
115 | opts.hfun_scal = "absolute"
116 | opts.hfun_hmax = float("inf") # null HFUN limits
117 | opts.hfun_hmin = float(+0.00)
118 |
119 | opts.mesh_dims = +2 # 2-dim. simplexes
120 | opts.mesh_eps1 = +1.
121 |
122 | ttic = time.time()
123 |
124 | jigsawpy.cmd.jigsaw(opts, mesh)
125 |
126 | ttoc = time.time()
127 |
128 | print("CPUSEC =", (ttoc - ttic))
129 |
130 | cost = jigsawpy.triscr2( # quality metrics!
131 | mesh.point["coord"],
132 | mesh.tria3["index"])
133 |
134 | print("TRISCR =", np.min(cost), np.mean(cost))
135 |
136 | cost = jigsawpy.pwrscr2(
137 | mesh.point["coord"],
138 | mesh.power,
139 | mesh.tria3["index"])
140 |
141 | print("PWRSCR =", np.min(cost), np.mean(cost))
142 |
143 | tbad = jigsawpy.centre2(
144 | mesh.point["coord"],
145 | mesh.power,
146 | mesh.tria3["index"])
147 |
148 | print("OBTUSE =",
149 | +np.count_nonzero(np.logical_not(tbad)))
150 |
151 | #------------------------------------ save mesh for Paraview
152 |
153 | print("Saving to ../cache/case_6a.vtk")
154 |
155 | jigsawpy.savevtk(os.path.join(
156 | dst_path, "case_6a.vtk"), mesh)
157 |
158 | print("Saving to ../cache/case_6b.vtk")
159 |
160 | jigsawpy.savevtk(os.path.join(
161 | dst_path, "case_6b.vtk"), hmat)
162 |
163 | return
164 |
--------------------------------------------------------------------------------
/tests/case_7_.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import time
4 | import numpy as np
5 |
6 | import jigsawpy
7 |
8 |
9 | def case_7_(src_path, dst_path):
10 |
11 | # DEMO-7: generate a multi-part mesh of the (contiguous) USA
12 | # using state boundaries to partition the mesh. A local
13 | # stereographic projection is employed. The domain is meshed
14 | # using uniform resolution.
15 |
16 | opts = jigsawpy.jigsaw_jig_t()
17 |
18 | geom = jigsawpy.jigsaw_msh_t()
19 | mesh = jigsawpy.jigsaw_msh_t()
20 |
21 | proj = jigsawpy.jigsaw_prj_t()
22 |
23 | #------------------------------------ setup files for JIGSAW
24 |
25 | opts.geom_file = \
26 | os.path.join(dst_path, "geom.msh")
27 |
28 | opts.jcfg_file = \
29 | os.path.join(dst_path, "us48.jig")
30 |
31 | opts.mesh_file = \
32 | os.path.join(dst_path, "mesh.msh")
33 |
34 | #------------------------------------ define JIGSAW geometry
35 |
36 | jigsawpy.loadmsh(os.path.join(
37 | src_path, "us48.msh"), geom)
38 |
39 | xmin = np.min(
40 | geom.point["coord"][:, 0])
41 | ymin = np.min(
42 | geom.point["coord"][:, 1])
43 | xmax = np.max(
44 | geom.point["coord"][:, 0])
45 | ymax = np.max(
46 | geom.point["coord"][:, 1])
47 |
48 | geom.point["coord"][:, :] *= np.pi / 180.
49 |
50 | proj.prjID = 'stereographic'
51 | proj.radii = +6.371E+003
52 | proj.xbase = \
53 | +0.500 * (xmin + xmax) * np.pi / 180.
54 | proj.ybase = \
55 | +0.500 * (ymin + ymax) * np.pi / 180.
56 |
57 | jigsawpy.project(geom, proj, "fwd")
58 |
59 | jigsawpy.savemsh(opts.geom_file, geom)
60 |
61 | #------------------------------------ make mesh using JIGSAW
62 |
63 | opts.hfun_hmax = .005
64 |
65 | opts.mesh_dims = +2 # 2-dim. simplexes
66 | opts.mesh_eps1 = +1 / 6.
67 |
68 | ttic = time.time()
69 |
70 | jigsawpy.cmd.jigsaw(opts, mesh)
71 |
72 | ttoc = time.time()
73 |
74 | print("CPUSEC =", (ttoc - ttic))
75 |
76 | cost = jigsawpy.triscr2( # quality metrics!
77 | mesh.point["coord"],
78 | mesh.tria3["index"])
79 |
80 | print("TRISCR =", np.min(cost), np.mean(cost))
81 |
82 | cost = jigsawpy.pwrscr2(
83 | mesh.point["coord"],
84 | mesh.power,
85 | mesh.tria3["index"])
86 |
87 | print("PWRSCR =", np.min(cost), np.mean(cost))
88 |
89 | tbad = jigsawpy.centre2(
90 | mesh.point["coord"],
91 | mesh.power,
92 | mesh.tria3["index"])
93 |
94 | print("OBTUSE =",
95 | +np.count_nonzero(np.logical_not(tbad)))
96 |
97 | #------------------------------------ save mesh for Paraview
98 |
99 | print("Saving to ../cache/case_7a.vtk")
100 |
101 | jigsawpy.savevtk(os.path.join(
102 | dst_path, "case_7a.vtk"), mesh)
103 |
104 | return
105 |
--------------------------------------------------------------------------------