import math
import random
import pymunk
from .Neutron import Neutron
from .Water import Water
from .FuelElement import FuelElement
from .Fuel import Fuel
from .Material import MaterialType as Material
from .helper import get_probability
# Mechanics class
[docs]
class Mechanics:
"""The Mechanics class is responsible for handling the physics of the simulation.
"""
def __init__(self, core = None):
"""The constructor for the Mechanics class.
:param core: The core object, defaults to None
:type core: Core, optional
"""
self.core = core
self.space = core.get_space()
self.angle_offset = math.radians(30)
self.water_absorption = 0.05
# Set the collision handler for neutron and moderator
NMC_handler = self.space.add_collision_handler(1, 2)
NMC_handler.begin = self.neutron_moderator_collision
# Collision handler for neutron and control rod
NCRC_handler = self.space.add_collision_handler(1, 5)
NCRC_handler.begin = self.neutron_control_rod_collision
# Fuel element collision handler
NFEC_handler = self.space.add_collision_handler(1, 3)
NFEC_handler.begin = self.neutron_fuel_element_collision
# Xenon collision handler
NX_handler = self.space.add_collision_handler(1, 8)
NX_handler.begin = self.neutron_xenon_collision
# Boundary collision handler
NB_handler = self.space.add_collision_handler(1, 10)
NB_handler.begin = self.neutron_boundary_collision
NB_handler.separate = self.neutron_boundary_collision
NW_handler = self.space.add_collision_handler(1, 11)
NW_handler.begin = self.neutron_water_collision_add
NW_handler.separate = self.neutron_water_collision_remove
[docs]
def neutron_water_collision_add(self, arbiter, space, data):
"""The method to handle neutron-water collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
prob = get_probability()
neutron_shape, water_shape = arbiter.shapes
if prob < self.water_absorption:
neutron = Neutron.body_to_neutron[(neutron_shape.body, neutron_shape)]
neutron.remove_neutron()
self.core.remove_neutron_from_core(neutron)
water = Water.body_to_water[(water_shape.body, water_shape)]
water.increase_number_of_neutrons_interacting()
water.change_temperature(20)
return True
except Exception as e:
return False
[docs]
def neutron_water_collision_remove(self, arbiter, space, data):
"""The method to handle neutron-water collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
neutron_shape, water_shape = arbiter.shapes
water = Water.body_to_water[(water_shape.body, water_shape)]
water.decrease_number_of_neutrons_interacting()
return True
except Exception as e:
return False
[docs]
def regulate_water_temperature(self):
"""The method to regulate the water temperature
:return: None
:rtype: None
"""
for water in self.core.get_water_list():
water.change_temperature(-0.5)
return None
[docs]
def regulate_fuel_element_occurence(self):
"""The method to regulate the fuel element occurence
:return: None
:rtype: None
"""
for fuel_rod in self.core.get_fuel_rod_list():
for fuel_element in fuel_rod.get_fuel_elements():
fuel_element.change_material()
return None
[docs]
def neutron_boundary_collision(self, arbiter, space, data):
"""The method to handle neutron-boundary collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
neutron_shape, boundary_shape = arbiter.shapes
neutron = Neutron.body_to_neutron[(neutron_shape.body, neutron_shape)]
neutron.remove_neutron()
self.core.remove_neutron_from_core(neutron)
return True
except Exception as e:
# print(e)
return False
[docs]
def neutron_moderator_collision(self, arbiter, space, data):
"""The method to handle neutron-moderator collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
neutron_shape, moderator_shape = arbiter.shapes
# Get the neutron's speed
current_velocity = neutron_shape.body.velocity
if abs(current_velocity.length - self.core.fast_speed.length) < 0.5:
# Get the collision normal, which represents the moderator surface at the collision point
collision_normal = arbiter.contact_point_set.normal
# Calculate the reflection of the current velocity across the collision normal
dot_product = current_velocity.dot(collision_normal)
reflected_direction = current_velocity - 2 * dot_product * collision_normal
reflected_direction = reflected_direction.normalized() # Normalize to get direction only
# Set the reflected direction to thermal speed
thermal_speed_magnitude = self.core.thermal_speed.length
new_velocity = reflected_direction * thermal_speed_magnitude
# Apply the new velocity to the neutron
neutron_shape.body.velocity = new_velocity
return True
except Exception as e:
# print(e)
return False
[docs]
def neutron_control_rod_collision(self, arbiter, space, data):
"""The method to handle neutron-control rod collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
neutron_shape, control_rod_shape = arbiter.shapes
neutron = Neutron.body_to_neutron[(neutron_shape.body, neutron_shape)]
neutron.remove_neutron()
self.core.remove_neutron_from_core(neutron)
return True
except Exception as e:
# print(e)
return False
[docs]
def neutron_fuel_element_collision(self, arbiter, space, data):
"""The method to handle neutron-fuel element collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
neutron_shape, fuel_element_shape = arbiter.shapes
neutron = Neutron.body_to_neutron[(neutron_shape.body, neutron_shape)]
fuel_element = FuelElement.body_to_fuel_element[(fuel_element_shape.body, fuel_element_shape)]
neutron_velocity = neutron.body.velocity
neutron_speed = neutron_velocity.length
threshold = 0.5
if abs(neutron_speed - self.core.thermal_speed.length) <= threshold:
fuel_element.set_material(Material.NON_FISSILE)
# Original direction
direction = neutron_velocity.normalized()
# Small angle offset in radians for deviation (5 degrees)
small_angle_offset = self.angle_offset
# Calculate the two slightly different directions
x1 = direction.x * math.cos(small_angle_offset) - direction.y * math.sin(small_angle_offset)
y1 = direction.x * math.sin(small_angle_offset) + direction.y * math.cos(small_angle_offset)
new_direction_n1 = pymunk.Vec2d(x1, y1)
x2 = direction.x * math.cos(-small_angle_offset) - direction.y * math.sin(-small_angle_offset)
y2 = direction.x * math.sin(-small_angle_offset) + direction.y * math.cos(-small_angle_offset)
new_direction_n2 = pymunk.Vec2d(x2, y2)
# Set the new speed for both directions to fast speed
fast_speed_magnitude = self.core.fast_speed.length
new_speed_n1 = new_direction_n1 * fast_speed_magnitude
new_speed_n2 = new_direction_n2 * fast_speed_magnitude
neutron1 = Neutron(speed=new_speed_n1, position=neutron.get_position(), mass=neutron.get_mass(), radius=neutron.get_radius())
neutron2 = Neutron(speed=new_speed_n2, position=neutron.get_position(), mass=neutron.get_mass(), radius=neutron.get_radius())
if get_probability() < 0.5:
new_speed_n3 = -direction * fast_speed_magnitude
neutron3 = Neutron(speed=new_speed_n3, position=neutron.get_position(), mass=neutron.get_mass(), radius=neutron.get_radius())
self.core.add_neutron_to_core(neutron3)
self.core.add_neutron_to_core(neutron1)
self.core.add_neutron_to_core(neutron2)
neutron.remove_neutron()
self.core.remove_neutron_from_core(neutron)
return True
except Exception as e:
# print(e)
return False
[docs]
def neutron_xenon_collision(self, arbiter, space, data):
"""The method to handle neutron-xenon collision
:param arbiter: arbiter object
:type arbiter: Arbiter
:param space: space object
:type space: Space
:param data: data object
:type data: Data
:return: True if successful, False otherwise
:rtype: bool
"""
try:
neutron_shape, xenon_shape = arbiter.shapes
current_velocity = neutron_shape.body.velocity
if abs(current_velocity.length - self.core.thermal_speed.length) < 0.5:
neutron = Neutron.body_to_neutron[(neutron_shape.body, neutron_shape)]
xenon = FuelElement.body_to_fuel_element[(xenon_shape.body, xenon_shape)]
neutron.remove_neutron()
self.core.remove_neutron_from_core(neutron)
xenon.set_material(Material.NON_FISSILE)
return True
except Exception as e:
return False
[docs]
def generate_random_neutron(self, limit):
"""The method to generate a random neutron
:param limit: The limit for the probability
:type limit: float
:return: The neutron object if successful, False otherwise
:rtype: Neutron
"""
try:
prob = get_probability()
if prob < limit:
neutron_speed = self.core.thermal_speed
random_angle = random.uniform(0, 2 * math.pi)
x = math.cos(random_angle)
y = math.sin(random_angle)
new_direction = pymunk.Vec2d(x, y)
neutron_speed = new_direction * neutron_speed.length
neutron_position = (random.uniform(0, self.core.length), random.uniform(0, self.core.width))
neutron = Neutron(speed=neutron_speed, position=neutron_position, mass=0.1, radius=5)
self.core.add_neutron_to_core(neutron)
return True
except Exception as e:
# print(e)
return False