def test_XyzToLatLonRadius(): """ Test the rotations.xyz_to_lat_lon_radius() function. """ # For (0/0) lat, lon, radius = rotations.xyz_to_lat_lon_radius(1.0, 0.0, 0.0) np.testing.assert_almost_equal(lat, 0.0) np.testing.assert_almost_equal(lon, 0.0) np.testing.assert_almost_equal(radius, 1.0) # At the North Pole lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, 0.0, 1.0) np.testing.assert_almost_equal(lat, 90.0) np.testing.assert_almost_equal(lon, 0.0) np.testing.assert_almost_equal(radius, 1.0) # At the South Pole lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, 0.0, -1.0) np.testing.assert_almost_equal(lat, -90.0) np.testing.assert_almost_equal(lon, 0.0) np.testing.assert_almost_equal(radius, 1.0) # At the "West Pole" lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, -1.0, 0.0) np.testing.assert_almost_equal(lat, 0.0) np.testing.assert_almost_equal(lon, -90.0) np.testing.assert_almost_equal(radius, 1.0) # At the "East Pole" lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, 1.0, 0.0) np.testing.assert_almost_equal(lat, 0.0) np.testing.assert_almost_equal(lon, 90.0) np.testing.assert_almost_equal(radius, 1.0)
def test_XyzToLatLonRadius(self): """ Test the rotations.xyz_to_lat_lon_radius() function. """ # For (0/0) lat, lon, radius = rotations.xyz_to_lat_lon_radius(1.0, 0.0, 0.0) self.assertAlmostEqual(lat, 0.0) self.assertAlmostEqual(lon, 0.0) self.assertAlmostEqual(radius, 1.0) # At the North Pole lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, 0.0, 1.0) self.assertAlmostEqual(lat, 90.0) self.assertAlmostEqual(lon, 0.0) self.assertAlmostEqual(radius, 1.0) # At the South Pole lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, 0.0, -1.0) self.assertAlmostEqual(lat, -90.0) self.assertAlmostEqual(lon, 0.0) self.assertAlmostEqual(radius, 1.0) # At the "West Pole" lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, -1.0, 0.0) self.assertAlmostEqual(lat, 0.0) self.assertAlmostEqual(lon, -90.0) self.assertAlmostEqual(radius, 1.0) # At the "East Pole" lat, lon, radius = rotations.xyz_to_lat_lon_radius(0.0, 1.0, 0.0) self.assertAlmostEqual(lat, 0.0) self.assertAlmostEqual(lon, 90.0) self.assertAlmostEqual(radius, 1.0)
def _read(self): """ Reads the HDF5 file and gathers basic information such as the coordinates of the edge nodes. In the case of domain that spans the entire earth, all points will lie inside the domain, therefore further processing is not necessary. """ try: self.m = h5py.File(self.mesh_file, mode="r") except AssertionError: msg = ( "Could not open the project's mesh file. " "Please ensure that the path specified " "in config is correct." ) raise LASIFNotFoundError(msg) # if less than 2 side sets, this must be a global mesh. Return self.side_set_names = list(self.m["SIDE_SETS"].keys()) if ( len(self.side_set_names) <= 2 and "outer_boundary" not in self.side_set_names ): self.is_global_mesh = True self.min_lat = -90.0 self.max_lat = 90.0 self.min_lon = -180.0 self.max_lon = 180.0 return if "a0" in self.side_set_names: self.is_global_mesh = True self.min_lat = -90.0 self.max_lat = 90.0 self.min_lon = -180.0 self.max_lon = 180.0 return side_elements = [] earth_surface_elements = [] earth_bottom_elements = [] for side_set in self.side_set_names: if side_set == "surface": continue elif side_set == "r0": earth_bottom_elements = self.m["SIDE_SETS"][side_set][ "elements" ][()] elif side_set == "r1": earth_surface_elements = self.m["SIDE_SETS"][side_set][ "elements" ][()] elif side_set == "r1_ol": earth_surface_elements = self.m["SIDE_SETS"][side_set][ "elements" ][()] else: side_elements.append( self.m["SIDE_SETS"][side_set]["elements"][()] ) side_elements_tmp = np.array([], dtype=np.int) for i in range(len(side_elements)): side_elements_tmp = np.concatenate( (side_elements_tmp, side_elements[i]) ) # Remove Duplicates side_elements = np.unique(side_elements_tmp) # Get node numbers of the nodes specifying the domain boundaries surface_boundaries = np.intersect1d( side_elements, earth_surface_elements ) bottom_boundaries = np.intersect1d( side_elements, earth_bottom_elements ) # Get coordinates coords = self.m["MODEL/coordinates"][()] self.domain_edge_coords = coords[surface_boundaries] self.earth_surface_coords = coords[earth_surface_elements] self.earth_bottom_coords = coords[earth_bottom_elements] self.bottom_edge_coords = coords[bottom_boundaries] # Get approximation of element width, take second smallest value # For now we will just take a random point on the surface and # take the maximum distance between gll points and use that # as the element with. It should be an overestimation x, y, z = self.earth_surface_coords[:, 0, :].T # # Get extent and center of domain # x, y, z = self.domain_edge_coords.T # # pick a random GLL point to represent the boundary # x = x[0] # y = y[0] # z = z[0] # get center lat/lon x_cen, y_cen, z_cen = np.median(x), np.median(y), np.median(z) self.center_lat, self.center_lon, _ = xyz_to_lat_lon_radius( x_cen, y_cen, z_cen ) lats, lons, _ = xyz_to_lat_lon_radius(x, y, z) self.min_lat = np.min(lats) self.max_lat = np.max(lats) self.min_lon = np.min(lons) self.max_lon = np.max(lons) # Find point outside the domain: outside_point = self.find_outside_point() # Get coords for the bottom edge of mesh # x, y, z = self.bottom_edge_coords.T x, y, z = self.earth_bottom_coords.T x, y, z = x[0], y[0], z[0] # Figure out maximum depth of mesh _, _, r = xyz_to_lat_lon_radius(x, y, z) min_r = min(r) self.max_depth = self.r_earth - min_r self.is_read = True # In order to create the self.edge_polygon we need to make sure that # the points on the boundary are arranged in a way that a proper # polygon will be drawn. sorted_indices = self.get_sorted_edge_coords() x, y, z = self.domain_edge_coords[np.append(sorted_indices, 0)].T lats, lons, _ = xyz_to_lat_lon_radius(x[0], y[0], z[0]) x, y, z = normalize_coordinates(x[0], y[0], z[0]) points = np.array((x, y, z)).T self.boundary = np.array([lats, lons]).T self.edge_polygon = lasif.spherical_geometry.SphericalPolygon( points, outside_point ) # Close file self.m.close()
def plot( self, ax=None, plot_inner_boundary: bool = False, ): """ Plots the domain Global domain is plotted using an equal area Mollweide projection. Smaller domains have eihter Orthographic projections or PlateCarree. :param ax: matplotlib axes, defaults to None :type ax: matplotlib.axes.Axes, optional :param plot_inner_boundary: plot the convex hull of the mesh surface nodes that lie inside the domain. Defaults to False :type plot_inner_boundary: bool, optional :return: The created GeoAxes instance. """ if not self.is_read: self._read() import matplotlib.pyplot as plt # if global mesh return moll transform = cp.crs.Geodetic() if self.is_global_mesh: projection = cp.crs.Mollweide() if ax is None: m = plt.axes(projection=projection) else: m = ax _plot_features(m, projection=projection) return m, projection lat_extent = self.max_lat - self.min_lat lon_extent = self.max_lon - self.min_lon max_extent = max(lat_extent, lon_extent) # Use a global plot for very large domains. if lat_extent >= 90.0 and lon_extent >= 90.0: projection = cp.crs.Mollweide() if ax is None: m = plt.axes(projection=projection) else: m = ax elif max_extent >= 75.0: projection = cp.crs.Orthographic( central_longitude=self.center_lon, central_latitude=self.center_lat, ) if ax is None: m = plt.axes(projection=projection) else: m = ax else: projection = cp.crs.PlateCarree(central_longitude=self.center_lon,) if ax is None: m = plt.axes(projection=projection,) else: m = ax m.set_extent( [ self.min_lon - 3.0, self.max_lon + 3.0, self.min_lat - 3.0, self.max_lat + 3.0, ], crs=transform, ) try: _plot_lines( m, self.boundary, transform=transform, color="red", lw=2, label="Domain Edge", ) if plot_inner_boundary: # Get surface points x, y, z = self.earth_surface_coords.T latlonrad = np.array(xyz_to_lat_lon_radius(x[0], y[0], z[0])) # This part is potentially slow when lots # of points need to be checked in_domain = [] idx = 0 for lat, lon, _ in latlonrad.T: if self.point_in_domain(latitude=lat, longitude=lon,): in_domain.append(idx) idx += 1 lats, lons, rad = np.array(latlonrad[:, in_domain]) # Get the complex hull from projected (to 2D) points from scipy.spatial import ConvexHull points = np.array((lons, lats)).T hull = ConvexHull(points) # Plot the hull simplices for simplex in hull.simplices: m.plot( points[simplex, 0], points[simplex, 1], c="black", transform=transform, ) m.plot( points[simplex, 0], points[simplex, 1], c="black", transform=transform, label="Convex Hull of Inner boundary", ) except LASIFError: # Back up plot if the other one fails, which happens for # very weird meshes sometimes. # This Scatter all edge nodes on the plotted domain x, y, z = self.domain_edge_coords.T lats, lons, _ = xyz_to_lat_lon_radius(x[0], y[0], z[0]) plt.scatter( lons, lats, color="k", label="Edge nodes", zorder=3000, transform=transform, ) _plot_features(m, projection=projection) m.legend(framealpha=0.5, loc="lower right") return m, projection
def generate_input_files(self, iteration_name, event_name, simulation_type): """ Generate the input files for one event. :param iteration_name: The name of the iteration. :param event_name: The name of the event for which to generate the input files. :param simulate_type: The type of simulation to perform. Possible values are: 'normal simulate', 'adjoint forward', 'adjoint reverse' """ from wfs_input_generator import InputFileGenerator # ===================================================================== # read iteration xml file, get event and list of stations # ===================================================================== iteration = self.comm.iterations.get(iteration_name) # Check that the event is part of the iterations. if event_name not in iteration.events: msg = ("Event '%s' not part of iteration '%s'.\nEvents available " "in iteration:\n\t%s" % (event_name, iteration_name, "\n\t".join( sorted(iteration.events.keys())))) raise ValueError(msg) event = self.comm.events.get(event_name) stations_for_event = iteration.events[event_name]["stations"].keys() # Get all stations and create a dictionary for the input file # generator. stations = self.comm.query.get_all_stations_for_event(event_name) stations = [{"id": key, "latitude": value["latitude"], "longitude": value["longitude"], "elevation_in_m": value["elevation_in_m"], "local_depth_in_m": value["local_depth_in_m"]} for key, value in stations.iteritems() if key in stations_for_event] # ===================================================================== # set solver options # ===================================================================== solver = iteration.solver_settings # Currently only SES3D 4.1 is supported solver_format = solver["solver"].lower() if solver_format not in ["ses3d 4.1", "ses3d 2.0", "specfem3d cartesian", "specfem3d globe cem"]: msg = ("Currently only SES3D 4.1, SES3D 2.0, SPECFEM3D " "CARTESIAN, and SPECFEM3D GLOBE CEM are supported.") raise ValueError(msg) solver_format = solver_format.replace(' ', '_') solver_format = solver_format.replace('.', '_') solver = solver["solver_settings"] # ===================================================================== # create the input file generator, add event and stations, # populate the configuration items # ===================================================================== # Add the event and the stations to the input file generator. gen = InputFileGenerator() gen.add_events(event["filename"]) gen.add_stations(stations) if solver_format in ["ses3d_4_1", "ses3d_2_0"]: # event tag gen.config.event_tag = event_name # Time configuration. npts = solver["simulation_parameters"]["number_of_time_steps"] delta = solver["simulation_parameters"]["time_increment"] gen.config.number_of_time_steps = npts gen.config.time_increment_in_s = delta # SES3D specific configuration gen.config.output_folder = solver["output_directory"].replace( "{{EVENT_NAME}}", event_name.replace(" ", "_")) gen.config.simulate_type = simulation_type gen.config.adjoint_forward_wavefield_output_folder = \ solver["adjoint_output_parameters"][ "forward_field_output_directory"].replace( "{{EVENT_NAME}}", event_name.replace(" ", "_")) gen.config.adjoint_forward_sampling_rate = \ solver["adjoint_output_parameters"][ "sampling_rate_of_forward_field"] # Visco-elastic dissipation diss = solver["simulation_parameters"]["is_dissipative"] gen.config.is_dissipative = diss # Only SES3D 4.1 has the relaxation parameters. if solver_format == "ses3d_4_1": gen.config.Q_model_relaxation_times = \ solver["relaxation_parameter_list"]["tau"] gen.config.Q_model_weights_of_relaxation_mechanisms = \ solver["relaxation_parameter_list"]["w"] # Discretization disc = solver["computational_setup"] gen.config.nx_global = disc["nx_global"] gen.config.ny_global = disc["ny_global"] gen.config.nz_global = disc["nz_global"] gen.config.px = disc["px_processors_in_theta_direction"] gen.config.py = disc["py_processors_in_phi_direction"] gen.config.pz = disc["pz_processors_in_r_direction"] gen.config.lagrange_polynomial_degree = \ disc["lagrange_polynomial_degree"] # Configure the mesh. domain = self.comm.project.domain gen.config.mesh_min_latitude = \ domain["bounds"]["minimum_latitude"] gen.config.mesh_max_latitude = \ domain["bounds"]["maximum_latitude"] gen.config.mesh_min_longitude = \ domain["bounds"]["minimum_longitude"] gen.config.mesh_max_longitude = \ domain["bounds"]["maximum_longitude"] gen.config.mesh_min_depth_in_km = \ domain["bounds"]["minimum_depth_in_km"] gen.config.mesh_max_depth_in_km = \ domain["bounds"]["maximum_depth_in_km"] # Set the rotation parameters. gen.config.rotation_angle_in_degree = domain["rotation_angle"] gen.config.rotation_axis = domain["rotation_axis"] # Make source time function gen.config.source_time_function = \ iteration.get_source_time_function()["data"] elif solver_format == "specfem3d_cartesian": gen.config.NSTEP = \ solver["simulation_parameters"]["number_of_time_steps"] gen.config.DT = \ solver["simulation_parameters"]["time_increment"] gen.config.NPROC = \ solver["computational_setup"]["number_of_processors"] if simulation_type == "normal simulation": msg = ("'normal_simulate' not supported for SPECFEM3D " "Cartesian. Please choose either 'adjoint_forward' or " "'adjoint_reverse'.") raise NotImplementedError(msg) elif simulation_type == "adjoint forward": gen.config.SIMULATION_TYPE = 1 elif simulation_type == "adjoint reverse": gen.config.SIMULATION_TYPE = 2 else: raise NotImplementedError solver_format = solver_format.upper() elif solver_format == "specfem3d_globe_cem": cs = solver["computational_setup"] gen.config.NPROC_XI = cs["number_of_processors_xi"] gen.config.NPROC_ETA = cs["number_of_processors_eta"] gen.config.NCHUNKS = cs["number_of_chunks"] gen.config.NEX_XI = cs["elements_per_chunk_xi"] gen.config.NEX_ETA = cs["elements_per_chunk_eta"] gen.config.OCEANS = cs["simulate_oceans"] gen.config.ELLIPTICITY = cs["simulate_ellipticity"] gen.config.TOPOGRAPHY = cs["simulate_topography"] gen.config.GRAVITY = cs["simulate_gravity"] gen.config.ROTATION = cs["simulate_rotation"] gen.config.ATTENUATION = cs["simulate_attenuation"] gen.config.ABSORBING_CONDITIONS = True if cs["fast_undo_attenuation"]: gen.config.PARTIAL_PHYS_DISPERSION_ONLY = True gen.config.UNDO_ATTENUATION = False else: gen.config.PARTIAL_PHYS_DISPERSION_ONLY = False gen.config.UNDO_ATTENUATION = True gen.config.GPU_MODE = cs["use_gpu"] gen.config.SOURCE_TIME_FUNCTION = \ iteration.get_source_time_function()["data"] if simulation_type == "normal simulation": gen.config.SIMULATION_TYPE = 1 gen.config.SAVE_FORWARD = False elif simulation_type == "adjoint forward": gen.config.SIMULATION_TYPE = 1 gen.config.SAVE_FORWARD = True elif simulation_type == "adjoint reverse": gen.config.SIMULATION_TYPE = 2 gen.config.SAVE_FORWARD = True else: raise NotImplementedError # Use the current domain setting to derive the bounds in the way # SPECFEM specifies them. domain = self.comm.project.domain lat_range = domain["bounds"]["maximum_latitude"] - \ domain["bounds"]["minimum_latitude"] lng_range = domain["bounds"]["maximum_longitude"] - \ domain["bounds"]["minimum_longitude"] c_lat = \ domain["bounds"]["minimum_latitude"] + lat_range / 2.0 c_lng = \ domain["bounds"]["minimum_longitude"] + lng_range / 2.0 # Rotate the point. c_lat_1, c_lng_1 = rotations.rotate_lat_lon( c_lat, c_lng, domain["rotation_axis"], domain["rotation_angle"]) # SES3D rotation. A = rotations._get_rotation_matrix( domain["rotation_axis"], domain["rotation_angle"]) latitude_rotation = -(c_lat_1 - c_lat) longitude_rotation = c_lng_1 - c_lng # Rotate the latitude. The rotation axis is latitude 0 and # the center longitude + 90 degree B = rotations._get_rotation_matrix( rotations.lat_lon_radius_to_xyz(0.0, c_lng + 90, 1.0), latitude_rotation) # Rotate around the North pole. C = rotations._get_rotation_matrix( [0.0, 0.0, 1.0], longitude_rotation) D = A * np.linalg.inv(C * B) axis, angle = rotations._get_axis_and_angle_from_rotation_matrix(D) rotated_axis = rotations.xyz_to_lat_lon_radius(*axis) # Consistency check if abs(rotated_axis[0] - c_lat_1) >= 0.01 or \ abs(rotated_axis[1] - c_lng_1) >= 0.01: axis *= -1.0 angle *= -1.0 rotated_axis = rotations.xyz_to_lat_lon_radius(*axis) if abs(rotated_axis[0] - c_lat_1) >= 0.01 or \ abs(rotated_axis[1] - c_lng_1) >= 0.01: msg = "Failed to describe the domain in terms that SPECFEM " \ "understands" raise LASIFError(msg) gen.config.ANGULAR_WIDTH_XI_IN_DEGREES = lng_range gen.config.ANGULAR_WIDTH_ETA_IN_DEGREES = lat_range gen.config.CENTER_LATITUDE_IN_DEGREES = c_lat_1 gen.config.CENTER_LONGITUDE_IN_DEGREES = c_lng_1 gen.config.GAMMA_ROTATION_AZIMUTH = angle gen.config.MODEL = cs["model"] pp = iteration.get_process_params() gen.config.RECORD_LENGTH_IN_MINUTES = \ (pp["npts"] * pp["dt"]) / 60.0 solver_format = solver_format.upper() else: msg = "Unknown solver '%s'." % solver_format raise NotImplementedError(msg) # ================================================================= # output # ================================================================= output_dir = self.comm.project.get_output_folder( "input_files___ITERATION_%s__%s__EVENT_%s" % ( iteration_name, simulation_type.replace(" ", "_"), event_name)) gen.write(format=solver_format, output_dir=output_dir) print "Written files to '%s'." % output_dir
def generate_input_files(self, iteration_name, event_name, simulation_type): """ Generate the input files for one event. :param iteration_name: The name of the iteration. :param event_name: The name of the event for which to generate the input files. :param simulate_type: The type of simulation to perform. Possible values are: 'normal simulate', 'adjoint forward', 'adjoint reverse' """ from wfs_input_generator import InputFileGenerator # ===================================================================== # read iteration xml file, get event and list of stations # ===================================================================== iteration = self.comm.iterations.get(iteration_name) # Check that the event is part of the iterations. if event_name not in iteration.events: msg = ("Event '%s' not part of iteration '%s'.\nEvents available " "in iteration:\n\t%s" % (event_name, iteration_name, "\n\t".join( sorted(iteration.events.keys())))) raise ValueError(msg) event = self.comm.events.get(event_name) stations_for_event = list( iteration.events[event_name]["stations"].keys()) # Get all stations and create a dictionary for the input file # generator. stations = self.comm.query.get_all_stations_for_event(event_name) stations = [{ "id": key, "latitude": value["latitude"], "longitude": value["longitude"], "elevation_in_m": value["elevation_in_m"], "local_depth_in_m": value["local_depth_in_m"] } for key, value in stations.items() if key in stations_for_event] # ===================================================================== # set solver options # ===================================================================== solver = iteration.solver_settings # Currently only SES3D 4.1 is supported solver_format = solver["solver"].lower() if solver_format not in [ "ses3d 4.1", "ses3d 2.0", "specfem3d cartesian", "specfem3d globe cem" ]: msg = ("Currently only SES3D 4.1, SES3D 2.0, SPECFEM3D " "CARTESIAN, and SPECFEM3D GLOBE CEM are supported.") raise ValueError(msg) solver_format = solver_format.replace(' ', '_') solver_format = solver_format.replace('.', '_') solver = solver["solver_settings"] # ===================================================================== # create the input file generator, add event and stations, # populate the configuration items # ===================================================================== # Add the event and the stations to the input file generator. gen = InputFileGenerator() gen.add_events(event["filename"]) gen.add_stations(stations) if solver_format in ["ses3d_4_1", "ses3d_2_0"]: # event tag gen.config.event_tag = event_name # Time configuration. npts = solver["simulation_parameters"]["number_of_time_steps"] delta = solver["simulation_parameters"]["time_increment"] gen.config.number_of_time_steps = npts gen.config.time_increment_in_s = delta # SES3D specific configuration gen.config.output_folder = solver["output_directory"].replace( "{{EVENT_NAME}}", event_name.replace(" ", "_")) gen.config.simulation_type = simulation_type gen.config.adjoint_forward_wavefield_output_folder = \ solver["adjoint_output_parameters"][ "forward_field_output_directory"].replace( "{{EVENT_NAME}}", event_name.replace(" ", "_")) gen.config.adjoint_forward_sampling_rate = \ solver["adjoint_output_parameters"][ "sampling_rate_of_forward_field"] # Visco-elastic dissipation diss = solver["simulation_parameters"]["is_dissipative"] gen.config.is_dissipative = diss # Only SES3D 4.1 has the relaxation parameters. if solver_format == "ses3d_4_1": gen.config.Q_model_relaxation_times = \ solver["relaxation_parameter_list"]["tau"] gen.config.Q_model_weights_of_relaxation_mechanisms = \ solver["relaxation_parameter_list"]["w"] # Discretization disc = solver["computational_setup"] gen.config.nx_global = disc["nx_global"] gen.config.ny_global = disc["ny_global"] gen.config.nz_global = disc["nz_global"] gen.config.px = disc["px_processors_in_theta_direction"] gen.config.py = disc["py_processors_in_phi_direction"] gen.config.pz = disc["pz_processors_in_r_direction"] gen.config.lagrange_polynomial_degree = \ disc["lagrange_polynomial_degree"] # Configure the mesh. domain = self.comm.project.domain gen.config.mesh_min_latitude = domain.min_latitude gen.config.mesh_max_latitude = domain.max_latitude gen.config.mesh_min_longitude = domain.min_longitude gen.config.mesh_max_longitude = domain.max_longitude gen.config.mesh_min_depth_in_km = domain.min_depth_in_km gen.config.mesh_max_depth_in_km = domain.max_depth_in_km # Set the rotation parameters. gen.config.rotation_angle_in_degree = \ domain.rotation_angle_in_degree gen.config.rotation_axis = domain.rotation_axis # Make source time function gen.config.source_time_function = \ iteration.get_source_time_function()["data"] elif solver_format == "specfem3d_cartesian": gen.config.NSTEP = \ solver["simulation_parameters"]["number_of_time_steps"] gen.config.DT = \ solver["simulation_parameters"]["time_increment"] gen.config.NPROC = \ solver["computational_setup"]["number_of_processors"] if simulation_type == "normal simulation": msg = ("'normal_simulate' not supported for SPECFEM3D " "Cartesian. Please choose either 'adjoint_forward' or " "'adjoint_reverse'.") raise NotImplementedError(msg) elif simulation_type == "adjoint forward": gen.config.SIMULATION_TYPE = 1 elif simulation_type == "adjoint reverse": gen.config.SIMULATION_TYPE = 2 else: raise NotImplementedError solver_format = solver_format.upper() elif solver_format == "specfem3d_globe_cem": cs = solver["computational_setup"] gen.config.NPROC_XI = cs["number_of_processors_xi"] gen.config.NPROC_ETA = cs["number_of_processors_eta"] gen.config.NCHUNKS = cs["number_of_chunks"] gen.config.NEX_XI = cs["elements_per_chunk_xi"] gen.config.NEX_ETA = cs["elements_per_chunk_eta"] gen.config.OCEANS = cs["simulate_oceans"] gen.config.ELLIPTICITY = cs["simulate_ellipticity"] gen.config.TOPOGRAPHY = cs["simulate_topography"] gen.config.GRAVITY = cs["simulate_gravity"] gen.config.ROTATION = cs["simulate_rotation"] gen.config.ATTENUATION = cs["simulate_attenuation"] gen.config.ABSORBING_CONDITIONS = True if cs["fast_undo_attenuation"]: gen.config.PARTIAL_PHYS_DISPERSION_ONLY = True gen.config.UNDO_ATTENUATION = False else: gen.config.PARTIAL_PHYS_DISPERSION_ONLY = False gen.config.UNDO_ATTENUATION = True gen.config.GPU_MODE = cs["use_gpu"] gen.config.SOURCE_TIME_FUNCTION = \ iteration.get_source_time_function()["data"] if simulation_type == "normal simulation": gen.config.SIMULATION_TYPE = 1 gen.config.SAVE_FORWARD = False elif simulation_type == "adjoint forward": gen.config.SIMULATION_TYPE = 1 gen.config.SAVE_FORWARD = True elif simulation_type == "adjoint reverse": gen.config.SIMULATION_TYPE = 2 gen.config.SAVE_FORWARD = True else: raise NotImplementedError # Use the current domain setting to derive the bounds in the way # SPECFEM specifies them. domain = self.comm.project.domain lat_range = domain.max_latitude - \ domain.min_latitude lng_range = domain.max_longitude - \ domain.min_longitude c_lat = \ domain.min_latitude + lat_range / 2.0 c_lng = \ domain.min_longitude + lng_range / 2.0 # Rotate the point. c_lat_1, c_lng_1 = rotations.rotate_lat_lon( c_lat, c_lng, domain.rotation_axis, domain.rotation_angle_in_degree) # SES3D rotation. A = rotations._get_rotation_matrix(domain.rotation_axis, domain.rotation_angle_in_degree) latitude_rotation = -(c_lat_1 - c_lat) longitude_rotation = c_lng_1 - c_lng # Rotate the latitude. The rotation axis is latitude 0 and # the center longitude + 90 degree B = rotations._get_rotation_matrix( rotations.lat_lon_radius_to_xyz(0.0, c_lng + 90, 1.0), latitude_rotation) # Rotate around the North pole. C = rotations._get_rotation_matrix([0.0, 0.0, 1.0], longitude_rotation) D = A * np.linalg.inv(C * B) axis, angle = rotations._get_axis_and_angle_from_rotation_matrix(D) rotated_axis = rotations.xyz_to_lat_lon_radius(*axis) # Consistency check if abs(rotated_axis[0] - c_lat_1) >= 0.01 or \ abs(rotated_axis[1] - c_lng_1) >= 0.01: axis *= -1.0 angle *= -1.0 rotated_axis = rotations.xyz_to_lat_lon_radius(*axis) if abs(rotated_axis[0] - c_lat_1) >= 0.01 or \ abs(rotated_axis[1] - c_lng_1) >= 0.01: msg = "Failed to describe the domain in terms that SPECFEM " \ "understands. The domain definition in the output " \ "files will NOT BE CORRECT!" warnings.warn(msg, LASIFWarning) gen.config.ANGULAR_WIDTH_XI_IN_DEGREES = lng_range gen.config.ANGULAR_WIDTH_ETA_IN_DEGREES = lat_range gen.config.CENTER_LATITUDE_IN_DEGREES = c_lat_1 gen.config.CENTER_LONGITUDE_IN_DEGREES = c_lng_1 gen.config.GAMMA_ROTATION_AZIMUTH = angle gen.config.MODEL = cs["model"] pp = iteration.get_process_params() gen.config.RECORD_LENGTH_IN_MINUTES = \ (pp["npts"] * pp["dt"]) / 60.0 solver_format = solver_format.upper() else: msg = "Unknown solver '%s'." % solver_format raise NotImplementedError(msg) # ================================================================= # output # ================================================================= output_dir = self.comm.project.get_output_folder( type="input_files", tag="ITERATION_%s__%s__EVENT_%s" % (iteration_name, simulation_type.replace(" ", "_"), event_name)) gen.write(format=solver_format, output_dir=output_dir) print("Written files to '%s'." % output_dir)