Maxwell's Demon

Maxwell's demon is a thought experiment created by the physicist James Clerk Maxwell in 1867 in which he suggested how the second law of thermodynamics might hypothetically be violated.[1] In the thought experiment, a demon controls a small door between two chambers of gas. As individual gas molecules approach the door, the demon quickly opens and shuts the door so that only fast molecules are passed into one of the chambers, while only slow molecules are passed into the other. Because faster molecules are hotter, the demon's behaviour causes one chamber to warm up and the other to cool down, thereby decreasing entropy and violating the second law of thermodynamics. This thought experiment has provoked debate and theoretical work on the relation between thermodynamics and information theory extending to the present day, with a number of scientists arguing that theoretical considerations rule out any practical device violating the second law in this way.

ozgunsdemon
In [ ]:
import numpy as np
            import matplotlib.pyplot as plt
            from celluloid import Camera
            
            # DEFINE FIXED VALUES
            
            # Set how many periods you'd like to run your world
            PERIOD_MAX = 1000
            
            # Set the boundaries of the box
            BOUND_X_MIN = -15
            BOUND_X_MAX = 15
            BOUND_Y_MIN = -15
            BOUND_Y_MAX = 15
            
            # GATE
            GATE_X = 0
            #GATE_Y_MIN = -2
            #GATE_Y_MAX = 2
            
            # SPEED TYPES
            SLOW = 0.2
            FAST = 1
            
            # Initial positions
            INITIAL_PARTICLE_POSITIONS_X = np.array([6, 5, -3, -8])
            INITIAL_PARTICLE_POSITIONS_Y = np.array([4, 20, -15, -11])
            # Initial speed
            INITIAL_PARTICLE_SPEEDS = np.array([SLOW, FAST, SLOW, FAST])
            # Initial Velocity direction as unit vectors
            INITIAL_PARTICLE_DIRECTIONS_X = np.array([-np.sqrt(2) / 2, 1, np.sqrt(3)/2, 0.6])
            INITIAL_PARTICLE_DIRECTIONS_Y = np.array([-np.sqrt(2) / 2, 0, 1/2, 0.8])
            
            # Total number of particles
            particle_total_number = len(INITIAL_PARTICLE_POSITIONS_X)
            
            # Create System State Matrix by defining the correct dimensions
            system_state_matrix = np.ndarray(shape=(PERIOD_MAX + 1, 5, particle_total_number))
            
            # Initialize the first entry of the state matrix with the initial values
            system_state_matrix[0] = \
                INITIAL_PARTICLE_POSITIONS_X, INITIAL_PARTICLE_POSITIONS_Y, INITIAL_PARTICLE_SPEEDS, INITIAL_PARTICLE_DIRECTIONS_X, \
                INITIAL_PARTICLE_DIRECTIONS_Y
            
            # Now let's make the particles move
            # The Loop to update system
            for i in np.arange(0, PERIOD_MAX):
                # At each loop, system will move from t=i to t=i+1
            
                # first get the "old" values at t=i
                # 2 dimensional position array
                particle_positions_at_i = system_state_matrix[i][0:2]
                # 1 dimensional speed array
                particle_speeds_at_i = system_state_matrix[i][2]
                # 2 dimensional velocity direction array
                particle_directions_at_i = system_state_matrix[i][3:5]
            
            
                # First calculate as if no boundary
                particle_positions_temporary_array = particle_positions_at_i + particle_speeds_at_i * particle_directions_at_i
            
                # Initialize the next period vectors temporarily
                particle_positions_at_i_plus_one = particle_positions_temporary_array
                # Make a deep copy to prevent wrong value updating
                particle_directions_at_i_plus_one = particle_directions_at_i.copy()
            
            
                # Now check the boundary conditions and make the necessary updates
                # Vertical bounds:
                positions_x_out_of_bounds_right = (particle_positions_temporary_array[0] > BOUND_X_MAX)
                positions_x_out_of_bounds_left = (particle_positions_temporary_array[0] < BOUND_X_MIN)
                positions_x_all_out_of_bound_vertical= np.logical_or(positions_x_out_of_bounds_right, positions_x_out_of_bounds_left)
            
                particle_directions_at_i_plus_one[0][positions_x_all_out_of_bound_vertical] *= -1
                particle_positions_at_i_plus_one[0][positions_x_out_of_bounds_right] = 2 * BOUND_X_MAX - \
                                                                                       particle_positions_at_i_plus_one[0][
                                                                                           positions_x_out_of_bounds_right]
                particle_positions_at_i_plus_one[0][positions_x_out_of_bounds_left] = 2 * BOUND_X_MIN - \
                                                                                      particle_positions_at_i_plus_one[0][
                                                                                          positions_x_out_of_bounds_left]
            
                # Horizontal bounds
                positions_y_out_of_bounds_up = (particle_positions_temporary_array[1] > BOUND_Y_MAX)
                positions_x_out_of_bounds_down = (particle_positions_temporary_array[1] < BOUND_X_MIN)
            
                particle_directions_at_i_plus_one[1][
                    np.logical_or(positions_y_out_of_bounds_up, positions_x_out_of_bounds_down)] *= -1
                particle_positions_at_i_plus_one[1][positions_y_out_of_bounds_up] = 2 * BOUND_Y_MAX - \
                                                                                    particle_positions_at_i_plus_one[1][
                                                                                        positions_y_out_of_bounds_up]
                particle_positions_at_i_plus_one[1][positions_x_out_of_bounds_down] = 2 * BOUND_Y_MIN - \
                                                                                      particle_positions_at_i_plus_one[1][
                                                                                          positions_x_out_of_bounds_down]
            
                # Check Gate conditions
                gate_blocking_left_to_right = np.logical_and.reduce((particle_positions_at_i[0] <= GATE_X,
                                                                    particle_positions_at_i_plus_one[0] > GATE_X,
                                                                    particle_speeds_at_i == SLOW))
                gate_blocking_right_to_left = np.logical_and.reduce((particle_positions_at_i[0] >= GATE_X,
                                                                   particle_positions_at_i_plus_one[0] < GATE_X,
                                                                   particle_speeds_at_i == FAST))
            
            
                gate_bouncers = np.logical_or(gate_blocking_left_to_right, gate_blocking_right_to_left)
                particle_positions_at_i_plus_one[0][gate_bouncers] = 2 * GATE_X - particle_positions_at_i_plus_one[0][gate_bouncers]
                particle_directions_at_i_plus_one[0][gate_bouncers] *= -1
            
                # Write the finalized data to the state matrix
                system_state_matrix[i + 1] = particle_positions_at_i_plus_one[0], particle_positions_at_i_plus_one[
                    1], particle_speeds_at_i, particle_directions_at_i_plus_one[0], particle_directions_at_i_plus_one[1]
            
            # Collect the data
            print(system_state_matrix)
            
            # Plotting
            fig = plt.figure()
            camera = Camera(fig)
            plt.axis([BOUND_X_MIN, BOUND_X_MAX, BOUND_Y_MIN, BOUND_Y_MAX])
            for i in np.arange(0, PERIOD_MAX):
                X = system_state_matrix[i][0]
                Y = system_state_matrix[i][1]
                Z = system_state_matrix[i][2]
            
                plt.scatter(X, Y, c=Z,cmap='bwr')
                camera.snap()
            animation = camera.animate()
            animation.save('ozgun_demon.html')
            
[[[  6.           5.          -3.          -8.        ]
              [  4.          20.         -15.         -11.        ]
              [  0.2          1.           0.2          1.        ]
              [ -0.70710678   1.           0.8660254    0.6       ]
              [ -0.70710678   0.           0.5          0.8       ]]
            
             [[  5.85857864   6.          -2.82679492  -7.4       ]
              [  3.85857864  10.         -14.9        -10.2       ]
              [  0.2          1.           0.2          1.        ]
              [ -0.70710678   1.           0.8660254    0.6       ]
              [ -0.70710678  -0.           0.5          0.8       ]]
            
             [[  5.71715729   7.          -2.65358984  -6.8       ]
              [  3.71715729  10.         -14.8         -9.4       ]
              [  0.2          1.           0.2          1.        ]
              [ -0.70710678   1.           0.8660254    0.6       ]
              [ -0.70710678  -0.           0.5          0.8       ]]
            
             ...
            
             [[-14.86148648  13.         -10.1413294    9.2       ]
              [-12.86148648  10.           5.2          7.4       ]
              [  0.2          1.           0.2          1.        ]
              [  0.70710678   1.           0.8660254   -0.6       ]
              [  0.70710678  -0.          -0.5          0.8       ]]
            
             [[-14.72006512  14.          -9.96812432   8.6       ]
              [-12.72006512  10.           5.1          8.2       ]
              [  0.2          1.           0.2          1.        ]
              [  0.70710678   1.           0.8660254   -0.6       ]
              [  0.70710678  -0.          -0.5          0.8       ]]
            
             [[-14.57864376  15.          -9.79491924   8.        ]
              [-12.57864376  10.           5.           9.        ]
              [  0.2          1.           0.2          1.        ]
              [  0.70710678   1.           0.8660254   -0.6       ]
              [  0.70710678  -0.          -0.5          0.8       ]]]
            
MovieWriter ffmpeg unavailable; trying to use <class 'matplotlib.animation.PillowWriter'> instead.
            
In [ ]: