├── cache └── .keep ├── tests ├── __init__.py ├── case_3_.py ├── case_7_.py ├── case_5_.py ├── case_1_.py ├── case_2_.py ├── case_6_.py └── case_4_.py ├── image └── voro.jpg ├── setup.cfg ├── .gitignore ├── LICENSE.md ├── example.py └── README.md /cache/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /image/voro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengwirda/jigsaw-geo-python/HEAD/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /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_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_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_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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## `JIGSAW(GEO): Mesh generation for geoscientific modelling` 2 | 3 |
4 |
5 |