How can I make a symmetric tridiagonal matrix in Python? - python

There is a new edit at the end of the topic.
I'm new in Python and I would like to know how could I make a simple tridiagonal matrix NxN.
I have three vectors that will be updated over a loop.
I'm working with something like this:
Note: I just want to know how zeros and what Python parameters I could use to adjust this.
Well, I have two codes here, the first one I wrote in Fortran and it works fine. And the second is what I tried to write in Python.
Fortran:
do i=2,n-1
do j=2,n-1
if (i.eq.j) then
D(i,j+1)=-u_med(i+1)/(delta_r(i)*delta_r(i+1))
t1 =u_med(i+1)/(delta_r(i)*delta_r(i))
t2 = u_med(i)/(delta_r(i)*delta_r(i))
D(i,j)= t1 + t2 + V(i)
D(i+1,j)=-u_med(i+1)/(delta_r(i)*delta_r(i+1))
end if
end do
end do
Python:
for i in range(2,n):
for j in range(2,n):
if i == j:
D[i][j+1] = - u_med[i+1]/(delta_r[i]*delta_r[i+1])
t1 = u_med[i+1]/(delta_r[i]*delta_r[i])
t2 = u_med[i]/(delta_r[i]*delta_r[i])
D[i][j]= t1 + t2 + V[i]
D[i+1][j]= - u_med[i+1]/(delta_r[i]*delta_r[i+1])
t1 = u_med[2]/(delta_r[1]*delta_r[1])
t2 = 0
D[1][1]= t1 + t2 + V[1]
D[1][2]= - u_med[2]/(delta_r[1]*delta_r[2])
D[2,1]= - u_med[2]/(delta_r[2]*delta_r[1])
t1 = 0
t2 = u_med[n]/(delta_r[n]*delta_r[n])
D[n][n]= t1 + t2 + V[n]
Which gives the error:
D[i][j+1] = - u_med[i+1]/(delta_r[i]*delta_r[i+1]) ValueError: setting
an array element with a sequence.
Example based on the above image:
example
Comments:
For u_med:
u_med = np.zeros((n,2))
for i in range(2,n):
tta1 = r[i]*u[i]
tta2 = r[i-1]*u[i-1]
u_med[i] = 0.5*(tta1 + tta2)/(r[i] - r[i-1])
u_med[1] = u_med[2]
For delta_r:
delta_r = np.zeros((n-1,2))
for i in range(2,n-1):
ft1 = r[i+1]*r[i+1]
ft2 = r[i-1]*r[i-1]
ft3 = 2*r[i]*(r[i+1] - r[i-1])
delta_r[i] = math.sqrt(0.125*abs(ft1 - ft2 + ft3))
For r and n:
ri=0
n1 = 51
r1 = ri
r2 = 250
hr1 = (r2-r1)/(n1-1)
r = np.zeros((n1,1))
for i in range(n1):
r[i] = r1 + i*hr1
u = np.zeros((n+1,1))
for i in range(1,n+1):
i = 1
And for D:
D = npm.zeros((n,n))
Edit: It seems to be because the u_med and delta_r are two-dimensional and I'm trying to assign it in D with an incompatible shape. It works in Fortran, but how can I approach it differently in Python?

If you create the diagonals as just arrays or lists, then you could use numpy.diag() to create diagonal matrices and add them together.
D = np.diag(x) + np.diag(x1, 1) + np.diag(x1, -1)
to create your matrix. Perhaps something like
A = mu[1:]/h[1:]**2 + mu[:1]/h[1:]**2 + U[1:]
B = mu[1:]/ (h[:-1]*h[1:])
D = np.diag(A) + np.diag(B, 1) + np.diag(B, -1)
going off of the image you linked.

Related

python: returns Magnus and Neudecker's duplication matrix of size n

Hi I am trying to write the following code which is written in matlab to write it in Python:
The following script is the matlab code
`
function d = dupmat(n)
% Returns Magnus and Neudecker's duplication matrix of size n
a = tril(ones(n));
i = find(a); # find non-zero elements
a(i) = 1:length(i);
a = a + tril(a,-1)';
j = vec(a);
m = n*(n+1)/2;
d = zeros(n*n,m);
for r = 1:nrows(d)
d(r, j(r)) = 1;
end
`
I have tried to write this code but it does not work
import numpy as np
def dupmat(n):
a = np.tril(np.ones(n))
i = np.nonzero(a) # find the non zero elements of the function
a = a[1:len(i)]
a = a + np.tril(a,-1)
j = np.vectorize(a)
m = n*(n+1)/2
d = np.zeros([n*n,int(m)])
for r in range(0,d.shape[0]):
if (d[r] == j[r] == 1):
d[r] = 1
return d

A root of a "saw-like" function

Say, we have f(t) = v * t + A * sin(w * t). I call such functions "saw-like":
I want to solve saw(t) = C, that is, find a root of saw(t) - C (still "saw-like").
I tried writing down a ternary search for function abs(saw(t) - C) to find its minima. If we are lucky (or crafty), it would be the root. Unfortunately, my code does not always work: sometimes we get stuck in those places:
My code (python3):
def calculate(fun):
eps = 0.000000001
eps_l = 0.1
x = terns(fun, 0, 100000000000000)
t = terns(fun, 0, x)
cnt = 0
while fun(x) > eps:
t = x
x = terns(fun, 0, t)
if abs(t - x) < eps_l:
cnt += 1
# A sorry attempt pass some wrong value as a right one.
# Gets us out of an infinite loop at least.
if cnt == 10:
break
return t
def terns(f, l, r):
eps = 0.00000000001
while r - l > eps:
x_1 = l + (r - l) / 3
x_2 = r - (r - l) / 3
if f(x_1) < f(x_2):
r = x_2
else:
l = x_1
return (l + r) / 2
So, how is it done? Is using ternary search the right way?
My other idea was somehow sending the equation over to the net, passing it to Wolfram Alpha and fetching the answers. Yet, I don't how it's done, as I am not quite fluent at python.
How could this be done?

Why is .subs not working in JupyterLab Python

I have a very long symbolic expression in JupyterLab which I am trying to substitute values in. The expression is very long so I won't elaborate on what it is unless it is necessary, but I have a simplified symbolic expression which outputs (this is using SymPy). I am trying to use the .subs command as seen below, but this is not replacing any of the variables I have and rather just outputting the same symbolic notation. I have used .subs before, but in a very limited capacity so I am not sure if there is a type issue or something like that. All of these variables represent those in the symbolic notation.
replacements=[(t1,np.pi/2), (t1d,1), (t1dd,1), (l1,1), (l2,1), (m1,1), (m2,1), (m3,1), (g,-10)]
Lagrangian = Lagrangian.subs(replacements)
display(Lagrangian)
These variables are defined in the following way using the SymPy command symbols and then are manipulated to get the following expression. Not all of these symbols defined are in the final expression and are intermediary symbols.
l1, l2= sp.symbols('l_1, l_2')
y1, y2, y3, y1d, y2d, y3d = sp.symbols('y_1, y_2, y_3, ydot_1, ydot_2, ydot_3')
t1, t2, t1d, t2d= sp.symbols('theta_1, theta_1, thetadot_1, thetadot_2')
t1dd, t2dd = sp.symbols('thetaddot_1, thetaddot_2')
m1, m2, m3 = sp.symbols('m_1, m_2, m_3')
I1, I2 = sp.symbols('I_1, I_2')
g = sp.symbols('g')
When it comes to Lagrangian, it is just a manipulatioon of alll of these variables. I will show some of the following manipulations down below, but I do not think that the are not very necessary in causing the issue.
I1 = 1/3*m1*l1**2
I2 = 1/3*m2*l2**2
t2 = sp.acos(l1/l2*sp.sin(t1))
t2d = sp.diff(t2, t1) * t1d
y1 = 0.5*l1*sp.cos(t1)
y1d = sp.diff(y1,t1) * t1d
y2 = l1*sp.cos(t1) + 0.5*l2*sp.cos(t2)
y2d = 2*y1d - 0.5*l2*sp.sin(t2)*t2d
y3 = l1*sp.cos(t1) + l2*sp.cos(t2)
y3d = 2*y1d + 2*y2d
T = 0.5*m1*y1d**2 + 0.5*m2*y2d**2 + 0.5*m3*y3d**2 + 0.5*I1*t1d**2 + 0.5*I2*t2d**2
U = m1*g*y1 + m2*g*y2 + m3*g*y3
print('T=')
display(sp.simplify(T))
print('U=')
display(sp.simplify(U))
dTdTheta_dot = sp.diff(T,t1d)
dTdTheta = sp.diff(T,t1)
dUdTheta = sp.diff(U,t1)
#d_dt =
print('dT/dtheta_dot=')
display(sp.simplify(dTdTheta_dot))
print('dT/dTheta=')
display(sp.simplify(dTdTheta))
print('dU/dTheta=')
display(sp.simplify(dUdTheta))
A = (1/3)*l1**4*l2**2*m2*t1d**2*sp.sin(t1)*sp.cos(t1)**3
dA = (1/3)*l1**4*l2**4*m2*(2*t1d*t1dd*sp.sin(t1)*sp.cos(t1)**3+t1d**2*sp.cos(t1)*t1d*sp.cos(t1)**3+
t1d**2*sp.sin(t1)*3*sp.cos(t1)**2*(-sp.sin(t1)*t1d))
B = l1**2*((1/3)*l2**2*m2*l1**2*t1d*sp.sin(t1)**3*sp.cos(t1)-(1/3)*l2**4*m2*t1d*sp.cos(t1)*sp.cos(t1))
dB = l1**2*(((1/3)*l1**2*l2**2*m2*t1dd*sp.sin(t1)**3*sp.cos(t1) - (1/3)*l1**2*l2**2*m2*t1d*3*sp.sin(t1)**2*sp.cos(t1)*t1d*sp.cos(t1) +
(1/3)*l1**2/l2**2*m2*t1d*sp.sin(t1)**3*(-sp.sin(t1))*t1d) - ((1/3)*l1**4*m2*t1dd*sp.sin(t1)*sp.cos(t1)+
(1/3)*l2**4*m2*t1d*sp.cos(t1)*t1d*sp.cos(t1) + (1/3)*l2**4*m2*t1d*sp.sin(t1)*(-sp.sin(t1))*t1d))
C = l1**2*(l1**4*t1d*sp.sin(t1)**4 - 2*l1**2*l2**2*t1d*sp.sin(t1)**2 + l2**4*t1d)*((1/8)*m1*sp.sin(2*t1) + (3/8)*m2*sp.sin(2*t1) - (1/2)*m2)
dC = l1**2 * ((l1**4*(t1dd*sp.sin(t1)**4 + t1d*4*sp.sin(t1)**3*sp.cos(t1)*t1d) - 2*l1**2*l2**2*(t1dd*sp.sin(t1)**2 + t1d*2*sp.sin(t1)*sp.cos(t1)*t1d)+l2**4*t1dd) *
((1/8)*m1*sp.sin(2*t1) + (3/8)*m2*sp.sin(2*t1) - (1/2)*m2*sp.cos(2*t1)) + (l1**4*t1d*sp.sin(t1)**4 - 2*l1**2*l2**2*t1d*sp.sin(t1) + l2**4*t1d) *
((1/8)*m1*sp.cos(2*t1)*2*t1d + (3/8)*m2*sp.cos(2*t1)*2*t1d - (1/2)*m2*(-sp.sin(2*t1)*2*t1d)))
D = (4*m2*sp.sin(2*t1)-3*m3*sp.cos(2*t1)) * (l1**2*sp.sin(t1)**2-l2**2)**2
dD = (4*m2*sp.cos(2*t1)*2*t1d-3*m2*(-sp.sin(2*t1))*2*t1d)*((l1**2*sp.sin(t1)**2-l2**2)**2) + (4*m2*sp.sin(2*t1)-3*m3*sp.cos(2*t1))*(2*(l1**2*sp.sin(t1)-l2**2)*(2*l1**2*sp.sin(t1)*sp.cos(t1)*t1d))
E = (l1**2*sp.sin(t1)**2-l2**2)**-2
dE = -2*(l1**2*sp.sin(t1)-l2**2)**-3*(2*l1**2*sp.sin(t1)*sp.cos(t1)*t1d)
F = (dA+dB+dC+dD)*E + (A+B+C+D)*dE
display(sp.simplify(F))
Lagrangian = F - dTdTheta + dUdTheta
display(sp.simplify(Lagrangian))
Does anyone know why .subs doesn't work? Any help would be much appreciated. Thanks in advance!

Storing data from 'while' loop in array

I have written a code that I want to use to reduce measurement data. For this, I iterate through 30 sets of measurement data. In each iteration, I use fsolve to solve a set of three non-linear equations. This give me an array containing three values that are then further processed (in the example below lbda, alp, bta, dlt, q, N). I can print the results but would need to collect the data for all of the 30 cycles in an 30 by 6 array to do some statistics on (i.e. np.mean on each of the 6 variables).
I've tried the, for me, most obvious function(s) np.append, np.vstack, np.concatenates at the end of each iteration but this only gives me a 1 by 6 array containing only the last iteration step rather than the desired array containing all of the 30 iteration steps.
# loading data above
m1 = data_arr_blkcorr [:,4] / data_arr_blkcorr [:,2]
m2 = data_arr_blkcorr [:,5] / data_arr_blkcorr [:,2]
m3 = data_arr_blkcorr [:,7] / data_arr_blkcorr [:,2]
N=-1
while (N<29):
N = N+1
T1 = 79.744440299369400
T2 = 4.756431967877120
T3 = 195.146815878103000
T4 = 1.333609171398
T5 = 0.540566631391
T6 = 1
T7 = 1.731261585620
T_all = np.array([T4, T5, T6, T7, T1, T2, T3])
n1 = 0.598169735
n2 = 1.509919737
n3 = 0.600477235
n4 = 0.9364071191658
n5 = 0.5815716133216
n6 = 1
n7 = 1.0455228260642
n_all = np.array([n4, n5, n6, n7, n1, n2, n3])
I1 = 94.905838
I2 = 96.906018
I3 = 97.905405
I4 = 99.907473
I5 = 91.90681
I6 = 93.90509
I7 = 95.90468
# some definition of variables here
A11 = T1-n1
A12 = T2-n2
A13 = T3-n3
A21 = -n1*P1
A22 = -n2*P2
A23 = -n3*P3
A31 = m1[N] * P1
A32 = m2[N] * P2
A33 = m3[N] * P3
b11 = m1[N] - n1
b12 = m2[N] - n2
b13 = m3[N] - n3
# some definition of variables here
T = np.array ([T1, T2, T3])
n = np. array([n1, n2, n3])
m = np.array([m1[N], m2[N], m3[N]])
P = np.array([P1, P2, P3])
def F(x):
return x[0]*T + (1-x[0])*n*np.exp(-x[1]/(1-x[0])*P) - m*np.exp(-x[2]*P)
y = fsolve(F, guess)
lbda = y[0]
alp = y[1]/(1-y[0])
bta = y[2]
dlt = (np.exp(-alp*P2)-1)*1000
N_all = n_all * np.exp(-alp*P_all)
q = (1 + (1 - lbda) / lbda * np.sum(N_all) / np.sum(T_all))**(-1)
print (lbda, alp, bta, dlt, q, N)
Going through the posts I have also used this (after a suggestion provided by Koke Cacao):
data_sum = None
new_data = [lbda, alp, bta, dlt, q, N]
data_sum = np.append([data_sum], new_data) if data_sum is not None else new_data
print(data_sum)
But this yields a list of 30 isolated 1 by 6 arrays to which I do not have access to on the whole (i.e. to calculate np.means for individual values over all 30 iteration steps).
0.0209809690838 0.00142553246898 1.61537217874 -0.0443566490317 0.492710128581 26
(0.020980969083774538, 0.0014255324689812997, 1.6153721787428821, -0.044356649031684903, 0.4927101285811698, 26)
0.0209791772348 0.00272489389093 1.61486845411 -0.0847856651612 0.492691689834 27
(0.020979177234773643, 0.0027248938909269797, 1.6148684541135419, -0.084785665161235535, 0.49269168983354455, 27)
0.0209792771323 0.004884280445 1.61191395635 -0.151970341101 0.49269849879 28
(0.020979277132325381, 0.0048842804449965851, 1.6119139563515672, -0.15197034110059349, 0.4926984987899769, 28)
0.0209799414614 0.00256323393277 1.61366560195 -0.0797557810515 0.492700571038 29
(0.020979941461433328, 0.0025632339327746521, 1.6136656019498359, -0.079755781051460417, 0.49270057103806092, 29)
Also, it concatenates results over multiple runs (even after shutting down Python and restarting it) and I cannot clear this (sort of) memory.
try creating an empty list outside your while loop and then append the array.
solution = []
while n < 29:
#your code here
solution.append([lbda, alp, bta, dlt, q, N])
you should declare an empty list outside the while loop scope and then append to it on each Iteration:
result = []
while(N<29):
# calculate something
result.append(your_data)
print(result) # that will give you all the data that you got from each Iteration

Solving system of nonlinear equations with Python, Euler's formula

I am working on solving and analyzing a system of differential equations in Python. First did I solve it with help of scipy.integrate dopri5 and scopes Odeint. Which worked out fine. Then I tried to solve the equations with use of the Euler's method. The equations and code is as followed,
dj = -mu*(J**3 - (C - C0)*J - F)
dc = J + C*F + a*J**2
df = J*F - C
T = 100
dt = 0.001
t = np.linspace(0, T, int(T/dt)+1)
j = np.zeros(len(t))
c = np.zeros(len(t))
f = np.zeros(len(t))
# Initial condition
j[0] = 0.1
c[0] = -0.5
f[0] = 0.1
a = 0.3025
C0 = 0.5
mu = 50
for i in range(len(t)):
j[i+1] = j[i] + (-mu * (j[i]**3 - (c[i] - C0)*j[i] - f[i]))*dt
c[i+1] = c[i] + (j[i] + c[i] * f[i] + (a * j[i])**2)*dt
f[i+1] = f[i] + (j[i] * f[i] - c[i])*dt
Is there any reason why the Euler's method should not work when both the other two are?
In the first iteration, i is 0, and your first line of the loop essentially is:
j[0] = j[-1] + (-mu * (j[-1]**3 - (c[-1] - C0)*j[-1] - f[-1]))*dt
j[-1] is the last element of j, just like c[-1] is the last element of c, etc. Initially they are all zeros, so j[0] becomes a 0, too, which overwrites the initial conditions. To fix this problem, change range(len(t)) to range(1,len(t)). (The model diverges after the first 9200 steps, anyway.)
As DYZ says, your calculation is incorrect on the first loop iteration because j[-1] is the last element of j, which you've initialised to zero.
However, your code wastes a lot of RAM. I assume you just want arrays containing T results, plus the initial values, rather than the results calculated on every step. The code below achieves that via a double for loop. We aren't really getting any benefit from Numpy in this code, so I don't bother importing it.
Note that Euler integration is not very accurate, and you generally need to use a much smaller step size than what's required by more sophisticated integration algorithms. As DYZ mentions, with your current step size the calculation diverges before the loop finishes.
Here's a modified version of your code using a smaller step size.
T = 100
dt = 0.00001
steps = int(T / dt)
substeps = int(steps / T)
# Recalculate `dt` to compensate for possible truncation
# in the `steps` and `substeps` calculations
dt = 1.0 / substeps
print('steps, substeps, dt:', steps, substeps, dt)
a = 0.3025
C0 = 0.5
mu = 50
#dj = -mu*(J**3 - (C - C0)*J - F)
#dc = J + C*F + a*J**2
#df = J*F - C
# Initial condition
j = 0.1
c = -0.5
f = 0.1
jlst, clst, flst = [j], [c], [f]
for i in range(T):
for _ in range(substeps):
j1 = j + (-mu * (j**3 - (c - C0)*j - f))*dt
c1 = c + (j + c * f + (a * j)**2)*dt
f1 = f + (j * f - c)*dt
j, c, f = j1, c1, f1
jlst.append(j)
clst.append(c)
flst.append(f)
def round_seq(seq, places=6):
return [round(u, places) for u in seq]
print('j:', round_seq(jlst), end='\n\n')
print('c:', round_seq(clst), end='\n\n')
print('f:', round_seq(flst), end='\n\n')
output
steps, substeps, dt: 10000000 100000 1e-05
j: [0.1, 0.585459, 1.26718, 3.557956, -1.311867, -0.647698, -0.133683, 0.395812, 0.964856, 3.009683, -2.025674, -1.047722, -0.48872, 0.044296, 0.581284, 1.245423, 14.725407, -1.715456, -0.907364, -0.372118, 0.167733, 0.705257, 1.511711, -3.588555, -1.476817, -0.778593, -0.253874, 0.289294, 0.837128, 1.985792, -2.652462, -1.28088, -0.657113, -0.132971, 0.409071, 0.983504, 3.229393, -2.1809, -1.113977, -0.539586, -0.009829, 0.528546, 1.156086, 8.23469, -1.838582, -0.967078, -0.423261, 0.113883, 0.650319, 1.381138, 12.045565, -1.575015, -0.833861, -0.305952, 0.23632, 0.778052, 1.734888, -2.925769, -1.362437, -0.709641, -0.186249, 0.356775, 0.917051, 2.507782, -2.367126, -1.184147, -0.590753, -0.063942, 0.476121, 1.07614, 5.085211, -1.976542, -1.029395, -0.474206, 0.059772, 0.596505, 1.273214, 17.083466, -1.682855, -0.890842, -0.357555, 0.182944, 0.721096, 1.554496, -3.331861, -1.450497, -0.763182, -0.239007, 0.30425, 0.85435, 2.076595, -2.584081, -1.258788, -0.642362, -0.117774, 0.423883, 1.003181, 3.521072, -2.132709, -1.094792, -0.525123]
c: [-0.5, -0.302644, 0.847742, 12.886781, 0.177404, -0.423405, -0.569541, -0.521669, -0.130084, 7.97828, -0.109606, -0.363033, -0.538874, -0.61005, -0.506872, 0.05076, 216.678959, -0.198445, -0.408569, -0.566869, -0.603713, -0.451729, 0.58959, 2.252504, -0.246645, -0.451, -0.588697, -0.587898, -0.375758, 2.152898, -0.087229, -0.295185, -0.49006, -0.603411, -0.562389, -0.263696, 8.901196, -0.132332, -0.342969, -0.525087, -0.609991, -0.526417, -0.077251, 67.082608, -0.177771, -0.389092, -0.555341, -0.607658, -0.47794, 0.293664, 147.817033, -0.225425, -0.432796, -0.579951, -0.595996, -0.412269, 1.235928, -0.037058, -0.273963, -0.473412, -0.597912, -0.574782, -0.318837, 4.581828, -0.113301, -0.3222, -0.51029, -0.608168, -0.543547, -0.172371, 24.718184, -0.157526, -0.369151, -0.542732, -0.609811, -0.500922, 0.09504, 291.915024, -0.204371, -0.414, -0.56993, -0.602265, -0.443622, 0.700005, 0.740665, -0.25268, -0.456048, -0.590933, -0.585265, -0.36427, 2.528225, -0.093699, -0.301181, -0.494644, -0.60469, -0.558516, -0.245806, 10.941068, -0.137816, -0.348805, -0.52912]
f: [0.1, 0.68085, 1.615135, 1.01107, -2.660947, -0.859348, -0.134789, 0.476782, 1.520241, 4.892319, -9.514924, -2.041217, -0.61413, 0.060247, 0.792463, 2.510586, 11.393914, -6.222736, -1.559576, -0.438133, 0.200729, 1.033274, 3.348756, -39.664752, -4.304545, -1.201378, -0.282146, 0.349631, 1.331995, 4.609547, -20.169056, -3.104072, -0.923759, -0.138225, 0.513633, 1.716341, 6.739864, -11.717002, -2.307614, -0.699883, 7.4e-05, 0.700823, 2.22957, 11.017447, -7.434886, -1.751919, -0.512171, 0.138566, 0.922012, 2.9434, -30.549886, -5.028825, -1.346261, -0.348547, 0.282981, 1.19254, 3.987366, -26.554232, -3.566328, -1.0374, -0.200198, 0.439487, 1.535198, 5.645421, -14.674838, -2.619369, -0.792589, -0.060175, 0.615387, 1.985246, 8.779969, -8.991742, -1.972575, -0.590788, 0.077534, 0.820118, 2.599728, 8.879606, -5.928246, -1.509453, -0.417854, 0.218635, 1.066761, 3.477148, -36.053938, -4.124934, -1.163178, -0.263755, 0.369033, 1.37438, 4.811848, -18.741635, -2.987496, -0.893457, -0.120864, 0.535433, 1.771958, 7.117055, -11.027021, -2.227847, -0.674889]
That takes about 75 seconds on my old 2GHz machine.
Using dt = 0.000005 (which takes almost 2 minutes on this machine) the final values of j, c, and f are -0.524774, -0.529217, -0.674293, respectively, so it looks like we're beginning to get convergence.
Thanks to LutzL for pointing out that dt may need adjusting because of the rounding in the steps and substeps calculations.

Categories

Resources