diff options
author | Adrian Kummerlaender | 2020-03-24 20:51:06 +0100 |
---|---|---|
committer | Adrian Kummerlaender | 2020-03-24 20:51:06 +0100 |
commit | 235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a (patch) | |
tree | 184c2011b8e14149051c1fac1c7eaee282724d0d /boltzgas/visual | |
parent | 955f517f3dc235c2abc96160da10787c1718e778 (diff) | |
download | boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.tar boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.tar.gz boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.tar.bz2 boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.tar.lz boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.tar.xz boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.tar.zst boltzgas-235123e65b5edb3d5f0c8f14b05e2d2e56f9e40a.zip |
Separate classes into modules
Diffstat (limited to 'boltzgas/visual')
-rw-r--r-- | boltzgas/visual/__init__.py | 4 | ||||
-rw-r--r-- | boltzgas/visual/box.py | 16 | ||||
-rw-r--r-- | boltzgas/visual/histogram.py | 125 | ||||
-rw-r--r-- | boltzgas/visual/shader.py | 15 | ||||
-rw-r--r-- | boltzgas/visual/tracer.py | 20 | ||||
-rw-r--r-- | boltzgas/visual/view.py | 154 |
6 files changed, 334 insertions, 0 deletions
diff --git a/boltzgas/visual/__init__.py b/boltzgas/visual/__init__.py new file mode 100644 index 0000000..3462d96 --- /dev/null +++ b/boltzgas/visual/__init__.py @@ -0,0 +1,4 @@ +from .box import ColoredBox +from .histogram import VelocityHistogram +from .tracer import Tracer +from .view import View diff --git a/boltzgas/visual/box.py b/boltzgas/visual/box.py new file mode 100644 index 0000000..0a6ab5a --- /dev/null +++ b/boltzgas/visual/box.py @@ -0,0 +1,16 @@ +from OpenGL.GL import * + +class ColoredBox: + def __init__(self, origin, extend, color): + self.origin = origin + self.extend = extend + self.color = color + + def display(self, uniform): + glUniform3f(uniform['face_color'], *self.color) + glBegin(GL_TRIANGLE_STRIP) + glVertex(self.origin[0], self.origin[1] , 0.) + glVertex(self.origin[0] + self.extend[0], self.origin[1] , 0.) + glVertex(self.origin[0] , self.origin[1] + self.extend[1], 0.) + glVertex(self.origin[0] + self.extend[1], self.origin[1] + self.extend[1], 0.) + glEnd() diff --git a/boltzgas/visual/histogram.py b/boltzgas/visual/histogram.py new file mode 100644 index 0000000..2f90b8b --- /dev/null +++ b/boltzgas/visual/histogram.py @@ -0,0 +1,125 @@ +from OpenGL.GL import * + +import numpy as np + +import scipy.stats as stats +import scipy.constants as const +from scipy.optimize import minimize + +import matplotlib +import matplotlib.pyplot as plt + +from concurrent.futures import Future, ProcessPoolExecutor + +def get_histogram(velocities, char_u): + maxwellian = stats.maxwell.fit(velocities) + + plt.style.use('dark_background') + + fig = plt.figure(figsize=(8,8)) + ax = fig.add_axes([0.1, 0.06, 0.88, 0.92]) + + plt.ylim(0, 0.003) + plt.ylabel('Probability') + + plt.xlim(0, 1.2*char_u) + plt.xlabel('Velocity magnitude [m/s]') + + plt.hist(velocities, bins=50, density=True, alpha=0.5, label='Simulated velocities') + + xs = np.linspace(0, 1.2*char_u, 100) + plt.plot(xs, stats.maxwell.pdf(xs, *maxwellian), label='Maxwell-Boltzmann distribution') + + plt.legend(loc='upper right') + + fig.canvas.draw() + buf = fig.canvas.tostring_rgb() + width, height = fig.canvas.get_width_height() + texture = np.frombuffer(buf, dtype=np.uint8).reshape(width, height, 3) + + plt.close(fig) + + return (texture, height, width) + + +class VelocityHistogram: + def __init__(self, gas, origin, extend): + self.gas = gas + self.origin = origin + self.extend = extend + self.steps = -1 + + self.pool = ProcessPoolExecutor(max_workers=1) + self.plotter = None + + self.tick = False + self.mixing = 0.0 + + def setup(self): + self.vertices = np.array([ + self.origin[0] , self.origin[1] , 0., 1., + self.origin[0] + self.extend[0], self.origin[1] , 1., 1., + self.origin[0] , self.origin[1] + self.extend[1], 0., 0., + self.origin[0] + self.extend[0], self.origin[1] + self.extend[1], 1., 0. + ], dtype=np.float32) + + self.vao = glGenVertexArrays(1) + self.vbo = glGenBuffers(1) + + glBindVertexArray(self.vao) + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + glBufferData(GL_ARRAY_BUFFER, self.vertices.tostring(), GL_STATIC_DRAW) + + glEnableVertexAttribArray(0) + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(0)) + glEnableVertexAttribArray(1) + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(2*np.dtype(np.float32).itemsize)) + + self.texture_id = glGenTextures(2) + + glBindTexture(GL_TEXTURE_2D, self.texture_id[0]) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, self.texture_id[1]) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + def update(self): + self.steps = self.steps + 1 + + if self.steps % 50 == 0 and self.plotter == None: + self.plotter = self.pool.submit(get_histogram, self.gas.get_velocity_norms(), self.gas.char_u) + + else: + if self.plotter != None and self.plotter.done(): + texture, width, height = self.plotter.result() + if self.tick: + glBindTexture(GL_TEXTURE_2D, self.texture_id[0]) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture) + self.tick = False + self.mixing = 1.0 + + else: + glBindTexture(GL_TEXTURE_2D, self.texture_id[1]) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture) + self.tick = True + self.mixing = 0.0 + + self.plotter = None + + + def display(self, uniform): + if self.tick: + self.mixing = min(self.mixing+0.1, 1.0); + else: + self.mixing = max(self.mixing-0.1, 0.0); + + glBindTextures(self.texture_id[0], 2, self.texture_id) + glUniform1iv(uniform['picture'], len(self.texture_id), self.texture_id) + glUniform1f(uniform['mixing'], self.mixing) + + glBindVertexArray(self.vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) + glBindVertexArray(0) + diff --git a/boltzgas/visual/shader.py b/boltzgas/visual/shader.py new file mode 100644 index 0000000..b4295a8 --- /dev/null +++ b/boltzgas/visual/shader.py @@ -0,0 +1,15 @@ +from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER +from OpenGL.GL import shaders + +class Shader: + def __init__(self, fragment_src, vertex_src, uniform): + self.program = shaders.compileProgram( + shaders.compileShader(vertex_src, GL_VERTEX_SHADER), + shaders.compileShader(fragment_src, GL_FRAGMENT_SHADER)) + self.uniform = { } + for name in uniform: + self.uniform[name] = shaders.glGetUniformLocation(self.program, name) + + def use(self): + shaders.glUseProgram(self.program) + diff --git a/boltzgas/visual/tracer.py b/boltzgas/visual/tracer.py new file mode 100644 index 0000000..a5b2203 --- /dev/null +++ b/boltzgas/visual/tracer.py @@ -0,0 +1,20 @@ +from OpenGL.GL import * + +class Tracer: + def __init__(self, gas, iParticle): + self.gas = gas + self.iParticle = iParticle + self.trace = [ ] + + def update(self): + position = self.gas.get_positions()[self.iParticle] + self.trace.append((position[0], position[1])) + + def display(self, uniform): + glUniform3f(uniform['face_color'], 1., 0., 0.) + glLineWidth(2) + glBegin(GL_LINE_STRIP) + for v in self.trace: + glVertex(*v, 0.) + glEnd() + diff --git a/boltzgas/visual/view.py b/boltzgas/visual/view.py new file mode 100644 index 0000000..d6e188b --- /dev/null +++ b/boltzgas/visual/view.py @@ -0,0 +1,154 @@ +from OpenGL.GL import * +from OpenGL.GLUT import * + +from pyrr import matrix44 + +import numpy as np + +from .shader import Shader + +particle_shader = ( + """ + #version 430 + + in vec3 color; + + void main(){ + if (length(gl_PointCoord - vec2(0.5)) > 0.5) { + discard; + } + + gl_FragColor = vec4(color.xyz, 0.0); + } + """, + """ + #version 430 + + layout (location=0) in vec2 particles; + + out vec3 color; + + uniform mat4 projection; + uniform vec3 face_color; + uniform vec3 trace_color; + uniform uint trace_id; + + void main() { + gl_Position = projection * vec4(particles, 0., 1.); + + if (gl_VertexID == trace_id) { + color = trace_color; + } else { + color = face_color; + } + } + """, + ['projection', 'face_color', 'trace_color', 'trace_id'] +) + +decoration_shader = ( + """ + #version 430 + + in vec3 color; + + void main(){ + gl_FragColor = vec4(color.xyz, 0.0); + } + """, + """ + #version 430 + + in vec3 vertex; + + out vec3 color; + + uniform mat4 projection; + uniform vec3 face_color; + + void main() { + gl_Position = projection * vec4(vertex, 1.); + color = face_color; + } + """, + ['projection', 'face_color'] +) + +texture_shader = ( + """ + #version 430 + + in vec2 tex_coord; + + uniform sampler2D picture[2]; + + uniform float mixing; + + void main() { + gl_FragColor = mix(texture(picture[0], tex_coord), texture(picture[1], tex_coord), mixing); + } + """, + """ + #version 430 + + layout (location=0) in vec2 screen_vertex; + layout (location=1) in vec2 texture_vertex; + + out vec2 tex_coord; + + uniform mat4 projection; + + void main() { + gl_Position = projection * vec4(screen_vertex, 0.0, 1.0); + tex_coord = texture_vertex; + } + """, + ['picture','projection','mixing'] +) + +class View: + def __init__(self, gas, decorations, windows): + self.gas = gas + self.decorations = decorations + self.windows = windows + + self.texture_shader = Shader(*texture_shader) + self.particle_shader = Shader(*particle_shader) + self.decoration_shader = Shader(*decoration_shader) + + def reshape(self, width, height): + glViewport(0,0,width,height) + + world_height = 1.4 + world_width = world_height / height * width + + projection = matrix44.create_orthogonal_projection(-world_width/2, world_width/2, -world_height/2, world_height/2, -1, 1) + translation = matrix44.create_from_translation([-1.05, -1.0/2, 0]) + + self.pixels_per_unit = height / world_height + self.projection = np.matmul(translation, projection) + + def display(self): + glClearColor(0.,0.,0.,1.) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + + self.decoration_shader.use() + glUniformMatrix4fv(self.decoration_shader.uniform['projection'], 1, False, np.asfortranarray(self.projection)) + for decoration in self.decorations: + decoration.display(self.decoration_shader.uniform) + + self.texture_shader.use() + glUniformMatrix4fv(self.texture_shader.uniform['projection'], 1, False, np.asfortranarray(self.projection)) + for window in self.windows: + window.display(self.texture_shader.uniform) + + self.particle_shader.use() + glUniformMatrix4fv(self.particle_shader.uniform['projection'], 1, False, np.asfortranarray(self.projection)) + glUniform3f(self.particle_shader.uniform['face_color'], 1., 1., 1.) + glUniform3f(self.particle_shader.uniform['trace_color'], 1., 0., 0.) + glUniform1ui(self.particle_shader.uniform['trace_id'], 4) + glEnable(GL_POINT_SPRITE) + glPointSize(2*self.gas.radius*self.pixels_per_unit) + self.gas.gl_draw_particles() + + glutSwapBuffers() |