namespace BWR_siumulator { public class ThermalPort { public double Measurement { get; set; } public double ValueToAffect { get; set; } public ThermalPort(double measurement, double valueToAffect) { Measurement = measurement; ValueToAffect = valueToAffect; } } public class Reactor { // --- Core Physics and Material Constants --- public const double CONTROL_ROD_SPEED = 1.0; public const double DECAY_HEAT_FRACTION = 0.065; public const double DECAY_HEAT_LAMBDA = 0.0077; public const double BETA = 0.0067; public const double LAMBDA_DECAY = 0.078; public const double L_PROMPT_NEUTRON = 0.00002; public const double FUEL_HEAT_CAPACITY = 2.8; public const double HEAT_TRANSFER_COEFF = 0.5; public const double ALPHA_SQRT_T_FUEL = -0.008; public const double CONTROL_ROD_WORTH = -0.05; public const double INTRINSIC_SOURCE = 0.25; public const double COOLANT_TEMP = 300; // K public const double STABLE_TEMP = 547; // K [The temperature at which the doppler effect has no effect on reactivity] // --- State Variables --- public double Power { get; private set; } // MW // --- Inspect Variables --- public double ReactivityDoppler { get; private set; } public double ReactivityRod { get; private set; } public double FuelTemp { get; private set; } // K public double HeatGenerated { get; private set; } public double Precursors { get; private set; } public double DecayHeatPrecursors { get; private set; } public double ReactivityTotal { get; private set; } public double ControlRodCurrentPosition { get; private set; } public double ControlRodTargetPosition { get; set; } // Can be set externally public double ReactorPeriod { get; private set; } public ThermalPort ThermalPort { get; private set; } public Reactor(double initialPower = 1.0, double fuelTemp = 300) { Power = initialPower; FuelTemp = fuelTemp; // Initialize precursors and decay heat to be in equilibrium Precursors = Power * BETA / (LAMBDA_DECAY * L_PROMPT_NEUTRON); DecayHeatPrecursors = Power * DECAY_HEAT_FRACTION / DECAY_HEAT_LAMBDA; ReactivityTotal = 0.0; ControlRodCurrentPosition = 100; ControlRodTargetPosition = 100; ReactorPeriod = double.PositiveInfinity; ThermalPort = new ThermalPort(FuelTemp, HeatGenerated); } private double GetReactivityFromRodPosition() { double positionRad = (ControlRodCurrentPosition / 100.0) * Math.PI; double effectiveness = (1 - Math.Cos(positionRad)) / 2.0; return CONTROL_ROD_WORTH * effectiveness; } /// /// Calculates reactivity from the fuel temperature. /// /// The reactivity due to fuel temperature (Doppler effect). private double CalculateDopplerReactivity() { return ALPHA_SQRT_T_FUEL * (Math.Sqrt(FuelTemp) - Math.Sqrt(STABLE_TEMP)); } /// /// Advances the simulation by one time step, dt. /// /// The time step in seconds. public void Step(double dt) { // --- UPDATE CONTROL ROD POSITION --- if (ControlRodCurrentPosition != ControlRodTargetPosition) { double difference = ControlRodTargetPosition - ControlRodCurrentPosition; double maxMove = CONTROL_ROD_SPEED * dt; if (Math.Abs(difference) < maxMove) { ControlRodCurrentPosition = ControlRodTargetPosition; } else if (difference > 0) { ControlRodCurrentPosition += maxMove; } else { ControlRodCurrentPosition -= maxMove; } } // --- PHYSICS CALCULATION --- // 1. Calculate Total Reactivity ReactivityDoppler = CalculateDopplerReactivity(); ReactivityRod = GetReactivityFromRodPosition(); ReactivityTotal = ReactivityDoppler + ReactivityRod; // 2. Solve Point Kinetics double powerOld = Power; double dtLambda = dt * LAMBDA_DECAY; double dtOverL = dt / L_PROMPT_NEUTRON; double numerator = Power + Precursors * dtLambda / (1 + dtLambda) + dt * INTRINSIC_SOURCE; double denominator = 1 - dtOverL * (ReactivityTotal - BETA) - (dtOverL * BETA * dtLambda) / (1 + dtLambda); Power = numerator / denominator; Precursors = (Precursors + dt * Power * BETA / L_PROMPT_NEUTRON) / (1 + dtLambda); // 3. Calculate Reactor Period double powerChange = Power - powerOld; if (Math.Abs(powerChange) > 1e-9 && Power > 1e-7) { ReactorPeriod = (Power * dt) / powerChange; if (ReactorPeriod < -1500 || ReactorPeriod > 1500) { ReactorPeriod = double.PositiveInfinity; } } else { ReactorPeriod = double.PositiveInfinity; } // 4. Solve for Decay Heat DecayHeatPrecursors = (DecayHeatPrecursors + dt * Power * DECAY_HEAT_FRACTION) / (1 + dt * DECAY_HEAT_LAMBDA); // 5. Solve Thermal-Hydraulics double promptHeat = Power * (1 - DECAY_HEAT_FRACTION); HeatGenerated = promptHeat + DecayHeatPrecursors; //double heatRemoved = HEAT_TRANSFER_COEFF * (FuelTemp - COOLANT_TEMP); //double dFuelTemp = (HeatGenerated - heatRemoved) / FUEL_HEAT_CAPACITY; double dFuelTemp = HeatGenerated / FUEL_HEAT_CAPACITY; FuelTemp += dFuelTemp * dt; // --- CLAMPING --- if (Power < 0) Power = 0; if (Precursors < 0) Precursors = 0; if (DecayHeatPrecursors < 0) DecayHeatPrecursors = 0; // Update ThermalPort ThermalPort.Measurement = FuelTemp; ThermalPort.ValueToAffect = HeatGenerated; } } }