Improving a numpy implementation of a simple spring network

倾然丶 夕夏残阳落幕 提交于 2019-12-03 21:55:29

Your method, at a cursory glance, appears to be an explicit under-relaxation type of method. Calculate the residual force at each knot, apply a factor of that force as a displacement, repeat until convergence. It's the repeating until convergence that takes the time. The more points you have, the longer each iteration takes, but you also need more iterations for the constraints at one end of the mesh to propagate to the other.

Have you considered an implicit method? Write the equation for the residual force at each non-constrained node, assemble them into a large matrix, and solve in one step. Information now propagates across the entire problem in a single step. As an additional benefit, the matrix you construct should be sparse, which scipy has a module for.

Wikipedia: explicit and implicit methods


EDIT Example of an implicit method matching (roughly) your problem. This solution is linear, so it doesn't take into account the effect of the calculated displacement on the force. You would need to iterate (or use non-linear techniques) to calculate this. Hope it helps.

#!/usr/bin/python3

import matplotlib.pyplot as pp
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import scipy as sp
import scipy.sparse
import scipy.sparse.linalg

#------------------------------------------------------------------------------#

# Generate a grid of knots
nX = 10
nY = 10
x = np.linspace(-0.5, 0.5, nX)
y = np.linspace(-0.5, 0.5, nY)
x, y = np.meshgrid(x, y)
knots = list(zip(x.flatten(), y.flatten()))

# Create links between the knots
links = []
# Horizontal links
for i in range(0, nY):
    for j in range(0, nX - 1):
        links.append((i*nX + j, i*nX + j + 1))
# Vertical links
for i in range(0, nY - 1):
    for j in range(0, nX):
        links.append((i*nX + j, (i + 1)*nX + j))

# Create constraints. This dict takes a knot index as a key and returns the
# fixed z-displacement associated with that knot.
constraints = {
    0          : 0.0,
    nX - 1     : 0.0,
    nX*(nY - 1): 0.0,
    nX*nY - 1  : 1.0,
    2*nX + 4   : 1.0,
    }

#------------------------------------------------------------------------------#

# Matrix i-coordinate, j-coordinate and value
Ai = []
Aj = []
Ax = []

# Right hand side array
B = np.zeros(len(knots))

# Loop over the links
for link in links:

    # Link geometry
    displacement = np.array([ knots[1][i] - knots[0][i] for i in range(2) ])
    distance = np.sqrt(displacement.dot(displacement))

    # For each node
    for i in range(2):

        # If it is not a constraint, add the force associated with the link to
        # the equation of the knot
        if link[i] not in constraints:

            Ai.append(link[i])
            Aj.append(link[i])
            Ax.append(-1/distance)

            Ai.append(link[i])
            Aj.append(link[not i])
            Ax.append(+1/distance)

        # If it is a constraint add a diagonal and a value
        else:

            Ai.append(link[i])
            Aj.append(link[i])
            Ax.append(+1.0)
            B[link[i]] += constraints[link[i]]

# Create the matrix and solve
A = sp.sparse.coo_matrix((Ax, (Ai, Aj))).tocsr()
X = sp.sparse.linalg.lsqr(A, B)[0]

#------------------------------------------------------------------------------#

# Plot the links
fg = pp.figure()
ax = fg.add_subplot(111, projection='3d')

for link in links:
    x = [ knots[i][0] for i in link ]
    y = [ knots[i][1] for i in link ]
    z = [ X[i] for i in link ]
    ax.plot(x, y, z)

pp.show()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!