diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4bb5747
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+
+# user spesific files
+.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+.vscode/*
\ No newline at end of file
diff --git a/BWR.ConsoleApp/Program.cs b/BWR.ConsoleApp/Program.cs
new file mode 100644
index 0000000..93ea96d
--- /dev/null
+++ b/BWR.ConsoleApp/Program.cs
@@ -0,0 +1,57 @@
+// MyBWRSimulator.ConsoleApp/Program.cs
+namespace MyBWRSimulator.ConsoleApp
+{
+ using MyBWRSimulator.Core.Simulation;
+ using MyBWRSimulator.Core.Components.Concrete; // Needed for specific component instantiation if not using config
+ using MyBWRSimulator.Core.Ports; // Needed for specific port instantiation if not using config
+ using System;
+ using System.IO;
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ Console.WriteLine("Starting BWR Simulator Console Application...");
+
+ var simulator = new Simulator();
+
+ // Define the path to your configuration file
+ // Adjust this path as needed for your project structure
+ string configFilePath = Path.Combine(Directory.GetCurrentDirectory(), "config", "PlantConfig.json");
+
+ // Option 1: Load configuration from JSON file
+ //simulator.LoadPlantConfiguration(configFilePath);
+
+ // Option 2 (Alternative for testing without config file): Manually create components and connect them
+
+ var reactor = new ReactorCore("RC001", "Main Reactor Core", 4);
+ var pump = new Pump("PMP001", "Recirculation Pump", 100.0, 50000.0); // maxFlow, maxHeadPressure
+ var pipe = new Pipe("PIP001", "Hot Leg Pipe", 20.0, 0.5); // length, diameter
+ var tank = new FluidTank("TK001", "Condensate Tank", 500.0, 1, 1, 250.0); // maxVol, numInlets, numOutlets, initialVol
+
+ simulator.AddComponent(reactor);
+ simulator.AddComponent(pump);
+ simulator.AddComponent(pipe);
+ simulator.AddComponent(tank);
+
+ // Establish connections manually (example)
+ // Note: You'd need to access the specific ports of each component.
+ // This is simplified and assumes direct port access for brevity.
+ // In a real scenario, you'd use GetFluidPorts() etc.
+ // For example: pump.GetThermalPorts().First(p => p.ID.Contains("PowerIn")).Connect(powerSource.GetThermalPorts().First(p => p.ID.Contains("PowerOut")));
+
+ // Example of manual connection (requires casting and knowing port IDs)
+ // This is simplified and might break if port IDs change.
+ // The ConfigurationLoader handles this more robustly.
+ reactor.GetFluidPortById("FeedwaterInlet").Connect(pipe.GetFluidPortById("Inlet"));
+ pipe.GetFluidPortById("Outlet").Connect(pump.GetFluidPortById("Inlet"));
+ pump.GetFluidPortById("Outlet").Connect(tank.GetFluidPortById("Outlet")); // Tank has Inlet1, Inlet2 etc.
+
+ // Run the simulation for 60 seconds with 1-second time steps
+ simulator.RunSimulation(60.0, 1.0);
+
+ Console.WriteLine("\nSimulation application finished. Press any key to exit.");
+ Console.ReadKey();
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Abstract/Component.cs b/BWR.core/Components/Abstract/Component.cs
new file mode 100644
index 0000000..30e079e
--- /dev/null
+++ b/BWR.core/Components/Abstract/Component.cs
@@ -0,0 +1,154 @@
+namespace MyBWRSimulator.Core.Components.Abstract
+{
+ using MyBWRSimulator.Core.Interfaces;
+ using MyBWRSimulator.Core.Ports;
+ using System.Collections.Generic;
+ using System.Linq; // Needed for .ToList()
+
+ ///
+ /// Abstract base class for all simulated physical components.
+ /// Implements ISimulatable, IFluidConnectable, and IThermalConnectable.
+ ///
+ public abstract class Component : ISimulatable, IFluidConnectable, IThermalConnectable
+ {
+ ///
+ /// Unique identifier for the component.
+ ///
+ public string ID { get; protected set; }
+
+ ///
+ /// Human-readable name of the component.
+ ///
+ public string Name { get; protected set; }
+
+ ///
+ /// Detailed description of the component.
+ ///
+ public string Description { get; protected set; }
+
+ ///
+ /// An integer value indicating the order in which components should be updated.
+ /// Lower numbers mean higher priority (updated first).
+ ///
+ public int UpdatePriority { get; protected set; }
+
+ ///
+ /// Indicates if the component is receiving sufficient Thermal power.
+ ///
+ public bool IsPowered { get; protected set; }
+
+ ///
+ /// Collection of fluid ports belonging to this component, accessible by their ID.
+ ///
+ protected Dictionary FluidPorts { get; set; } = new Dictionary();
+
+ ///
+ /// Collection of Thermal ports belonging to this component, accessible by their ID.
+ ///
+ protected Dictionary ThermalPorts { get; set; } = new Dictionary();
+
+ ///
+ /// Constructor for the Component base class.
+ ///
+ /// Unique identifier.
+ /// Human-readable name.
+ /// Description of the component.
+ /// Priority for simulation updates.
+ protected Component(string id, string name, string description, int updatePriority)
+ {
+ ID = id;
+ Name = name;
+ Description = description;
+ UpdatePriority = updatePriority;
+ }
+
+ ///
+ /// Initializes the component. Called once when the simulation starts or component is added.
+ ///
+ public virtual void Initialize()
+ {
+ Console.WriteLine($"Initializing {Name} ({ID})...");
+ // Placeholder for component-specific initialization logic
+ }
+
+ ///
+ /// Shuts down the component. Called once when the simulation ends or component is removed.
+ ///
+ public virtual void Shutdown()
+ {
+ Console.WriteLine($"Shutting down {Name} ({ID})...");
+ // Placeholder for component-specific cleanup logic
+ }
+
+ ///
+ /// Abstract method to update the component's state over a time step.
+ /// Must be implemented by concrete component classes.
+ ///
+ /// The time elapsed.
+ public abstract void Update(double deltaTime);
+
+ ///
+ /// Returns a list of all fluid ports for this component.
+ ///
+ /// A list of FluidPort objects.
+ public List GetFluidPorts() => FluidPorts.Values.ToList();
+
+ ///
+ /// Gets a fluid port by its unique ID.
+ ///
+ /// The ID of the fluid port.
+ /// The FluidPort object if found, otherwise null.
+ public FluidPort GetFluidPortById(string id)
+ {
+ FluidPorts.TryGetValue(id, out FluidPort port);
+ return port;
+ }
+
+ ///
+ /// Returns a list of all Thermal ports for this component.
+ ///
+ /// A list of Thermal objects.
+ public List GetThermalPorts() => ThermalPorts.Values.ToList();
+
+ ///
+ /// Gets an Thermal port by its unique ID.
+ ///
+ /// The ID of the Thermal port.
+ /// The ThermalPort object if found, otherwise null.
+ public ThermalPort GetThermalPortById(string id)
+ {
+ ThermalPorts.TryGetValue(id, out ThermalPort port);
+ return port;
+ }
+
+ ///
+ /// Helper method to add a fluid port to this component.
+ ///
+ /// The FluidPort to add.
+ /// Thrown if a port with the same ID already exists.
+ protected void AddFluidPort(FluidPort port)
+ {
+ if (FluidPorts.ContainsKey(port.ID))
+ {
+ throw new ArgumentException($"A fluid port with ID '{port.ID}' already exists on component '{Name}' ({ID}).");
+ }
+ port.ParentComponent = this;
+ FluidPorts.Add(port.ID, port);
+ }
+
+ ///
+ /// Helper method to add an Thermal port to this component.
+ ///
+ /// The ThermalPort to add.
+ /// Thrown if an Thermal port with the same ID already exists.
+ protected void AddThermalPort(ThermalPort port)
+ {
+ if (ThermalPorts.ContainsKey(port.ID))
+ {
+ throw new ArgumentException($"An Thermal port with ID '{port.ID}' already exists on component '{Name}' ({ID}).");
+ }
+ port.ParentComponent = this;
+ ThermalPorts.Add(port.ID, port);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Abstract/Port.cs b/BWR.core/Components/Abstract/Port.cs
new file mode 100644
index 0000000..c93b380
--- /dev/null
+++ b/BWR.core/Components/Abstract/Port.cs
@@ -0,0 +1,77 @@
+// MyBWRSimulator.Core/Components/Abstract/Port.cs
+namespace MyBWRSimulator.Core.Components.Abstract
+{
+ ///
+ /// Abstract base class for all types of connection points on components.
+ ///
+ public abstract class Port
+ {
+ ///
+ /// Unique ID for the port (e.g., "Inlet1", "OutletA").
+ ///
+ public string ID { get; protected set; }
+
+ ///
+ /// A reference to the Port it's connected to. Null if not connected.
+ ///
+ public Port ConnectedPort { get; protected set; }
+
+ ///
+ /// A reference back to the component this port belongs to.
+ ///
+ public Component ParentComponent { get; internal set; } // Internal set by AddPort methods
+
+ ///
+ /// Constructor for the Port base class.
+ ///
+ /// Unique identifier for the port.
+ protected Port(string id)
+ {
+ ID = id;
+ }
+
+ ///
+ /// Establishes a connection between this port and another port.
+ ///
+ /// The port to connect to.
+ public virtual void Connect(Port otherPort)
+ {
+ if (otherPort == null)
+ {
+ Console.WriteLine($"Error: Cannot connect {ID} to null port.");
+ return;
+ }
+ if (ConnectedPort != null && ConnectedPort != otherPort)
+ {
+ Console.WriteLine($"Warning: {ID} is already connected to {ConnectedPort.ID}. Disconnecting first.");
+ Disconnect();
+ }
+
+ ConnectedPort = otherPort;
+ // For bidirectional connection, the other port should also connect to this one
+ if (otherPort.ConnectedPort != this)
+ {
+ otherPort.Connect(this);
+ }
+ Console.WriteLine($"Port {ID} connected to {otherPort.ID}.");
+ }
+
+ ///
+ /// Breaks the connection of this port.
+ ///
+ public virtual void Disconnect()
+ {
+ if (ConnectedPort != null)
+ {
+ Port oldConnectedPort = ConnectedPort;
+ ConnectedPort = null;
+ // Ensure the other port also disconnects from this one
+ if (oldConnectedPort.ConnectedPort == this)
+ {
+ oldConnectedPort.Disconnect();
+ }
+ Console.WriteLine($"Port {ID} disconnected from {oldConnectedPort.ID}.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Concrete/FluidTank.cs b/BWR.core/Components/Concrete/FluidTank.cs
new file mode 100644
index 0000000..6cc0881
--- /dev/null
+++ b/BWR.core/Components/Concrete/FluidTank.cs
@@ -0,0 +1,67 @@
+// MyBWRSimulator.Core/Components/Concrete/FluidTank.cs
+namespace MyBWRSimulator.Core.Components.Concrete
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+ //using MyBWRSimulator.Core.Components.Enums;
+ using MyBWRSimulator.Core.Ports;
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a Fluid Tank component in the BWR simulation.
+ ///
+ public class FluidTank : Component
+ {
+ public double MaxVolume { get; private set; } // Maximum capacity of the tank (m^3)
+ public double CurrentVolume { get; private set; } // Current fluid volume in the tank (m^3)
+ public double CurrentLevel => CurrentVolume / MaxVolume; // 0.0 to 1.0 (0% to 100%)
+ public double ContentsTemperature { get; private set; } // Average temperature of fluid in tank
+
+ public FluidPort Inlet => GetFluidPortById("Inlet");
+ public FluidPort Outlet => GetFluidPortById("Outlet");
+
+ public FluidTank(string id, string name, double maxVolume, int numInlets, int numOutlets, double initialVolume = 0.0, double initialTemp = 293.15)
+ : base(id, name, $"A fluid tank with max volume {maxVolume} m^3.", 5) // Priority 5 (updates last)
+ {
+ MaxVolume = maxVolume;
+ CurrentVolume = Math.Clamp(initialVolume, 0.0, MaxVolume);
+ ContentsTemperature = initialTemp;
+
+
+ AddFluidPort(new FluidPort("Inlet"));
+ AddFluidPort(new FluidPort("Outlet"));
+ }
+
+ public override void Update(double deltaTime)
+ {
+ double FlowVolume = 0.0;
+ double InletEnergy = 0.0; // For temperature calculation
+
+ FlowVolume += Inlet.FlowRate * deltaTime; // FlowRate is volume/time, so FlowRate * deltaTime = volume
+ InletEnergy += Inlet.FlowRate * Inlet.Temperature * deltaTime; // Simple energy balance
+
+ // Sum outgoing flow (assuming outlets draw based on their connected components)
+ // For a tank, outlets typically "demand" flow, and the tank supplies it if available.
+ // For simplicity here, we'll assume outlets simply remove fluid.
+ // In a real system, outlet flow depends on downstream pressure and tank level.
+ FlowVolume -= Outlet.FlowRate * deltaTime;
+
+
+ // Update volume
+ double newVolume = CurrentVolume + FlowVolume;
+ CurrentVolume = Math.Clamp(newVolume, 0.0, MaxVolume);
+
+ // Update outlet port properties (e.g., pressure based on level)
+ // This is a simplification; in reality, outlet pressure also depends on downstream pressure
+ double pressureFromLevel = CurrentLevel * 100000; // Example: 100kPa at full level
+
+ Outlet.Pressure = pressureFromLevel; // Or a more complex calculation
+ Outlet.Temperature = ContentsTemperature;
+ // Mirror values to connected ports
+ Outlet.MirrorToConnectedPort();
+
+
+ Console.WriteLine($"{Name} ({ID}) - Volume: {CurrentVolume:F2} m^3 ({CurrentLevel * 100:F1}%), Temp: {ContentsTemperature:F1} K");
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Concrete/PressureVessel.cs b/BWR.core/Components/Concrete/PressureVessel.cs
new file mode 100644
index 0000000..e69de29
diff --git a/BWR.core/Components/Concrete/Pump.cs b/BWR.core/Components/Concrete/Pump.cs
new file mode 100644
index 0000000..fd051de
--- /dev/null
+++ b/BWR.core/Components/Concrete/Pump.cs
@@ -0,0 +1,74 @@
+// MyBWRSimulator.Core/Components/Concrete/Pump.cs
+namespace MyBWRSimulator.Core.Components.Concrete
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+ using MyBWRSimulator.Core.Ports;
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a Pump component in the BWR simulation.
+ ///
+ public class Pump : Component
+ {
+ public double PumpSpeed { get; set; } // 0.0 to 1.0 (0% to 100%)
+ public double MaxFlowRate { get; private set; } // Max flow at 100% speed
+ public double MaxHeadPressure { get; private set; } // Max pressure at 100% speed
+
+ // Publicly expose ports for easier named access if needed, but GetFluidPortById is preferred
+ public FluidPort Inlet => GetFluidPortById("Inlet");
+ public FluidPort Outlet => GetFluidPortById("Outlet");
+
+
+ public Pump(string id, string name, double maxFlowRate, double maxHeadPressure)
+ : base(id, name, $"A pump component with max flow {maxFlowRate} and max head {maxHeadPressure}.", 1) // Priority 1
+ {
+ MaxFlowRate = maxFlowRate;
+ MaxHeadPressure = maxHeadPressure;
+ PumpSpeed = 0.0; // Initially off
+
+ // Use descriptive IDs for ports
+ AddFluidPort(new FluidPort("Inlet"));
+ AddFluidPort(new FluidPort("Outlet"));
+ }
+
+ public override void Update(double deltaTime)
+ {
+ if (PumpSpeed > 0)
+ {
+ // Simple pump model: Flow proportional to speed, pressure based on head
+ double actualFlowRate = PumpSpeed * MaxFlowRate;
+ double actualPressureIncrease = PumpSpeed * MaxHeadPressure;
+
+ // Read from inlet
+ double inletPressure = Inlet.Pressure;
+ double inletTemperature = Inlet.Temperature;
+
+ // Calculate outlet properties
+ Outlet.FlowRate = actualFlowRate;
+ Outlet.Pressure = inletPressure + actualPressureIncrease;
+ Outlet.Temperature = inletTemperature; // Assuming no temperature change by pump
+
+ // Mirror values to connected ports
+ Outlet.MirrorToConnectedPort();
+
+ Console.WriteLine($"{Name} ({ID}) - Flow: {actualFlowRate:F2} kg/s, Outlet P: {Outlet.Pressure:F2} Pa");
+ }
+ else
+ {
+ // If not powered or off, no flow
+ Outlet.FlowRate = 0.0;
+ Outlet.Pressure = Inlet.Pressure; // Outlet pressure equals inlet if no pumping
+ Outlet.Temperature = Inlet.Temperature;
+ Outlet.MirrorToConnectedPort();
+ Console.WriteLine($"{Name} ({ID}) - Off/No Power.");
+ }
+ }
+
+ public void SetPumpSpeed(double speed)
+ {
+ PumpSpeed = Math.Clamp(speed, 0.0, 1.0);
+ Console.WriteLine($"{Name} ({ID}) - Pump speed set to {PumpSpeed * 100:F0}%.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Concrete/ReactorCore.cs b/BWR.core/Components/Concrete/ReactorCore.cs
new file mode 100644
index 0000000..3afe4ce
--- /dev/null
+++ b/BWR.core/Components/Concrete/ReactorCore.cs
@@ -0,0 +1,25 @@
+// MyBWRSimulator.Core/Components/Concrete/Pump.cs
+namespace MyBWRSimulator.Core.Components.Concrete
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+ //using MyBWRSimulator.Core.Components.Enums;
+ using MyBWRSimulator.Core.Ports;
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a Pump component in the BWR simulation.
+ ///
+ public class ReactorCore : Component
+ {
+ public ReactorCore(string id, string name, int priority)
+ : base(id, name, "Simulates all fuel rods and control rods", priority)
+ {
+ AddFluidPort(new FluidPort("FeedwaterInlet"));
+ }
+ public override void Update(double dt)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Concrete/pipe.cs b/BWR.core/Components/Concrete/pipe.cs
new file mode 100644
index 0000000..91ca8ef
--- /dev/null
+++ b/BWR.core/Components/Concrete/pipe.cs
@@ -0,0 +1,63 @@
+// MyBWRSimulator.Core/Components/Concrete/Pipe.cs
+namespace MyBWRSimulator.Core.Components.Concrete
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+ using MyBWRSimulator.Core.Ports;
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a Pipe component in the BWR simulation.
+ ///
+ public class Pipe : Component
+ {
+ public double Length { get; private set; } // Length of the pipe (m)
+ public double Diameter { get; private set; } // Inner diameter of the pipe (m)
+ public double Roughness { get; private set; } = 0.000045; // Example roughness for steel (m)
+
+ // Publicly expose ports for easier named access if needed
+ public FluidPort Inlet => GetFluidPortById("Inlet");
+ public FluidPort Outlet => GetFluidPortById("Outlet");
+
+ public Pipe(string id, string name, double length, double diameter)
+ : base(id, name, $"A pipe of length {length}m and diameter {diameter}m.", 4) // Priority 4
+ {
+ Length = length;
+ Diameter = diameter;
+
+ // Use descriptive IDs for ports
+ AddFluidPort(new FluidPort("Inlet"));
+ AddFluidPort(new FluidPort("Outlet"));
+ }
+
+ public override void Update(double deltaTime)
+ {
+ // Read properties from the inlet port (which should have been updated by upstream component)
+ double inletFlowRate = Inlet.FlowRate;
+ double inletPressure = Inlet.Pressure;
+ double inletTemperature = Inlet.Temperature;
+
+ // Simple pressure drop calculation (e.g., Darcy-Weisbach or simpler linear drop)
+ // This is very simplified. For high fidelity, you'd need fluid properties (density, viscosity).
+ double pressureDrop = 0.0;
+ if (inletFlowRate > 0)
+ {
+ // A very basic linear pressure drop for demonstration
+ pressureDrop = (inletFlowRate * Length * 10.0) / (Diameter * Diameter);
+ }
+
+ // Simple heat loss/gain (e.g., based on ambient temperature, not modeled here)
+ double outletTemperature = inletTemperature; // Assuming no heat loss for now
+
+ // Update outlet port properties
+ Outlet.FlowRate = inletFlowRate;
+ Outlet.Pressure = inletPressure - pressureDrop;
+ Outlet.Temperature = outletTemperature;
+
+ // Mirror values to connected ports
+ Outlet.MirrorToConnectedPort();
+
+ Console.WriteLine($"{Name} ({ID}) - Flow: {inletFlowRate:F2} kg/s, Pressure Drop: {pressureDrop:F2} Pa");
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Components/Enums/FluidPortType.cs b/BWR.core/Components/Enums/FluidPortType.cs
new file mode 100644
index 0000000..8192ec5
--- /dev/null
+++ b/BWR.core/Components/Enums/FluidPortType.cs
@@ -0,0 +1,14 @@
+// MyBWRSimulator.Core/Components/Enums/FluidPortType.cs
+/*
+namespace MyBWRSimulator.Core.Components.Enums
+{
+ ///
+ /// Represents the type of a fluid port (e.g., inlet or outlet).
+ ///
+ public enum FluidPortType
+ {
+ Inlet,
+ Outlet
+ }
+}
+*/
\ No newline at end of file
diff --git a/BWR.core/Data/ConfigurationLoader.cs b/BWR.core/Data/ConfigurationLoader.cs
new file mode 100644
index 0000000..5a619eb
--- /dev/null
+++ b/BWR.core/Data/ConfigurationLoader.cs
@@ -0,0 +1,167 @@
+// MyBWRSimulator.Core/Data/ConfigurationLoader.cs
+namespace MyBWRSimulator.Core.Data
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+ using MyBWRSimulator.Core.Components.Concrete;
+ using MyBWRSimulator.Core.Ports;
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+
+ ///
+ /// Helper class for deserializing component and connection configurations.
+ ///
+ public class ComponentConfig
+ {
+ public string Type { get; set; }
+ public string ID { get; set; }
+ public string Name { get; set; }
+ public int UpdatePriority { get; set; }
+ public JsonElement InitialState { get; set; } // Use JsonElement to handle dynamic properties
+ }
+
+ ///
+ /// Helper class for deserializing connection configurations.
+ ///
+ public class PortReference
+ {
+ public string ComponentID { get; set; }
+ public string PortID { get; set; }
+ }
+
+ ///
+ /// Overall configuration structure.
+ ///
+ public class PlantConfiguration
+ {
+ public List Components { get; set; }
+ public List Connections { get; set; }
+ }
+
+ ///
+ /// Helper class for deserializing connection configurations.
+ ///
+ public class ConnectionConfig
+ {
+ public PortReference PortA { get; set; }
+ public PortReference PortB { get; set; }
+ }
+
+
+ ///
+ /// Handles loading simulation configuration from a JSON file.
+ ///
+ public class ConfigurationLoader
+ {
+ ///
+ /// Loads components and establishes connections from a JSON configuration file.
+ ///
+ /// The path to the JSON configuration file.
+ /// An output list to populate with instantiated components.
+ public static void LoadConfiguration(string filePath, out List components)
+ {
+ components = new List();
+ if (!File.Exists(filePath))
+ {
+ Console.WriteLine($"Error: Configuration file not found at {filePath}");
+ return;
+ }
+
+ try
+ {
+ string jsonString = File.ReadAllText(filePath);
+ var config = JsonSerializer.Deserialize(jsonString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+
+ if (config?.Components == null)
+ {
+ Console.WriteLine("Error: No components found in configuration.");
+ return;
+ }
+
+ // 1. Instantiate Components
+ foreach (var compConfig in config.Components)
+ {
+ Component newComponent = null;
+ switch (compConfig.Type)
+ {
+ case "Pump":
+ // Example: Deserialize specific properties for Pump
+ var pumpProps = compConfig.InitialState.Deserialize>();
+ newComponent = new Pump(compConfig.ID, compConfig.Name, pumpProps["MaxFlowRate"], pumpProps["MaxHeadPressure"]);
+ if (pumpProps.TryGetValue("PumpSpeed", out double pumpSpeed))
+ {
+ ((Pump)newComponent).SetPumpSpeed(pumpSpeed);
+ }
+ break;
+ case "FluidTank":
+ // Example: Deserialize specific properties for FluidTank
+ var tankProps = compConfig.InitialState.Deserialize>();
+ newComponent = new FluidTank(compConfig.ID, compConfig.Name, tankProps["MaxVolume"], (int)tankProps["NumInlets"], (int)tankProps["NumOutlets"], tankProps["InitialVolume"]);
+ break;
+ case "Pipe":
+ var pipeProps = compConfig.InitialState.Deserialize>();
+ newComponent = new Pipe(compConfig.ID, compConfig.Name, pipeProps["Length"], pipeProps["Diameter"]);
+ break;
+ case "ReactorCore":
+ newComponent = new ReactorCore(compConfig.ID, compConfig.Name, compConfig.UpdatePriority);
+ // ReactorCore might have no specific initial state beyond base properties
+ break;
+ // Add more component types here
+ default:
+ Console.WriteLine($"Warning: Unknown component type '{compConfig.Type}' for ID '{compConfig.ID}'. Skipping.");
+ break;
+ }
+
+ if (newComponent != null)
+ {
+ components.Add(newComponent);
+ newComponent.Initialize(); // Initialize after creation
+ }
+ }
+
+ // 2. Establish Connections
+ if (config.Connections != null)
+ {
+ foreach (var connConfig in config.Connections)
+ {
+ var compA = components.FirstOrDefault(c => c.ID == connConfig.PortA.ComponentID);
+ var compB = components.FirstOrDefault(c => c.ID == connConfig.PortB.ComponentID);
+
+ if (compA == null || compB == null)
+ {
+ Console.WriteLine($"Warning: Could not find one or both components for connection: {connConfig.PortA.ComponentID}.{connConfig.PortA.PortID} <-> {connConfig.PortB.ComponentID}.{connConfig.PortB.PortID}");
+ continue;
+ }
+
+ // Use the new GetPortById methods for more robust lookup
+ Port portA = compA.GetFluidPortById(connConfig.PortA.PortID) as Port ??
+ compA.GetThermalPortById(connConfig.PortA.PortID) as Port;
+
+ Port portB = compB.GetFluidPortById(connConfig.PortB.PortID) as Port ??
+ compB.GetThermalPortById(connConfig.PortB.PortID) as Port;
+
+ if (portA != null && portB != null)
+ {
+ portA.Connect(portB);
+ }
+ else
+ {
+ Console.WriteLine($"Warning: Could not find one or both ports for connection: {connConfig.PortA.ComponentID}.{connConfig.PortA.PortID} <-> {connConfig.PortB.ComponentID}.{connConfig.PortB.PortID}");
+ }
+ }
+ }
+ }
+ catch (JsonException ex)
+ {
+ Console.WriteLine($"Error parsing JSON configuration: {ex.Message}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An unexpected error occurred during configuration loading: {ex.Message}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Interfaces/IFluidConnectable.cs b/BWR.core/Interfaces/IFluidConnectable.cs
new file mode 100644
index 0000000..a631b53
--- /dev/null
+++ b/BWR.core/Interfaces/IFluidConnectable.cs
@@ -0,0 +1,18 @@
+// MyBWRSimulator.Core/Interfaces/IFluidConnectable.cs
+namespace MyBWRSimulator.Core.Interfaces
+{
+ using MyBWRSimulator.Core.Ports;
+ using System.Collections.Generic;
+
+ ///
+ /// Defines the contract for components that have fluid connections.
+ ///
+ public interface IFluidConnectable
+ {
+ ///
+ /// Gets a list of fluid ports associated with this component.
+ ///
+ /// A list of FluidPort objects.
+ List GetFluidPorts();
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Interfaces/ISimulatable.cs b/BWR.core/Interfaces/ISimulatable.cs
new file mode 100644
index 0000000..f11085f
--- /dev/null
+++ b/BWR.core/Interfaces/ISimulatable.cs
@@ -0,0 +1,15 @@
+// MyBWRSimulator.Core/Interfaces/ISimulatable.cs
+namespace MyBWRSimulator.Core.Interfaces
+{
+ ///
+ /// Defines the contract for any object that can participate in the simulation's time-stepping.
+ ///
+ public interface ISimulatable
+ {
+ ///
+ /// Updates the state of the object over a given time step.
+ ///
+ /// The time elapsed since the last update.
+ void Update(double deltaTime);
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Interfaces/IThermalConnectable.cs b/BWR.core/Interfaces/IThermalConnectable.cs
new file mode 100644
index 0000000..0270dab
--- /dev/null
+++ b/BWR.core/Interfaces/IThermalConnectable.cs
@@ -0,0 +1,18 @@
+// MyBWRSimulator.Core/Interfaces/IThermalConnectable.cs
+namespace MyBWRSimulator.Core.Interfaces
+{
+ using MyBWRSimulator.Core.Ports;
+ using System.Collections.Generic;
+
+ ///
+ /// Defines the contract for components that have Thermal connections.
+ ///
+ public interface IThermalConnectable
+ {
+ ///
+ /// Gets a list of Thermal ports associated with this component.
+ ///
+ /// A list of ThermalPort objects.
+ List GetThermalPorts();
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Ports/FluidPort.cs b/BWR.core/Ports/FluidPort.cs
new file mode 100644
index 0000000..5a9927c
--- /dev/null
+++ b/BWR.core/Ports/FluidPort.cs
@@ -0,0 +1,71 @@
+// MyBWRSimulator.Core/Ports/FluidPort.cs
+namespace MyBWRSimulator.Core.Ports
+{
+ //using MyBWRSimulator.Core.Components.Enums;
+ using MyBWRSimulator.Core.Components.Abstract;
+
+ ///
+ /// Represents a fluid connection point on a component.
+ ///
+ public class FluidPort : Port
+ {
+ ///
+ /// Current fluid flow rate through this port (e.g., kg/s).
+ ///
+ public double FlowRate { get; set; }
+
+ ///
+ /// Current fluid pressure at this port (e.g., Pa).
+ ///
+ public double Pressure { get; set; }
+
+ ///
+ /// Current fluid temperature at this port (e.g., K or C).
+ ///
+ public double Temperature { get; set; }
+
+ ///
+ /// Current volume of fluid within the port itself (optional, for more detailed modeling).
+ ///
+ public double CurrentVolume { get; set; }
+
+ ///
+ /// Constructor for a FluidPort.
+ ///
+ /// Unique identifier for the port.
+ /// The type of fluid port (Inlet or Outlet).
+ public FluidPort(string id) : base(id)
+ {
+ FlowRate = 0.0;
+ Pressure = 0.0;
+ Temperature = 0.0;
+ CurrentVolume = 0.0;
+ }
+
+ ///
+ /// Connects this fluid port to another fluid port.
+ /// Includes basic type checking.
+ ///
+ /// The port to connect to.
+ public override void Connect(Port otherPort)
+ {
+ base.Connect(otherPort);
+ }
+
+ ///
+ /// Mirrors fluid properties from this port to its connected port.
+ /// This is for the direct property assignment data flow model.
+ ///
+ public void MirrorToConnectedPort()
+ {
+ if (ConnectedPort is FluidPort connectedFluidPort)
+ {
+ connectedFluidPort.FlowRate = this.FlowRate;
+ connectedFluidPort.Pressure = this.Pressure;
+ connectedFluidPort.Temperature = this.Temperature;
+ // Optionally, mirror CurrentVolume if applicable
+ // connectedFluidPort.CurrentVolume = this.CurrentVolume;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Ports/ThermalPort.cs b/BWR.core/Ports/ThermalPort.cs
new file mode 100644
index 0000000..6985c4c
--- /dev/null
+++ b/BWR.core/Ports/ThermalPort.cs
@@ -0,0 +1,68 @@
+// MyBWRSimulator.Core/Ports/ThermalPort.cs
+namespace MyBWRSimulator.Core.Ports
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+
+ ///
+ /// Represents an Thermal connection point on a component.
+ ///
+ public class ThermalPort : Port
+ {
+ ///
+ /// Current voltage at this port (e.g., Volts).
+ ///
+ public double Voltage { get; set; }
+
+ ///
+ /// Current amperage at this port (e.g., Amperes).
+ ///
+ public double Amperage { get; set; }
+
+ ///
+ /// True if this port is actively supplying power.
+ ///
+ public bool IsSupplyingPower { get; set; }
+
+ ///
+ /// Constructor for an ThermalPort.
+ ///
+ /// Unique identifier for the port.
+ public ThermalPort(string id) : base(id)
+ {
+ Voltage = 0.0;
+ Amperage = 0.0;
+ IsSupplyingPower = false;
+ }
+
+ ///
+ /// Connects this Thermal port to another Thermal port.
+ /// Includes basic type checking.
+ ///
+ /// The port to connect to.
+ public override void Connect(Port otherPort)
+ {
+ if (otherPort is ThermalPort)
+ {
+ base.Connect(otherPort);
+ }
+ else
+ {
+ Console.WriteLine($"Error: Cannot connect ThermalPort {ID} to non-ThermalPort {otherPort.ID}.");
+ }
+ }
+
+ ///
+ /// Mirrors Thermal properties from this port to its connected port.
+ /// This is for the direct property assignment data flow model.
+ ///
+ public void MirrorToConnectedPort()
+ {
+ if (ConnectedPort is ThermalPort connectedThermalPort)
+ {
+ connectedThermalPort.Voltage = this.Voltage;
+ connectedThermalPort.Amperage = this.Amperage;
+ connectedThermalPort.IsSupplyingPower = this.IsSupplyingPower;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Simulator.cs b/BWR.core/Simulator.cs
new file mode 100644
index 0000000..be71899
--- /dev/null
+++ b/BWR.core/Simulator.cs
@@ -0,0 +1,141 @@
+// MyBWRSimulator.Core/Simulation/Simulator.cs
+namespace MyBWRSimulator.Core.Simulation
+{
+ using MyBWRSimulator.Core.Components.Abstract;
+ using MyBWRSimulator.Core.Data;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ ///
+ /// The orchestrator of the simulation. Manages components, runs the simulation loop, and provides data.
+ ///
+ public class Simulator
+ {
+ private List _components;
+ private double _simulationTime;
+ private bool _isPaused;
+
+ public Simulator()
+ {
+ _components = new List();
+ _simulationTime = 0.0;
+ _isPaused = false;
+ }
+
+ ///
+ /// Adds a component to the simulation.
+ ///
+ /// The component to add.
+ public void AddComponent(Component component)
+ {
+ if (component != null && !_components.Contains(component))
+ {
+ _components.Add(component);
+ _components = _components.OrderBy(c => c.UpdatePriority).ToList(); // Re-sort after adding
+ component.Initialize();
+ Console.WriteLine($"Component '{component.Name}' ({component.ID}) added.");
+ }
+ }
+
+ ///
+ /// Removes a component from the simulation.
+ ///
+ /// The component to remove.
+ public void RemoveComponent(Component component)
+ {
+ if (component != null && _components.Remove(component))
+ {
+ _components = _components.OrderBy(c => c.UpdatePriority).ToList(); // Re-sort after removing
+ component.Shutdown();
+ Console.WriteLine($"Component '{component.Name}' ({component.ID}) removed.");
+ }
+ }
+
+ ///
+ /// Loads components and connections from a configuration file.
+ ///
+ /// The path to the configuration JSON file.
+ public void LoadPlantConfiguration(string filePath)
+ {
+ ConfigurationLoader.LoadConfiguration(filePath, out _components);
+ _components = _components.OrderBy(c => c.UpdatePriority).ToList(); // Ensure sorted after load
+ Console.WriteLine($"Loaded { _components.Count} components from configuration.");
+ }
+
+ ///
+ /// Runs the simulation for a specified duration with a given time step.
+ ///
+ /// Total simulation duration (e.g., in seconds).
+ /// Time step for each update iteration (e.g., in seconds).
+ public void RunSimulation(double duration, double timeStep)
+ {
+ Console.WriteLine($"Starting simulation for {duration} seconds with a time step of {timeStep} seconds...");
+ _isPaused = false;
+
+ while (_simulationTime < duration && !_isPaused)
+ {
+ Console.WriteLine($"\n--- Simulation Time: {_simulationTime:F2} s ---");
+ foreach (var component in _components)
+ {
+ component.Update(timeStep);
+ }
+ _simulationTime += timeStep;
+
+ // For console output, add a small delay to see updates
+ System.Threading.Thread.Sleep(100);
+ }
+
+ if (_simulationTime >= duration)
+ {
+ Console.WriteLine("\nSimulation finished.");
+ }
+ else if (_isPaused)
+ {
+ Console.WriteLine("\nSimulation paused.");
+ }
+
+ // Call shutdown for all components at the end of the simulation
+ foreach (var component in _components)
+ {
+ component.Shutdown();
+ }
+ }
+
+ ///
+ /// Pauses the simulation.
+ ///
+ public void PauseSimulation()
+ {
+ _isPaused = true;
+ }
+
+ ///
+ /// Resumes the simulation.
+ ///
+ public void ResumeSimulation()
+ {
+ _isPaused = false;
+ }
+
+ ///
+ /// Gets current simulation data from all components.
+ /// (Placeholder - you'll define the structure of this data more concretely later)
+ ///
+ /// A dictionary containing simulation data.
+ public Dictionary GetSimulationData()
+ {
+ var data = new Dictionary();
+ data["SimulationTime"] = _simulationTime;
+ data["ComponentStates"] = _components.Select(c => new
+ {
+ c.ID,
+ c.Name
+ // Add specific properties for each component type here
+ // Example for Pump: (c as Pump)?.PumpSpeed
+ // Example for FluidTank: (c as FluidTank)?.CurrentVolume
+ }).ToList();
+ return data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BWR.core/Utilities/Constants.cs b/BWR.core/Utilities/Constants.cs
new file mode 100644
index 0000000..e69de29
diff --git a/BWR_rewrite.csproj b/BWR_rewrite.csproj
new file mode 100644
index 0000000..90c53ef
--- /dev/null
+++ b/BWR_rewrite.csproj
@@ -0,0 +1,9 @@
+
+
+
+ Exe
+ net8.0
+ enable
+
+
+
diff --git a/BWR_rewrite.sln b/BWR_rewrite.sln
new file mode 100644
index 0000000..8dddf44
--- /dev/null
+++ b/BWR_rewrite.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BWR_rewrite", "BWR_rewrite.csproj", "{06A4A2A6-D413-5904-24B0-D5788442CB13}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {06A4A2A6-D413-5904-24B0-D5788442CB13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06A4A2A6-D413-5904-24B0-D5788442CB13}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06A4A2A6-D413-5904-24B0-D5788442CB13}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06A4A2A6-D413-5904-24B0-D5788442CB13}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {504A20E3-1BAE-41B3-AC23-5F6EC8680BB7}
+ EndGlobalSection
+EndGlobal
diff --git a/Other.cs b/Other.cs
new file mode 100644
index 0000000..9e282d6
--- /dev/null
+++ b/Other.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace BWR_simulator
+{
+ class PlantMngr
+ {
+ // simulation step all plant components
+ public void StepAll()
+ {
+
+ }
+
+ public void DisplayStats()
+ {
+
+ }
+ }
+
+ public static class Constants
+ {
+ public const double WATER_MOLAR_MASS = 0.018015; // kg/mol
+ public const double IDEAL_GAS_CONSTANT = 9425;
+ public const double WATER_LATENT_HEAT = 406500; // j*kg/k
+ public const double WATER_BOILING_POINT_NORM = 373; // temp in K the fluid boils at, under 1 atm
+ public const double WATER_HEAT_CAPACITY = 4182; // J/kg*K
+ }
+}
\ No newline at end of file
diff --git a/PressureVessel.cs b/PressureVessel.cs
new file mode 100644
index 0000000..55d50d8
--- /dev/null
+++ b/PressureVessel.cs
@@ -0,0 +1,97 @@
+using System;
+
+namespace BWR_simulator
+{
+ public class PressureVessel
+ {
+ public readonly double TOTAL_VOLUME; // Liters
+ private const double VESSEL_COOLING_COEFF = 1000;
+ private const double ROD_HEATING_COEFF = 2000;
+
+ // fluid density coeffcients to calculate fluid density at different pressures and temps
+ private readonly double[] FDC =
+ [
+ 999.83311,
+ 0.0752,
+ 0.0089,
+ 7.36413e-5,
+ 4.74639e-7,
+ 1.34888e-9,
+ 1.36e-11
+ ];
+ public double HotwellTemp { get; private set; }
+
+ public double GasMass { get; private set; }
+ public double GasVolume { get; private set; }
+ public double GasDensity { get; private set; }
+ public double Pressure { get; private set; }
+
+ public double FluidMass { get; private set; }
+ public double FluidDensity { get; private set; }
+ public double FluidVolume { get; private set; }
+ public double FluidTemp { get; private set; }
+ public double FluidBoilingPoint { get; private set; }
+
+ public double FeedwaterFlow { get; private set; }
+ public double ReliefValvePos { get; private set; }
+ public double InputEnergy { get; private set; }
+
+ public PressureVessel(double totalVolume = 9500,
+ double fluidTemp = 300,
+ double fluidMass = 6200,
+ double gasMass = 0)
+ {
+ TOTAL_VOLUME = totalVolume;
+ FluidTemp = fluidTemp;
+ FluidMass = fluidMass;
+ GasMass = gasMass;
+
+ InputEnergy = 0;
+ ReliefValvePos = 0;
+ FeedwaterFlow = 0;
+ Pressure = 1; // calcualte initial pressure
+ FluidBoilingPoint = Constants.WATER_BOILING_POINT_NORM; // calculate boiling point givien initial pressure
+ FluidDensity = 1;
+
+ HotwellTemp = 300;
+ CalculateDensity();
+
+ }
+ private void CalculateDensity()
+ {
+ FluidVolume = FluidMass / FluidDensity;
+ GasVolume = TOTAL_VOLUME - FluidVolume;
+ GasDensity = GasMass / GasVolume;
+ }
+ private void CalculateEnergyInput(double dt)
+ {
+ InputEnergy -= VESSEL_COOLING_COEFF * (HotwellTemp - FluidTemp) * dt;
+ InputEnergy += ROD_HEATING_COEFF * (600 - FluidTemp) * dt;
+ InputEnergy /= 1e6;
+ }
+ public void Step(double dt)
+ {
+ CalculateEnergyInput(dt);
+ FluidVolume += FeedwaterFlow * dt;
+ FluidBoilingPoint = Math.Pow((1/Constants.WATER_BOILING_POINT_NORM - (8.314 * Math.Log(Pressure))/40700), -1);
+
+ FluidTemp += (InputEnergy * 1e6) / (FluidMass * Constants.WATER_HEAT_CAPACITY);
+ if (FluidTemp > FluidBoilingPoint)
+ {
+ InputEnergy = FluidMass * Constants.WATER_HEAT_CAPACITY * FluidTemp - FluidBoilingPoint;
+ FluidTemp = FluidBoilingPoint;
+ double d_vapour = InputEnergy / Constants.WATER_LATENT_HEAT;
+ FluidMass -= d_vapour;
+ GasMass += d_vapour;
+
+ CalculateDensity();
+ }
+
+ Pressure = 1 + ((GasMass/Constants.WATER_MOLAR_MASS) * (Constants.IDEAL_GAS_CONSTANT * FluidTemp / TOTAL_VOLUME - FluidVolume));
+ if (Pressure < 1) {Pressure = 1;}
+
+ GasMass -= (Pressure - 1) * ReliefValvePos * dt;
+ if (GasMass < 0) {GasMass = 0;}
+ }
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..832ba64
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,22 @@
+/*using System;
+using System.Collections.Generic;
+using BWR_simulator;
+
+namespace BWR_siumulator
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+
+ PressureVessel p1 = new();
+
+ for(int i = 0; i < 10; i++)
+ {
+ Console.WriteLine($"Fluid Temp: {Math.Round(p1.FluidTemp - 273, 1)}");
+ p1.Step(0.01);
+ System.Threading.Thread.Sleep(50);
+ }
+ }
+ }
+}*/
diff --git a/Reactor.cs b/Reactor.cs
new file mode 100644
index 0000000..1ed05ca
--- /dev/null
+++ b/Reactor.cs
@@ -0,0 +1,162 @@
+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;
+ }
+ }
+}
\ No newline at end of file