@@ -666,10 +666,115 @@ USMesh *mesh_create_from_zarr(USFile *file) {
666666
667667#endif /* HAVE_ZARR */
668668
669+ /* Try to load MPAS dual-mesh connectivity (cellsOnVertex).
670+ * In MPAS, each Voronoi vertex connects exactly 3 cells.
671+ * cellsOnVertex(nVertices, vertexDegree=3) gives triangle connectivity
672+ * where the "vertices" of each triangle are cell centers (our mesh points).
673+ * Boundary triangles referencing cell 0 (invalid in 1-based) are skipped.
674+ */
675+ static int load_mpas_connectivity (USMesh * mesh , int ncid ) {
676+ int varid , status ;
677+
678+ status = nc_inq_varid (ncid , "cellsOnVertex" , & varid );
679+ if (status != NC_NOERR ) return -1 ;
680+
681+ int ndims ;
682+ nc_inq_varndims (ncid , varid , & ndims );
683+ if (ndims != 2 ) return -1 ;
684+
685+ int dimids [2 ];
686+ size_t dim_sizes [2 ];
687+ nc_inq_vardimid (ncid , varid , dimids );
688+ nc_inq_dimlen (ncid , dimids [0 ], & dim_sizes [0 ]);
689+ nc_inq_dimlen (ncid , dimids [1 ], & dim_sizes [1 ]);
690+
691+ /* Expect (nVertices, vertexDegree=3) */
692+ size_t n_tri_raw , vdeg ;
693+ if (dim_sizes [1 ] == 3 ) {
694+ n_tri_raw = dim_sizes [0 ];
695+ vdeg = dim_sizes [1 ];
696+ } else if (dim_sizes [0 ] == 3 ) {
697+ n_tri_raw = dim_sizes [1 ];
698+ vdeg = dim_sizes [0 ];
699+ } else {
700+ return -1 ; /* Not a triangle dual mesh */
701+ }
702+
703+ int * raw = malloc (n_tri_raw * vdeg * sizeof (int ));
704+ if (!raw ) return -1 ;
705+
706+ status = nc_get_var_int (ncid , varid , raw );
707+ if (status != NC_NOERR ) {
708+ free (raw );
709+ return -1 ;
710+ }
711+
712+ int transpose = (dim_sizes [0 ] == 3 );
713+
714+ /* First pass: count valid triangles (skip those referencing cell 0 = boundary) */
715+ size_t n_valid = 0 ;
716+ for (size_t i = 0 ; i < n_tri_raw ; i ++ ) {
717+ int v0 , v1 , v2 ;
718+ if (transpose ) {
719+ v0 = raw [0 * n_tri_raw + i ];
720+ v1 = raw [1 * n_tri_raw + i ];
721+ v2 = raw [2 * n_tri_raw + i ];
722+ } else {
723+ v0 = raw [i * 3 + 0 ];
724+ v1 = raw [i * 3 + 1 ];
725+ v2 = raw [i * 3 + 2 ];
726+ }
727+ if (v0 > 0 && v1 > 0 && v2 > 0 ) n_valid ++ ;
728+ }
729+
730+ int * elem_nodes = malloc (n_valid * 3 * sizeof (int ));
731+ if (!elem_nodes ) {
732+ free (raw );
733+ return -1 ;
734+ }
735+
736+ /* Second pass: store valid triangles, convert to 0-based */
737+ size_t out = 0 ;
738+ for (size_t i = 0 ; i < n_tri_raw ; i ++ ) {
739+ int v0 , v1 , v2 ;
740+ if (transpose ) {
741+ v0 = raw [0 * n_tri_raw + i ];
742+ v1 = raw [1 * n_tri_raw + i ];
743+ v2 = raw [2 * n_tri_raw + i ];
744+ } else {
745+ v0 = raw [i * 3 + 0 ];
746+ v1 = raw [i * 3 + 1 ];
747+ v2 = raw [i * 3 + 2 ];
748+ }
749+ if (v0 > 0 && v1 > 0 && v2 > 0 ) {
750+ /* Also validate indices are within range */
751+ if ((size_t )(v0 - 1 ) < mesh -> n_points &&
752+ (size_t )(v1 - 1 ) < mesh -> n_points &&
753+ (size_t )(v2 - 1 ) < mesh -> n_points ) {
754+ elem_nodes [out * 3 + 0 ] = v0 - 1 ;
755+ elem_nodes [out * 3 + 1 ] = v1 - 1 ;
756+ elem_nodes [out * 3 + 2 ] = v2 - 1 ;
757+ out ++ ;
758+ }
759+ }
760+ }
761+
762+ free (raw );
763+
764+ mesh -> n_elements = out ;
765+ mesh -> n_vertices = 3 ;
766+ mesh -> elem_nodes = elem_nodes ;
767+
768+ printf ("Loaded MPAS dual-mesh connectivity: %zu triangles from cellsOnVertex "
769+ "(%zu raw, %zu boundary skipped)\n" ,
770+ out , n_tri_raw , n_tri_raw - out );
771+ return 0 ;
772+ }
773+
669774/* Load element connectivity from mesh file for polygon rendering */
670775static int load_element_connectivity (USMesh * mesh , int ncid ) {
671776 int status , varid ;
672-
777+
673778 /* Try to find face_nodes variable (UGRID convention) */
674779 static const char * CONN_NAMES [] = {
675780 "face_nodes" , "face_node_connectivity" , "elem" , NULL
@@ -683,23 +788,24 @@ static int load_element_connectivity(USMesh *mesh, int ncid) {
683788 }
684789 }
685790 if (!found_conn ) {
686- return -1 ; /* No connectivity found - not an error, just no polygon mode */
791+ /* Try MPAS dual-mesh connectivity (cellsOnVertex) */
792+ return load_mpas_connectivity (mesh , ncid );
687793 }
688-
794+
689795 /* Get dimensions */
690796 int ndims ;
691797 nc_inq_varndims (ncid , varid , & ndims );
692798 if (ndims != 2 ) {
693799 fprintf (stderr , "face_nodes: expected 2D, got %dD\n" , ndims );
694800 return -1 ;
695801 }
696-
802+
697803 int dimids [2 ];
698804 size_t dim_sizes [2 ];
699805 nc_inq_vardimid (ncid , varid , dimids );
700806 nc_inq_dimlen (ncid , dimids [0 ], & dim_sizes [0 ]);
701807 nc_inq_dimlen (ncid , dimids [1 ], & dim_sizes [1 ]);
702-
808+
703809 /* Determine which dimension is vertices (n3=3) vs elements */
704810 size_t n_vertices , n_elements ;
705811 int transpose = 0 ;
@@ -715,32 +821,32 @@ static int load_element_connectivity(USMesh *mesh, int ncid) {
715821 fprintf (stderr , "face_nodes: cannot identify vertex dimension\n" );
716822 return -1 ;
717823 }
718-
824+
719825 printf ("Loading element connectivity: %zu elements, %zu vertices each\n" ,
720826 n_elements , n_vertices );
721-
827+
722828 /* Allocate and read connectivity */
723829 int * raw_data = malloc (n_elements * n_vertices * sizeof (int ));
724830 if (!raw_data ) return -1 ;
725-
831+
726832 status = nc_get_var_int (ncid , varid , raw_data );
727833 if (status != NC_NOERR ) {
728834 fprintf (stderr , "Failed to read face_nodes: %s\n" , nc_strerror (status ));
729835 free (raw_data );
730836 return -1 ;
731837 }
732-
838+
733839 /* Get start_index attribute (1-based or 0-based indexing) */
734840 int start_index = 1 ; /* Default to 1-based (Fortran) */
735841 nc_get_att_int (ncid , varid , "start_index" , & start_index );
736-
842+
737843 /* Allocate final array and transpose if needed */
738844 mesh -> elem_nodes = malloc (n_elements * n_vertices * sizeof (int ));
739845 if (!mesh -> elem_nodes ) {
740846 free (raw_data );
741847 return -1 ;
742848 }
743-
849+
744850 if (transpose ) {
745851 /* Data is (n3, elem), transpose to (elem, n3) and convert to 0-based */
746852 for (size_t e = 0 ; e < n_elements ; e ++ ) {
@@ -755,12 +861,12 @@ static int load_element_connectivity(USMesh *mesh, int ncid) {
755861 mesh -> elem_nodes [i ] = raw_data [i ] - start_index ;
756862 }
757863 }
758-
864+
759865 free (raw_data );
760-
866+
761867 mesh -> n_elements = n_elements ;
762868 mesh -> n_vertices = (int )n_vertices ;
763-
869+
764870 printf ("Loaded %zu triangular elements for polygon rendering\n" , n_elements );
765871 return 0 ;
766872}
0 commit comments