def test_connectivity_start_index(self): """Test a mesh where some connectivities have start_index = 1.""" # Make a mesh with both faces *and* some edges mesh = make_mesh(n_edges=7) # Get the face-node and edge-node connectivities face_nodes_conn = mesh.face_node_connectivity edge_nodes_conn = mesh.edge_node_connectivity edge_nodes_conn2 = Connectivity( indices=edge_nodes_conn.indices + 1, cf_role=edge_nodes_conn.cf_role, var_name="edges_x_2", start_index=1, ) # Make a new mesh with altered connectivities. mesh2 = Mesh( topology_dimension=mesh.topology_dimension, node_coords_and_axes=zip(mesh.node_coords, XY_LOCS), face_coords_and_axes=zip(mesh.face_coords, XY_LOCS), connectivities=[face_nodes_conn, edge_nodes_conn2], ) # Save and snapshot the result tempfile_path = self.check_save_mesh(mesh2) dims, vars = scan_dataset(tempfile_path) # Check shape and dimensions of the associated connectivity variables. (mesh_name,) = vars_meshnames(vars) mesh_props = vars[mesh_name] faceconn_name = mesh_props["face_node_connectivity"] edgeconn_name = mesh_props["edge_node_connectivity"] faceconn_props = vars[faceconn_name] edgeconn_props = vars[edgeconn_name] self.assertEqual(faceconn_props["start_index"], 0) self.assertEqual(edgeconn_props["start_index"], 1)
def test_2d_coord(self): cube = simple_2d_w_multidim_coords()[:3, :3] coord_1, coord_2 = cube.coords() with self.assertRaisesRegex( ValueError, "Expected coordinate ndim == 1" ): _ = Mesh.from_coords(coord_1, coord_2)
def test_1_bound(self): lon = AuxCoord(points=[0.5, 1.5, 2.5], bounds=[[0], [1], [2]]) lat = AuxCoord(points=[0, 1, 2], bounds=[[0.5], [1.5], [2.5]]) with self.assertRaisesRegex( ValueError, r"Expected coordinate bounds.shape \(n, >=2\)" ): _ = Mesh.from_coords(lon, lat)
def sample_mesh(n_nodes=None, n_faces=None, n_edges=None): """ Make a test mesh. Mesh has faces edges, face-coords and edge-coords, numbers of which can be controlled. """ if n_nodes is None: n_nodes = _TEST_N_NODES if n_faces is None: n_faces = _TEST_N_FACES if n_edges is None: n_edges = _TEST_N_EDGES node_x = AuxCoord( 1100 + np.arange(n_nodes), standard_name="longitude", units="degrees_east", long_name="long-name", var_name="var-name", attributes={"a": 1, "b": "c"}, ) node_y = AuxCoord(1200 + np.arange(n_nodes), standard_name="latitude") # Define a rather arbitrary edge-nodes connectivity. # Some nodes are left out, because n_edges*2 < n_nodes. conns = np.arange(n_edges * 2, dtype=int) # Missing nodes include #0-5, because we add 5. conns = ((conns + 5) % n_nodes).reshape((n_edges, 2)) edge_nodes = Connectivity(conns, cf_role="edge_node_connectivity") conns = np.arange(n_edges * 2, dtype=int) # Some numbers for the edge coordinates. edge_x = AuxCoord(2100 + np.arange(n_edges), standard_name="longitude") edge_y = AuxCoord(2200 + np.arange(n_edges), standard_name="latitude") # Define a rather arbitrary face-nodes connectivity. # Some nodes are left out, because n_faces*n_bounds < n_nodes. conns = np.arange(n_faces * _TEST_N_BOUNDS, dtype=int) conns = (conns % n_nodes).reshape((n_faces, _TEST_N_BOUNDS)) face_nodes = Connectivity(conns, cf_role="face_node_connectivity") # Some numbers for the edge coordinates. face_x = AuxCoord(3100 + np.arange(n_faces), standard_name="longitude") face_y = AuxCoord(3200 + np.arange(n_faces), standard_name="latitude") mesh = Mesh( topology_dimension=2, node_coords_and_axes=[(node_x, "x"), (node_y, "y")], connectivities=[face_nodes, edge_nodes], edge_coords_and_axes=[(edge_x, "x"), (edge_y, "y")], face_coords_and_axes=[(face_x, "x"), (face_y, "y")], ) return mesh
def test_connectivity_dim_order(self): """ Test a mesh with some connectivities in the 'other' order. This should also create a property with the dimension name. """ # Make a mesh with both faces *and* some edges mesh = make_mesh(n_edges=7) # Get the face-node and edge-node connectivities face_nodes_conn = mesh.face_node_connectivity edge_nodes_conn = mesh.edge_node_connectivity # Transpose them : N.B. this sets src_dim=1, as it should be. nodesfirst_faces_conn = face_nodes_conn.transpose() nodesfirst_edges_conn = edge_nodes_conn.transpose() # Make a new mesh with both face and edge connectivities 'transposed'. mesh2 = Mesh( topology_dimension=mesh.topology_dimension, node_coords_and_axes=zip(mesh.node_coords, XY_LOCS), face_coords_and_axes=zip(mesh.face_coords, XY_LOCS), connectivities=[nodesfirst_faces_conn, nodesfirst_edges_conn], ) # Save and snapshot the result tempfile_path = self.check_save_mesh(mesh2) dims, vars = scan_dataset(tempfile_path) # Check shape and dimensions of the associated connectivity variables. (mesh_name,) = vars_meshnames(vars) mesh_props = vars[mesh_name] faceconn_name = mesh_props["face_node_connectivity"] edgeconn_name = mesh_props["edge_node_connectivity"] faceconn_props = vars[faceconn_name] edgeconn_props = vars[edgeconn_name] self.assertEqual( faceconn_props[_VAR_DIMS], ["Mesh_2d_face_N_nodes", "Mesh2d_face"] ) self.assertEqual( edgeconn_props[_VAR_DIMS], ["Mesh_2d_edge_N_nodes", "Mesh2d_edge"] ) # Check the dimension lengths are also as expected self.assertEqual(dims["Mesh2d_face"], 2) self.assertEqual(dims["Mesh_2d_face_N_nodes"], 4) self.assertEqual(dims["Mesh2d_edge"], 7) self.assertEqual(dims["Mesh_2d_edge_N_nodes"], 2) # the mesh has extra location-dimension properties self.assertEqual(mesh_props["face_dimension"], "Mesh2d_face") self.assertEqual(mesh_props["edge_dimension"], "Mesh2d_edge")
def _make_test_meshcoord( self, lazy_sources=False, location="face", inds_start_index=0, inds_location_axis=0, facenodes_changes=None, ): # Construct a miniature face-nodes mesh for testing. # NOTE: we will make our connectivity arrays with standard # start_index=0 and location_axis=0 : We only adjust that (if required) when # creating the actual connectivities. face_nodes_array = np.array( [ [0, 2, 1, 3], [1, 3, 10, 13], [2, 7, 9, 19], [ 3, 4, 7, -1, ], # This one has a "missing" point (it's a triangle) [8, 1, 7, 2], ] ) # Connectivity uses *masked* for missing points. face_nodes_array = np.ma.masked_less(face_nodes_array, 0) if facenodes_changes: facenodes_changes = facenodes_changes.copy() facenodes_changes.pop("n_extra_bad_points") for indices, value in facenodes_changes.items(): face_nodes_array[indices] = value # Construct a miniature edge-nodes mesh for testing. edge_nodes_array = np.array([[0, 2], [1, 3], [1, 4], [3, 7]]) # Connectivity uses *masked* for missing points. edge_nodes_array = np.ma.masked_less(edge_nodes_array, 0) n_faces = face_nodes_array.shape[0] n_edges = edge_nodes_array.shape[0] n_nodes = int(face_nodes_array.max() + 1) self.NODECOORDS_BASENUM = 1100.0 self.EDGECOORDS_BASENUM = 1200.0 self.FACECOORDS_BASENUM = 1300.0 node_xs = self.NODECOORDS_BASENUM + np.arange(n_nodes) edge_xs = self.EDGECOORDS_BASENUM + np.arange(n_edges) face_xs = self.FACECOORDS_BASENUM + np.arange(n_faces) # Record all these for re-use in tests self.n_faces = n_faces self.n_nodes = n_nodes self.face_xs = face_xs self.node_xs = node_xs self.edge_xs = edge_xs self.face_nodes_array = face_nodes_array self.edge_nodes_array = edge_nodes_array # convert source data to Dask arrays if asked. if lazy_sources: def lazify(arr): return da.from_array(arr, chunks=-1, meta=np.ndarray) node_xs = lazify(node_xs) face_xs = lazify(face_xs) edge_xs = lazify(edge_xs) face_nodes_array = lazify(face_nodes_array) edge_nodes_array = lazify(edge_nodes_array) # Build a mesh with this info stored in it. co_nodex = AuxCoord( node_xs, standard_name="longitude", long_name="node_x", units=1 ) co_facex = AuxCoord( face_xs, standard_name="longitude", long_name="face_x", units=1 ) co_edgex = AuxCoord( edge_xs, standard_name="longitude", long_name="edge_x", units=1 ) # N.B. the Mesh requires 'Y's as well. co_nodey = co_nodex.copy() co_nodey.rename("latitude") co_nodey.long_name = "node_y" co_facey = co_facex.copy() co_facey.rename("latitude") co_facey.long_name = "face_y" co_edgey = co_edgex.copy() co_edgey.rename("edge_y") co_edgey.long_name = "edge_y" face_node_conn = Connectivity( inds_start_index + ( face_nodes_array.transpose() if inds_location_axis == 1 else face_nodes_array ), cf_role="face_node_connectivity", long_name="face_nodes", start_index=inds_start_index, location_axis=inds_location_axis, ) edge_node_conn = Connectivity( inds_start_index + ( edge_nodes_array.transpose() if inds_location_axis == 1 else edge_nodes_array ), cf_role="edge_node_connectivity", long_name="edge_nodes", start_index=inds_start_index, location_axis=inds_location_axis, ) self.mesh = Mesh( topology_dimension=2, node_coords_and_axes=[(co_nodex, "x"), (co_nodey, "y")], connectivities=[face_node_conn, edge_node_conn], face_coords_and_axes=[(co_facex, "x"), (co_facey, "y")], edge_coords_and_axes=[(co_edgex, "x"), (co_edgey, "y")], ) # Construct a test meshcoord. meshcoord = MeshCoord(mesh=self.mesh, location=location, axis="x") self.meshcoord = meshcoord return meshcoord
def sample_mesh(n_nodes=None, n_faces=None, n_edges=None, lazy_values=False): """ Make a test mesh. Mesh has nodes, plus faces and/or edges, with face-coords and edge-coords, numbers of which can be controlled. Args: * n_nodes (int or None): Number of nodes in mesh. Default is 15. Cannot be 0. * n_edges (int or None): Number of edges in mesh. Default is 5. If not 0, edge coords and an 'edge_node_connectivity' are included. * n_faces (int or None): Number of faces in mesh. Default is 3. If not 0, face coords and a 'face_node_connectivity' are included. * lazy_values (bool): If True, all content values of coords and connectivities are lazy. """ if lazy_values: import dask.array as da arr = da else: arr = np if n_nodes is None: n_nodes = _TEST_N_NODES if n_faces is None: n_faces = _TEST_N_FACES if n_edges is None: n_edges = _TEST_N_EDGES node_x = AuxCoord( 1100 + arr.arange(n_nodes), standard_name="longitude", units="degrees_east", long_name="long-name", var_name="var-name", attributes={ "a": 1, "b": "c" }, ) node_y = AuxCoord(1200 + arr.arange(n_nodes), standard_name="latitude") connectivities = [] if n_edges == 0: edge_coords_and_axes = None else: # Define a rather arbitrary edge-nodes connectivity. # Some nodes are left out, because n_edges*2 < n_nodes. conns = arr.arange(n_edges * 2, dtype=int) # Missing nodes include #0-5, because we add 5. conns = ((conns + 5) % n_nodes).reshape((n_edges, 2)) edge_nodes = Connectivity(conns, cf_role="edge_node_connectivity") connectivities.append(edge_nodes) edge_x = AuxCoord(2100 + arr.arange(n_edges), standard_name="longitude") edge_y = AuxCoord(2200 + arr.arange(n_edges), standard_name="latitude") edge_coords_and_axes = [(edge_x, "x"), (edge_y, "y")] if n_faces == 0: face_coords_and_axes = None else: # Define a rather arbitrary face-nodes connectivity. # Some nodes are left out, because n_faces*n_bounds < n_nodes. conns = arr.arange(n_faces * _TEST_N_BOUNDS, dtype=int) conns = (conns % n_nodes).reshape((n_faces, _TEST_N_BOUNDS)) face_nodes = Connectivity(conns, cf_role="face_node_connectivity") connectivities.append(face_nodes) # Some numbers for the edge coordinates. face_x = AuxCoord(3100 + arr.arange(n_faces), standard_name="longitude") face_y = AuxCoord(3200 + arr.arange(n_faces), standard_name="latitude") face_coords_and_axes = [(face_x, "x"), (face_y, "y")] mesh = Mesh( topology_dimension=2, node_coords_and_axes=[(node_x, "x"), (node_y, "y")], connectivities=connectivities, edge_coords_and_axes=edge_coords_and_axes, face_coords_and_axes=face_coords_and_axes, ) return mesh
def build_mesh( n_nodes=2, n_faces=0, n_edges=0, nodecoord_xyargs=None, edgecoord_xyargs=None, facecoord_xyargs=None, conn_role_kwargs=None, # mapping {connectivity-role: connectivity-kwargs} mesh_kwargs=None, ): """ Make a test mesh. Mesh has faces edges, face-coords and edge-coords, numbers of which can be controlled. Args: * n_nodes, n_faces, n_edges (int): Basic dimensions of mesh components. Zero means no such location. * nodecoord_xyargs, edgecoord_xyargs, facecoord_xyargs (pair of dict): Pairs (x,y) of settings kwargs, applied after initial creation the relevant location coordinates. * conn_role_kwargs (dict of string:dict): Mapping from cf_role name to settings kwargs for connectivities, applied after initially creating them. * mesh_kwargs (dict): Dictionary of key settings to apply to the Mesh, after creating it. """ def applyargs(coord, kwargs): if kwargs: for key, val in kwargs.items(): # kwargs is a dict setattr(coord, key, val) def apply_xyargs(coords, xyargs): if xyargs: for coord, kwargs in zip(coords, xyargs): # coords and xyargs both iterables : implicitly=(x,y) applyargs(coord, kwargs) node_coords = [ AuxCoord(np.arange(n_nodes), standard_name=name) for name in XY_NAMES ] apply_xyargs(node_coords, nodecoord_xyargs) connectivities = {} edge_coords = [] face_coords = [] topology_dimension = 0 if n_edges: topology_dimension = 1 connectivities["edge_node_connectivity"] = Connectivity( np.zeros((n_edges, 2), np.int32), cf_role="edge_node_connectivity" ) edge_coords = [ AuxCoord(np.arange(n_edges), standard_name=name) for name in XY_NAMES ] apply_xyargs(edge_coords, edgecoord_xyargs) if n_faces: topology_dimension = 2 connectivities["face_node_connectivity"] = Connectivity( np.zeros((n_faces, 4), np.int32), cf_role="face_node_connectivity" ) face_coords = [ AuxCoord(np.arange(n_faces), standard_name=name) for name in XY_NAMES ] apply_xyargs(face_coords, facecoord_xyargs) mesh_dims = {"node": n_nodes, "edge": n_edges, "face": n_faces} if conn_role_kwargs: for role, kwargs in conn_role_kwargs.items(): if role in connectivities: conn = connectivities[role] else: loc_from, loc_to, _ = role.split("_") dims = [mesh_dims[loc] for loc in (loc_from, loc_to)] conn = Connectivity( np.zeros(dims, dtype=np.int32), cf_role=role ) connectivities[role] = conn applyargs(conn, kwargs) mesh = Mesh( topology_dimension=topology_dimension, node_coords_and_axes=zip(node_coords, XY_LOCS), edge_coords_and_axes=zip(edge_coords, XY_LOCS), face_coords_and_axes=zip(face_coords, XY_LOCS), connectivities=connectivities.values(), ) applyargs(mesh, mesh_kwargs) return mesh
def create(self): return Mesh.from_coords(self.lon, self.lat)
def test_no_bounds(self): lon = AuxCoord(points=[0.5, 1.5, 2.5]) lat = AuxCoord(points=[0, 1, 2]) with self.assertRaisesRegex(ValueError, "bounds missing from"): _ = Mesh.from_coords(lon, lat)