MOAB: Mesh Oriented datABase
(version 5.4.1)
|
Use direct access to MOAB data to avoid calling through API, in Fortran90
This example creates a 1d row of quad elements, such that all quad and vertex handles are contiguous in the handle space and in the database. Then it shows how to get access to pointers to MOAB-native data for vertex coordinates, quad connectivity, and tag storage (vertex to quad adjacency lists aren't accessible from Fortran because they are std::vector's). This allows applications to access this data directly without going through MOAB's API. In cases where the mesh is not changing (or only mesh vertices are moving), this can save significant execution time in applications.
Using direct access (or MOAB in general) from Fortran is complicated in two specific ways: 1) There is no way to use arrays with specified dimension starting/ending values with ISO_C_BINDING; therefore, all arrays passed back from MOAB/iMesh must use 1-based indices; this makes it a bit more difficult to traverse the direct arrays. In this example, I explicitly use indices that look like my_array(1+v_ind...) to remind users of this. 2) Arithmetic on handles is complicated by the fact that Fortran has no unsigned integer type. I get around this by assigning to a C-typed variable first, before the handle arithmetic. This seems to work fine. C-typed variables are part of the Fortran95 standard.
---------------------- | | | | | | | | ... | | | | ----------------------
To compile:
make DirectAccessNoHolesF90 MOAB_DIR=<installdir>
To run: ./DirectAccessNoHolesF90 [-nquads <# quads>]
VSM: Note that IBM xlf compilers do not like dynamic sizing functions for C_F_POINTER calls. This leads to internal compiler failure. http://www-01.ibm.com/support/docview.wss?uid=swg1IV49428
00001 !> @example DirectAccessNoHolesF90.F90 00002 !! \brief Use direct access to MOAB data to avoid calling through API, in Fortran90 \n 00003 !! 00004 !! This example creates a 1d row of quad elements, such that all quad and vertex handles 00005 !! are contiguous in the handle space and in the database. Then it shows how to get access 00006 !! to pointers to MOAB-native data for vertex coordinates, quad connectivity, and tag storage 00007 !! (vertex to quad adjacency lists aren't accessible from Fortran because they are std::vector's). 00008 !! This allows applications to access this data directly 00009 !! without going through MOAB's API. In cases where the mesh is not changing (or only mesh 00010 !! vertices are moving), this can save significant execution time in applications. 00011 !! 00012 !! Using direct access (or MOAB in general) from Fortran is complicated in two specific ways: 00013 !! 1) There is no way to use arrays with specified dimension starting/ending values with ISO_C_BINDING; 00014 !! therefore, all arrays passed back from MOAB/iMesh must use 1-based indices; this makes it a bit 00015 !! more difficult to traverse the direct arrays. In this example, I explicitly use indices that 00016 !! look like my_array(1+v_ind...) to remind users of this. 00017 !! 2) Arithmetic on handles is complicated by the fact that Fortran has no unsigned integer type. I get 00018 !! around this by assigning to a C-typed variable first, before the handle arithmetic. This seems to 00019 !! work fine. C-typed variables are part of the Fortran95 standard. 00020 !! 00021 !! ---------------------- 00022 !! | | | | 00023 !! | | | | ... 00024 !! | | | | 00025 !! ---------------------- 00026 !! 00027 !! -# Initialize MOAB \n 00028 !! -# Create a quad mesh, as depicted above 00029 !! -# Create 2 dense tags (tag1, tag2) for avg position to assign to quads, and # verts per quad (tag3) 00030 !! -# Get connectivity, coordinate, tag1 iterators 00031 !! -# Iterate through quads, computing midpoint based on vertex positions, set on quad-based tag1 00032 !! -# Iterate through vertices, summing positions into tag2 on connected quads and incrementing vertex count 00033 !! -# Iterate through quads, normalizing tag2 by vertex count and comparing values of tag1 and tag2 00034 !! 00035 !! <b>To compile</b>: \n 00036 !! make DirectAccessNoHolesF90 MOAB_DIR=<installdir> \n 00037 !! <b>To run</b>: ./DirectAccessNoHolesF90 [-nquads <# quads>]\n 00038 !! 00039 !! VSM: Note that IBM xlf compilers do not like dynamic sizing functions for 00040 !! C_F_POINTER calls. This leads to internal compiler failure. 00041 !! http://www-01.ibm.com/support/docview.wss?uid=swg1IV49428 00042 !! 00043 #define CHECK(a) if (a .ne. iBase_SUCCESS) call exit(a) 00044 00045 program DirectAccessNoHolesF90 00046 use, intrinsic :: ISO_C_BINDING 00047 implicit none 00048 #include "iMesh_f.h" 00049 00050 ! declarations 00051 iMesh_Instance :: imesh 00052 iBase_EntitySetHandle root_set 00053 integer :: nverts, tmpflag 00054 integer ierr, nquads, nquads_tmp 00055 iBase_TagHandle tag1h, tag2h 00056 TYPE(C_PTR) verts_ptr, quads_ptr, conn_ptr, x_ptr, y_ptr, z_ptr, tag1_ptr, tag2_ptr 00057 TYPE(C_PTR) tmp_quads_ptr, offsets_ptr ! pointers that are equivalence'd to arrays using c_f_pointer 00058 real(C_DOUBLE), pointer :: x(:), y(:), z(:), tag1(:), tag2(:) ! arrays equivalence'd to pointers 00059 integer, pointer :: offsets(:) ! arrays equivalence'd to pointers 00060 iBase_EntityHandle, pointer :: quads(:), verts(:), conn(:), tmp_quads(:) ! arrays equivalence'd to pointers 00061 iBase_EntityArrIterator viter, qiter 00062 integer(C_SIZE_T) :: startv, startq, v_ind, e_ind ! needed to do handle arithmetic 00063 integer count, vpere, e, i, j, v, offsets_size, tmp_quads_size, n_dis 00064 character*120 opt 00065 00066 ! initialize interface and get root set 00067 call iMesh_newMesh("", imesh, ierr) 00068 CHECK(ierr) 00069 call iMesh_getRootSet(%val(imesh), root_set, ierr) 00070 CHECK(ierr) 00071 00072 ! create mesh 00073 nquads_tmp = 1000 00074 call create_mesh_no_holes(imesh, nquads_tmp, ierr) 00075 CHECK(ierr) 00076 00077 ! get all vertices and quads as arrays 00078 nverts = 0 00079 call iMesh_getEntities(%val(imesh), %val(root_set), %val(0), %val(iBase_VERTEX), & 00080 verts_ptr, nverts, nverts, ierr) 00081 CHECK(ierr) 00082 call c_f_pointer(verts_ptr, verts, [nverts]) 00083 nquads = 0 00084 call iMesh_getEntities(%val(imesh), %val(root_set), %val(2), %val(iMesh_QUADRILATERAL), & 00085 quads_ptr, nquads, nquads, ierr) 00086 CHECK(ierr) 00087 call c_f_pointer(quads_ptr, quads, [nquads]) 00088 00089 ! First vertex/quad is at start of vertex/quad list, and is offset for vertex/quad index calculation 00090 startv = verts(1) 00091 startq = quads(1) 00092 call iMesh_freeMemory(%val(imesh), quads_ptr) 00093 00094 ! create tag1 (element-based avg), tag2 (vertex-based avg) 00095 opt = 'moab:TAG_STORAGE_TYPE=DENSE moab:TAG_DEFAULT_valUE=0' 00096 call iMesh_createTagWithOptions(%val(imesh), 'tag1', opt, %val(3), %val(iBase_DOUBLE), & 00097 tag1h, ierr, %val(4), %val(56)) 00098 CHECK(ierr) 00099 call iMesh_createTagWithOptions(%val(imesh), 'tag2', opt, %val(3), %val(iBase_DOUBLE), & 00100 tag2h, ierr, %val(4), %val(56)) 00101 CHECK(ierr) 00102 00103 ! Get iterator to verts, then pointer to coordinates 00104 call iMesh_initEntArrIter(%val(imesh), %val(root_set), %val(iBase_VERTEX), %val(iMesh_ALL_TOPOLOGIES), & 00105 %val(nverts), %val(0), viter, ierr) 00106 CHECK(ierr) 00107 call iMesh_coordsIterate(%val(imesh), %val(viter), x_ptr, y_ptr, z_ptr, count, ierr) 00108 CHECK(ierr) 00109 CHECK(count-nverts) ! check that all verts are in one contiguous chunk 00110 call c_f_pointer(x_ptr, x, [nverts]) 00111 call c_f_pointer(y_ptr, y, [nverts]) 00112 call c_f_pointer(z_ptr, z, [nverts]) 00113 00114 ! Get iterator to quads, then pointers to connectivity and tags 00115 call iMesh_initEntArrIter(%val(imesh), %val(root_set), %val(iBase_FACE), %val(iMesh_ALL_TOPOLOGIES), & 00116 %val(nquads), %val(0), qiter, ierr) 00117 CHECK(ierr) 00118 call iMesh_connectIterate(%val(imesh), %val(qiter), conn_ptr, vpere, count, ierr) 00119 CHECK(ierr) 00120 call c_f_pointer(conn_ptr, conn, [vpere*nquads]) 00121 call iMesh_tagIterate(%val(imesh), %val(tag1h), %val(qiter), tag1_ptr, count, ierr) 00122 CHECK(ierr) 00123 call c_f_pointer(tag1_ptr, tag1, [3*nquads]) 00124 call iMesh_tagIterate(%val(imesh), %val(tag2h), %val(qiter), tag2_ptr, count, ierr) 00125 CHECK(ierr) 00126 call c_f_pointer(tag2_ptr, tag2, [3*nquads]) 00127 00128 ! iterate over elements, computing tag1 from coords positions; 00129 ! use (1+... in array indices for 1-based indexing 00130 do i = 0, nquads-1 00131 tag1(1+3*i+0) = 0.0 00132 tag1(1+3*i+1) = 0.0 00133 tag1(1+3*i+2) = 0.0 ! initialize position for this element 00134 do j = 0, vpere-1 ! loop over vertices in this quad 00135 v_ind = conn(1+vpere*i+j) ! assign to v_ind to allow handle arithmetic 00136 v_ind = v_ind - startv ! vert index is just the offset from start vertex 00137 tag1(1+3*i+0) = tag1(1+3*i+0) + x(1+v_ind) 00138 tag1(1+3*i+1) = tag1(1+3*i+1) + y(1+v_ind) ! sum vertex positions into tag1... 00139 tag1(1+3*i+2) = tag1(1+3*i+2) + z(1+v_ind) 00140 end do 00141 tag1(1+3*i+0) = tag1(1+3*i+0) / vpere; 00142 tag1(1+3*i+1) = tag1(1+3*i+1) / vpere ! then normalize 00143 tag1(1+3*i+2) = tag1(1+3*i+2) / vpere; 00144 end do ! loop over elements in chunk 00145 00146 ! Iterate through vertices, summing positions into tag2 on connected elements; 00147 ! get adjacencies all at once, 00148 ! could also get them per vertex (time/memory tradeoff) 00149 tmp_quads_size = 0 00150 offsets_size = 0 00151 tmpflag = iMesh_QUADRILATERAL 00152 call iMesh_getEntArrAdj(%val(imesh), verts, %val(nverts), %val(tmpflag), & 00153 tmp_quads_ptr, tmp_quads_size, tmp_quads_size, & 00154 offsets_ptr, offsets_size, offsets_size, ierr) 00155 CHECK(ierr) 00156 00157 call c_f_pointer(tmp_quads_ptr, tmp_quads, [tmp_quads_size]) 00158 call c_f_pointer(offsets_ptr, offsets, [offsets_size]) 00159 call iMesh_freeMemory(%val(imesh), verts_ptr) 00160 do v = 0, nverts-1 00161 do e = 1+offsets(1+v), 1+offsets(1+v+1)-1 ! 1+ because offsets are in terms of 0-based arrays 00162 e_ind = tmp_quads(e) ! assign to e_ind to allow handle arithmetic 00163 e_ind = e_ind - startq ! e_ind is the quad handle minus the starting quad handle 00164 tag2(1+3*e_ind+0) = tag2(1+3*e_ind+0) + x(1+v) 00165 tag2(1+3*e_ind+1) = tag2(1+3*e_ind+1) + y(1+v) ! tag on each element is 3 doubles, x/y/z 00166 tag2(1+3*e_ind+2) = tag2(1+3*e_ind+2) + z(1+v) 00167 end do 00168 end do 00169 call iMesh_freeMemory(%val(imesh), tmp_quads_ptr) 00170 call iMesh_freeMemory(%val(imesh), offsets_ptr) 00171 00172 ! Normalize tag2 by vertex count (vpere); 00173 ! loop over elements using same approach as before 00174 ! At the same time, compare values of tag1 and tag2 00175 n_dis = 0 00176 do e = 0, nquads-1 00177 do j = 0, 2 00178 tag2(1+3*e+j) = tag2(1+3*e+j) / vpere ! normalize by # verts 00179 end do 00180 if (tag1(1+3*e) .ne. tag2(1+3*e) .or. tag1(1+3*e+1) .ne. tag2(1+3*e+1) .or. tag1(1+3*e+2) .ne. tag2(1+3*e+2)) then 00181 print *, "Tag1, tag2 disagree for element ", e 00182 print *, "tag1: ", tag1(1+3*e), tag1(1+3*e+1), tag1(1+3*e+2) 00183 print *, "tag2: ", tag2(1+3*e), tag2(1+3*e+1), tag2(1+3*e+2) 00184 n_dis = n_dis + 1 00185 endif 00186 end do 00187 if (n_dis .eq. 0) then 00188 print *, 'All tags agree, success!' 00189 endif 00190 00191 ! Ok, we are done, shut down MOAB 00192 call iMesh_dtor(%val(imesh), ierr) 00193 CHECK(ierr) 00194 end program DirectAccessNoHolesF90 00195 00196 subroutine create_mesh_no_holes(imesh, nquads, ierr) 00197 use, intrinsic :: ISO_C_BINDING 00198 implicit none 00199 #include "iMesh_f.h" 00200 00201 iMesh_Instance imesh 00202 integer nquads, ierr 00203 00204 real(C_DOUBLE), pointer :: coords(:,:) 00205 TYPE(C_PTR) tmp_ents_ptr, stat_ptr 00206 iBase_EntityHandle, pointer :: connect(:), tmp_ents(:) 00207 integer nverts, tmp_size, stat_size, i 00208 00209 ! first make the mesh, a 1d array of quads with left hand side x = elem_num; 00210 ! vertices are numbered in layers 00211 ! create verts, num is 2(nquads+1) because they're in a 1d row 00212 nverts = 2*(nquads+1) 00213 allocate(coords(0:2, 0:nverts-1)) 00214 do i = 0, nquads-1 00215 coords(0,2*i) = i 00216 coords(0,2*i+1) = i ! x values are all i 00217 coords(1,2*i) = 0.0 00218 coords(1,2*i+1) = 1.0 ! y coords 00219 coords(2,2*i) = 0.0 00220 coords(2,2*i+1) = 0.0 ! z values, all zero (2d mesh) 00221 end do 00222 ! last two vertices 00223 coords(0, nverts-2) = nquads 00224 coords(0, nverts-1) = nquads 00225 coords(1, nverts-2) = 0.0 00226 coords(1, nverts-1) = 1.0 ! y coords 00227 coords(2, nverts-2) = 0.0 00228 coords(2, nverts-1) = 0.0 ! z values, all zero (2d mesh) 00229 tmp_size = 0 00230 !!!!!! VSM: This is the culprit call that screws up IBM xlf compiler 00231 call iMesh_createVtxArr(%val(imesh), %val(nverts), %val(iBase_INTERLEAVED), & 00232 coords, %val(3*nverts), tmp_ents_ptr, tmp_size, tmp_size, ierr) 00233 CHECK(ierr) 00234 call c_f_pointer(tmp_ents_ptr, tmp_ents, [nverts]) 00235 deallocate(coords) 00236 allocate(connect(0:4*nquads-1)) 00237 do i = 0, nquads-1 00238 connect(4*i+0) = tmp_ents(1+2*i) 00239 connect(4*i+1) = tmp_ents(1+2*i+2) 00240 connect(4*i+2) = tmp_ents(1+2*i+3) 00241 connect(4*i+3) = tmp_ents(1+2*i+1) 00242 end do 00243 stat_size = 0 00244 stat_ptr = C_NULL_PTR 00245 ! re-use tmp_ents here; 00246 ! we know it'll always be larger than nquads so it's ok 00247 call iMesh_createEntArr(%val(imesh), %val(iMesh_QUADRILATERAL), connect, %val(4*nquads), & 00248 tmp_ents_ptr, tmp_size, tmp_size, stat_ptr, stat_size, stat_size, ierr) 00249 CHECK(ierr) 00250 00251 ierr = iBase_SUCCESS 00252 00253 call iMesh_freeMemory(%val(imesh), tmp_ents_ptr) 00254 call iMesh_freeMemory(%val(imesh), stat_ptr) 00255 deallocate(connect) 00256 00257 return 00258 end subroutine create_mesh_no_holes 00259