# Create conjugate isochrons from a ridge¶

This example creates a conjugate pair of isochrons from a mid-ocean ridge at each specified geological time in a series.

## Sample code¶

import pygplates

# Load one or more rotation files into a rotation model.
rotation_model = pygplates.RotationModel('rotations.rot')

# Load the mid-ocean ridge features.
ridge_features = pygplates.FeatureCollection('ridges.gpml')

# The times at which to create isochrons.
isochron_creation_times = [40, 30, 20, 10, 0]

# We'll store the created isochrons here - later we'll write it to a file.
isochron_feature_collection = pygplates.FeatureCollection()

# Iterate over the ridge features.
for ridge_feature in ridge_features:

# Get the ridge left and right plate ids, and time of appearance.
left_plate_id = ridge_feature.get_left_plate()
right_plate_id = ridge_feature.get_right_plate()
time_of_appearance, time_of_disappearance = ridge_feature.get_valid_time()

# Iterate over our list of creation times for the left/right isochrons.
for isochron_creation_time in isochron_creation_times:

# If creation time is later than ridge birth time then we can create an isochron.
if isochron_creation_time < time_of_appearance:

# Reconstruct the mid-ocean ridge to isochron creation time.
# The ridge geometry will be in the same position as the left/right isochrons at that time.
reconstructed_ridges = []
pygplates.reconstruct(ridge_feature, rotation_model, reconstructed_ridges, isochron_creation_time)

# Get the isochron geometry from the ridge reconstruction.
# This is the geometry at 'isochron_creation_time' (not present day).
isochron_geometry_at_creation_time = [reconstructed_ridge.get_reconstructed_geometry()
for reconstructed_ridge in reconstructed_ridges]

# Create the left and right isochrons.
# Since they are conjugates they have swapped left and right plate IDs.
# And reverse reconstruct the mid-ocean ridge geometries to present day.
left_isochron_feature = pygplates.Feature.create_reconstructable_feature(
pygplates.FeatureType.gpml_isochron,
isochron_geometry_at_creation_time,
name = ridge_feature.get_name(None),
description = ridge_feature.get_description(None),
valid_time = (isochron_creation_time, 0),
reconstruction_plate_id = left_plate_id,
conjugate_plate_id = right_plate_id,
reverse_reconstruct = (rotation_model, isochron_creation_time))
right_isochron_feature = pygplates.Feature.create_reconstructable_feature(
pygplates.FeatureType.gpml_isochron,
isochron_geometry_at_creation_time,
name = ridge_feature.get_name(None),
description = ridge_feature.get_description(None),
valid_time = (isochron_creation_time, 0),
reconstruction_plate_id = right_plate_id,
conjugate_plate_id = left_plate_id,
reverse_reconstruct = (rotation_model, isochron_creation_time))

# Add isochrons to feature collection.

# Write the isochrons to a new file.
isochron_feature_collection.write('isochrons.gpml')


## Details¶

The rotations are loaded from a rotation file into a pygplates.RotationModel.

rotation_model = pygplates.RotationModel('rotations.rot')


The ridge features are loaded into a pygplates.FeatureCollection.

ridge_features = pygplates.FeatureCollection('ridges.gpml')


The plate IDs and time period are obtained using pygplates.Feature.get_left_plate(), pygplates.Feature.get_right_plate() and pygplates.Feature.get_valid_time().

left_plate_id = ridge_feature.get_left_plate()
right_plate_id = ridge_feature.get_right_plate()
time_of_appearance, time_of_disappearance = ridge_feature.get_valid_time()


Smaller time values are closer to present day (younger).

if isochron_creation_time < time_of_appearance:


The ridges are reconstructed to their locations at time ‘isochron_creation_time’ using pygplates.reconstruct().

reconstructed_ridges = []
pygplates.reconstruct(ridge_feature, rotation_model, reconstructed_ridges, isochron_creation_time)


A Python list comprehension is used to build a list of pygplates.GeometryOnSphere from a list of pygplates.ReconstructedFeatureGeometry.

isochron_geometry_at_creation_time = [reconstructed_ridge.get_reconstructed_geometry()
for reconstructed_ridge in reconstructed_ridges]


Isochron features are created using pygplates.Feature.create_reconstructable_feature().

left_isochron_feature = pygplates.Feature.create_reconstructable_feature(
pygplates.FeatureType.gpml_isochron,
isochron_geometry_at_creation_time,
name = ridge_feature.get_name(None),
description = ridge_feature.get_description(None),
valid_time = (isochron_creation_time, 0),
reconstruction_plate_id = left_plate_id,
conjugate_plate_id = right_plate_id,
reverse_reconstruct = (rotation_model, isochron_creation_time))


The reverse_reconstruct parameter is needed because all features must store their geometry in present day coordinates which means reverse reconstructing from isochron_creation_time to present day using the rotation model.

Note

The use of None in, for example, ridge_feature.get_name(None) results in a name property only getting created if the ridge feature has a name.

And finally the isochrons are saved to a new file using pygplates.FeatureCollection.write().

isochron_feature_collection.write('isochrons.gpml')


If we want to be a bit more robust then we can check that our ridge features are actually ridges and we can make sure they contain left/right plate IDs and a time of appearance/disappearance:

...

# Iterate over the ridge features.
for ridge_feature in ridge_features:

# Ignore anything that's not a mid-ocean ridge.
if ridge_feature.get_feature_type() != pygplates.FeatureType.gpml_mid_ocean_ridge:
continue

# Get the ridge left and right plate ids, and time of appearance.
# We don't need to specify 'None', but if we do then it allows us to test if the ridge feature
# is missing plate IDs or begin/end time period.
left_plate_id = ridge_feature.get_left_plate(None)
right_plate_id = ridge_feature.get_right_plate(None)
valid_time = ridge_feature.get_valid_time(None)

# Ignore mid-ocean ridges that don't have a left and right plate id and time of appearance.
if (left_plate_id is None or
right_plate_id is None or
valid_time is None):
continue

# Extract time of appearance/disappearance from the tuple.
time_of_appearance, time_of_disappearance = valid_time

...


By specifying None in:

left_plate_id = ridge_feature.get_left_plate(None)
right_plate_id = ridge_feature.get_right_plate(None)
valid_time = ridge_feature.get_valid_time(None)

...we will get None returned to us if the feature property (eg, left plate ID) is missing in the ridge feature.
If we didn’t specify None then a default value would be returned if a property was missing. For get_left_plate() and get_right_plate() this is plate ID 0 and for get_valid_time() this is a time period from distant past to distant future.