I want to create survival games with infinite block terrain(like Minecraft). So i using ursina python game engine, you can see it here
So i using perlin noise to create the terrain with build-in ursina block model. I test for first 25 block and it work pretty good with above 100 FPS, so i start increase to 250 block and more because I want a infinite terrain. But i ran to some problem, when i increase to 100 block or more, my FPS start to decrease below 30 FPS (With i create just one layer).
Here is my code:
#-------------------------------Noise.py(I got on the github)-------------------------
# Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com)
# see LICENSE.txt for details
"""Perlin noise -- pure python implementation"""
__version__ = '$Id: perlin.py 521 2008-12-15 03:03:52Z casey.duncan $'
from math import floor, fmod, sqrt
from random import randint
# 3D Gradient vectors
_GRAD3 = ((1,1,0),(-1,1,0),(1,-1,0),(-1,-1,0),
(1,0,1),(-1,0,1),(1,0,-1),(-1,0,-1),
(0,1,1),(0,-1,1),(0,1,-1),(0,-1,-1),
(1,1,0),(0,-1,1),(-1,1,0),(0,-1,-1),
)
# 4D Gradient vectors
_GRAD4 = ((0,1,1,1), (0,1,1,-1), (0,1,-1,1), (0,1,-1,-1),
(0,-1,1,1), (0,-1,1,-1), (0,-1,-1,1), (0,-1,-1,-1),
(1,0,1,1), (1,0,1,-1), (1,0,-1,1), (1,0,-1,-1),
(-1,0,1,1), (-1,0,1,-1), (-1,0,-1,1), (-1,0,-1,-1),
(1,1,0,1), (1,1,0,-1), (1,-1,0,1), (1,-1,0,-1),
(-1,1,0,1), (-1,1,0,-1), (-1,-1,0,1), (-1,-1,0,-1),
(1,1,1,0), (1,1,-1,0), (1,-1,1,0), (1,-1,-1,0),
(-1,1,1,0), (-1,1,-1,0), (-1,-1,1,0), (-1,-1,-1,0))
# A lookup table to traverse the simplex around a given point in 4D.
# Details can be found where this table is used, in the 4D noise method.
_SIMPLEX = (
(0,1,2,3),(0,1,3,2),(0,0,0,0),(0,2,3,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,2,3,0),
(0,2,1,3),(0,0,0,0),(0,3,1,2),(0,3,2,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,3,2,0),
(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),
(1,2,0,3),(0,0,0,0),(1,3,0,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,3,0,1),(2,3,1,0),
(1,0,2,3),(1,0,3,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,0,3,1),(0,0,0,0),(2,1,3,0),
(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),
(2,0,1,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,0,1,2),(3,0,2,1),(0,0,0,0),(3,1,2,0),
(2,1,0,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,1,0,2),(0,0,0,0),(3,2,0,1),(3,2,1,0))
# Simplex skew constants
_F2 = 0.5 * (sqrt(3.0) - 1.0)
_G2 = (3.0 - sqrt(3.0)) / 6.0
_F3 = 1.0 / 3.0
_G3 = 1.0 / 6.0
class BaseNoise:
"""Noise abstract base class"""
permutation = (151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,
129,22,39,253,9,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180)
period = len(permutation)
# Double permutation array so we don't need to wrap
permutation = permutation * 2
randint_function = randint
def __init__(self, period=None, permutation_table=None, randint_function=None):
"""Initialize the noise generator. With no arguments, the default
period and permutation table are used (256). The default permutation
table generates the exact same noise pattern each time.
An integer period can be specified, to generate a random permutation
table with period elements. The period determines the (integer)
interval that the noise repeats, which is useful for creating tiled
textures. period should be a power-of-two, though this is not
enforced. Note that the speed of the noise algorithm is indpendent of
the period size, though larger periods mean a larger table, which
consume more memory.
A permutation table consisting of an iterable sequence of whole
numbers can be specified directly. This should have a power-of-two
length. Typical permutation tables are a sequnce of unique integers in
the range [0,period) in random order, though other arrangements could
prove useful, they will not be "pure" simplex noise. The largest
element in the sequence must be no larger than period-1.
period and permutation_table may not be specified together.
A substitute for the method random.randint(a, b) can be chosen. The
method must take two integer parameters a and b and return an integer N
such that a <= N <= b.
"""
if randint_function is not None: # do this before calling randomize()
if not hasattr(randint_function, '__call__'):
raise TypeError(
'randint_function has to be a function')
self.randint_function = randint_function
if period is None:
period = self.period # enforce actually calling randomize()
if period is not None and permutation_table is not None:
raise ValueError(
'Can specify either period or permutation_table, not both')
if period is not None:
self.randomize(period)
elif permutation_table is not None:
self.permutation = tuple(permutation_table) * 2
self.period = len(permutation_table)
def randomize(self, period=None):
"""Randomize the permutation table used by the noise functions. This
makes them generate a different noise pattern for the same inputs.
"""
if period is not None:
self.period = period
perm = list(range(self.period))
perm_right = self.period - 1
for i in list(perm):
j = self.randint_function(0, perm_right)
perm[i], perm[j] = perm[j], perm[i]
self.permutation = tuple(perm) * 2
class SimplexNoise(BaseNoise):
"""Perlin simplex noise generator
Adapted from Stefan Gustavson's Java implementation described here:
http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
To summarize:
"In 2001, Ken Perlin presented 'simplex noise', a replacement for his classic
noise algorithm. Classic 'Perlin noise' won him an academy award and has
become an ubiquitous procedural primitive for computer graphics over the
years, but in hindsight it has quite a few limitations. Ken Perlin himself
designed simplex noise specifically to overcome those limitations, and he
spent a lot of good thinking on it. Therefore, it is a better idea than his
original algorithm. A few of the more prominent advantages are:
* Simplex noise has a lower computational complexity and requires fewer
multiplications.
* Simplex noise scales to higher dimensions (4D, 5D and up) with much less
computational cost, the complexity is O(N) for N dimensions instead of
the O(2^N) of classic Noise.
* Simplex noise has no noticeable directional artifacts. Simplex noise has
a well-defined and continuous gradient everywhere that can be computed
quite cheaply.
* Simplex noise is easy to implement in hardware."
"""
def noise2(self, x, y):
"""2D Perlin simplex noise.
Return a floating point value from -1 to 1 for the given x, y coordinate.
The same value is always returned for a given x, y pair unless the
permutation table changes (see randomize above).
"""
# Skew input space to determine which simplex (triangle) we are in
s = (x + y) * _F2
i = floor(x + s)
j = floor(y + s)
t = (i + j) * _G2
x0 = x - (i - t) # "Unskewed" distances from cell origin
y0 = y - (j - t)
if x0 > y0:
i1 = 1; j1 = 0 # Lower triangle, XY order: (0,0)->(1,0)->(1,1)
else:
i1 = 0; j1 = 1 # Upper triangle, YX order: (0,0)->(0,1)->(1,1)
x1 = x0 - i1 + _G2 # Offsets for middle corner in (x,y) unskewed coords
y1 = y0 - j1 + _G2
x2 = x0 + _G2 * 2.0 - 1.0 # Offsets for last corner in (x,y) unskewed coords
y2 = y0 + _G2 * 2.0 - 1.0
# Determine hashed gradient indices of the three simplex corners
perm = self.permutation
ii = int(i) % self.period
jj = int(j) % self.period
gi0 = perm[ii + perm[jj]] % 12
gi1 = perm[ii + i1 + perm[jj + j1]] % 12
gi2 = perm[ii + 1 + perm[jj + 1]] % 12
# Calculate the contribution from the three corners
tt = 0.5 - x0**2 - y0**2
if tt > 0:
g = _GRAD3[gi0]
noise = tt**4 * (g[0] * x0 + g[1] * y0)
else:
noise = 0.0
tt = 0.5 - x1**2 - y1**2
if tt > 0:
g = _GRAD3[gi1]
noise += tt**4 * (g[0] * x1 + g[1] * y1)
tt = 0.5 - x2**2 - y2**2
if tt > 0:
g = _GRAD3[gi2]
noise += tt**4 * (g[0] * x2 + g[1] * y2)
return noise * 70.0 # scale noise to [-1, 1]
def noise3(self, x, y, z):
"""3D Perlin simplex noise.
Return a floating point value from -1 to 1 for the given x, y, z coordinate.
The same value is always returned for a given x, y, z pair unless the
permutation table changes (see randomize above).
"""
# Skew the input space to determine which simplex cell we're in
s = (x + y + z) * _F3
i = floor(x + s)
j = floor(y + s)
k = floor(z + s)
t = (i + j + k) * _G3
x0 = x - (i - t) # "Unskewed" distances from cell origin
y0 = y - (j - t)
z0 = z - (k - t)
# For the 3D case, the simplex shape is a slightly irregular tetrahedron.
# Determine which simplex we are in.
if x0 >= y0:
if y0 >= z0:
i1 = 1; j1 = 0; k1 = 0
i2 = 1; j2 = 1; k2 = 0
elif x0 >= z0:
i1 = 1; j1 = 0; k1 = 0
i2 = 1; j2 = 0; k2 = 1
else:
i1 = 0; j1 = 0; k1 = 1
i2 = 1; j2 = 0; k2 = 1
else: # x0 < y0
if y0 < z0:
i1 = 0; j1 = 0; k1 = 1
i2 = 0; j2 = 1; k2 = 1
elif x0 < z0:
i1 = 0; j1 = 1; k1 = 0
i2 = 0; j2 = 1; k2 = 1
else:
i1 = 0; j1 = 1; k1 = 0
i2 = 1; j2 = 1; k2 = 0
# Offsets for remaining corners
x1 = x0 - i1 + _G3
y1 = y0 - j1 + _G3
z1 = z0 - k1 + _G3
x2 = x0 - i2 + 2.0 * _G3
y2 = y0 - j2 + 2.0 * _G3
z2 = z0 - k2 + 2.0 * _G3
x3 = x0 - 1.0 + 3.0 * _G3
y3 = y0 - 1.0 + 3.0 * _G3
z3 = z0 - 1.0 + 3.0 * _G3
# Calculate the hashed gradient indices of the four simplex corners
perm = self.permutation
ii = int(i) % self.period
jj = int(j) % self.period
kk = int(k) % self.period
gi0 = perm[ii + perm[jj + perm[kk]]] % 12
gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12
gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12
gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12
# Calculate the contribution from the four corners
noise = 0.0
tt = 0.6 - x0**2 - y0**2 - z0**2
if tt > 0:
g = _GRAD3[gi0]
noise = tt**4 * (g[0] * x0 + g[1] * y0 + g[2] * z0)
else:
noise = 0.0
tt = 0.6 - x1**2 - y1**2 - z1**2
if tt > 0:
g = _GRAD3[gi1]
noise += tt**4 * (g[0] * x1 + g[1] * y1 + g[2] * z1)
tt = 0.6 - x2**2 - y2**2 - z2**2
if tt > 0:
g = _GRAD3[gi2]
noise += tt**4 * (g[0] * x2 + g[1] * y2 + g[2] * z2)
tt = 0.6 - x3**2 - y3**2 - z3**2
if tt > 0:
g = _GRAD3[gi3]
noise += tt**4 * (g[0] * x3 + g[1] * y3 + g[2] * z3)
return noise * 32.0
def lerp(t, a, b):
return a + t * (b - a)
def grad3(hash, x, y, z):
g = _GRAD3[hash % 16]
return x*g[0] + y*g[1] + z*g[2]
class TileableNoise(BaseNoise):
"""Tileable implemention of Perlin "improved" noise. This
is based on the reference implementation published here:
http://mrl.nyu.edu/~perlin/noise/
"""
def noise3(self, x, y, z, repeat, base=0.0):
"""Tileable 3D noise.
repeat specifies the integer interval in each dimension
when the noise pattern repeats.
base allows a different texture to be generated for
the same repeat interval.
"""
i = int(fmod(floor(x), repeat))
j = int(fmod(floor(y), repeat))
k = int(fmod(floor(z), repeat))
ii = (i + 1) % repeat
jj = (j + 1) % repeat
kk = (k + 1) % repeat
if base:
i += base; j += base; k += base
ii += base; jj += base; kk += base
x -= floor(x); y -= floor(y); z -= floor(z)
fx = x**3 * (x * (x * 6 - 15) + 10)
fy = y**3 * (y * (y * 6 - 15) + 10)
fz = z**3 * (z * (z * 6 - 15) + 10)
perm = self.permutation
A = perm[i]
AA = perm[A + j]
AB = perm[A + jj]
B = perm[ii]
BA = perm[B + j]
BB = perm[B + jj]
return lerp(fz, lerp(fy, lerp(fx, grad3(perm[AA + k], x, y, z),
grad3(perm[BA + k], x - 1, y, z)),
lerp(fx, grad3(perm[AB + k], x, y - 1, z),
grad3(perm[BB + k], x - 1, y - 1, z))),
lerp(fy, lerp(fx, grad3(perm[AA + kk], x, y, z - 1),
grad3(perm[BA + kk], x - 1, y, z - 1)),
lerp(fx, grad3(perm[AB + kk], x, y - 1, z - 1),
grad3(perm[BB + kk], x - 1, y - 1, z - 1))))
#--------------------------Math.py(For InverseLefp)--------------------------------
def Clamp(t: float, minimum: float, maximum: float):
"""Float result between a min and max values."""
value = t
if t < minimum:
value = minimum
elif t > maximum:
value = maximum
return value
def InverseLefp(a: float, b: float, value: float):
if a != b:
return Clamp((value - a) / (b - a), 0, 1)
return 0
#-----------------------------Game.py(Main code)----------------------
from ursina import *
from ursina.prefabs import *
from ursina.prefabs.first_person_controller import *
from Math import InverseLefp
import Noise
app = Ursina()
#The maximum height of the terrain
maxHeight = 10
#Control the width and height of the map
mapWidth = 10
mapHeight = 10
#A class that create a block
class Voxel(Button):
def __init__(self, position=(0,0,0)):
super().__init__(
parent = scene,
position = position,
model = 'cube',
origin_y = .5,
texture = 'white_cube',
color = color.color(0, 0, random.uniform(.9, 1.0)),
highlight_color = color.lime,
)
#Detect user key input
def input(self, key):
if self.hovered:
if key == 'right mouse down':
#Place block if user right click
voxel = Voxel(position=self.position + mouse.normal)
if key == 'left mouse down':
#Break block if user left click
destroy(self)
if key == 'escape':
#Exit the game if user press the esc key
app.userExit()
#Return perlin noise value between 0 and 1 with x, y position with scale = noiseScale
def GeneratedNoiseMap(y: int, x: int, noiseScale: float):
#Check if the noise scale was invalid or not
if noiseScale <= 0:
noiseScale = 0.001
sampleX = x / noiseScale
sampleY = y / noiseScale
#The Noise.SimplexNoise().noise2 will return the value between -1 and 1
perlinValue = Noise.SimplexNoise().noise2(sampleX, sampleY)
#The InverseLefp will make the value scale to between 0 and 1
perlinValue = InverseLefp(-1, 1, perlinValue)
return perlinValue
for z in range(mapHeight):
for x in range(mapWidth):
#Calculating the height of the block and round it to integer
height = round(GeneratedNoiseMap(z, x, 20) * maxHeight)
#Place the block and make it always below the player
block = Voxel(position=(x, height - maxHeight - 1, z))
#Set the collider of the block
block.collider = 'mesh'
#Character movement
player = FirstPersonController()
#Run the game
app.run()
All file in same folder.
It was working fine but the FPS is very low, so can anyone help?
I'm not able to test this code at the moment but this should serve as a starting point:
level_parent = Entity(model=Mesh(vertices=[], uvs=[]))
for z in range(mapHeight):
for x in range(mapWidth):
height = round(GeneratedNoiseMap(z, x, 20) * maxHeight)
block = Voxel(position=(x, height - maxHeight - 1, z))
level_parent.model.vertices.extend(block.model.vertices)
level_parent.collider = 'mesh' # call this only once after all vertices are set up
For texturing, you might have to add the block.uvs from each block to level_parent.model.uvs as well. Alternatively, call level_parent.model.project_uvs() after setting up the vertices.
On my version of ursina engine (5.0.0) only this code:
`
level_parent = Entity(model=Mesh(vertices=[], uvs=[]))
for z in range(mapHeight):
for x in range(mapWidth):
height = round(GeneratedNoiseMap(z, x, 20) * maxHeight)
block = Voxel(position=(x, height - maxHeight - 1, z))
#level_parent.model.vertices.extend(block.model.vertices)
level_parent.combine().vertices.extend(block.combine().vertices)
level_parent.collider = 'mesh'
`
is working.
I've spent a long time finally getting my algorithm to invert Laplace transforms and live plot them to work, but unfortunately, although it seems to run fine within the function, it continuously just plots zeros!
I've wasted so much time getting this thing to work, and now I have no clue what could be wrong. So forgive me but I think I'll have to post the whole thing.
The equation this is based on is a series-integral and it iterates until the series converges. However, it converges super slowly so I used Aitken's delta-squared method to speed the whole thing up. The goal is to populate array y with each Aitken's iteration from 0.000001 to 10 and then compare the estimation' integral with the previous iteration's if a random point in the former is close enough to the latter. This loops until the integrals have a difference of less than 0.01 or a set amount of iterations is reached (~30000).
This happens for 6 equations simultaneously using parallel processing. The graph live plots all 6 (in blue) compared to each of the actual equations they're converging towards (in red) using matplotlib.
For debugging, I basically tried putting a bunch of print statements everywhere in the f_u function to see what was going on. Everything looked like it was going fine. But in the __main__ function and plot functions when I did the same, I was getting all zeros for the same array. I thought it was a local vs. global issue so I put global y in the plot and f_u, with no luck.
To execute this, you're gonna need all the packages in the import statement, including numba. You can remove the #jit decorations on the Laplace functions if you don't want numba, but it will be sloooooow.
Here it is:
import matplotlib.pyplot as plt
from matplotlib import style
from matplotlib import animation
import numpy as np
from scipy.integrate import quad, simps
from scipy.special import jv
import math
import random
import datetime
import multiprocessing as mp
from numba import jit
start = datetime.datetime.now() # Record time start
# Initialize plot
style.use('fivethirtyeight')
fig, axes = plt.subplots(3, 2)
((ax1, ax2), (ax3, ax4), (ax5, ax6)) = axes
plt.autoscale(enable=True)
for ax in axes.flat:
ax.label_outer()
# Initialize variables
gamma = np.array([np.nan, 1, 1, 0, 1, 2, 1]) # Bromwich contour parameter for each equation
num = 6 # Number of equations to be processed in total
n = 0 # Repetition constant for equation 3, set to either zero or 1 (diverges at 1)
max_count = 1000 # Number of data points in x and y
epsilon = 10 ** -40 # Minimum number to divide by in Aitken's iteration
err = 10 ** -4 # Maximum difference in y to trigger comparison in closing()
max_err = 0.01 # Maximum difference in integrals computed in closing() to end iteration process
x_0 = 0 # Set first integral to 0 for now
result = np.zeros(7) # Vector to store Aitken's iteration for closing() comparison
previous = np.zeros(7) # Vector to store iteration before Aitken's for closing() comparison
end = np.zeros(7) # Vector to store end conditions -- running = 0, ended = 1
u, step = np.linspace(0.000001, 10, max_count, retstep=True) # Initializing x values
y = np.empty([num, 4, max_count]) # Initializing y values -- form is y[equation number][iteration in Aitken's][x value]
y_denom = np.empty([num, max_count]) # Initializing array to store Aitken's denominator values for each equation
x_iter = np.empty([num, max_count]) # Initializing array to store Aitken's iteration as starting point for next iteration
def printer(num, k): # Prints the equation number, iteration and y value at 10
if num == 1:
print(str(num) + " " + str(k) + " " + str(y[0][3][max_count-1]) + "\n")
elif num == 2:
print(str(num) + " " + str(k) + " " + str(y[1][3][max_count-1]) + "\n")
elif num == 3:
print(str(num) + " " + str(k) + " " + str(y[2][3][max_count-1]) + "\n")
elif num == 4:
print(str(num) + " " + str(k) + " " + str(y[3][3][max_count-1]) + "\n")
elif num == 5:
print(str(num) + " " + str(k) + " " + str(y[4][3][max_count-1]) + "\n")
elif num == 6:
print(str(num) + " " + str(k) + " " + str(y[5][3][max_count-1]) + "\n")
def plot1(): # Plots estimation 1 (blue) and actual equation 1 (red)
global y
ax1.clear()
line1, = ax1.plot(u, y[0][3][0:max_count])
ax1.plot(u, np.sin(u), 'tab:red')
return line1,
def plot2(): # Plots estimation 2 (blue) and actual equation 2 (red)
global y
ax2.clear()
line2, = ax2.plot(u, y[1][3][0:max_count])
ax2.plot(u, np.heaviside(u, 1), 'tab:red')
return line2,
def plot3(): # Plots estimation 3 (blue) and actual equation 3 (red)
global y
ax3.clear()
line3, = ax3.plot(u, y[2][3][0:max_count])
ax3.plot(u, np.cos(2 * np.sqrt(u)) / np.sqrt(np.pi * u), 'tab:red')
return line3,
def plot4(): # Plots estimation 4 (blue) and actual equation 4 (red)
global y
ax4.clear()
line4, = ax4.plot(u, y[3][3][0:max_count])
ax4.plot(u, np.log(u), 'tab:red')
return line4,
def plot5(): # Plots estimation 5 (blue) and actual equation 5 (red)
global y
ax5.clear()
line5, = ax5.plot(u, y[4][3][0:max_count])
ax5.plot(u, np.heaviside(u - 1, 1) - np.heaviside(u - 2, 1), 'tab:red')
return line5,
def plot6(): # Plots estimation 6 (blue) and actual equation 6 (red)
global y
ax6.clear()
line6, = ax6.plot(u, y[5][3][0:max_count])
ax6.plot(u, jv(0, u), 'tab:red')
return line6,
def animate(i): # Plots all equations, used to live-update graph
ln1, = plot1()
ln2, = plot2()
ln3, = plot3()
ln4, = plot4()
ln5, = plot5()
ln6, = plot6()
return ln1, ln2, ln3, ln4, ln5, ln6,
#jit(nopython=True, cache=True)
def f_p1(omega, u, k): # Equation 1, Laplace form
b = (omega + k * np.pi) / u
a = gamma[1]
f1 = a * (1 / (a ** 2 + (b + 1) ** 2) + 1 / (a ** 2 + (b - 1) ** 2)) * np.cos(omega)
return f1
#jit(nopython=True, cache=True)
def f_p2(omega, u, k): # Equation 2, Laplace form
b = (omega + k * np.pi) / u
a = gamma[2]
f2 = a / (a ** 2 + b ** 2) * np.cos(omega)
return f2
#jit(nopython=True, cache=True)
def f_p3(omega, u, k): # Equation 3, Laplace form
b = (omega + k * np.pi) / u
a = gamma[3]
f3 = np.e ** (-a / (a ** 2 + b ** 2)) * (a ** 2 + b ** 2) ** (1 / 4) * np.cos(
(math.atan2(b, a) + 2 * n * np.pi) / 2) * np.cos(b / (a ** 2 + b ** 2)) / (
np.sqrt(a ** 2 + b ** 2) * (np.cos((math.atan2(b, a) + 2 * n * np.pi) / 2) ** 2 + np.sin(
(math.atan2(b, a) + 2 * n * np.pi) / 2))) * np.cos(omega)
return f3
#jit(nopython=True, cache=True)
def f_p4(omega, u, k): # Equation 4, Laplace form
b = (omega + k * np.pi) / u
a = gamma[4]
f4 = -(a * np.log(a ** 2 + b ** 2) + a * np.e) / (a ** 2 + b ** 2) * np.cos(omega)
return f4
#jit(nopython=True, cache=True)
def f_p5(omega, u, k): # Equation 5, Laplace form
b = (omega + k * np.pi) / u
a = gamma[5]
f5 = a * (np.e ** (-a) * np.cos(b) - np.e ** (-2 * a) * np.cos(2 * b)) / (a ** 2 + b ** 2) * np.cos(omega)
return f5
#jit(nopython=True, cache=True)
def f_p6(omega, u, k): # Equation 6, Laplace form
b = (omega + k * np.pi) / u
a = gamma[6]
f6 = np.sqrt((a ** 2 - b ** 2 + 1) ** 2 + (2 * a * b) ** 2) * np.cos(
(math.atan2(2 * a * b, a ** 2 - b ** 2 + 1) + 2 * n * np.pi) / 2) / (
((a ** 2 - b ** 2 + 1) ** 2 + (2 * a * b) ** 2) * (
np.cos((math.atan2(2 * a * b, a ** 2 - b ** 2 + 1) + 2 * n * np.pi) / 2) ** 2 + np.sin(
(math.atan2(2 * a * b, a ** 2 - b ** 2 + 1) + 2 * n * np.pi) / 2) ** 2)) * np.cos(omega)
return f6
def closing(result, previous, num, k): # Checks for convergence. If so, adds each term to f_0 and ends process
if abs(result - previous) < max_err:
diff = abs(result - previous)
print("Equation " + str(num) + " converges to " + str(diff) + " with " + str(k) + " iterations." + "\n")
if num == 1:
for i in range(max_count):
y[0][3][i] += f_0(num)
end[1] += 1
return 1
elif num == 2:
for i in range(max_count):
y[1][3][i] += f_0(num)
end[2] += 1
return 1
elif num == 3:
for i in range(max_count):
y[2][3][i] += f_0(num)
end[3] += 1
return 1
elif num == 4:
for i in range(max_count):
y[3][3][i] += f_0(num)
end[4] += 1
return 1
elif num == 5:
for i in range(max_count):
y[4][3][i] += f_0(num)
end[5] += 1
return 1
elif num == 6:
for i in range(max_count):
y[5][3][i] += f_0(num)
end[6] += 1
return 1
else:
print("Equation " + str(num) + " has not yet converged.\n")
return 0
def f_0(num): # Initial integral in the series. Added at end after closing() convergence test
for i in range(max_count):
if num == 1:
x_0 = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * quad(
f_p1, 0, np.pi / 2, args=(u[i], 0))[0]
elif num == 2:
x_0 = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * quad(
f_p2, 0, np.pi / 2, args=(u[i], 0))[0]
elif num == 3:
x_0 = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * quad(
f_p3, 0, np.pi / 2, args=(u[i], 0))[0]
elif num == 4:
x_0 = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * quad(
f_p4, 0, np.pi / 2, args=(u[i], 0))[0]
elif num == 5:
x_0 = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * quad(
f_p5, 0, np.pi / 2, args=(u[i], 0))[0]
elif num == 6:
x_0 = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * quad(
f_p6, 0, np.pi / 2, args=(u[i], 0))[0]
return x_0
def f_u(u, num): # Main algorithm. Here, the summation integrals iterate
global y, result, previous # Forces retrieval of from global of these arrays? (attempt to debug)
k = 1 # Number of iterations
for i in range(max_count): # First overall series iteration (initial 1st)
if num == 1:
y[0][0][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p1, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 2:
y[1][0][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p2, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 3:
y[2][0][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p3, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 4:
y[3][0][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p4, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 5:
y[4][0][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p5, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 6:
y[5][0][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p6, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
for j in range(10000): # Iteration loop. Goes until closing() or k = ~6000 for each equation
if j > 0:
for i in range(max_count): # Sets the first iteration (out of 3 for Aitken's) equal to the previous Aitken's or the first iteration if j = 0 (1st)
if num == 1:
y[0][0][i] = x_iter[0][i]
elif num == 2:
y[1][0][i] = x_iter[1][i]
elif num == 3:
y[2][0][i] = x_iter[2][i]
elif num == 4:
y[3][0][i] = x_iter[3][i]
elif num == 5:
y[4][0][i] = x_iter[4][i]
elif num == 6:
y[5][0][i] = x_iter[5][i]
else:
k += 1
for i in range(max_count): # Second iteration for Aitken's (2nd)
if num == 1:
y[0][1][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p1, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 2:
y[1][1][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p2, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 3:
y[2][1][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p3, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 4:
y[3][1][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p4, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 5:
y[4][1][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p5, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 6:
y[5][1][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p6, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
k += 1
for i in range(max_count): # Third iteration for Aitken's (3rd)
if num == 1:
y[0][2][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p1, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 2:
y[1][2][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p2, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 3:
y[2][2][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p3, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 4:
y[3][2][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p4, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 5:
y[4][2][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p5, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
elif num == 6:
y[5][2][i] = 2 * np.e ** (gamma[num] * u[i]) / (np.pi * u[i]) * (-1) ** k * quad(
f_p6, -np.pi / 2, np.pi / 2, args=(u[i], k))[0]
k += 1
for i in range(max_count): # Aitken's delta-squared method iteration using the previous 3 (4th)
if num == 1:
y_denom[0][i] = (y[0][2][i] - y[0][1][i]) - (y[0][1][i] - y[0][0][i])
if abs(y_denom[0][i]) < epsilon:
print("Denominator too small to calculate. Exiting...\n")
return 9
y[0][3][i] = y[0][2][i] - ((y[0][2][i] - y[0][1][i]) ** 2) / y_denom[0][i]
elif num == 2:
y_denom[1][i] = (y[1][2][i] - y[1][1][i]) - (y[1][1][i] - y[1][0][i])
if abs(y_denom[1][i]) < epsilon:
print("Denominator too small to calculate. Exiting...\n")
return 9
y[1][3][i] = y[1][2][i] - ((y[1][2][i] - y[1][1][i]) ** 2) / y_denom[1][i]
elif num == 3:
y_denom[2][i] = (y[2][2][i] - y[2][1][i]) - (y[2][1][i] - y[2][0][i])
if abs(y_denom[2][i]) < epsilon:
print("Denominator too small to calculate. Exiting...\n")
return 9
y[2][3][i] = y[2][2][i] - ((y[2][2][i] - y[2][1][i]) ** 2) / y_denom[2][i]
elif num == 4:
y_denom[3][i] = (y[3][2][i] - y[3][1][i]) - (y[3][1][i] - y[3][0][i])
if abs(y_denom[3][i]) < epsilon:
print("Denominator too small to calculate. Exiting...\n")
return 9
y[3][3][i] = y[3][2][i] - ((y[3][2][i] - y[3][1][i]) ** 2) / y_denom[3][i]
elif num == 5:
y_denom[4][i] = (y[4][2][i] - y[4][1][i]) - (y[4][1][i] - y[4][0][i])
if abs(y_denom[4][i]) < epsilon:
print("Denominator too small to calculate. Exiting...\n")
return 9
y[4][3][i] = y[4][2][i] - ((y[4][2][i] - y[4][1][i]) ** 2) / y_denom[4][i]
elif num == 6:
y_denom[5][i] = (y[5][2][i] - y[5][1][i]) - (y[5][1][i] - y[5][0][i])
if abs(y_denom[5][i]) < epsilon:
print("Denominator too small to calculate. Exiting...\n")
return 9
y[5][3][i] = y[5][2][i] - ((y[5][2][i] - y[5][1][i]) ** 2) / y_denom[5][i]
k += 1
printer(num, k)
rand = random.randrange(0, max_count) # Setting random number for x value
# Comparison between y at random x in Aitken's vs the previous iteration. If close enough, triggers closing()
if num == 1:
if abs(y[0][3][rand] - y[0][2][rand]) < err:
result[num] = simps(y[0][3][0:max_count], u, dx=step) #Integrates Aitken's iteration (4th)
previous[num] = simps(y[0][2][0:max_count], u, dx=step) #Integrates integration before Aitken's (3rd)
c1 = closing(result[num], previous[num], num, k)
if c1 == 1:
break
elif num == 2:
if abs(y[1][3][rand] - y[1][2][rand]) < err:
result[num] = simps(y[1][3][0:max_count], u, dx=step)
previous[num] = simps(y[1][2][0:max_count], u, dx=step)
c2 = closing(result[num], previous[num], num, k)
if c2 == 1:
break
elif num == 3:
if abs(y[2][3][rand] - y[2][2][rand]) < err:
result[num] = simps(y[2][3][0:max_count], u, dx=step)
previous[num] = simps(y[2][2][0:max_count], u, dx=step)
c3 = closing(result[num], previous[num], num, k)
if c3 == 1:
break
elif num == 4:
if abs(y[3][3][rand] - y[3][2][rand]) < err:
result[num] = simps(y[3][3][0:max_count], u, dx=step)
previous[num] = simps(y[3][2][0:max_count], u, dx=step)
c4 = closing(result[num], previous[num], num, k)
if c4 == 1:
break
elif num == 5:
if abs(y[4][3][rand] - y[4][2][rand]) < err:
result[num] = simps(y[4][3][0:max_count], u, dx=step)
previous[num] = simps(y[4][2][0:max_count], u, dx=step)
c5 = closing(result[num], previous[num], num, k)
if c5 == 1:
break
elif num == 6:
if abs(y[5][3][rand] - y[5][2][rand]) < err:
result[num] = simps(y[5][3][0:max_count], u, dx=step)
previous[num] = simps(y[5][2][0:max_count], u, dx=step)
c6 = closing(result[num], previous[num], num, k)
if c6 == 1:
break
for i in range(max_count): # Setting current Aitken's iteration to first iteration
if num == 1:
x_iter[0][i] = y[0][3][i]
elif num == 2:
x_iter[1][i] = y[1][3][i]
elif num == 3:
x_iter[2][i] = y[2][3][i]
elif num == 4:
x_iter[3][i] = y[3][3][i]
elif num == 5:
x_iter[4][i] = y[4][3][i]
elif num == 6:
x_iter[5][i] = y[5][3][i]
# If iteration limit reached, sets process closing condition to 1 and returns to main
end[num] = 1
return
if __name__ == '__main__':
i = 0
# Define and begin processes, one for each equation
p1 = mp.Process(target=f_u, args=(u, 1))
p2 = mp.Process(target=f_u, args=(u, 2))
p3 = mp.Process(target=f_u, args=(u, 3))
p4 = mp.Process(target=f_u, args=(u, 4))
p5 = mp.Process(target=f_u, args=(u, 5))
p6 = mp.Process(target=f_u, args=(u, 6))
p1.start()
p2.start()
p3.start()
p4.start()
p5.start()
p6.start()
# While any equation is running, update and show the plot every second
while end[1] != 1 or end[2] != 1 or end[3] != 1 or end[4] != 1 or end[5] != 1 or end[6] != 1:
animation.FuncAnimation(fig, animate, interval=1000, blit=True)
plt.pause(1)
plt.show(block=False)
plt.pause(1)
# Joining and closing each process when complete
p1.join()
p1.terminate()
p2.join()
p2.terminate()
p3.join()
p3.terminate()
p4.join()
p4.terminate()
p5.join()
p5.terminate()
p6.join()
p6.terminate()
end = datetime.datetime.now() # Record time end
print("Runtime: " + str(end - start))
# Updates and plots final result
animate(i)
plt.show()
exit(0)
Hope you can help me out. Thanks in advance.