# Python 3.5.2 # Chemistry Calculator: # Relative Formula Mass (Mr), Percentage by Mass, Empirical Formula (In progress), Number of Moles and Mass # by: Jingjie YANG (j.yang19 at ejm.org) import math import string import fractions import functools import time atoms = { 'H': 1.01, 'He': 4.00, 'Li': 6.94, 'Be': 9.01, 'B': 10.81, 'C': 12.01, 'N': 14.01, 'O': 16.00, 'F': 19.00, 'Ne': 20.18, 'Na': 22.99, 'Mg': 24.31, 'Al': 26.98, 'Si': 28.09, 'P': 30.97, 'S': 32.07, 'Cl': 35.45, 'Ar': 39.95, 'K': 39.10, 'Ca': 40.08, 'Sc': 44.96, 'Ti': 47.78, 'V': 50.94, 'Cr': 52.00, 'Mn': 54.94, 'Fe': 55.85, 'Co': 58.93, 'Ni': 58.69, 'Cu': 63.55, 'Zn': 65.38, 'Ga': 69.72, 'Ge': 72.63, 'As': 74.92, 'Se': 78.92, 'Br': 79.90, 'Kr': 83.90, 'Rb': 85.47, 'Sr': 87.62, 'Y': 88.91, 'Zr': 91.22, 'Nb': 92.91, 'Mo': 95.96, 'Tc': 98, # (98) 'Ru': 101.07, 'Rh': 106.42, 'Pd': 106.42, 'Ag': 107.87, 'Cd': 112.41, 'In': 114.82, 'Sn': 118.71, 'Sb': 121.76, 'Te': 127.60, 'I': 126.90, 'Xe': 131.29, 'Cs': 132.91, 'Ba': 137.33, 'La': 138.91, 'Ce': 140.12, 'Pr': 140.91, 'Nd': 144.24, 'Pm': 145, # (145) 'Sm': 150.36, 'Eu': 151.96, 'Gd': 157.25, 'Tb': 158.93, 'Dy': 162.50, 'Ho': 164.93, 'Er': 167.26, 'Tm': 168.93, 'Yb': 173.05, 'Lu': 174.97, 'Hf': 178.49, 'Ta': 180.95, 'W': 183.84, 'Re': 186.21, 'Os': 190.23, 'Ir': 192.22, 'Pt': 195.08, 'Au': 196.97, 'Hg': 200.59, 'Tl': 204.38, 'Pb': 207.20, 'Bi': 208.98, 'Po': 209, # (209) 'At': 210, # (210) 'Rn': 222, # (222) 'Fr': 223, # (223) 'Ra': 226, # (226) 'Ac': 227, # (227) 'Th': 232.04, 'Pa': 231.04, 'U': 238.03, 'Np': 237, # (237) 'Pu': 244, # (244) 'Am': 243, # (243) 'Cm': 247, # (247) 'Bk': 247, # (247) 'Cf': 251, # (251) 'Es': 252, # (252) 'Fm': 257, # (257) 'Md': 258, # (258) 'No': 259, # (259) 'Lr': 262, # (262) 'Rf': 267, # (267) 'Db': 268, # (268) 'Sg': 269, # (269) 'Bh': 270, # (270) 'Hs': 269, # (269) 'Mt': 278, # (262) 'Ds': 281, # (281) 'Rg': 281, # (281) 'Cn': 285, # (285) 'Uut': 286, # (286) 'Uuq': 289, # (289) 'Uup': 288, # (288) 'Uuh': 293, # (293) 'Uus': 294, # (294) 'Uuo': 294 # (294) } def get_quantity(num_index: int, str_in: str) -> int: """Used in function process_formula to get quantity of atom from string input.""" quantity = [] while num_index < len(str_in): if str_in[num_index].isdigit(): quantity.append(str_in[num_index]) num_index += 1 else: break if not quantity: quantity = 1 else: quantity = int(''.join(quantity)) return quantity def process_formula(str_in: str) -> dict: """Process string formula to dictionary containing atom and corresponding quantity. i.e. process_formula('(KI)3O') = {'K': 3, 'I': 3, 'O': 1}""" dict_out = {} quantity_ratio = {} # handling parenthesises index = 0 while index < len(str_in): char = str_in[index] if char == '(': end = index + 1 while str_in[end] != ')': end += 1 num_index = end + 1 quantity = get_quantity(num_index, str_in) for i in range(index + 1, end): quantity_ratio[i] = quantity elif char in string.ascii_uppercase: end = index + 1 while end < len(str_in) and str_in[end] in string.ascii_lowercase: end += 1 atom_alt = str_in[index:end] if atom_alt in atoms: element = atom_alt elif char in atoms: element = char else: raise ValueError quantity = get_quantity(end, str_in) if end in quantity_ratio: quantity *= quantity_ratio[end] add_to_dict(element, quantity, dict_out) index += 1 return dict_out def add_to_dict(new_item: str, new_value: int, target_dict: dict): """Used in process_formula to get correct quantity of atoms.""" if new_item not in target_dict: target_dict[new_item] = new_value else: target_dict[new_item] += new_value def mr_calc(dict_in: dict) -> float: """Calculate relative formula mass for dictionary input processed by function process_formula.""" out = 0 for element, quantity in dict_in.items(): out += atoms[element] * quantity return out def percentage_calc(element: str, dict_in: dict) -> float: """Calculate the percentage by mass of an element in the compound. """ element_mass = mr_calc({element: dict_in[element]}) total_mass = mr_calc(dict_in) percentage = element_mass / total_mass * 100 return round(percentage, 6) def get_mass(rel_mass: float, num_mole: float) -> float: """Calculate mass of compound given its relative formula mass and number of moles.""" return round(num_mole * rel_mass, 6) def get_mole(rel_mass: float, mass: float) -> float: """Calculate number of moles of compound given its relative formula mass and mass.""" return round(mass / rel_mass, 6) def get_ratio(dict_in: dict) -> dict: """Calculate empirical formula of a compound given its atoms' mass or percentage of mass in the compound.""" dict_processing = {} dict_out = {} for elem, ele_weight in dict_in.items(): dict_processing[elem] = ele_weight / atoms[elem] dict_processing[elem] = fractions.Fraction(dict_processing[elem]).limit_denominator(16) if dict_processing[elem] == 0: dict_processing[elem] = fractions.Fraction(ele_weight / atoms[elem] * 10).limit_denominator(16) denominators = [_.denominator for _ in dict_processing.values()] multiple = lcm_multiple(denominators) modelling = False for elem, ele_ratio in dict_processing.items(): dict_out[elem] = ele_ratio.numerator * multiple // ele_ratio.denominator if dict_out[elem] == 0 or dict_out[elem] >= 16: modelling = True print(elem, round(ele_ratio.numerator / ele_ratio.denominator, 6)) if modelling: for elem, ele_ratio in dict_out.items(): digits = len(str(ele_ratio)) dict_out[elem] = round(ele_ratio, -1 * digits + 1) simplifier = gcd_multiple(list(dict_out.values())) for elem, ele_ratio in dict_out.items(): dict_out[elem] = ele_ratio // simplifier return dict_out def lcm(a: int, b: int) -> int: """Return lowest common multiple.""" return a * b // math.gcd(a, b) def gcd_multiple(list_in: list) -> int: """Return greatest common divisor of integers in list_in.""" return functools.reduce(math.gcd, list_in) def lcm_multiple(list_in: list) -> int: """Return lowest common multiple of integers in list_in.""" return functools.reduce(lcm, list_in) def smart_calculate(dict_in: dict, details: str) -> str: """Smart handling inputs and printing out available information""" mr = mr_calc(dict_in) out_msg = ["Mr = {}".format(str(mr))] element, element_percentage, mass, mol = [None for _ in range(4)] if ':' not in details: return '\n'.join(out_msg) details = details.split(';') for detail in details: formula_property, value = detail.split(':') if formula_property == "element": element = value elif formula_property == "mass": mass = eval(value) else: mol = eval(value) if element is not None and element in atoms.keys(): element_percentage = percentage_calc(element, dict_in) if mass is not None and mol is None: mol = get_mole(mr, mass) if mol is not None and mass is None: mass = get_mass(mr, mol) if element_percentage is not None: out_msg.append("% of {}: {}".format(element, str(element_percentage))) if mass is not None: out_msg.append("mass = {} g".format(str(mass))) if mol is not None: out_msg.append("mol = {}".format(str(mol))) return '\n'.join(out_msg) # Change to True active = True while active: print("===START===") formula = input("Enter a formula if you have one: ") if formula: formula_processed = process_formula(formula) option = input("Enter all known details in this format:\n" "element: <element>; mass: <mass>\n" "Available arguments: element, mass, mol\n" "Add additional details: ").replace(" ", '') start = time.time() print(smart_calculate(formula_processed, option)) else: elements = {} print("Enter elements and their associated mass / percentage; enter to end") while True: ele = input("Element: ") if ele == '': break weight = input(ele + " (" + str(atoms[ele]) + "): ") elements[ele] = eval(weight) start = time.time() result = get_ratio(elements) result_to_print = [ele + str(ele_quantity) if ele_quantity != 1 else ele for ele, ele_quantity in result.items()] result_to_print = ''.join(result_to_print) print(result_to_print) time_end = time.time() time_taken = round(time_end - start, 6) print("===END:", time_taken, "seconds===\n")
Run
Reset
Share
Import
Link
Embed
Language▼
English
中文
Python Fiddle
Python Cloud IDE
Follow @python_fiddle
Browser Version Not Supported
Due to Python Fiddle's reliance on advanced JavaScript techniques, older browsers might have problems running it correctly. Please download the latest version of your favourite browser.
Chrome 10+
Firefox 4+
Safari 5+
IE 10+
Let me try anyway!
url:
Go
Python Snippet
Stackoverflow Question