#python #numpy #multidimensional-array #physics #numerical-methods
#python #numpy #многомерный массив #физика #численные методы
Вопрос:
Я пытаюсь создать матрицу из трех индексов, которая содержит 1 значение (V) для каждого узла числовой пространственной сетки (xyz) (проблема реального мира: электростатический потенциал, создаваемый конечными параллельными пластинами в точке пространства). Эта матрица изначально должна быть заполнена нулями, за исключением некоторых определенных точек (где находятся таблички и ограничения пространства), а затем итеративно обновлять значение в каждом узле в соответствии со следующим методом трафарета из 7 точек (индексы jk и l координат x y z соответственно):
V [j, k, l] = (V [j 1, k, l] V [j-1, k, l] V [j, k 1, l] V [j, k-1, l] V [j, k,l 1] V[j, k, l-1]) / 6 (т. е. Замените значение узла средним значением других 6 соседних узлов)
Я пробовал np.zeros и np.meshgrid, но я думаю, что, возможно, у меня просто серьезный концептуальный и базовый пробел в отношении массивов, поскольку, похоже, ничто не делает то, что я хочу. Любая ориентация была бы очень признательна и извините, если я неправильно объяснил себя. Вот некоторый код, который я пробовал:
V1 = 10
V2 = -5
Mx = 101
My = 151
Mz = 301
V = np.zeros([Mx, My, Mz]).astype(int)
V[46, 51:101, 101:201] = V1 #the values of these nodes should stay fixed throughout iteration
V[56, 51:101, 101:201] = V2 #the values of these nodes should stay fixed throughout iteration
V[1,:,:] =V[100,:,:] =V[:,1,:] =V[:,150,:] =V[:,:,1] =V[:,:,300] = 0 #the values of these nodes should stay fixed throughout iteration
for j in V:
for k in j:
for l in k:
V[j, k, l] = (V[j 1, k, l] V[j-1, k, l] V[j, k 1, l] V[j, k-1, l] V[j, k, l 1] V[j, k, l-1])/6
(Обновление после справки от пользователя kcw78)
Реализация предложенного кода и попытка реализовать цикл while, который продолжается до тех пор, пока ошибка не упадет ниже допустимого значения или ошибка в двух последовательных циклах не будет одинаковой. В инструкции о назначении говорится более конкретно:
«Будет выполнено столько этих циклов, сколько необходимо, чтобы ошибка упала ниже определенного заданного допуска, rtol. И какова хорошая мера ошибки здесь? Мы будем использовать максимальное значение локального остатка, определяемое как (абсолютное значение) разницы между потенциальным значением в центральном узле и средним арифметическим других значений в шаблоне. В качестве дополнительной меры предосторожности мы также сравним ошибки любых двух последовательных циклов и остановим ослабление, если они станут равными. Лучшее решение больше невозможно «.
Теперь пытаюсь выполнить приведенный ниже код, но не уверен, попал ли он в бесконечный цикл while или просто занимает много времени, так как я должен остановить его через 20 минут без получения какого-либо вывода (также не уверен, может быть, мне следует использовать .all() вместо .any()):
import numpy as np
V1 = 10
V2 = -5
Mx = 101
My = 151
Mz = 301
rtol = 10**-2
V1_set = { (46,k,l) for k in range(51,101,1) for l in range(101,201,1) }
V2_set = { (56,k,l) for k in range(51,101,1) for l in range(101,201,1) }
V = np.zeros((Mx, My, Mz))
Vnew = np.copy(V)
V[46, 51:101, 101:201] = V1
V[56, 51:101, 101:201] = V2
V[1,:,:] =V[100,:,:] =V[:,1,:] =V[:,150,:] =V[:,:,1] =V[:,:,300] = 0
check_set = set().union(V1_set,V2_set)
error = np.zeros((Mx, My, Mz))
errornew = np.zeros((Mx, My, Mz))
while float(errornew.any()) < rtol or error.any() != errornew.any():
V = Vnew
error = errornew
for j in range(1,V.shape[0]-1):
for k in range(1,V.shape[1]-1):
for l in range(1,V.shape[2]-1):
if (j,k,l) not in check_set:
Vnew[j, k, l] = (V[j 1, k, l] V[j-1, k, l] V[j, k 1, l] V[j, k-1, l] V[j, k, l 1] V[j, k, l-1])/6
errornew[j, k, l] = abs(Vnew[j, k, l]-V[j, k, l])
Ответ №1:
Если я понимаю ваш вопрос, вам потребуется 2 изменения:
- Сначала вам понадобятся дополнительные переменные, чтобы проверить позиции, которые фиксируются на протяжении всей итерации. Для этого я добавил наборы с
(j,k,l)
кортежами. Чтобы вы могли следовать моей логике, я изначально создал 3 набора; по 1 для каждого из этих индексов: 1) фиксированный V1 (V1_set
), 2) фиксированный V2 (V2_set
) и 3) boundary (zero_set
), затем объединил все 3 набора в один набор (вызываетсяcheck_set
). Вы могли бы начать с одного набора и обновлять его по мере добавления. Примечание: в вашем коде естьV[1,:,:] = 0
, но я думаю, что вы действительно хотитеV[0,:,:] = 0
. Дайте мне знать, если я неправильно это истолковал. - Во-вторых, вам нужно выполнить цикл по длине оси в каждом направлении (атрибуты есть
V.shape[0], V.shape[1], V.shape[2]
). Внутри цикла я проверяю каждую(i,j,k)
изcheck_set
них и вычисляю новоеV1[j, k, l]
значение только в том случае, если его НЕТ в наборе.
Смотрите код ниже:
V1 = 10
V2 = -5
Mx = 101
My = 151
Mz = 301
V1_set = { (46,k,l) for k in range(51,101,1) for l in range(101,201,1) }
V2_set = { (56,k,l) for k in range(51,101,1) for l in range(101,201,1) }
zero_set = set()
zero_set.update( { (0,k,l) for k in range(My) for l in range(Mz) } )
zero_set.update( { (100,k,l) for k in range(My) for l in range(Mz) } )
zero_set.update( { (j,0,l) for j in range(Mx) for l in range(Mz) } )
zero_set.update( { (j,150,l) for j in range(Mx) for l in range(Mz) } )
zero_set.update( { (j,k,0) for j in range(Mx) for k in range(My) } )
zero_set.update( { (j,k,300) for j in range(Mx) for k in range(My) } )
check_set = set().union(V1_set,V2_set,zero_set)
V = np.zeros((Mx, My, Mz)).astype(int)
V[46, 51:101, 101:201] = V1 #the values of these nodes should stay fixed throughout iteration
V[56, 51:101, 101:201] = V2 #the values of these nodes should stay fixed throughout iteration
V[1,:,:] =V[100,:,:] =V[:,1,:] =V[:,150,:] =V[:,:,1] =V[:,:,300] = 0 #the values of these nodes should stay fixed throughout iteration
for j in range(V.shape[0]):
for k in range(V.shape[1]):
for l in range(V.shape[2]):
if (j,k,l) not in check_set:
V[j, k, l] = (V[j 1, k, l] V[j-1, k, l] V[j, k 1, l] V[j, k-1, l] V[j, k, l 1] V[j, k, l-1])/6
После публикации приведенного выше решения мне пришло в голову, что диапазоны, используемые в zero_set
, предназначены для того, чтобы избежать первого / последнего индексов (границы массива). Если это так, то в этом нет никакой необходимости zero_set
. Вы можете справиться с этим, изменив аргументы диапазона, как показано ниже:
check_set = set().union(V1_set,V2_set)
for j in range(1,V.shape[0]-1):
for k in range(1,V.shape[1]-1):
for l in range(1,V.shape[2]-1):
if (j,k,l) not in check_set:
V[j, k, l] = (V[j 1, k, l] V[j-1, k, l] V[j, k 1, l] V[j, k-1, l] V[j, k, l 1] V[j, k, l-1])/6
Дополнительные замечания для рассмотрения:
- Я заметил , что вы создали array
V
с.astype(int)
помощью . Вы уверены, что это то, чего вы хотите (а не поплавки)? Как правило, ваши вычисления не будут возвращать целочисленные значения. - В зависимости от того, как написан ваш код, вы меняете значения
V[j,k,l]
по ходу дела. Таким образом, вы используете обновленные значенияV[j,k,l]
дляj,k,l
меньших, чем текущееj,k,l
, и предыдущиеV[j,k,l]
значения дляj,k,l
больших, чем текущееj,k,l
. - Наконец, я предполагаю, что вы собираетесь повторять это вычисление до тех пор, пока изменение между 2 циклами не станет «приемлемо малым». Если это так, вам нужно иметь 2 копии массива («старый» и «новый»), чтобы получить разницу. Позаботьтесь об использовании
.copy()
при копировании для создания нового / другого объекта np.array.
Комментарии:
1. Миллион благодарностей за вашу помощь, и вы правы: нет
.astype(int)
, и я должен зацикливаться до приемлемо небольшой ошибки. Обновление исходного сообщения тем, что я пытаюсь сейчас.
Ответ №2:
Это обновленный ответ, основанный на новой информации и коде, добавленных в начальный пост. У вас есть как минимум 1 проблема с вашей логикой. if (j,k,l) not in check_set:
Блок пропускает значения (j, k, l), которые вы хотите сохранить постоянными. В результате вы не выполняете вычисления Vnew
в этих точках. Это вызовет проблемы с вычислением изменений с каждой итерацией (и даст неверный результат). Кроме того, я думаю, вам нужно V = Vnew.copy()
. В противном случае V
и Vnew
ссылайтесь на тот же объект.
Вот мой простой подход к итерации с жестко запрограммированной погрешностью.
check_set = set().union(V1_set,V2_set)
Vi = V.copy()
Vn = np.zeros((Mx, My, Mz))
diff = max(abs(V1), abs(V2))
i = 1
print('Start Cycle#',i,'; diff =',diff)
while diff > 0.25:
for j in range(1,V.shape[0]-1):
for k in range(1,V.shape[1]-1):
for l in range(1,V.shape[2]-1):
if (j,k,l) in check_set:
Vn[j, k, l] = Vi[j, k, l]
else:
Vn[j, k, l] = (Vi[j 1, k, l] Vi[j-1, k, l] Vi[j, k 1, l] Vi[j, k-1, l] Vi[j, k, l 1] Vi[j, k, l-1])/6
diff = max(abs(np.amax(Vn-Vi)), abs(np.amin(Vn-Vi)))
print('Cycle#',i,'completed; diff =',diff)
i = 1
Vi = Vn.copy()
Эта реализация будет «сходиться» за 10 итераций. Однако это только проверяет, что ошибка между двумя последовательными циклами меньше жестко заданного допуска (аналогично второй части желаемой проверки ошибок).
Я НЕ реализовал первую проверку ошибок: «используйте максимальное значение локального остатка, определяемое как (абсолютное значение) разницы между потенциальным значением в центральном узле и средним арифметическим других значений в шаблоне». Я не уверен на 100% в намерении. Является ли трафарет 6 точками вокруг [j,k,l]
? Если это так, я думаю, вам понадобится аналогичный расчет ПОСЛЕ того, как вы вычислите новые значения Vn, что-то вроде этого:
error[j, k, l] = abs(Vn[j, k, l] - (Vn[j 1, k, l] Vn[j-1, k, l] Vn[j, k 1, l] Vn[j, k-1, l] Vn[j, k, l 1] Vn[j, k, l-1])/6 )