fabiencasenave commited on
Commit
f4d7da3
0 Parent(s):

initial commit

Browse files
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM continuumio/anaconda3:main
5
+
6
+ WORKDIR /code
7
+ COPY ./environment.yml /code/environment.yml
8
+
9
+ RUN apt-get update && apt-get install -y libgles2-mesa-dev
10
+
11
+ # Create the environment using the environment.yml file
12
+ RUN conda env create -f /code/environment.yml
13
+
14
+ # Set up a new user named "user" with user ID 1000
15
+ RUN useradd -m -u 1000 user
16
+ # Switch to the "user" user
17
+ USER user
18
+ # Set home to the user's home directory
19
+ ENV HOME=/home/user \
20
+ PYTHONPATH=$HOME/app \
21
+ PYTHONUNBUFFERED=1 \
22
+ GRADIO_ALLOW_FLAGGING=never \
23
+ GRADIO_NUM_PORTS=1 \
24
+ GRADIO_SERVER_NAME=0.0.0.0 \
25
+ GRADIO_THEME=huggingface \
26
+ SYSTEM=spaces
27
+
28
+ # Set the working directory to the user's home directory
29
+ WORKDIR $HOME/app
30
+
31
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
32
+ COPY --chown=user . $HOME/app
33
+
34
+ CMD ["./run.sh"]
README.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: MMGP demo
3
+ emoji: 🚀
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: docker
7
+ pinned: false
8
+ app_port: 7860
9
+ ---
10
+
11
+ This space provides a demo of a experiment from [the paper](https://arxiv.org/abs/2305.12871):
12
+
13
+ ```
14
+ @misc{casenave2023mmgpmeshmorphinggaussian,
15
+ title={MMGP: a Mesh Morphing Gaussian Process-based machine learning method for regression of physical problems under non-parameterized geometrical variability},
16
+ author={Fabien Casenave and Brian Staber and Xavier Roynard},
17
+ year={2023},
18
+ eprint={2305.12871},
19
+ archivePrefix={arXiv},
20
+ primaryClass={cs.LG},
21
+ url={https://arxiv.org/abs/2305.12871},
22
+ }
23
+ ```
app.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pickle
3
+ from datasets import load_dataset
4
+ from plaid.containers.sample import Sample
5
+
6
+
7
+ import numpy as np
8
+ import pyrender
9
+ from trimesh import Trimesh
10
+ import matplotlib as mpl
11
+ import matplotlib.cm as cm
12
+
13
+ from utils_inference import infer
14
+
15
+
16
+ import os
17
+ # switch to "osmesa" or "egl" before loading pyrender
18
+ os.environ["PYOPENGL_PLATFORM"] = "egl"
19
+
20
+
21
+ hf_dataset = load_dataset("PLAID-datasets/AirfRANS_remeshed", split="all_samples")
22
+
23
+ file = open('training_data.pkl', 'rb')
24
+ training_data = pickle.load(file)
25
+ file.close()
26
+
27
+ train_ids = hf_dataset.description['split']['ML4PhySim_Challenge_train']
28
+
29
+ out_fields_names = hf_dataset.description['out_fields_names']
30
+ in_scalars_names = hf_dataset.description['in_scalars_names']
31
+ out_scalars_names = hf_dataset.description['out_scalars_names']
32
+
33
+
34
+ nb_samples = len(hf_dataset)
35
+
36
+ # <h2><b><a href='https://arxiv.org/abs/2305.12871' target='_blank'><b>MMGP</b> demo on the <a href='https://huggingface.co/datasets/PLAID-datasets/AirfRANS_remeshed' target='_blank'><b>AirfRANS_remeshed dataset</b></b></h2>
37
+
38
+ # <a href='https://arxiv.org/abs/2305.12871' target='_blank'><b>MMGP paper</b>,
39
+
40
+ _HEADER_ = '''
41
+ <h2><b>MMGP demo on the <a href='https://huggingface.co/datasets/PLAID-datasets/AirfRANS_remeshed' target='_blank'><b>AirfRANS_remeshed dataset</b></b></h2>
42
+ '''
43
+
44
+ _HEADER_2 = '''
45
+ The model is already trained. The morphing is the same as the one used in the [MMGP paper](https://arxiv.org/abs/2305.12871),
46
+ but is much less involved than the one used in the winning entry of the [ML4PhySim competition](https://www.codabench.org/competitions/1534/).
47
+ The training set has 103 samples and is the one used in this competition (some evaluations are out-of-distribution).
48
+
49
+ The inference takes approx 5 seconds, and is done from scratch (no precomputation is used during the inference when evaluating samples).
50
+ This means that the morphing and the finite element interpolations are re-done at each evaluation.
51
+
52
+ After choosing a sample id, please change the field name in the dropdown menu to update the visualization.
53
+ '''
54
+
55
+
56
+ def round_num(num)->str:
57
+ return '%s' % float('%.3g' % num)
58
+
59
+
60
+ def compute_inference(sample_id_str):
61
+ sample_id = int(sample_id_str)
62
+
63
+ sample_ = hf_dataset[sample_id]["sample"]
64
+ plaid_sample = Sample.model_validate(pickle.loads(sample_))
65
+
66
+ prediction = infer(hf_dataset, sample_id, training_data)
67
+ reference = {fieldn:plaid_sample.get_field(fieldn) for fieldn in out_fields_names}
68
+
69
+ nodes = plaid_sample.get_nodes()
70
+ if nodes.shape[1] == 2:
71
+ nodes__ = np.zeros((nodes.shape[0],nodes.shape[1]+1))
72
+ nodes__[:,:-1] = nodes
73
+ nodes = nodes__
74
+
75
+ triangles = plaid_sample.get_elements()['TRI_3']
76
+ trimesh = Trimesh(vertices = nodes, faces = triangles)
77
+
78
+ file = open('computed_inference.pkl', 'wb')
79
+ pickle.dump([trimesh, reference, prediction], file)
80
+ file.close()
81
+
82
+
83
+ str__ = f"Training sample {sample_id_str}"
84
+
85
+ if sample_id in train_ids:
86
+ str__ += " (in the training set)\n\n"
87
+ else:
88
+ str__ += " (not in the training set)\n\n"
89
+
90
+ str__ += str(plaid_sample)+"\n"
91
+
92
+
93
+ if len(hf_dataset.description['in_scalars_names'])>0:
94
+ str__ += "\nInput scalars:\n"
95
+ for sname in hf_dataset.description['in_scalars_names']:
96
+ str__ += f"- {sname}: {round_num(plaid_sample.get_scalar(sname))}\n"
97
+
98
+ str__ += f"\nNumber of nodes in the mesh: {nodes.shape[0]}"
99
+
100
+ return str__
101
+
102
+
103
+ def show_pred(fieldn):
104
+
105
+ file = open('computed_inference.pkl', 'rb')
106
+ data = pickle.load(file)
107
+ file.close()
108
+
109
+ trimesh, reference, prediction = data[0], data[1], data[2]
110
+
111
+ ref = reference[fieldn]
112
+ pred = prediction[fieldn]
113
+
114
+ norm = mpl.colors.Normalize(vmin=np.min(ref), vmax=np.max(ref))
115
+ cmap = cm.seismic#cm.coolwarm
116
+ m = cm.ScalarMappable(norm=norm, cmap=cmap)
117
+ vertex_colors = m.to_rgba(pred)[:,:3]
118
+
119
+ trimesh.visual.vertex_colors = vertex_colors
120
+
121
+ mesh = pyrender.Mesh.from_trimesh(trimesh, smooth=False)
122
+
123
+ # compose scene
124
+ scene = pyrender.Scene(ambient_light=[.1, .1, .3], bg_color=[0, 0, 0])
125
+ camera = pyrender.PerspectiveCamera( yfov=np.pi / 3.0)
126
+ light = pyrender.DirectionalLight(color=[1,1,1], intensity=1000.)
127
+
128
+ scene.add(mesh, pose= np.eye(4))
129
+ scene.add(light, pose= np.eye(4))
130
+
131
+ scene.add(camera, pose=[[ 1, 0, 0, 1],
132
+ [ 0, 1, 0, 0],
133
+ [ 0, 0, 1, 6],
134
+ [ 0, 0, 0, 1]])
135
+
136
+ # render scene
137
+ r = pyrender.OffscreenRenderer(1024, 1024)
138
+ color, _ = r.render(scene)
139
+
140
+ return color
141
+
142
+
143
+ def show_ref(fieldn):
144
+
145
+ file = open('computed_inference.pkl', 'rb')
146
+ data = pickle.load(file)
147
+ file.close()
148
+
149
+ trimesh, reference, prediction = data[0], data[1], data[2]
150
+
151
+ ref = reference[fieldn]
152
+
153
+ norm = mpl.colors.Normalize(vmin=np.min(ref), vmax=np.max(ref))
154
+ cmap = cm.seismic#cm.coolwarm
155
+ m = cm.ScalarMappable(norm=norm, cmap=cmap)
156
+ vertex_colors = m.to_rgba(ref)[:,:3]
157
+
158
+ trimesh.visual.vertex_colors = vertex_colors
159
+
160
+ mesh = pyrender.Mesh.from_trimesh(trimesh, smooth=False)
161
+
162
+ # compose scene
163
+ scene = pyrender.Scene(ambient_light=[.1, .1, .3], bg_color=[0, 0, 0])
164
+ camera = pyrender.PerspectiveCamera( yfov=np.pi / 3.0)
165
+ light = pyrender.DirectionalLight(color=[1,1,1], intensity=1000.)
166
+
167
+ scene.add(mesh, pose= np.eye(4))
168
+ scene.add(light, pose= np.eye(4))
169
+
170
+ scene.add(camera, pose=[[ 1, 0, 0, 1],
171
+ [ 0, 1, 0, 0],
172
+ [ 0, 0, 1, 6],
173
+ [ 0, 0, 0, 1]])
174
+
175
+ # render scene
176
+ r = pyrender.OffscreenRenderer(1024, 1024)
177
+ color, _ = r.render(scene)
178
+
179
+ return color
180
+
181
+
182
+ def show_err(fieldn):
183
+
184
+ file = open('computed_inference.pkl', 'rb')
185
+ data = pickle.load(file)
186
+ file.close()
187
+
188
+ trimesh, reference, prediction = data[0], data[1], data[2]
189
+
190
+ ref = reference[fieldn]
191
+ pred = prediction[fieldn]
192
+
193
+ norm = mpl.colors.Normalize(vmin=np.min(ref), vmax=np.max(ref))
194
+ cmap = cm.seismic#cm.coolwarm
195
+ m = cm.ScalarMappable(norm=norm, cmap=cmap)
196
+ vertex_colors = m.to_rgba(pred-ref)[:,:3]
197
+
198
+ trimesh.visual.vertex_colors = vertex_colors
199
+
200
+ mesh = pyrender.Mesh.from_trimesh(trimesh, smooth=False)
201
+
202
+ # compose scene
203
+ scene = pyrender.Scene(ambient_light=[.1, .1, .3], bg_color=[0, 0, 0])
204
+ camera = pyrender.PerspectiveCamera( yfov=np.pi / 3.0)
205
+ light = pyrender.DirectionalLight(color=[1,1,1], intensity=1000.)
206
+
207
+ scene.add(mesh, pose= np.eye(4))
208
+ scene.add(light, pose= np.eye(4))
209
+
210
+ scene.add(camera, pose=[[ 1, 0, 0, 1],
211
+ [ 0, 1, 0, 0],
212
+ [ 0, 0, 1, 6],
213
+ [ 0, 0, 0, 1]])
214
+
215
+ # render scene
216
+ r = pyrender.OffscreenRenderer(1024, 1024)
217
+ color, _ = r.render(scene)
218
+
219
+ return color
220
+
221
+
222
+ if __name__ == "__main__":
223
+
224
+ with gr.Blocks() as demo:
225
+
226
+ # trimesh, reference, prediction = compute_inference(0)
227
+ gr.Markdown(_HEADER_)
228
+ gr.Markdown(_HEADER_2)
229
+ with gr.Row(variant="panel"):
230
+ with gr.Column():
231
+ d1 = gr.Slider(0, nb_samples-1, value=0, label="Training sample id", info="Choose between 0 and "+str(nb_samples-1))
232
+ # output1 = gr.Text(label="Inference status")
233
+ output4 = gr.Text(label="Information on sample")
234
+ output5 = gr.Image(label="Error")
235
+ with gr.Column():
236
+ d2 = gr.Dropdown(out_fields_names, value=out_fields_names[0], label="Field name")
237
+ output2 = gr.Image(label="Reference")
238
+ output3 = gr.Image(label="MMGP prediction")
239
+
240
+ # d1.input(compute_inference, [d1], [output1, output4])
241
+ d1.input(compute_inference, [d1], [output4])
242
+ d2.input(show_ref, [d2], [output2])
243
+ d2.input(show_pred, [d2], [output3])
244
+ d2.input(show_err, [d2], [output5])
245
+
246
+ demo.launch()
environment.yml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: demo
2
+ channels:
3
+ - conda-forge
4
+ - nodefaults
5
+
6
+ dependencies:
7
+ - python=3.11
8
+ - muscat
9
+ - plaid
10
+ - datasets
11
+ - pyrender
12
+ - matplotlib
13
+ - gradio
14
+ - pytorch
15
+ # - pyopengl
gp_torch.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import torch
3
+ import torch.nn as nn
4
+
5
+ class GaussianProcessRegressor(nn.Module):
6
+ def __init__(
7
+ self,
8
+ length_scale=1.0,
9
+ noise_scale=1.0,
10
+ amplitude_scale=1.0,
11
+ ):
12
+ super().__init__()
13
+ if isinstance(length_scale, float):
14
+ length_scale = np.array([length_scale])
15
+ elif isinstance(length_scale, np.ndarray):
16
+ assert length_scale.ndim == 1
17
+ else:
18
+ raise TypeError()
19
+
20
+ self.register_parameter(
21
+ "length_scale_",
22
+ param=nn.Parameter(torch.Tensor(np.log(length_scale)), requires_grad=True),
23
+ )
24
+ self.register_parameter(
25
+ "noise_scale_",
26
+ param=nn.Parameter(torch.tensor(np.log(noise_scale)), requires_grad=True),
27
+ )
28
+ self.register_parameter(
29
+ "amplitude_scale_",
30
+ param=nn.Parameter(
31
+ torch.tensor(np.log(amplitude_scale)), requires_grad=True
32
+ ),
33
+ )
34
+
35
+ self.nll = None
36
+
37
+ def forward(self, x):
38
+ alpha = self.alpha
39
+ k = self.Kxy(self.X, x)
40
+ mu = k.T.mm(alpha)
41
+ return mu
42
+
43
+ def log_marginal_likelihood(self, X, y):
44
+ D = X.shape[1]
45
+ K = self.Kxx(X)
46
+ L = torch.linalg.cholesky(K)
47
+ alpha = torch.linalg.solve(L.T, torch.linalg.solve(L, y))
48
+ marginal_likelihood = (
49
+ -0.5 * y.T.mm(alpha)
50
+ - torch.log(torch.diag(L)).sum()
51
+ - D * 0.5 * np.log(2 * np.pi)
52
+ )
53
+ self.L = L
54
+ self.alpha = alpha
55
+ self.K = K
56
+ return marginal_likelihood
57
+
58
+ def Kxx(self, X):
59
+ param = self.length_scale_.exp().sqrt()
60
+ sqdist = torch.cdist(X / param[None], X / param[None]) ** 2
61
+
62
+ res = self.amplitude_scale_.exp() * torch.exp(-0.5 * sqdist) + self.noise_scale_.exp() * torch.eye(len(X)).type_as(X)
63
+
64
+ return res
65
+
66
+ def Kxy(self, X, Z):
67
+ param = self.length_scale_.exp().sqrt()
68
+ sqdist = torch.cdist(X / param[None], Z / param[None]) ** 2
69
+ res = self.amplitude_scale_.exp() * torch.exp(-0.5 * sqdist)
70
+
71
+ return res
72
+
73
+ def fit(self, X, y, opt, num_steps):
74
+ assert X.shape[1] == len(self.length_scale_)
75
+ self.y = y
76
+ self.X = X
77
+
78
+ scheduler = torch.optim.lr_scheduler.ExponentialLR(opt, gamma=0.9)
79
+
80
+ self.train()
81
+ nll_hist = []
82
+ for it in range(num_steps):
83
+ opt.zero_grad()
84
+ try:
85
+ nll = -self.log_marginal_likelihood(self.X, self.y).sum()
86
+ except torch.linalg.LinAlgError:
87
+ break
88
+ nll.backward()
89
+ opt.step()
90
+ if it%10==0 and it<1000:
91
+ scheduler.step()
92
+ nll_hist.append(nll.item())
93
+ return nll_hist
old_gradio_config/default_docker_config.txt ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FROM python:3.10
2
+
3
+ # RUN useradd -m -u 1000 user
4
+
5
+ # RUN apt-get update && apt-get install -y fakeroot && mv /usr/bin/apt-get /usr/bin/.apt-get && echo '#!/usr/bin/env sh\nfakeroot /usr/bin/.apt-get $@' > /usr/bin/apt-get && chmod +x /usr/bin/apt-get && rm -rf /var/lib/apt/lists/* && useradd -m -u 1000 user
6
+
7
+ # COPY --chown=1000:1000 --from=root / /
8
+
9
+ # WORKDIR /home/user/app
10
+
11
+ # RUN apt-get update && apt-get install -y git git-lfs ffmpeg libsm6 libxext6 cmake rsync libgl1-mesa-glx libgles2-mesa-dev && rm -rf /var/lib/apt/lists/* && git lfs install
12
+
13
+ # RUN pip install --no-cache-dir pip==22.3.1 && pip install --no-cache-dir datasets "huggingface-hub>=0.19" "hf-transfer>=0.1.4" "protobuf<4" "click<8.1" "pydantic~=1.0"
14
+
15
+ # RUN --mount=target=/tmp/pre-requirements.txt,source=pre-requirements.txt pip install --no-cache-dir -r /tmp/pre-requirements.txt
16
+
17
+ # RUN --mount=target=/tmp/requirements.txt,source=requirements.txt pip install --no-cache-dir -r /tmp/requirements.txt
18
+
19
+ # RUN export MUSCAT_USE_EIGENCYEIGEN=1
20
+
21
+ # RUN wget https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.zip
22
+
23
+ # RUN unzip boost_1_82_0.zip
24
+
25
+ # RUN MUSCAT_USE_EIGENCYEIGEN=1
26
+
27
+ # RUN MUSCAT_EXTRA_INCLUDE_DIRS=%cd%\boost_1_82_0
28
+
29
+ # RUN pip install Muscat@https://gitlab.com/drti/muscat/-/archive/2.2.1/muscat-2.0.2.tar.bz2
30
+
31
+ # RUN pip install --no-cache-dir gradio[oauth]==4.37.1 "uvicorn>=0.14.0" spaces
32
+
33
+ # COPY --link --chown=1000 ./ /home/user/app
34
+
35
+ # RUN pip freeze > /tmp/freeze.txt
36
+
37
+ # COPY --from=pipfreeze --link --chown=1000 /tmp/freeze.txt /tmp/freeze.txt
38
+
39
+
40
+
41
+
42
+ # # Set up a new user named "user" with user ID 1000
43
+ # RUN useradd -m -u 1000 user
44
+
45
+ # # Switch to the "user" user
46
+ # USER user
47
+
48
+ # # Set home to the user's home directory
49
+ # ENV HOME=/home/user \
50
+ # PATH=/home/user/.local/bin:$PATH
51
+
52
+ # # Set the working directory to the user's home directory
53
+ # WORKDIR $HOME/app
54
+
55
+ # # Copy the current directory contents into the container at $HOME/app setting the owner to the user
56
+ # COPY --chown=user . $HOME/app
57
+
58
+ # CMD ["python", "app.py"]
old_gradio_config/packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ libgles2-mesa-dev
old_gradio_config/pre-requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ eigency
2
+ mkl
3
+ numpy
4
+ sympy
5
+ mkl-include
6
+ cython
7
+ wheel
8
+ tbb-devel
9
+ dill
old_gradio_config/requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ git+https://gitlab.com/drti/plaid@version_0.0.10_python310
2
+ numpy==1.26.4
3
+ tqdm
4
+ pyyaml
5
+ pycgns
6
+ scikit-learn
7
+ datasets
8
+ pyrender
9
+ matplotlib
run.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ CONDA_ENV=$(head -1 /code/environment.yml | cut -d" " -f2)
3
+ eval "$(conda shell.bash hook)"
4
+ conda activate $CONDA_ENV
5
+ python app.py
training_data.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5a70d30164ee7489c55edb72d37a48c78a62a9d846b2fd2bda55574984537244
3
+ size 22328714
utils_inference.py ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Muscat.Containers.ElementsDescription as ED
2
+
3
+ from Muscat.FE.FETools import PrepareFEComputation
4
+ from Muscat.FE.Fields.FEField import FEField
5
+ import Muscat.Containers.MeshModificationTools as MMT
6
+
7
+
8
+ from Muscat.Containers import MeshGraphTools as MGT
9
+ from Muscat.Containers.Filters import FilterObjects as FO
10
+ from Muscat.Containers.Filters import FilterOperators as FOp
11
+ import Muscat.Containers.MeshInspectionTools as MIT
12
+
13
+
14
+ out_fields_names = ['Ux', 'Uy', 'p', 'nut']
15
+ in_scalars_names = ['angle_of_attack', 'inlet_velocity']
16
+
17
+ from Muscat.Containers.NativeTransfer import NativeTransfer
18
+ import copy
19
+ import bisect
20
+ import numpy as np
21
+ from scipy import spatial
22
+ import pickle
23
+ from Muscat.Bridges.CGNSBridge import CGNSToMesh
24
+
25
+
26
+ import torch
27
+ device = torch.device("cpu")
28
+
29
+
30
+ num_steps = [2_000]
31
+
32
+ from plaid.containers.dataset import Sample
33
+
34
+
35
+
36
+
37
+
38
+ def pretreat_sample(mesh):
39
+ ###################
40
+ # Compute the skin of the mesh (containing the external boundary and the airfoil boundary)
41
+ ###################
42
+ MMT.ComputeSkin(mesh, md = 2, inPlace = True)
43
+
44
+ ###################
45
+ # Extract ids of the bar elements corresponding to the airfoil
46
+ ###################
47
+ ff1 = FO.ElementFilter(zone = lambda p: (-p[:,0]-1.99))
48
+ ff2 = FO.ElementFilter(zone = lambda p: (p[:,0]-3.99))
49
+ ff3 = FO.ElementFilter(zone = lambda p: (-p[:,1]-1.49))
50
+ ff4 = FO.ElementFilter(zone = lambda p: (p[:,1]-1.49))
51
+ efAirfoil = FOp.IntersectionFilter(filters=[ff1, ff2, ff3, ff4])
52
+ airfoil_ids = efAirfoil.GetIdsToTreat(mesh, "bar2")
53
+
54
+ ###################
55
+ # Preparations
56
+ ###################
57
+ ext_bound = np.setdiff1d(mesh.elements["bar2"].GetTag("Skin").GetIds(), airfoil_ids)
58
+ mesh.elements["bar2"].GetTag("External_boundary").SetIds(ext_bound)
59
+ mesh.elements["bar2"].GetTag("Airfoil").SetIds(airfoil_ids)
60
+ nfExtBoundary = FO.NodeFilter(eTag = "External_boundary")
61
+ nodeIndexExtBoundary = nfExtBoundary.GetNodesIndices(mesh)
62
+ mesh.GetNodalTag("External_boundary").AddToTag(nodeIndexExtBoundary)
63
+ nfAirfoil = FO.NodeFilter(eTag = "Airfoil")
64
+ nodeIndexAirfoil = nfAirfoil.GetNodesIndices(mesh)
65
+ mesh.GetNodalTag("Airfoil").AddToTag(nodeIndexAirfoil)
66
+
67
+
68
+ ###################
69
+ # Add node tag for the intrado and extrado
70
+ ###################
71
+ nfAirfoil = FO.NodeFilter(eTag = "Airfoil")
72
+ nodeIndexAirfoil = nfAirfoil.GetNodesIndices(mesh)
73
+ mesh.GetNodalTag("Airfoil").AddToTag(nodeIndexAirfoil)
74
+
75
+ airfoil = ExtractAirfoil(mesh)
76
+ indices_extrado = airfoil[0][0]
77
+ indices_intrado = airfoil[0][1]
78
+ mesh.GetNodalTag("Extrado").AddToTag(indices_extrado)
79
+ mesh.GetNodalTag("Intrado").AddToTag(indices_intrado)
80
+
81
+ efExtrado = FO.ElementFilter(nTag = "Extrado")
82
+ efIntrado = FO.ElementFilter(nTag = "Intrado")
83
+ mesh.elements["bar2"].GetTag("Extrado").SetIds(efExtrado.GetIdsToTreat(mesh, "bar2"))
84
+ mesh.elements["bar2"].GetTag("Intrado").SetIds(efIntrado.GetIdsToTreat(mesh, "bar2"))
85
+
86
+
87
+ def ExtractPathFromMeshOfBars(mesh, startingClosestToPoint, trigo_dir = True):
88
+
89
+ nodeGraph0Airfoild = MGT.ComputeNodeToNodeGraph(mesh, dimensionality=1)
90
+ nodeGraphAirfoild = [list(nodeGraph0Airfoild[i].keys()) for i in range(nodeGraph0Airfoild.number_of_nodes())]
91
+
92
+ tree = spatial.KDTree(mesh.nodes)
93
+ _, indicesTrailEdge = tree.query([startingClosestToPoint], k=1)
94
+
95
+ p1init = indicesTrailEdge[0]
96
+
97
+ temp1=mesh.nodes[nodeGraphAirfoild[p1init][0]][1]
98
+ temp2=mesh.nodes[nodeGraphAirfoild[p1init][1]][1]
99
+
100
+ if trigo_dir:
101
+ condition = temp1 > temp2
102
+ else:
103
+ condition = temp1 < temp2
104
+
105
+ if condition:
106
+ p2 = nodeGraphAirfoild[p1init][0]
107
+ else:
108
+ p2 = nodeGraphAirfoild[p1init][1]
109
+
110
+ p1 = p1init
111
+ path = [p1, p2]
112
+ while p2 != p1init:
113
+ p2save = p2
114
+ tempArray = np.asarray(nodeGraphAirfoild[p2])
115
+ p2 = tempArray[tempArray!=p1][0]
116
+ p1 = p2save
117
+ path.append(p2)
118
+
119
+ return path
120
+
121
+
122
+ def ExtractAirfoil(mesh):
123
+
124
+ efAirfoil = FO.ElementFilter(elementType=ED.Bar_2, eTag=["Airfoil"])
125
+ airfoilMesh = MIT.ExtractElementsByElementFilter(mesh, efAirfoil)
126
+
127
+ path = ExtractPathFromMeshOfBars(airfoilMesh, np.array([1.,0.]))
128
+
129
+ tree = spatial.KDTree(airfoilMesh.nodes[path])
130
+ _, indicesLeadEdge = tree.query([[0.,0.]], k=1)
131
+
132
+ indices_extrado = path[:indicesLeadEdge[0]+1]
133
+ indices_intrado = path[indicesLeadEdge[0]:]
134
+
135
+ indices_airfoil = [indices_extrado, indices_intrado]
136
+
137
+ nodes_extrado = mesh.nodes[indices_extrado]
138
+ nodes_intrado = mesh.nodes[indices_intrado]
139
+
140
+ nodes_airfoil = [nodes_extrado, nodes_intrado]
141
+
142
+ return indices_airfoil, nodes_airfoil
143
+
144
+
145
+ def computeAirfoilCurvAbscissa(airfoil):
146
+
147
+ indices_airfoil = airfoil[0]
148
+ nodes_airfoil = airfoil[1]
149
+
150
+ curv_abscissa = []
151
+ for i in range(2):
152
+ local_curv_abscissa = np.zeros(len(indices_airfoil[i]))
153
+ for j in range(1,len(local_curv_abscissa)):
154
+ local_curv_abscissa[j] = local_curv_abscissa[j-1] + np.linalg.norm(nodes_airfoil[i][j]-nodes_airfoil[i][j-1])
155
+ local_curv_abscissa /= local_curv_abscissa[-1]
156
+ curv_abscissa.append(local_curv_abscissa)
157
+
158
+ return curv_abscissa
159
+
160
+
161
+ def MapAirfoil(airfoil_ref, curv_abscissa_ref, curv_abscissa):
162
+
163
+ nodes_airfoil_ref = airfoil_ref[1]
164
+ dim_nodes = nodes_airfoil_ref[0][0].shape[0]
165
+
166
+ mapped_airfoil = []
167
+ for i in range(2):
168
+ local_mapped_airfoil = np.zeros((len(curv_abscissa[i])-1, dim_nodes))
169
+ for j in range(len(curv_abscissa[i])-1):
170
+ index = max(bisect.bisect_right(curv_abscissa_ref[i], curv_abscissa[i][j]) - 1, 0)
171
+
172
+ a = nodes_airfoil_ref[i][index]
173
+ b = nodes_airfoil_ref[i][index+1]
174
+ dl = curv_abscissa[i][j] - curv_abscissa_ref[i][index]
175
+ dir = (b-a)/np.linalg.norm(b-a)
176
+ local_mapped_airfoil[j] = a + dl * dir
177
+ mapped_airfoil.append(local_mapped_airfoil)
178
+
179
+ return mapped_airfoil
180
+
181
+
182
+
183
+
184
+ def GetFieldTransferOpCppStep1(inputField, nbThreads = None):
185
+
186
+ method="Interp/Clamp"
187
+
188
+ nt = NativeTransfer()
189
+
190
+ if nbThreads is not None:
191
+ nt.SetMaxConcurrency(nbThreads)
192
+
193
+ nt.SetTransferMethod(method)
194
+ defaultOptions = {"usePointSearch": True,
195
+ "useElementSearch": False,
196
+ "useElementSearchFast": False,
197
+ "useEdgeSearch": True,
198
+ }
199
+
200
+ options = {}
201
+
202
+ defaultOptions.update(options)
203
+
204
+ dispatch = {"usePointSearch": nt.SetUsePointSearch,
205
+ "useElementSearch": nt.SetUseElementSearch,
206
+ "useElementSearchFast": nt.SetUseElementSearchFast,
207
+ "useEdgeSearch": nt.SetUseEdgeSearch,
208
+ "DifferentialOperator": nt.SetDifferentialOperator,
209
+ }
210
+
211
+ for k, v in defaultOptions.items():
212
+ if k in dispatch.keys():
213
+ dispatch[k](v)
214
+ else:
215
+ raise RuntimeError(f"Option {k} not valid")
216
+
217
+ nt.SetSourceFEField(inputField, None)
218
+
219
+ return nt
220
+
221
+
222
+
223
+ def GetFieldTransferOpCppStep2(nt, targetPoints):
224
+
225
+ nt.SetTargetPoints(targetPoints)
226
+
227
+ nt.Compute()
228
+ op = nt.GetOperator()
229
+ status = nt.GetStatus()
230
+ return op, status
231
+
232
+
233
+
234
+
235
+ def morph_sample(mesh, airfoil_0, curv_abscissa_0):
236
+
237
+ airfoil_1 = ExtractAirfoil(mesh)
238
+ curv_abscissa_1 = computeAirfoilCurvAbscissa(airfoil_1)
239
+
240
+ mapped_airfoil = MapAirfoil(airfoil_0, curv_abscissa_0, curv_abscissa_1)
241
+
242
+ ##############################################################
243
+ # Compute global target displacement and masks for RBF field morphing
244
+ ##############################################################
245
+ indices_extrado_to_morph_1 = airfoil_1[0][0][:-1]
246
+ indices_intrado_to_morph_1 = airfoil_1[0][1][:-1]
247
+ other_boundary_ids_1 = mesh.GetNodalTag("External_boundary").GetIds()
248
+
249
+ l1 = len(indices_extrado_to_morph_1)
250
+ l2 = len(indices_intrado_to_morph_1)
251
+ l3 = len(other_boundary_ids_1)
252
+
253
+ targetDisplacement = np.zeros((l1 + l2 + l3, 2))
254
+ targetDisplacementMask = np.zeros((l1 + l2 + l3), dtype = int)
255
+
256
+ targetDisplacement[:l1] = mapped_airfoil[0] - mesh.nodes[indices_extrado_to_morph_1]
257
+ targetDisplacement[l1:l1+l2] = mapped_airfoil[1] - mesh.nodes[indices_intrado_to_morph_1]
258
+ targetDisplacement[l1+l2:l1+l2+l3] = np.zeros((l3,2))
259
+
260
+ targetDisplacementMask[:l1] = indices_extrado_to_morph_1
261
+ targetDisplacementMask[l1:l1+l2] = indices_intrado_to_morph_1
262
+ targetDisplacementMask[l1+l2:l1+l2+l3] = other_boundary_ids_1
263
+
264
+ ##############################################################
265
+ # Compute the morphing
266
+ ##############################################################
267
+
268
+ # RBF morphing
269
+
270
+ mesh_nodes = mesh.nodes.copy()
271
+ morphed_nodes = MMT.Morphing(mesh, targetDisplacement, targetDisplacementMask, radius=None)
272
+ mesh.nodes = morphed_nodes
273
+
274
+ mesh.nodeFields['X'] = mesh_nodes[:,0]
275
+ mesh.nodeFields['Y'] = mesh_nodes[:,1]
276
+
277
+ return mesh
278
+
279
+
280
+ def project_sample(morphed_mesh, morphed_mesh_0):
281
+ projected_mesh = copy.deepcopy(morphed_mesh_0)
282
+
283
+ space_, numberings_, _, _ = PrepareFEComputation(morphed_mesh)
284
+ inputFEField = FEField(name="dummy", mesh=morphed_mesh, space=space_, numbering=numberings_[0])
285
+
286
+ nt = GetFieldTransferOpCppStep1(inputFEField, 1)
287
+ FE_interpolation_op, _ = GetFieldTransferOpCppStep2(nt, morphed_mesh_0.nodes)
288
+
289
+ for pfn in out_fields_names + ['X', 'Y']:
290
+ projected_mesh.nodeFields[pfn] = FE_interpolation_op.dot(morphed_mesh.nodeFields[pfn])
291
+
292
+ return projected_mesh
293
+
294
+
295
+
296
+
297
+ def pretreat_morph_and_project_mesh(i_sample, dataset, indices, morphed_mesh_0, airfoil_0, curv_abscissa_0):
298
+
299
+
300
+ ###################
301
+ ## 1) Pretreat data
302
+ ###################
303
+ sample = Sample.model_validate(pickle.loads(dataset[int(indices[i_sample])]["sample"]))
304
+ mesh = CGNSToMesh(sample.get_mesh())
305
+ pretreat_sample(mesh)
306
+
307
+ ###################
308
+ ## 2) Morph data
309
+ ###################
310
+ morphed_mesh = morph_sample(mesh, airfoil_0, curv_abscissa_0)
311
+
312
+ # print("morphed_mesh =", morphed_mesh)
313
+
314
+ # from Muscat.IO import XdmfWriter as XW
315
+ # XW.WriteMeshToXdmf("morphed_mesh.xdmf", morphed_mesh)
316
+
317
+ ###################
318
+ ## 3) Project data
319
+ ###################
320
+ projected_mesh = project_sample(morphed_mesh, morphed_mesh_0)
321
+
322
+ # print("projected_mesh =", projected_mesh)
323
+
324
+ # from Muscat.IO import XdmfWriter as XW
325
+ # XW.WriteMeshToXdmf("projected_mesh.xdmf", projected_mesh)
326
+
327
+ in_scalars = [sample.get_scalar(sn) for sn in in_scalars_names]
328
+
329
+ return [projected_mesh, in_scalars, morphed_mesh.nodes]
330
+
331
+
332
+ def infer(dataset, indice, training_data):
333
+
334
+ common_mesh, correlationOperator2c, airfoil_0, curv_abscissa_0, scalar_scalers, scalerX, pca_clouds, pca_fields, y_scalers, kmodels, X_train, y_train = training_data
335
+
336
+ projected_mesh, in_scalars, morphed_mesh_nodes = pretreat_morph_and_project_mesh(indice, dataset = dataset, indices = np.arange(len(dataset)), morphed_mesh_0 = common_mesh, airfoil_0 = airfoil_0, curv_abscissa_0 = curv_abscissa_0)
337
+
338
+ space_, numberings_, _, _ = PrepareFEComputation(common_mesh)
339
+ inputFEField_0 = FEField(name="dummy", mesh=common_mesh, space=space_, numbering=numberings_[0])
340
+ nt_0 = GetFieldTransferOpCppStep1(inputFEField_0, nbThreads = 2)
341
+
342
+ iffo = GetFieldTransferOpCppStep2(nt_0, morphed_mesh_nodes)[0]
343
+
344
+ clouds = np.stack([projected_mesh.nodeFields["X"], projected_mesh.nodeFields["Y"]], axis=1)
345
+
346
+ scalars = np.array(in_scalars)
347
+
348
+ clouds = clouds.reshape(-1, 1)
349
+
350
+ X_pca = np.dot(pca_clouds, correlationOperator2c.dot(clouds)).T
351
+ X_scalars = scalar_scalers.transform(scalars.reshape(1, -1))
352
+ unscaled_X = np.concatenate([X_pca, X_scalars], axis=-1)
353
+ X = scalerX.transform(unscaled_X)
354
+
355
+
356
+ predictions = {}
357
+ y = []
358
+ for i, fn in enumerate(out_fields_names):
359
+
360
+ X_ = torch.tensor(X, dtype=torch.float64).to(device)
361
+
362
+ output_dim = pca_fields[0].shape[0]
363
+
364
+ n_samples = 1
365
+ y_pred_i = np.empty((n_samples, output_dim, len(num_steps)))
366
+
367
+ for j in range(output_dim):
368
+ for k in range(len(num_steps)):
369
+ y_pred_i[:,j,k] = kmodels[i][j][k](X_).detach().cpu().numpy().squeeze()
370
+
371
+ y.append(y_pred_i)
372
+ y_pred_i = np.mean(y_pred_i, axis = 2)
373
+
374
+ y_pred_i = y_scalers[i].inverse_transform(y_pred_i)
375
+
376
+ y_pred_common_i = np.dot(y_pred_i, pca_fields[i]).flatten()
377
+
378
+ predictions[fn] = iffo.dot(y_pred_common_i)
379
+
380
+ return predictions