Skip to content

Commit 993927c

Browse files
Deepprakash222Leguark
authored andcommitted
added a test_gradient folder to generate sample for c, m=Gempy(c) and dm/dc
1 parent 2483141 commit 993927c

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
2+
import torch
3+
import numpy as np
4+
from datetime import datetime
5+
from torch.autograd.functional import jacobian
6+
import pyro
7+
import pyro.distributions as dist
8+
from pyro.infer import Predictive
9+
10+
from pyro.nn import PyroModule
11+
12+
import gempy as gp
13+
import gempy_engine
14+
from gempy_engine.core.backend_tensor import BackendTensor
15+
16+
17+
from helpers import *
18+
19+
20+
class GempyModel(PyroModule):
21+
def __init__(self, interpolation_input_, geo_model_test, num_layers, slope, dtype):
22+
super(GempyModel, self).__init__()
23+
24+
BackendTensor.change_backend_gempy(engine_backend=gp.data.AvailableBackends.PYTORCH)
25+
self.interpolation_input_ = interpolation_input_
26+
self.geo_model_test = geo_model_test
27+
self.num_layers = num_layers
28+
self.dtype = dtype
29+
self.geo_model_test.interpolation_options.sigmoid_slope = slope
30+
###############################################################################
31+
# Seed the randomness
32+
###############################################################################
33+
seed = 42
34+
np.random.seed(seed)
35+
torch.manual_seed(seed)
36+
# Ensure deterministic behavior
37+
torch.backends.cudnn.deterministic = True
38+
torch.backends.cudnn.benchmark = False
39+
# Setting the seed for Pyro sampling
40+
41+
pyro.set_rng_seed(42)
42+
43+
44+
def create_sample(self):
45+
46+
"""
47+
This Pyro model represents the probabilistic aspects of the geological model.
48+
It defines a prior distribution for the top layer's location and
49+
computes the thickness of the geological layer as an observed variable.
50+
51+
52+
interpolation_input_: represents the dictionary of random variables for surface parameters
53+
geo_model_test : gempy model
54+
55+
num_layers: represents the number of layers we want to include in the model
56+
57+
"""
58+
59+
Random_variable ={}
60+
61+
# Create a random variable based on the provided dictionary used to modify input data of gempy
62+
counter=1
63+
for interpolation_input_data in self.interpolation_input_[:self.num_layers]:
64+
65+
# Check if user wants to create random variable based on modifying the surface points of gempy
66+
if interpolation_input_data["update"]=="interface_data":
67+
# Check what kind of distribution is needed
68+
if interpolation_input_data["prior_distribution"]=="normal":
69+
mean = interpolation_input_data["normal"]["mean"]
70+
std = interpolation_input_data["normal"]["std"]
71+
Random_variable["mu_"+ str(counter)] = pyro.sample("mu_"+ str(counter), dist.Normal(mean, std))
72+
#print(Random_variable["mu_"+ str(counter)])
73+
74+
elif interpolation_input_data["prior_distribution"]=="uniform":
75+
min = interpolation_input_data["uniform"]["min"]
76+
max = interpolation_input_data["uniform"]["min"]
77+
Random_variable["mu_"+ str(interpolation_input_data['id'])] = pyro.sample("mu_"+ str(interpolation_input_data['id']), dist.Uniform(min, max))
78+
79+
80+
else:
81+
print("We have to include the distribution")
82+
83+
# # Check which co-ordinates direction we wants to allow and modify the surface point data
84+
counter=counter+1
85+
86+
87+
88+
def GenerateInputSamples(self, number_samples):
89+
90+
pyro.clear_param_store()
91+
# We can build a probabilistic model using pyro by calling it
92+
93+
dot = pyro.render_model(self.create_sample, model_args=())
94+
# Generate 50 samples
95+
num_samples = number_samples # N
96+
predictive = Predictive(self.create_sample, num_samples=num_samples)
97+
samples = predictive()
98+
99+
samples_list=[]
100+
for i in range(len(self.interpolation_input_)):
101+
samples_list.append(samples["mu_"+str(i+1)].reshape(-1,1))
102+
######store the samples ######
103+
parameters=torch.hstack(samples_list) # (N, p) = number of sample X number of paramter
104+
105+
return parameters.detach().numpy()
106+
107+
def GempyForward(self, *params):
108+
index=0
109+
interpolation_input = self.geo_model_test.interpolation_input
110+
111+
for interpolation_input_data in self.interpolation_input_[:self.num_layers]:
112+
# Check which co-ordinates direction we wants to allow and modify the surface point data
113+
if interpolation_input_data["direction"]=="X":
114+
interpolation_input.surface_points.sp_coords = torch.index_put(
115+
interpolation_input.surface_points.sp_coords,
116+
(torch.tensor([interpolation_input_data["id"]]), torch.tensor([0])),
117+
params[index])
118+
elif interpolation_input_data["direction"]=="Y":
119+
interpolation_input.surface_points.sp_coords = torch.index_put(
120+
interpolation_input.surface_points.sp_coords,
121+
(torch.tensor([interpolation_input_data["id"]]), torch.tensor([1])),
122+
params[index])
123+
elif interpolation_input_data["direction"]=="Z":
124+
interpolation_input.surface_points.sp_coords = torch.index_put(
125+
interpolation_input.surface_points.sp_coords,
126+
(interpolation_input_data["id"], torch.tensor([2])),
127+
params[index])
128+
else:
129+
print("Wrong direction")
130+
131+
index=index+1
132+
133+
self.geo_model_test.solutions = gempy_engine.compute_model(
134+
interpolation_input=interpolation_input,
135+
options=self.geo_model_test.interpolation_options,
136+
data_descriptor=self.geo_model_test.input_data_descriptor,
137+
geophysics_input=self.geo_model_test.geophysics_input,
138+
)
139+
140+
# Compute and observe the thickness of the geological layer
141+
142+
m_samples = self.geo_model_test.solutions.octrees_output[0].last_output_center.custom_grid_values
143+
return m_samples
144+
145+
def GenerateOutputSamples(self, Inputs_samples):
146+
147+
Inputs_samples = torch.tensor(Inputs_samples, dtype=self.dtype)
148+
m_data =[]
149+
dmdc_data =[]
150+
for i in range(Inputs_samples.shape[0]):
151+
params_tuple = tuple([Inputs_samples[i,j].clone().requires_grad_(True) for j in range(Inputs_samples.shape[1])])
152+
153+
m_samples = self.GempyForward(*params_tuple)
154+
m_data.append(m_samples)
155+
J = jacobian(self.GempyForward, params_tuple)
156+
J_matrix = torch.tensor([[J[j][i] for j in range(len(J))] for i in range(J[0].shape[0])])
157+
dmdc_data.append(J_matrix)
158+
159+
return torch.stack(m_data).detach().numpy() , torch.stack(dmdc_data).detach().numpy()
160+
161+
162+
163+
164+
def generate_input_output_gempy_data(mesh, number_samples, slope=200, filename=None):
165+
# ---------------- 1️⃣ Check and create a 3D custom grid ----------------
166+
mesh_coordinates = mesh
167+
data ={}
168+
geo_model_test = create_initial_gempy_model(refinement=3, save=False)
169+
if mesh_coordinates.shape[1]==2:
170+
xyz_coord = np.insert(mesh_coordinates, 1, 0, axis=1)
171+
elif mesh_coordinates.shape[1]==3:
172+
xyz_coord = mesh_coordinates
173+
174+
gp.set_custom_grid(geo_model_test.grid, xyz_coord=xyz_coord)
175+
geo_model_test.interpolation_options.mesh_extraction = False
176+
177+
sp_coords_copy_test = geo_model_test.interpolation_input.surface_points.sp_coords.copy()
178+
179+
###############################################################################
180+
# 2️⃣ Make a list of gempy parameter which would be treated as a random variable
181+
###############################################################################
182+
dtype =torch.float64
183+
test_list=[]
184+
std = 0.06
185+
test_list.append({"update":"interface_data","id":torch.tensor([1]), "direction":"Z", "prior_distribution":"normal","normal":{"mean":torch.tensor(sp_coords_copy_test[1,2],dtype=dtype), "std":torch.tensor(std,dtype=dtype)}})
186+
test_list.append({"update":"interface_data","id":torch.tensor([4]), "direction":"Z", "prior_distribution":"normal","normal":{"mean":torch.tensor(sp_coords_copy_test[4,2],dtype=dtype), "std":torch.tensor(std,dtype=dtype)}})
187+
num_layers = len(test_list) # length of the list
188+
189+
Gempy = GempyModel(test_list, geo_model_test, num_layers, slope=slope, dtype=torch.float64)
190+
191+
# ---------------- 3️⃣ Generate the samples for input parameters. c ∼ N(µ, Σ) ----------------
192+
193+
start_sample_input_time = datetime.now()
194+
c = Gempy.GenerateInputSamples(number_samples=number_samples)
195+
end_sample_input_time = datetime.now()
196+
total_time_input = end_sample_input_time - start_sample_input_time
197+
198+
data["input"] = c.tolist()
199+
# ---------------- 3️⃣ Generate the samples for output of gempy and the Jacobian matrix. m= Gempy(c) and dm/dc ----------------
200+
start_sample_output_time = datetime.now()
201+
m_data, dmdc_data = Gempy.GenerateOutputSamples(Inputs_samples=c)
202+
end_sample_output_time = datetime.now()
203+
total_time_output = end_sample_output_time - start_sample_output_time
204+
205+
data["Gempy_output"] = m_data.tolist()
206+
data["Jacobian_Gempy"] = dmdc_data.tolist()
207+
208+
return data, total_time_input, total_time_output
209+
210+
211+

test/test_gradient/helpers.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
import numpy as np
3+
4+
import matplotlib.pyplot as plt
5+
6+
import gempy as gp
7+
import gempy_viewer as gpv
8+
9+
10+
11+
def create_initial_gempy_model(refinement,filename='prior_model.png', save=True):
12+
""" Create an initial gempy model objet
13+
14+
Args:
15+
refinement (int): Refinement of grid
16+
save (bool, optional): Whether you want to save the image
17+
18+
"""
19+
geo_model_test = gp.create_geomodel(
20+
project_name='Gempy_abc_Test',
21+
extent=[0, 1, -0.1, 0.1, 0, 1],
22+
resolution=[100,10,100],
23+
refinement=refinement,
24+
structural_frame= gp.data.StructuralFrame.initialize_default_structure()
25+
)
26+
27+
brk1 = 0.3
28+
brk2 = 0.5
29+
30+
31+
gp.add_surface_points(
32+
geo_model=geo_model_test,
33+
x=[0.1,0.5, 0.9],
34+
y=[0.0, 0.0, 0.0],
35+
z=[brk1, brk1 , brk1],
36+
elements_names=['surface1','surface1', 'surface1']
37+
)
38+
39+
gp.add_orientations(
40+
geo_model=geo_model_test,
41+
x=[0.5],
42+
y=[0.0],
43+
z=[0.0],
44+
elements_names=['surface1'],
45+
pole_vector=[[0, 0, 0.5]]
46+
)
47+
geo_model_test.update_transform(gp.data.GlobalAnisotropy.NONE)
48+
49+
element2 = gp.data.StructuralElement(
50+
name='surface2',
51+
color=next(geo_model_test.structural_frame.color_generator),
52+
surface_points=gp.data.SurfacePointsTable.from_arrays(
53+
x=np.array([0.1,0.5, 0.9]),
54+
y=np.array([0.0, 0.0, 0.0]),
55+
z=np.array([brk2, brk2, brk2]),
56+
names='surface2'
57+
),
58+
orientations=gp.data.OrientationsTable.initialize_empty()
59+
)
60+
61+
62+
geo_model_test.update_transform(gp.data.GlobalAnisotropy.NONE)
63+
64+
geo_model_test.structural_frame.structural_groups[0].append_element(element2)
65+
gp.add_orientations(
66+
geo_model=geo_model_test,
67+
x=[0.5],
68+
y=[0.0],
69+
z=[1.0],
70+
elements_names=['surface2'],
71+
pole_vector=[[0, 0, 0.5]]
72+
)
73+
geo_model_test.structural_frame.structural_groups[0].elements[0], geo_model_test.structural_frame.structural_groups[0].elements[1] = \
74+
geo_model_test.structural_frame.structural_groups[0].elements[1], geo_model_test.structural_frame.structural_groups[0].elements[0]
75+
76+
gp.compute_model(geo_model_test)
77+
if save:
78+
picture_test = gpv.plot_2d(geo_model_test, cell_number=5, legend='force')
79+
plt.savefig(filename)
80+
81+
return geo_model_test

test/test_gradient/prior_model.png

28.5 KB
Loading

test/test_gradient/save_gempy_data.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import os,sys
2+
from datetime import datetime
3+
import numpy as np
4+
5+
import gempy as gp
6+
import gempy_engine
7+
import gempy_viewer as gpv
8+
9+
10+
11+
12+
from helpers import *
13+
from generate_samples import *
14+
15+
16+
import warnings
17+
warnings.filterwarnings("ignore")
18+
19+
20+
def main():
21+
22+
23+
# ---------------- 1️⃣ Create the Mesh ----------------
24+
25+
26+
n_points = 32 # number of points along each axis
27+
28+
x = np.linspace(0, 1, n_points)
29+
z = np.linspace(0, 1, n_points)
30+
31+
X, Z = np.meshgrid(x, z) # creates grid of shape (n_points, n_points)
32+
33+
mesh = np.stack([X.ravel(), Z.ravel()], axis=1) # shape: (n_points^2, 2)
34+
35+
# ---------------- 2️⃣ Generate samples for the input paramter of the gempy, output and it's jacobian ----------------
36+
data, total_time_input, total_time_output = generate_input_output_gempy_data(mesh=mesh, number_samples=50)
37+
38+
39+
filename = "./data_9_parameter.json"
40+
c ,m_data, dmdc_data = np.array(data["input"]),np.array(data["Gempy_output"]), np.array(data["Jacobian_Gempy"])
41+
print("Shapes-" , "Gempy Input: ", c.shape, "Gempy Output:", m_data.shape, "Jacobian shape:", dmdc_data.shape, "\n")
42+
43+
print("Time required to generate samples for the input:\n", total_time_input)
44+
print("Time required to generate samples for the output of gempy and it's Jacobian matrix:\n", total_time_output)
45+
46+
# ---------------- 3️⃣ Save the samples in a file ----------------
47+
# with open(filename, 'w') as file:
48+
# json.dump(data, file)
49+
50+
51+
52+
if __name__ == "__main__":
53+
54+
# Your main script code starts here
55+
print("Script started...")
56+
57+
# Record the start time
58+
start_time = datetime.now()
59+
60+
main()
61+
# Record the end time
62+
end_time = datetime.now()
63+
64+
# Your main script code ends here
65+
print("Script ended...")
66+
67+
# Calculate the elapsed time
68+
elapsed_time = end_time - start_time
69+
70+
print(f"Elapsed time: {elapsed_time}")

0 commit comments

Comments
 (0)