[Context] : I'm currently developing a program in nuclear physics and i would like to have a simple access to all nuclides.
Technical specifications are :
- Since informations are static, I want them to be hard coded. (means no external file to read) -> Enum seems to be a good starting point then.
- Each nuclide should carry :
- Atomic number A
- Mass number Z
- Isomeric number I
- Half decay time
- Natural decay mode
- The access via an API should be really really simple something like
Nuclides.get(Z,A,I)/Nuclide.get("C14")or equivalent is recommended. The number of nuclides is almost 3000.
[Remarks] This post is a follow-up question : Initial post
@Antot : Here is the new post.
[Design choices]
According to Antot suggestions I significantly improved the main design of the class. Especially fields/determineAtomicNumber/determineIsomericState :
/**
* This class represents a Nuclide
* <p>
* A nuclide is completely defined by :
* <ul>
* <li>atomic number</li>
* <li>mass number</li>
* <li>isomeric number/state</li>
* </ul>
*
* @author Johann MARTINET
*/
public class Nuclide {
/**All nuclide Symbols (Z order)*/
public static final String[] SYMBOLS = {"H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Ha", "Sg", "Ns", "Hs", "Mt", "Ds","Rg"};
/**
* All possible isomeric state :
* gs = ground state (I=0)
* m (I=1)
* m2 (I=2)
* m3 (I=3)
*/
public static final String[] STATES = {"gs","m","m2","m3"};
/** Symbol of this nuclide*/
private final String symbol;
/** Atomic number*/
private final int atomicNumber;
/** Mass number*/
private final int massNumber;
/** Isomeric state*/
private final int isomericState;
/** Reactions of this nuclide*/
private final String reactions;
/** Half decay time*/
private final double decayTime;
/**
* Default constructor
* @param symbol symbol of the nuclide
* @param massNumber massNumber of the nuclide
* @param isomericState isomeric state of the nuclide
* @param decayTime half decay time of the nuclide
* @param reactions reactions of the nuclide
*/
private Nuclide(String symbol, int massNumber, String isomericState, double decayTime, String reactions) {
this.symbol = symbol;
this.massNumber = massNumber;
this.atomicNumber = determineAtomicNumber(symbol);
this.isomericState = determineIsomericState(isomericState);
this.decayTime = decayTime;
this.reactions = reactions;
}
/**
* Determine the atomic number from conventional symbol
* @param symbol symbol (should be one of Nuclide.SYMBOLS)
* @return (int) atomic number associated to the given symbol
* @throws IllegalArgumentException if symbol is not included in Nuclide.SYMBOLS
*/
private static int determineAtomicNumber(String symbol) {
for (int i = 0; i < SYMBOLS.length; i++)
{
if (symbol.equals(SYMBOLS[i])){return i + 1;}
}
throw new IllegalArgumentException("Failed to determine atomic number, invalid symbol: " + symbol+ " , should be one of Nuclide.SYMBOLS");
}
/**
* Determine the isomericState from conventional symbol defined in Nuclide.STATES
* @param isomericState String representation of the isomericState (should be one of Nuclide.STATES)
* @return (int) isomeric number associated to the given symbol
* @throws IllegalArgumentException if symbol is not included in Nuclide.STATES
*/
private static int determineIsomericState(String isomericState) {
for(int i = 0; i < STATES.length; i++)
{
if(isomericState.equals(STATES[i])){return i + 1;}
}
throw new IllegalArgumentException("Failed to determine isomeric number, invalid symbol: " + isomericState+ " , should be one of Nuclide.STATES");
}
//...
The question that comes first is :
Is it worth replacing SYMBOLS and STATES by enums ?
/** Isomeric states*/
public enum State
{
/**Ground state*/
GROUND_STATE(0),
/** first isomeric state*/
M(1),
/** second isomeric state*/
M2(2),
/** third isomeric state*/
M3(3);
/** Isomeric number of this state*/
private final int isomericNumber;
/**
* Default constructor
* @param isomericNumber isomericNumber of the state
*/
private State(int isomericNumber){this.isomericNumber = isomericNumber;}
}
public enum Symbol
{
H("H"),
He("He"),
\\...
private final String symbol;
private Symbol(String symbol){this.symbol = symbol)
}
And then change constructor and all nuclides enum like this :
private Nuclide(Symbol symbol, int massNumber, State isomericState, double decayTime, String reactions)
/** Americium*/
public enum Am implements NuclideAware{
\\...
Am242(242, State.GROUND_STATE, 5.767200e+04, "b-:8.270000e+01,ce:1.730000e+01"),
Am242m(242, State.M, 4.449620e+09, "it:9.955000e+01,a:4.500000e-01,fs:1.600000e-08"),
Am242m2(242, State.M2, 1.400000e-02, "fs:1.000000e+02"),
\\...
private Nuclide nuclide;
Am(int A, String isomericState, double decayTime, String reactions) {this.nuclide = new Nuclide(Symbol.Am, A, isomericState, decayTime, reactions);}
public Nuclide getNuclide() {return nuclide;}
}
This way, we can remove determineIsomericState and determineAtomicNumber methods and IllegalArgumentException are not needed anymore.
[Nuclide.get(String ...)]
Once more I used Antot suggestion and I significantly improved this method via OOP and Regex :
/**
* Interface used as a badge for nuclide
* @author Johann MARTINET
*/
public interface NuclideAware
{
/**
* Return the nuclide
* @return nuclide
*/
Nuclide getNuclide();
}
Which gives a more straightforward get method :
/**
* Return a nuclide from the given name. The name should follow this format :
* [Sy][A]m[I]
* where :
* Sy is a symbol included in Nuclide.SYMBOLS
* A is the mass number of the nuclide
* Is is an isomeric state included in Nuclide.STATES
* Example : "Am242m2"
* @param name name of the nuclide (format = Symbol-A-IsomericState)
* @return (Nuclide) The corresponding nuclide
*/
public static Nuclide get(String name)
{
String symbol ="", massNumber ="", isomericState ="";
String regex = "^([A-Za-z]+)(\\d+)m(\\d+)$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(name);
if(matcher.find() && matcher.hitEnd())
{
symbol = matcher.group(1);
massNumber = matcher.group(2);
isomericState = matcher.group(3);
}
final String targetNuclide = symbol + massNumber + "m" + isomericState;
try
{
Class<?> c = Class.forName(Nuclide.class.getName() + "$" + symbol);
Object[] objects = c.getEnumConstants();
for(Object obj : objects)
{
if(obj.toString().equals(targetNuclide))
{
return ((NuclideAware) obj).getNuclide();
}
}
}catch(ClassNotFoundException e)
{
e.printStackTrace();
}
throw new IllegalArgumentException("Impossible to find the nuclide with name:" + name);
}
As Antot already stated in initial post :
This still remains rather brutal and rigid
Then the second question is :
Is there a much more flexible way to implement the entire thing using more OOP features ?
Thank you for reading