December 28, 2020
The most complicated feature by far was getting electrons to configure properly across shells. Mainly caused by the order in which electrons are added. I assume there are many ways to solve this problem but I felt a recursive solution was the most elegant.
DISCLAIMER: This doc is about mimicking a real world process with code NOT presenting completely accurate scientific information. I’m not an expert on this.
The s, p, d, and f blocks indicate energy levels within each shell. Each energy level can hold a certain number of electrons (s 2) (p 6) (d 10) (f 14). In real atoms, electrons are are added to the lowest energy level available (closest to the center).
In general, the patten is add to the highest available f then d then p then s blocks. However, as seen in the configuration order below, the order jumps between shells with the f being 3 shells deep by the time electrons get added to it. This meant I wouldn’t be adding to the Outermost shell every time.
/// <summary> The shell directly below this one </summary>
public Shell NextShell { get; set; }
Each shell has a Maximum number of electrons based on the energy levels present. For Example shell 1 only has an s block, while shell 3 has s, p, and d blocks.
public static int[] sblock = { 2, 2, 2, 2, 2, 2, 2 };
public static int[] pblock = { 0, 6, 6, 6, 6, 6, 6 };
public static int[] dblock = { 0, 0, 10, 10, 10, 10, 0 };
public static int[] fblock = { 0, 0, 0, 14, 14, 0, 0 };
//Amount: 2, 2, 6, 2, 6, 2, 10, 6, 2, 10, 6, 2, 14, 10, 6, 2, 14, 10, 6
//Order: 1s, 2s, 2p, 3s, 3p, 4s, 3d, 4p, 5s, 4d, 5p, 6s, 4f, 5d, 6p, 7s, 5f, 6d, 7p
public static int GetMaxElectrons(int shell)
{
return sblock[shell] + pblock[shell] + dblock[shell] + fblock[shell];
}
And since most shells don’t contain every energy level so checks for whether the shell is full also check the maximum number of particles
public int ElectronCount => particles.Count; //public get for particles.Count
public bool sBlockFull => ElectronCount >= 2; //checks s block full
public bool pBlockFull => (ElectronCount >= 8 || MaxParticles <= 2) && sBlockFull; //checks p s blocks full
public bool dBlockFull => (ElectronCount >= 18 || MaxParticles <= 8) && pBlockFull; //checks d p s blocks full
public bool fBlockFull => (ElectronCount >= 32 || MaxParticles <= 18) && dBlockFull; //check f d p s blocks full
public int MaxParticles { get; set; } //Assigned by GetMaxElectrons()
Helper method for handling the shell specific & Unity interactions
//helper add function for actually adding the particle into a shell
private void Add(Particle particle)
{
//add the particle
particles.Add(particle);
particle.transform.SetParent(transform);
particle.Radius = scale / 4;
//calculate the new seperation distance
CalcSeperationDistance();
ColorParticles();
}
Actual method that takes care of the logic for configuring particles. This method is always called on the Outer Shell and works it’s way down from there.
(skipping step 0 for the end)
/// <summary>
/// Figures out where to Add a particle
/// </summary>
/// <param name="particle">Particle to be added</param>
/// <returns>true if sucessfully added</returns>
public bool AddParticle(Particle particle)
{
//0 recursively fill in electrons in PREVIOUS LEVEL that MUST be there
if (NextShell)
{
if (!NextShell.pBlockFull)
{
return NextShell.AddParticle(particle);
}
}
//1 Fill shell sBlock
if (!sBlockFull)
{
Add(particle);
return true;
}
if(NextShell)
{
//2 Fill shell-2 fBlock
if(NextShell.NextShell && !NextShell.NextShell.fBlockFull)
{
NextShell.NextShell.Add(particle);
return true;
}
//3 Fill shell-1 dBlock
if (!NextShell.dBlockFull) {
NextShell.Add(particle);
return true;
}
}
//4 Fill shell blocks
if(!pBlockFull)
{
Add(particle);
return true;
}
//No open place for electron
return false;
}
This is where things got really complicated. In the end it’s not that much code, but figuring this out took a lot of time and effort.
Just like when adding, Helper method for handling the shell specific & Unity interactions
//helper remove function for actually removing particle
private void Remove(Particle particle)
{
particles.Remove(particle);
//calculate the new seperation distance
CalcSeperationDistance();
ColorParticles();
}
Helper method for moving particles between shells.
/// <summary> Helper funtion for moving particles between shells </summary>
/// <param name="from">Shell to take a particle from</param>
/// <param name="to">Shell to add particle to</param>
private void TransferParticle(Shell from, Shell to)
{
Particle transferParticle = from.particles[0];
from.Remove(transferParticle);
to.Add(transferParticle);
}
Part of the Logic below. Transfers particles from lower energy levels of the f and d block into the current OuterShell
//check if sBlock not full (should only occur on outer shell)
if (!sBlockFull && NextShell != null) //
{
//raise shell-1 dBlock into sBlock
if (NextShell.ElectronCount > 8)
TransferParticle(NextShell, this);
//dBlock empty raise shell-2 fBlock into sBlcok
else if (NextShell.NextShell != null && NextShell.NextShell.ElectronCount > 18)
TransferParticle(NextShell.NextShell, this);
}
Actual method that takes care of the logic for configuring particles. This method is always called on the Outer Shell and works it’s way down from there.
/// <summary>
/// Removes a particle from this shell
/// </summary>
/// <param name="particle">Particle to remove</param>
/// <returns></returns>
public bool RemoveParticle(Particle particle)
{
//make sure the particle is an electron and actually in this shell
if (particles.Contains(particle))
{
//remove particle from this layer
Remove(particle);
particle.transform.SetParent(null);
FallUp();
return true;
}
//not in shell, check the next one
else if (NextShell != null)
{
//recursively check if particle in next shell
if (NextShell.RemoveParticle(particle))
{
//make sure there are electrons AND NOT (sBlock into dBlock OR pBlock into fBlock)
if (ElectronCount > 0 && !((ElectronCount <= 2 && NextShell.pBlockFull) || (ElectronCount <= 8 && NextShell.dBlockFull))) {
//replace the removed partcicle with one from this shell
TransferParticle(this, NextShell);
FallUp();
}
return true;
}
}
return false;
}