def test_get_side_set_node_list_quad(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=2, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.zeros(5)) e.put_elem_blk_info(1, b"QUAD", 6, 4, 0) e.put_elem_connectivity(1, np.arange(6 * 4) + 7) e.put_side_set_params(4, 4, 0) e.put_side_set(4, np.arange(4, dtype=np.int32) + 2, np.arange(4, dtype=np.int32) + 1) with exodus(filename, mode="r") as e: num_nodes, local_node_ids = e.get_side_set_node_list(id=4) np.testing.assert_equal(num_nodes, [2, 2, 2, 2]) np.testing.assert_equal(local_node_ids, [11, 12, 16, 17, 21, 22, 26, 23])
def test_get_element_variable_values(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") e = exodus(filename, mode="w", title="Example", array_type="numpy", numDims=3, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) e.set_element_variable_number(5) e.put_element_variable_name("random", 3) # requires an actual element block. e.put_elem_blk_info(1, "HEX", 6, 3, 0) e.put_element_variable_values(1, "random", 1, np.arange(6)) e.close() with exodus(filename, mode="a") as e: values = e.get_element_variable_values(1, "random", 1) np.testing.assert_equal(values, np.arange(6)) # Raises a value error if the variable does not exist. with pytest.raises(ValueError): e.get_element_variable_values(1, "rando", 1)
def test_get_elem_connectivity_only_some_indices(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") # Generate test file. with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=2, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.zeros(5)) e.put_elem_blk_info(1, "HEX", 3, 8, 0) e.put_elem_connectivity(1, np.arange(3 * 8) + 7) with exodus(filename, mode="r") as e: conn, num_elem, num_nodes_per_elem = e.get_elem_connectivity( id=1, indices=[1, 3]) np.testing.assert_equal(conn, (np.arange(3 * 8) + 7).reshape( (3, 8))[[0, 2]]) assert num_elem == 3 assert num_nodes_per_elem == 8
def test_get_coord_2d(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=2, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.zeros(5)) with exodus(filename, mode="r") as e: with pytest.raises(ValueError) as f: e.get_coord(0) assert f.value.args[0] == "Invalid index. Coordinate bounds: [1, 5]." with pytest.raises(ValueError) as f: e.get_coord(6) assert f.value.args[0] == "Invalid index. Coordinate bounds: [1, 5]." np.testing.assert_allclose(e.get_coord(1), [0.0, 0.0, 0.0]) np.testing.assert_allclose(e.get_coord(2), [1.0, 2.0, 0.0]) np.testing.assert_allclose(e.get_coord(3), [2.0, 4.0, 0.0])
def test_get_side_set_node_list_hex(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") # Hex elements. with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=3, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.arange(5, dtype=np.float64) * 3) e.put_elem_blk_info(1, "HEX", 6, 8, 0) e.put_elem_connectivity(1, np.arange(6 * 8) + 7) e.put_side_set_params(4, 5, 0) e.put_side_set(4, np.arange(5, dtype=np.int32) + 2, np.arange(5, dtype=np.int32) + 1) with exodus(filename, mode="r") as e: num_nodes, local_node_ids = e.get_side_set_node_list(id=4) np.testing.assert_equal(num_nodes, [4, 4, 4, 4, 4]) np.testing.assert_equal(local_node_ids, [ 15, 16, 20, 19, 24, 25, 29, 28, 33, 34, 38, 37, 39, 43, 46, 42, 47, 50, 49, 48 ])
def test_get_side_set_invalid_id(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") # Put two side sets. Note that the ids of the side sets have little to # do with their naming inside the file... with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=3, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=2, io_size=io_size["io_size"]) as e: e.put_side_set_params(4, 5, 0) e.put_side_set(4, np.ones(5, dtype=np.int32) * 2, np.ones(5, dtype=np.int32) * 3) e.put_side_set_name(4, "edge of the world") e.put_side_set_params(2, 5, 0) e.put_side_set(2, np.ones(5, dtype=np.int32) * 7, np.ones(5, dtype=np.int32) * 8) e.put_side_set_name(2, "hallo") with exodus(filename, mode="r") as e: with pytest.raises(ValueError) as f: e.get_side_set(id=7) assert f.value.args[0] == \ "No side set with id 7 in file. Available ids: 4, 2."
def test_get_side_set_hex(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") # Hex elements. with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=3, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.arange(5, dtype=np.float64) * 3) e.put_elem_blk_info(1, "HEX", 6, 8, 0) e.put_elem_connectivity(1, np.arange(6 * 8) + 7) e.put_side_set_params(4, 5, 0) e.put_side_set(4, np.arange(5, dtype=np.int32) + 2, np.arange(5, dtype=np.int32) + 1) with exodus(filename, mode="r") as e: elem_idx, side_ids = e.get_side_set(id=4) np.testing.assert_equal(elem_idx, [2, 3, 4, 5, 6]) np.testing.assert_equal(side_ids, [1, 2, 3, 4, 5])
def test_get_side_set_ids(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") # Put two side sets. Note that the ids of the side sets have little to # do with their naming inside the file... with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=3, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=2, io_size=io_size["io_size"]) as e: e.put_side_set_params(4, 5, 0) e.put_side_set(4, np.ones(5, dtype=np.int32) * 2, np.ones(5, dtype=np.int32) * 3) e.put_side_set_name(4, "edge of the world") e.put_side_set_params(2, 5, 0) e.put_side_set(2, np.ones(5, dtype=np.int32) * 7, np.ones(5, dtype=np.int32) * 8) e.put_side_set_name(2, "hallo") with exodus(filename, mode="r") as e: assert e.get_side_set_ids() == [4, 2]
def read(self): """ Reads the original exodus model files.""" final_file = os.path.join(self.directory, "final.e") initial_file = os.path.join(self.directory, "initial.e") # Read yaml file containing information on the ses3d submodel. with io.open(os.path.join(self.directory, 'modelinfo.yml'), 'rt') as fh: try: self.model_info = yaml.load(fh, Loader=yaml.FullLoader) print('Evaluating Salvus 1 model: {}'.format(self.model_info['model'])) except yaml.YAMLError as exc: print(exc) # Read perturbations taper = self.get_edge_taper(exodus_file=initial_file) # This is just hardcoded, since there is only one Salvusv1 model. edge_taper = True for param in self.params: with pyexodus.exodus(final_file) as e_final: val = e_final.get_node_variable_values(param, step=1) with pyexodus.exodus(initial_file) as e_init: val -= e_init.get_node_variable_values(param, step=1) if edge_taper: self.perturbations[param] = val * taper else: self.perturbations[param] = val # read points with pyexodus.exodus(initial_file) as e_init: # get coords and convert to km x, y, z = e_init.get_coords() self.points = np.array((x, y, z)).T / 1000.0 if "radius_1D" in e_init.get_node_variable_names(): self.radius_1d = e_init.get_node_variable_values("radius_1D", step=1) rads_3d = np.sqrt(self.points[:, 0] ** 2 + self.points[:, 1] ** 2 + self.points[:, 2] ** 2) # convert coordinates to CSEM reference frame self.points[:, 0] = self.points[:, 0] * self.radius_1d * \ 6371.0 / rads_3d self.points[:, 1] = self.points[:, 1] * self.radius_1d * \ 6371.0 / rads_3d self.points[:, 2] = self.points[:, 2] * self.radius_1d * \ 6371.0 / rads_3d self.connectivity, self.nelem, self.nodes_per_element = \ e_init.get_elem_connectivity(id=1) # subtract 1, because exodus uses one based numbering self.connectivity -= 1 self.connectivity = self.connectivity.astype("int64")
def attach_field(self, name, values): """ Write values with name to exodus file :param name: name of the variable to be written :param values: numpy array of values to be written :return: """ assert self.mode in ["a"], ("Attach field option only " "available in mode 'a'") with exodus(self._filename, self.mode) as e: if values.size == self.nelem: e.put_element_variable_values(blockId=1, name=name, step=1, values=values) elif values.size == self.npoint: # print(name) idx = e.get_node_variable_names().index(name) + 1 # print(idx) # print(name) # print(e.get_node_variable_names()) e.put_node_variable_name(name, index=idx) # print(e.get_node_variable_names()) e.put_node_variable_values(name, 1, values) else: raise ValueError("Shape matches neither the nodes nor the " "elements")
def rotate_mesh(mesh, event_loc, backwards=False): """ Rotate the coordinates of a mesh to make the source show up below the North Pole of the mesh. Can also be used to rotate backwards. :param mesh: filename of mesh to be rotated :param event_loc: location of event to be rotated to N [lat, lon] :param backwards: Backrotation uses transpose of rot matrix """ event_vec = [np.cos(event_loc[0]) * np.cos(event_loc[1]), np.cos(event_loc[0]) * np.sin(event_loc[1]), np.sin(event_loc[0])] event_vec = np.array(event_vec) / np.linalg.norm(event_vec) north_vec = np.array([0.0, 0.0, 1.0]) rotate_axis = np.cross(event_vec, north_vec) rotate_axis /= np.linalg.norm(rotate_axis) # Make sure that both axis and angle make sense with r-hand-rule rot_angle = np.arccos(np.dot(event_vec, north_vec)) rot_mat = get_rot_matrix(rot_angle, rotate_axis[0], rotate_axis[1], rotate_axis[2]) if backwards: rot_mat = rot_mat.T mesh = exodus(mesh, mode="a") points = mesh.get_coords() rotated_points = rotate(x=points[0], y=points[1], z=points[2], matrix=rot_mat) rotated_points = rotated_points.T mesh.put_coords(rotated_points[:, 0], rotated_points[:, 1], rotated_points[:, 2])
def test_num_dims_accessor(tmpdir, io_size): # 2D filename = os.path.join(tmpdir.strpath, "example_2d.e") with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=2, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.zeros(5)) with exodus(filename, mode="r") as e: assert e.num_dims == 2 # 3D filename = os.path.join(tmpdir.strpath, "example_3d.e") with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=3, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.zeros(5)) with exodus(filename, mode="r") as e: assert e.num_dims == 3
def test_get_coords_2d(tmpdir, io_size): filename = os.path.join(tmpdir.strpath, "example.e") with exodus(filename, mode="w", title="Example", array_type="numpy", numDims=2, numNodes=5, numElems=6, numBlocks=1, numNodeSets=0, numSideSets=1, io_size=io_size["io_size"]) as e: e.put_coords(xCoords=np.arange(5, dtype=np.float64), yCoords=np.arange(5, dtype=np.float64) * 2, zCoords=np.zeros(5)) with exodus(filename, mode="r") as e: np.testing.assert_allclose( e.get_coords(), [np.arange(5), np.arange(5) * 2, np.zeros(5)])
def get_element_field(self, name): """ Get values from elemental field. :param name: name of the variable to be retrieved :return element field values: """ assert self.mode in ["r", "a"], ("Attach field option only " "available in mode 'r' or 'a'") assert name in self.elem_var_names, ("Could not find " "the requested field") with exodus(self._filename, self.mode) as e: values = e.get_element_variable_values(blockId=1, name=name, step=1) return values
def get_nodal_field(self, name): """ Get values from nodal field. :param name: name of the variable to be retrieved :return nodal field values: """ assert self.mode in ["r", "a"], ("Attach field option only " "available in mode 'r' or 'a'") with exodus(self._filename, self.mode) as e: assert (name in e.get_node_variable_names() ), "Could not find the requested field" values = e.get_node_variable_values(name=name, step=1) return values
def __init__(self, filename, mode='r'): self._filename = filename assert mode in ['a', 'r'], "Only mode 'a', 'r' is supported" self.mode = mode self.connectivity = None self.nodes_per_element = None self.ndim = None self.nelem = None self.x = None self.y = None self.z = None self.elem_var_names = None self.points = None self.e = exodus(self._filename, self.mode) # Read File self._read()
def _read(self): """ Retrieves basic information from the exodus file :return: """ with exodus(self._filename, self.mode) as e: self.ndim = e.num_dims # assert e.num_dims in [3], "Only '3D' exodus files are supported." self.connectivity, self.nelem, self.nodes_per_element = e.get_elem_connectivity( id=1) # subtract 1 from connectivity # as exodus in 1 based, whereas python is not self.connectivity = np.array(self.connectivity, dtype="int64") - 1 self.elem_var_names = e.get_element_variable_names() self.points = np.array((e.get_coords())).T.astype(np.float64) self.nodal_parameters = e.get_node_variable_names()
def get_edge_taper(self, exodus_file, taper_width=1200e3): """ Returns an edge taper that can be applied to the perturbations. This is Sine taper based on the distance to the edge of the domain. This requires the field inner_boundary to be present By default uses the bottom and the sides of the domain. Currently, this only applies to the Iran model. """ from scipy.spatial import cKDTree with pyexodus.exodus(exodus_file, mode="r") as init_e: side_set_names = init_e.get_side_set_names() if "inner_boundary" not in side_set_names: raise Exception("Currently, inner_boundary is required") side_nodes = [] for side_set in side_set_names: if side_set == "surface": continue if side_set == "r1": continue else: idx = init_e.get_side_set_ids()[ side_set_names.index(side_set)] _, nodes_side_set = init_e.get_side_set_node_list(idx) side_nodes.extend(nodes_side_set) side_nodes = np.unique(side_nodes) # remove duplicates side_nodes -= 1 # exodus is 1 based, python is not points = np.array(init_e.get_coords()).T side_points = points[side_nodes] # Setup KD tree to query distances to nearest edge point fast side_point_tree = cKDTree(side_points) dist, point_indices = side_point_tree.query(points, k=1) taper = np.ones_like(dist) # Compute sine taper taper[dist <= taper_width] = \ (np.sin((dist[dist <= taper_width] / taper_width) * np.pi - 0.5 * np.pi) + 1) / 2 return taper
def write_exodus(self, filename, overwrite=True, title=None): # pragma: no cover # NoQa from pyexodus import exodus is_pyexodus = True # remove file, if it exists if overwrite: try: os.remove(filename) # if the file does not exist, do nothing except OSError: pass if title is None: title = filename.split('.')[0] e = exodus(filename, mode='w', title=title, array_type='numpy', numDims=self.ndim, numNodes=self.npoint, numElems=self.nelem, numBlocks=1, numNodeSets=0, numSideSets=self.nside_sets) e.put_info_records(["%s = %s" % t for t in self.global_strings]) if self.ndim == 2: e.put_coords(self.points[:, 0] * self.scale, self.points[:, 1] * self.scale, np.zeros(self.npoint)) elif self.ndim == 3: e.put_coords(self.points[:, 0] * self.scale, self.points[:, 1] * self.scale, self.points[:, 2] * self.scale) name = elem.name[(self.ndim, self.nodes_per_element)] e.put_elem_blk_info(1, name, self.nelem, self.nodes_per_element, 0) if is_pyexodus: e.put_elem_connectivity(1, self.connectivity, shift_indices=1) # Memory intensive! else: # pramga: no cover e.put_elem_connectivity(1, self.connectivity.ravel() + 1) # we store the variables in the results section of the exodus file, # hence need to define a time step e.put_time(1, 0.) # write global variables e.set_global_variable_number(self.nglobal_variables) for i, (name, data) in enumerate(sorted(self.global_variables, key=lambda x: x[0])): e.put_global_variable_name(name, i + 1) e.put_global_variable_value(name, 1, data) # write elemental fields e.set_element_variable_number(self.nelemental_fields) for i, (name, data) in enumerate(sorted(self.elemental_fields, key=lambda x: x[0])): e.put_element_variable_name(name, i + 1) e.put_element_variable_values(1, name, 1, data) # write nodal fields e.set_node_variable_number(self.nnodal_fields) for i, (name, data) in enumerate(sorted(self.nodal_fields, key=lambda x: x[0])): e.put_node_variable_name(name, i + 1) e.put_node_variable_values(name, 1, data) # write side sets for i, (name, elements, sides) in enumerate( sorted(self.side_sets, key=lambda x: x[0])): e.put_side_set_params(i + 1, elements.size, 0) e.put_side_set(i + 1, elements + 1, sides + 1) e.put_side_set_name(i + 1, name) e.close()