relativus commited on
Commit
aa602ee
·
verified ·
1 Parent(s): 8a7adc1

Delete src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +0 -305
src/streamlit_app.py DELETED
@@ -1,305 +0,0 @@
1
- import numpy as np
2
- import pandas as pd
3
- import plotly.graph_objects as go
4
- from scipy.interpolate import LinearNDInterpolator
5
- from scipy.spatial import Delaunay
6
- import plotly.express as px
7
-
8
- def barycentric_to_cartesian(df):
9
- """
10
- Преобразует барицентрические координаты в декартовы для тетраэдра.
11
- """
12
- # Вершины правильного тетраэдра
13
- vertices = np.array([
14
- [0, 0, 0], # v0
15
- [1, 0, 0], # v1
16
- [0.5, np.sqrt(3)/2, 0], # v2
17
- [0.5, np.sqrt(3)/6, np.sqrt(6)/3] # v3
18
- ])
19
-
20
- # Преобразование координат
21
- bary_coords = df[['x1', 'x2', 'x3', 'x4']].values
22
- cartesian_coords = np.dot(bary_coords, vertices)
23
-
24
- result_df = df.copy()
25
- result_df['x'] = cartesian_coords[:, 0]
26
- result_df['y'] = cartesian_coords[:, 1]
27
- result_df['z'] = cartesian_coords[:, 2]
28
-
29
- return result_df
30
-
31
-
32
- def add_tetrahedron_wireframe(fig, vertices):
33
- """Добавляет каркас тетраэдра и подписи вершин"""
34
- edges = [
35
- [0, 1], [0, 2], [0, 3],
36
- [1, 2], [1, 3], [2, 3]
37
- ]
38
-
39
- for edge in edges:
40
- x_line = [vertices[edge[0]][0], vertices[edge[1]][0]]
41
- y_line = [vertices[edge[0]][1], vertices[edge[1]][1]]
42
- z_line = [vertices[edge[0]][2], vertices[edge[1]][2]]
43
-
44
- fig.add_trace(go.Scatter3d(
45
- x=x_line, y=y_line, z=z_line,
46
- mode='lines',
47
- line=dict(color='black', width=4),
48
- showlegend=False
49
- ))
50
-
51
- vertex_labels = ['V₁ (x₁=1)', 'V₂ (x₂=1)', 'V₃ (x₃=1)', 'V₄ (x₄=1)']
52
- fig.add_trace(go.Scatter3d(
53
- x=vertices[:, 0],
54
- y=vertices[:, 1],
55
- z=vertices[:, 2],
56
- mode='text+markers',
57
- text=vertex_labels,
58
- textposition="top center",
59
- marker=dict(size=8, color='red'),
60
- showlegend=False
61
- ))
62
-
63
-
64
- def create_tetrahedron_isosurfaces_correct(df, temperatures, resolution=60):
65
- """
66
- Правильное построение изоповерхностей с помощью Marching Tetrahedra.
67
- """
68
- df_cartesian = barycentric_to_cartesian(df)
69
-
70
- # Вершины тетраэдра
71
- vertices = np.array([
72
- [0, 0, 0],
73
- [1, 0, 0],
74
- [0.5, np.sqrt(3)/2, 0],
75
- [0.5, np.sqrt(3)/6, np.sqrt(6)/3]
76
- ])
77
-
78
- # Создаем интерполятор в ДЕКАРТОВЫХ координатах
79
- points = df_cartesian[['x', 'y', 'z']].values
80
- temp_values = df_cartesian['T, C'].values
81
-
82
- valid_mask = ~np.isnan(temp_values)
83
- points = points[valid_mask]
84
- temp_values = temp_values[valid_mask]
85
-
86
- interpolator = LinearNDInterpolator(points, temp_values)
87
-
88
- # Создаем регулярную сетку в барицентрических координатах
89
- u = np.linspace(0, 1, resolution)
90
- v = np.linspace(0, 1, resolution)
91
- w = np.linspace(0, 1, resolution)
92
-
93
- U, V, W = np.meshgrid(u, v, w, indexing='ij')
94
-
95
- # Фильтруем точки внутри тетраэдра (u+v+w <= 1)
96
- mask = (U + V + W) <= 1
97
- U_valid = U[mask]
98
- V_valid = V[mask]
99
- W_valid = W[mask]
100
- T_valid = 1 - U_valid - V_valid - W_valid
101
-
102
- # Преобразуем в декартовы координаты
103
- bary_coords = np.column_stack([U_valid, V_valid, W_valid, T_valid])
104
- grid_points = np.dot(bary_coords, vertices)
105
-
106
- # Интерполируем температуру в ДЕКАРТОВЫХ координатах
107
- grid_temps = interpolator(grid_points)
108
-
109
- # Создаем триангуляцию для регулярной сетки
110
- print("Создаем триангуляцию для регулярной сетки...")
111
- try:
112
- tri = Delaunay(grid_points)
113
- print(f"Создано {len(tri.simplices)} тетраэдров")
114
- except Exception as e:
115
- print(f"Ошибка триангуляции: {e}")
116
- return None
117
-
118
- fig = go.Figure()
119
-
120
- for temp in temperatures:
121
- print(f"Строим изоповерхность для {temp}°C")
122
-
123
- vertices_list = []
124
- faces_list = []
125
-
126
- # Для каждого тетраэдра в сетке
127
- for tetra in tri.simplices:
128
- tetra_points = grid_points[tetra]
129
- tetra_temps = grid_temps[tetra]
130
-
131
- # Определяем, какие вершины выше/ниже изоповерхности
132
- above = tetra_temps >= temp
133
- below = tetra_temps < temp
134
-
135
- # Если все вершины с одной стороны - пропускаем
136
- if np.all(above) or np.all(below):
137
- continue
138
-
139
- # Находим пересечения изоповерхности с р��брами тетраэдра
140
- intersections = []
141
- intersection_edges = []
142
-
143
- # Проверяем все ребра тетраэдра
144
- edges = [(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)]
145
- for i, (idx1, idx2) in enumerate(edges):
146
- temp1, temp2 = tetra_temps[idx1], tetra_temps[idx2]
147
- point1, point2 = tetra_points[idx1], tetra_points[idx2]
148
-
149
- # Если ребро пересекает изоповерхность
150
- if (temp1 >= temp and temp2 < temp) or (temp1 < temp and temp2 >= temp):
151
- # Линейная интерполяция
152
- t = (temp - temp1) / (temp2 - temp1)
153
- intersection_point = point1 + t * (point2 - point1)
154
- intersections.append(intersection_point)
155
- intersection_edges.append((idx1, idx2))
156
-
157
- # Формируем полигоны в зависимости от количества пересечений
158
- if len(intersections) == 3:
159
- # Один треугольник
160
- start_idx = len(vertices_list)
161
- vertices_list.extend(intersections)
162
- faces_list.append([start_idx, start_idx+1, start_idx+2])
163
-
164
- elif len(intersections) == 4:
165
- # Четырехугольник - разбиваем на 2 треугольника
166
- start_idx = len(vertices_list)
167
- vertices_list.extend(intersections)
168
-
169
- # Первый вариант разбиения (диагональ 0-2)
170
- faces_list.append([start_idx, start_idx+1, start_idx+2])
171
- faces_list.append([start_idx, start_idx+2, start_idx+3])
172
-
173
- # Альтернативный вариант разбиения (диагональ 1-3)
174
- # faces_list.append([start_idx, start_idx+1, start_idx+3])
175
- # faces_list.append([start_idx+1, start_idx+2, start_idx+3])
176
-
177
- # Добавляем изоповерхность
178
- if vertices_list:
179
- vertices_array = np.array(vertices_list)
180
- x = vertices_array[:, 0]
181
- y = vertices_array[:, 1]
182
- z = vertices_array[:, 2]
183
-
184
- i_list, j_list, k_list = [], [], []
185
- for face in faces_list:
186
- i_list.append(face[0])
187
- j_list.append(face[1])
188
- k_list.append(face[2])
189
-
190
- color_idx = temperatures.index(temp) % len(px.colors.qualitative.Set1)
191
- fig.add_trace(go.Mesh3d(
192
- x=x, y=y, z=z,
193
- i=i_list, j=j_list, k=k_list,
194
- color=px.colors.qualitative.Set1[color_idx],
195
- opacity=0.7,
196
- name=f'{temp}°C',
197
- flatshading=True,
198
- lighting=dict(diffuse=0.8, ambient=0.3),
199
- lightposition=dict(x=100, y=100, z=100)
200
- ))
201
- print(f"Добавлено {len(faces_list)} треугольников для температуры {temp}°C")
202
- else:
203
- print(f"Не найдено пересечений для температуры {temp}°C")
204
-
205
- # Добавляем каркас тетраэдра
206
- add_tetrahedron_wireframe(fig, vertices)
207
-
208
- fig.update_layout(
209
- title='Правильные изоповерхности в тетраэдре',
210
- scene=dict(
211
- xaxis_title='X',
212
- yaxis_title='Y',
213
- zaxis_title='Z',
214
- aspectmode='data',
215
- camera=dict(eye=dict(x=1.5, y=1.5, z=1.5))
216
- ),
217
- width=1000,
218
- height=800
219
- )
220
-
221
- return fig
222
-
223
-
224
- # 🎯 Создание тестовых данных
225
- def generate_test_data(n_points=100):
226
- """
227
- Генерирует случайные барицентрические координаты и температуры
228
- """
229
- # Генерируем случайные барицентрические координаты
230
- # x1 + x2 + x3 + x4 = 1, все >= 0
231
- data = np.random.dirichlet([1, 1, 1, 1], size=n_points)
232
-
233
- # Создаем DataFrame
234
- df = pd.DataFrame(data, columns=['x1', 'x2', 'x3', 'x4'])
235
-
236
- # Генерируем температуры в разумном диапазоне
237
- # Например, температура зависит от барицентрических координат
238
- df['T, C'] = 50 + 30 * (df['x1'] * 0.5 + df['x2'] * 0.3 + df['x3'] * 0.2 + df['x4'] * 0.1) + \
239
- np.random.normal(0, 2, size=n_points) # добавляем шум
240
-
241
- return df
242
-
243
-
244
- # 🚀 Основной код для Streamlit
245
- if __name__ == "__main__":
246
- import streamlit as st
247
-
248
- st.title("Изоповерхности в тетраэдре")
249
-
250
- # 📁 Загрузка Excel-ф��йла
251
- uploaded_file = st.file_uploader(
252
- "Загрузите Excel файл с данными (x1, x2, x3, x4, T, C)",
253
- type=["xlsx", "xls"],
254
- help="Файл должен содержать столбцы: x1, x2, x3, x4, T, C"
255
- )
256
-
257
- if uploaded_file is not None:
258
- # Читаем Excel-файл
259
- try:
260
- data = pd.read_excel(uploaded_file)
261
- st.success("Файл успешно загружен!")
262
-
263
- # Проверяем, что все нужные столбцы есть
264
- required_columns = ['x1', 'x2', 'x3', 'x4', 'T, C']
265
- missing_cols = [col for col in required_columns if col not in data.columns]
266
-
267
- if missing_cols:
268
- st.error(f"⚠️ В файле отсутствуют следующие столбцы: {missing_cols}")
269
- st.stop()
270
-
271
- st.write("Пример данных из файла:")
272
- st.dataframe(data.head(10))
273
-
274
- except Exception as e:
275
- st.error(f"Ошибка при чтении файла: {e}")
276
- st.stop()
277
- else:
278
- # Если файл не загружен - используем тестовые данные
279
- st.info("Файл не загружен. Используются тестовые данные.")
280
- data = generate_test_data(n_points=200)
281
-
282
-
283
- # Параметры
284
- available_temps = sorted(data['T, C'].dropna().unique())
285
- if len(available_temps) > 0:
286
- default_temps = [available_temps[0]] if len(available_temps) == 1 else [available_temps[0], available_temps[-1]]
287
- else:
288
- default_temps = [64.5, 81]
289
-
290
- target_temperatures = st.multiselect(
291
- "Выберите температуры для изоповерхностей",
292
- options=sorted(data['T, C'].dropna().unique()),
293
- default=default_temps
294
- )
295
-
296
- resolution = st.slider("Разрешение сетки", min_value=20, max_value=100, value=60)
297
-
298
- if st.button("Построить изоповерхности"):
299
- with st.spinner("Идет построение..."):
300
- fig = create_tetrahedron_isosurfaces_correct(data, target_temperatures, resolution=resolution)
301
-
302
- if fig:
303
- st.plotly_chart(fig, use_container_width=True)
304
- else:
305
- st.error("Не удалось построить изоповерхности")