1
\$\begingroup\$

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 :
    1. Atomic number A
    2. Mass number Z
    3. Isomeric number I
    4. Half decay time
    5. 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.

I tried this but it uses reflexion for the get method (example Nuclide.get("Li10"))and it seems to be an overkill :

public class Nuclide {
    public static 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"};
    private String symbol;
    private int Z;
    private int A;
    private int isomericState;
    private String reactions;
    private double decayTime;

    private Nuclide() {}
    private Nuclide(String symbol, int A, String isomericState, double decayTime, String reactions) {
        this.symbol = symbol;
        this.A = A;
        this.isomericState = determineIsomericState(isomericState);
        this.Z = determineZ(symbol);

    }

    private static int determineZ(String symbol) {
        for (int i = 0; i < Symbols.length; i++) {
            if (symbol.equals(Symbols[i])) return i + 1;
        }
        return -1;
    }
    private int determineIsomericState(String isomericState) {
        if (isomericState.equals("gs")) this.isomericState = 0;
        else if (isomericState.equals("m")) this.isomericState = 1;
        else if (isomericState.equals("m2")) this.isomericState = 2;
        else if (isomericState.equals("m3")) this.isomericState = 3;
        else {
            System.out.println(isomericState + " isomere inconu");
            this.isomericState = -1;
        }
        return this.isomericState;
    }
    public static Nuclide get(String name) {
        char[] nameArray = name.toCharArray();
        String symbol = "";
        String A = "";
        String I = "";
        boolean isSymbolRead = false, isARead = false;
        for (char c : nameArray) {
            if (Character.isLetter(c) && !isSymbolRead) {symbol += c;}
            if (Character.isDigit(c) && !symbol.equals("") && !isARead) {
                isSymbolRead = true;
                A += c;
            }
            if (c == 'm' && isSymbolRead) {isARead = true;}
            if (Character.isDigit(c) && isSymbolRead && isARead) {I += c;}
        }
        String classPath = Nuclide.class.getCanonicalName();
        try {
            Class<?> c = Class.forName(classPath + "$" + symbol);
            Object[] objects = c.getEnumConstants();
            for (Object obj : objects) {
                if (obj.toString().equals(symbol + A + I)) {
                    Method method = obj.getClass().getDeclaredMethod("getNuclide");
                    Nuclide nuclide = (Nuclide) (method.invoke(obj));
                    return nuclide;
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }


//Then all enums

public enum n {
        n1(1, "gs", 6.146000e+02, "b-:1.000000e+02");
        private Nuclide nuclide;
        n(int A, String isomericState, double decayTime, String reactions) {this.nuclide = new Nuclide("n", A, isomericState, decayTime, reactions);}
        public Nuclide getNuclide() {return nuclide;}
    }
    public enum H {
        H1(1, "gs", 0.000000e+00, "s"),
        H2(2, "gs", 0.000000e+00, "s"),
        H3(3, "gs", 3.891050e+08, "b-:1.000000e+02"),
        H4(4, "gs", 1.000000e-22, "n:1.000000e+02"),
        H5(5, "gs", 8.000000e-23, "n:1.000000e+02"),
        H6(6, "gs", 3.200000e-22, "nn:1.000000e+02");
        private Nuclide nuclide;
        H(int A, String isomericState, double decayTime, String reactions) {this.nuclide = new Nuclide("H", A, isomericState, decayTime, reactions);}
        public Nuclide getNuclide() {return nuclide;}
    }
    public enum He {
        He3(3, "gs", 0.000000e+00, "s"),
        He4(4, "gs", 0.000000e+00, "s"),
        He5(5, "gs", 7.600000e-22, "n:1.000000e+02"),
        He6(6, "gs", 8.081000e-01, "b-:1.000000e+02"),
        He7(7, "gs", 2.900000e-21, "n:1.000000e+02"),
        He8(8, "gs", 1.220000e-01, "b-:8.800000e+01,b-n:1.200000e+01"),
        He9(9, "gs", 7.000000e-21, "n:1.000000e+02"),
        He10(10, "gs", 2.700000e-21, "nn:1.000000e+02");
        private Nuclide nuclide;
        He(int A, String isomericState, double decayTime, String reactions) {this.nuclide = new Nuclide("He", A, isomericState, decayTime, reactions);}
        public Nuclide getNuclide() {return nuclide;}
    }
    public enum Li {
        Li4(4, "gs", 9.100000e-23, "p:1.000000e+02"),
        Li5(5, "gs", 3.000000e-22, "p:1.000000e+02"),
        Li6(6, "gs", 0.000000e+00, "s"),
        Li7(7, "gs", 0.000000e+00, "s"),
        Li8(8, "gs", 8.380000e-01, "b-a:1.000000e+02"),
        Li9(9, "gs", 1.783000e-01, "b-:5.050000e+01,b-n:4.950000e+01"),
        Li10(10, "gs", 2.000000e-21, "n:1.000000e+02"),
        Li11(11, "gs", 8.590000e-03, "b-:9.100000e+00,b-n:8.490000e+01,b-n:4.100000e+00,b-n:1.900000e+00"),
        Li12(12, "gs", 1.000000e-08, "n:1.000000e+02");
        private Nuclide nuclide;
        Li(int A, String isomericState, double decayTime, String reactions) {this.nuclide = new Nuclide("Li", A, isomericState, decayTime, reactions);}
        public Nuclide getNuclide() {return nuclide;}
    }

// ...
}

I found another post which is just for element table. I didn't get the point of the private static Holder class !?

Is there a better way to define the Nuclide class (without reflexion) or I should stick to this definition ? Thank you

\$\endgroup\$
  • 1
    \$\begingroup\$ Why don't you want to read an external file? \$\endgroup\$ – 200_success Feb 8 '18 at 18:46
  • \$\begingroup\$ Thank you for reading my post and for your suggestion. This restriction is for 2 different reason : (First) Prevents all possible error like FileNotFound, file has been changed, etc (Second) Have access at compile time to all the nuclides. Don't hesitate if i'm not clear enough \$\endgroup\$ – Johann MARTINET Feb 9 '18 at 14:07
  • \$\begingroup\$ I agree with the decision to hard-code the data in Java. You don't read the names of days of the week from a file at start up, do you? Or the names of the months? So why would you want to read the names of the nuclides from a file? \$\endgroup\$ – DodgyCodeException Feb 9 '18 at 14:10
  • \$\begingroup\$ This is exactly my point of view, Thank you \$\endgroup\$ – Johann MARTINET Feb 9 '18 at 14:38
  • \$\begingroup\$ @JohannMARTINET Are you aware of the fact that you can actually package any kind of resource files within your executable JAR file? You couldn't get a FileNotFoundException with such a setup. I'd still recommend using a dedicated file to list the nuclides, and then just reading and parsing that file. \$\endgroup\$ – ZeroOne Feb 10 '18 at 23:59
1
\$\begingroup\$

It's a pity that there is a restriction to avoid the usage of an external resource file. This would be more flexible to read the data from elsewhere, thus separating the concrete stuff from its abstract representation in the code. And in case of a fix/update no rebuild of the application would be necessary.

Ok, so let's remain on the requirement that the data should be hardcoded.

Before remarks on the design choices, here are some observations about the improvement of the current code.

The Private Fields

Since the information wrapped in Nuclide mostly prepresents constants, all the fields should be final.

private final String symbol;
private final int z; // lower case and should better be called "massNumber"
private final int a; // lower case and should better be called "atomicNumber"
private final int isomericState;
private final String reactions;
private final double decayTime;

BTW, the static Sympols should be upper-cased.

determineZ

It's OK for the loop, but the return statement should be replaced with

throw new IllegalArgumentException("Failed to determine Z, invalid symbol: " + symbol);

This prevents from having invalid data, since all the expected symbols are normally declared in SYMBOLS.

determineIsomericState

  • The multiple if-else-if are overkill, this case can also be seen by analogy with determineZ method: declare a constant array with expected isometric states and return the i + 1 of the one that matched in the loop.

  • isomericState ref should not be assigned inside this method, only the int value should be returned.

Nuclide get(String)

Well, the contents of this method is really too complex and firstly should be split in two parts (dedicated methods): 1) parse name arg in order to extract symbol, a and i; 2) invoke getNuclide method on the target object.

name Parsing

This approach with a sort of ad-hoc parser is particularly difficult to test, because of the high complexity of if conditions.

There is a much simpler way to implement it: regular expressions! It looks like the name arg is expected to be something like "He7m2". A corresponding regular expression would be ^([A-Za-z]+)(\d+)m(\d+)$. The values in the matched groups will correspond respectively to symbol, a and i. If name does not match the expression, this is a good reason to throw another IAE.

getNuclide Invocation

The use of reflection is the drawback of the choice of enums to wrap the data.

If you have to keep the enums, there might be a simplification, avoiding the brutal method invocation by name. It consists of the following:

1) Create an interface that provides access to the target method:

interface NuclideAware {
  Nuclide getNuclide();
}

2) Make each enum implement this interface by adding implements NuclideAware to the headers. The method is already implemented in each one.

3) Reduce the reflective invocation to the following:

final String targetNuclide = symbol + A + I;
try {
  Class<?> c = Class.forName(classPath + "$" + symbol);
  Object[] objects = c.getEnumConstants();
  for (Object obj : objects) {
    if (obj.toString().equals(targetNuclide)) {
      return ((NuclideAware) obj).getNuclide();
    }
  }
} catch (ClassNotFoundException e) {
  e.printStackTrace();
}

But this still remains rather brutal and rigid. There should be a much more flexible way to implement the entire thing using more OOP features.

Since this answer becomes much longer than the question, I suggest doing the job in two steps. If you find useful the remarks I wrote here, please apply them and publish a follow-up question where we will discuss further improvements.

\$\endgroup\$
  • \$\begingroup\$ First of all, Thank you for your answer, I will apply your suggestions and come back with a better class. When you say "publish a follow-up question" you just mean I open another post with a link to this one ? (sorry I am a new user) \$\endgroup\$ – Johann MARTINET Feb 9 '18 at 14:15
  • \$\begingroup\$ You are welcome! "Publish a follow-up question" means exactly what you said. \$\endgroup\$ – Antot Feb 9 '18 at 16:15
  • \$\begingroup\$ I created a new post link \$\endgroup\$ – Johann MARTINET Feb 9 '18 at 17:55
  • \$\begingroup\$ @ Antot Your answer was deleted for the new post ... \$\endgroup\$ – Johann MARTINET Feb 15 '18 at 7:53
  • \$\begingroup\$ I've restored it. \$\endgroup\$ – Antot Feb 15 '18 at 20:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.