0.2.0 AtomicWeight

This commit is contained in:
Rob Tillaart 2023-04-15 20:15:18 +02:00
parent 43235d51a2
commit 9601f79f49
22 changed files with 926 additions and 505 deletions

View File

@ -2,225 +2,101 @@
// FILE: AtomicWeight.cpp
// AUTHOR: Rob Tillaart
// DATE: 2022-03-09
// VERSION: 0.1.5
// VERSION: 0.2.0
// PURPOSE: Arduino library for atomic weights
// URL: https://github.com/RobTillaart/AtomicWeight
#include "AtomicWeight.h"
/////////////////////////////////////////////////////////////////////////
//
// list of elements
// weight = weight * 222.909
//
struct element
{
char name[3];
uint16_t weight;
}
elements[119] =
{
{"n", 225}, // neutronium
{"H", 225},
{"He", 892},
{"Li", 2186},
{"Be", 2009},
{"B", 2410},
{"C", 2677},
{"N", 3122},
{"O", 3566},
{"F", 4235},
{"Ne", 4498},
{"Na", 5125},
{"Mg", 5418},
{"Al", 6014},
{"Si", 6261},
{"P", 6904},
{"S", 7148},
{"Cl", 7903},
{"Ar", 8905},
{"K", 8715},
{"Ca", 8934},
{"Sc", 10021},
{"Ti", 10670},
{"V", 11355},
{"Cr", 11590},
{"Mn", 12246},
{"Fe", 12448},
{"Co", 13137},
{"Ni", 13083},
{"Cu", 14165},
{"Zn", 14580},
{"Ga", 15542},
{"Ge", 16192},
{"As", 16701},
{"Se", 17601},
{"Br", 17811},
{"Kr", 18679},
{"Rb", 19052},
{"Sr", 19531},
{"Y", 19818},
{"Zr", 20335},
{"Nb", 20710},
{"Mo", 21386},
{"Tc", 21845},
{"Ru", 22529},
{"Rh", 22939},
{"Pd", 23722},
{"Ag", 24045},
{"Cd", 25057},
{"In", 25594},
{"Sn", 26462},
{"Sb", 27141},
{"Te", 28443},
{"I", 28288},
{"Xe", 29266},
{"Cs", 29626},
{"Ba", 30611},
{"La", 30963},
{"Ce", 31233},
{"Pr", 31410},
{"Nd", 32152},
{"Pm", 32322},
{"Sm", 33517},
{"Eu", 33874},
{"Gd", 35052},
{"Tb", 35426},
{"Dy", 36223},
{"Ho", 36764},
{"Er", 37284},
{"Tm", 37657},
{"Yb", 38572},
{"Lu", 39002},
{"Hf", 39787},
{"Ta", 40335},
{"W", 40980},
{"Re", 41507},
{"Os", 42404},
{"Ir", 42847},
{"Pt", 43485},
{"Au", 43906},
{"Hg", 44713},
{"Tl", 45559},
{"Pb", 46187},
{"Bi", 46584},
{"Po", 46588},
{"At", 46811},
{"Rn", 49486},
{"Fr", 49709},
{"Ra", 50377},
{"Ac", 50600},
{"Th", 51723},
{"Pa", 51500},
{"U", 53059},
{"Np", 52829},
{"Pu", 54390},
{"Am", 54167},
{"Cm", 55059},
{"Bk", 55059},
{"Cf", 55950},
{"Es", 56173},
{"Fm", 57288},
{"Md", 57511},
{"No", 57733},
{"Lr", 58402},
{"Rf", 58179},
{"Db", 58402},
{"Sg", 59294},
{"Bh", 58848},
{"Hs", 61746},
{"Mt", 59740},
{"Ds", 62637},
{"Rg", 60631},
{"Cn", 63529},
{"Nh", 63752},
{"Fl", 64421},
{"Mc", 64421},
{"Lv", 65312},
{"Ts", 65535},
{"Og", 65535},
};
#include "elements_uint16.h"
PTOE::PTOE(const uint8_t size)
{
_size = size;
_found = 0;
_size = size;
_found = 0;
}
////////////////////////////////////////////////////////////////
//
// BASIC
//
uint8_t PTOE::size()
{
return _size;
}
uint8_t PTOE::electrons(const uint8_t el)
{
return el;
}
uint8_t PTOE::neutrons(const uint8_t el)
{
return round(weight(el)) - el;
}
uint8_t PTOE::protons(const uint8_t el)
{
return el;
}
float PTOE::weight(const uint8_t el)
{
if (el > _size) return 0; // catch out of range.
return elements[el].weight * _weightFactor;
}
float PTOE::weight(const char * formula, const char * abbrev)
{
p = (char *)formula;
return _weight('\0', abbrev);
}
float PTOE::massPercentage(const char * formula, const char * abbrev)
{
float total = weight(formula);
if (total == 0) return 0;
p = (char *)formula;
return 100.0 * _weight('\0', abbrev) / total;
return _size;
}
char * PTOE::name(const uint8_t el)
{
// catch out of range.
if (el > _size) return NULL;
return elements[el].name;
// catch out of range.
if (el > _size) return NULL;
return elements[el].name;
}
uint8_t PTOE::find(const char * abbrev)
{
// case insensitive?
// caching?
// param check?
// uint8_t len = strlen(abbrev);
// if ((len == 1) || (len == 2))
// {
for (uint8_t i = 0; i < _size; i++)
{
if (strcmp(elements[i].name, abbrev) == 0) return i;
}
// }
return 255;
// case insensitive?
// caching?
// param check?
// uint8_t len = strlen(abbrev);
// if ((len == 1) || (len == 2))
// {
for (uint8_t i = 0; i < _size; i++)
{
if (strcmp(elements[i].name, abbrev) == 0) return i;
}
// }
return 255;
}
uint8_t PTOE::electrons(const uint8_t el)
{
return el;
}
uint8_t PTOE::neutrons(const uint8_t el)
{
return round(weight(el)) - el;
}
uint8_t PTOE::protons(const uint8_t el)
{
return el;
}
////////////////////////////////////////////////////////////////
//
// WEIGHT
//
float PTOE::weight(const uint8_t el)
{
if (el > _size) return 0; // catch out of range.
return elements[el].weight * ATOMIC_WEIGHT_FACTOR;
}
float PTOE::weight(const char * formula, const char * abbrev)
{
p = (char *)formula;
return _weight('\0', abbrev);
}
float PTOE::massPercentage(const char * formula, const char * abbrev)
{
float total = weight(formula);
if (total == 0) return 0;
p = (char *)formula;
return 100.0 * _weight('\0', abbrev) / total;
}
////////////////////////////////////////////////////////////////
@ -229,13 +105,118 @@ uint8_t PTOE::find(const char * abbrev)
//
float PTOE::moles2grams(const char * formula, float moles)
{
return moles * weight(formula);
return moles * weight(formula);
}
float PTOE::grams2moles(const char * formula, float grams)
{
return grams / weight(formula);
return grams / weight(formula);
}
////////////////////////////////////////////////////////////////
//
// SPLIT FORMULA IN ELEMENTS
//
uint8_t PTOE::splitElements(const char * formula)
{
uint8_t count = 0;
char elem[3] = { 0, 0, 0 };
char * p = (char *) formula;
while (*p != '\0')
{
// SKIP non element info
if (*p == '(')
{
p++; // skip '('
continue;
}
if (*p == ')')
{
p++; // skip ')'
continue;
}
if (isdigit(*p))
{
p++; // skip digit
continue;
}
// GET ELEMENT := [ Upper | Upper,lower ]
elem[0] = 0;
elem[1] = 0;
if (! isupper(*p)) return 0; // fail
elem[0] = *p;
p++;
if (islower(*p))
{
elem[1] = *p;
p++;
}
// FIND INDEX OF ELEMENT
int z = find(elem);
if (z == 255)
{
return 0; // fail
}
// DO WE HAVE IDENTIFIED IT ALREADY?
bool found = false;
for (int i = 0; i < count; i++)
{
if (_splitList[i] == z)
{
found = true;
}
}
if ((found == false) && (count < ATOMIC_WEIGHT_MAX_SPLIT_LIST))
{
_splitList[count] = z;
count++;
}
}
// // DEBUG
// for (int i = 0; i < count; i++)
// {
// Serial.print(i);
// Serial.print('\t');
// Serial.print(_splitList[i]);
// Serial.print('\t');
// Serial.println(name(_splitList[i]));
// }
_found = count;
return count;
}
uint8_t PTOE::element(uint8_t el)
{
if (el >= _found) return 255;
return _splitList[el];
}
////////////////////////////////////////////////////////////////
//
// COUNT
//
uint32_t PTOE::count(const char * formula, const char * el)
{
p = (char *)formula;
return _count('\0', el);
}
float PTOE::atomPercentage(const char * formula, const char * el)
{
float total = count(formula);
if (total == 0) return 0;
p = (char *)formula;
return 100.0 * _count('\0', el) / total;
}
@ -245,108 +226,7 @@ float PTOE::grams2moles(const char * formula, float grams)
//
float PTOE::weightFactor()
{
return _weightFactor;
}
////////////////////////////////////////////////////////////////
//
// EXPERIMENTAL
//
uint8_t PTOE::splitElements(const char * formula)
{
uint8_t count = 0;
char elem[3] = { 0, 0, 0 };
char * p = (char *) formula;
while (*p != '\0')
{
// SKIP non element info
if (*p == '(')
{
p++; // skip '('
continue;
}
if (*p == ')')
{
p++; // skip ')'
continue;
}
if (isdigit(*p))
{
p++; // skip digit
continue;
}
// GET ELEMENT := [ Upper | Upper,lower ]
elem[0] = 0;
elem[1] = 0;
if (! isupper(*p)) return 0; // fail
elem[0] = *p;
p++;
if (islower(*p))
{
elem[1] = *p;
p++;
}
// FIND INDEX OF ELEMENT
int z = find(elem);
if (z == 255)
{
return 0; // fail
}
// DO WE HAVE IDENTIFIED IT ALREADY?
bool found = false;
for (int i = 0; i < count; i++)
{
if (_splitList[i] == z)
{
found = true;
}
}
if ((found == false) && (count < ATOMIC_WEIGHT_MAX_SPLIT_LIST))
{
_splitList[count] = z;
count++;
}
}
// // DEBUG
// for (int i = 0; i < count; i++)
// {
// Serial.print(i);
// Serial.print('\t');
// Serial.print(_splitList[i]);
// Serial.print('\t');
// Serial.println(name(_splitList[i]));
// }
_found = count;
return count;
}
uint8_t PTOE::element(uint8_t el)
{
if (el >= _found) return 255;
return _splitList[el];
}
uint32_t PTOE::count(const char * formula, const char * el)
{
p = (char *)formula;
return _count('\0', el);
}
float PTOE::atomPercentage(const char * formula, const char * el)
{
float total = count(formula);
if (total == 0) return 0;
p = (char *)formula;
return 100.0 * _count('\0', el) / total;
return ATOMIC_WEIGHT_FACTOR;
}
@ -356,117 +236,117 @@ float PTOE::atomPercentage(const char * formula, const char * el)
//
float PTOE::_weight(const char sep, const char * abbrev)
{
float sum = 0;
float w = 0;
char elem[3] = { 0, 0, 0 };
int count = 0;
float sum = 0;
float w = 0;
char elem[3] = { 0, 0, 0 };
uint32_t count = 0;
while (*p != sep)
{
w = 0;
// HANDLE GROUP (...)
if (*p == '(')
{
p++; // skip '('
w = _weight(')', abbrev);
p++; // skip ')'
}
else
{
// GET ELEMENT := [ Upper | Upper,lower ]
elem[1] = 0;
if (! isupper(*p)) return 0; // fail
elem[0] = *p;
p++;
if (islower(*p))
{
elem[1] = *p;
p++;
}
// can be optimized?
if ((abbrev == NULL) || (strcmp(elem, abbrev) == 0))
{
int z = find(elem);
if (z == 255) return 0; // fail
w = weight(z);
}
}
while (*p != sep)
{
w = 0;
// HANDLE GROUP (...)
if (*p == '(')
{
p++; // skip '('
w = _weight(')', abbrev);
p++; // skip ')'
}
else
{
// GET ELEMENT := [ Upper | Upper,lower ]
elem[1] = 0;
if (! isupper(*p)) return 0; // fail
elem[0] = *p;
p++;
if (islower(*p))
{
elem[1] = *p;
p++;
}
// can be optimized?
if ((abbrev == NULL) || (strcmp(elem, abbrev) == 0))
{
int z = find(elem);
if (z == 255) return 0; // fail
w = weight(z);
}
}
count = 0;
// get optional digits
while (isdigit(*p))
{
count = count * 10 + (*p - '0');
p++;
}
// correct for no digits
if (count == 0) count = 1;
count = 0;
// get optional digits
while (isdigit(*p))
{
count = count * 10 + (*p - '0');
p++;
}
// correct for no digits
if (count == 0) count = 1;
// DEBUG
// Serial.println(w);
// Serial.println(count);
// DEBUG
// Serial.println(w);
// Serial.println(count);
sum += (w * count);
}
return sum;
sum += (w * count);
}
return sum;
}
uint32_t PTOE::_count(const char sep, const char * abbrev)
{
uint32_t sum = 0;
char elem[3] = { 0, 0, 0 };
int count = 0;
int w = 0;
uint32_t sum = 0;
char elem[3] = { 0, 0, 0 };
uint32_t count = 0;
int w = 0;
while (*p != sep)
{
// HANDLE GROUP (...)
if (*p == '(')
{
p++; // skip '('
w = _count(')', abbrev);
p++; // skip ')'
}
else
{
w = 0;
// GET ELEMENT := [ Upper | Upper,lower ]
elem[1] = 0;
if (! isupper(*p)) return 0; // fail
elem[0] = *p;
p++;
if (islower(*p))
{
elem[1] = *p;
p++;
}
// can be optimized
if ((abbrev == NULL) || (strcmp(elem, abbrev) == 0))
{
int z = find(elem);
if (z == 255) return 0; // fail
w = 1;
}
}
while (*p != sep)
{
// HANDLE GROUP (...)
if (*p == '(')
{
p++; // skip '('
w = _count(')', abbrev);
p++; // skip ')'
}
else
{
w = 0;
// GET ELEMENT := [ Upper | Upper,lower ]
elem[1] = 0;
if (! isupper(*p)) return 0; // fail
elem[0] = *p;
p++;
if (islower(*p))
{
elem[1] = *p;
p++;
}
// can be optimized
if ((abbrev == NULL) || (strcmp(elem, abbrev) == 0))
{
int z = find(elem);
if (z == 255) return 0; // fail
w = 1;
}
}
count = 0;
// get optional digits
while (isdigit(*p))
{
count = count * 10 + (*p - '0');
p++;
}
// correct for no digits
if (count == 0) count = 1;
count = 0;
// get optional digits
while (isdigit(*p))
{
count = count * 10 + (*p - '0');
p++;
}
// correct for no digits
if (count == 0) count = 1;
// DEBUG
// Serial.println(w);
// Serial.println(count);
// DEBUG
// Serial.println(w);
// Serial.println(count);
sum += w * count;
}
return sum;
sum += w * count;
}
return sum;
}

View File

@ -3,14 +3,15 @@
// FILE: AtomicWeight.h
// AUTHOR: Rob Tillaart
// DATE: 2022-03-09
// VERSION: 0.1.5
// VERSION: 0.2.0
// PURPOSE: Arduino library for atomic weights
// URL: https://github.com/RobTillaart/AtomicWeight
#include "Arduino.h"
#define ATOMIC_WEIGHT_LIB_VERSION (F("0.1.5"))
#define ATOMIC_WEIGHT_LIB_VERSION (F("0.2.0"))
#ifndef ATOMIC_WEIGHT_MAX_SPLIT_LIST
@ -18,6 +19,16 @@
#endif
// Miscellaneous related constants.
const float AVOGADRO = 6.02214076e+23; // 1.0 / DALTON.
const float DALTON = 1.66053907e-24; // weight in grams of one nucleon.
const float ELEKTRON_VOLT_JOULE = 1.602176565e-19; // eV in Joule
const float ELEKTRON_VOLT_GRAM = 1.7826619e-39; // eV in grams
const float DALTON_EV = DALTON / ELEKTRON_VOLT_GRAM;
const float DALTON_JOULE = DALTON / ELEKTRON_VOLT_JOULE;
/////////////////////////////////////////////////////////////////////////
//
// PERIODIC TABLE OF ELEMENTS Class
@ -29,12 +40,16 @@ public:
uint8_t size();
// BASIC
char * name(const uint8_t el);
uint8_t find(const char * abbrev);
uint8_t electrons(const uint8_t el);
uint8_t neutrons(const uint8_t el);
uint8_t protons(const uint8_t el);
// weight of one atom
// WEIGHT of one atom
float weight(const uint8_t el);
// if (el != NULL) weights one element in a formula, e.g el == "H"
// if (el == NULL) weights the whole formula
@ -43,28 +58,17 @@ public:
float massPercentage(const char * formula, const char * abbrev);
char * name(const uint8_t el);
uint8_t find(const char * abbrev);
// CONVERSION
float moles2grams(const char * formula, float moles = 1.0);
float grams2moles(const char * formula, float grams = 1.0);
// DEBUG
float weightFactor();
////////////////////////////////////////////////////////////////
//
// EXPERIMENTAL 0.1.4
//
// SPLIT FORMULA IN ELEMENTS
uint8_t splitElements(const char * formula);
uint8_t element(uint8_t el);
// COUNT
// if (el != NULL) count atoms of one element in a formula, e.g el == "H"
// if (el == NULL) count all atoms in the whole formula
uint32_t count(const char * formula, const char * el = NULL);
@ -72,9 +76,12 @@ public:
float atomPercentage(const char * formula, const char * el);
// DEBUG
float weightFactor();
private:
uint8_t _size;
const float _weightFactor = 1.0 / 222.909;
// if (el == NULL) ==> whole weight otherwise only of element.
float _weight(char sep, const char * abbrev);

View File

@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.0] - 2023-04-15
- use new weight factor 201.3868 to reduce maximum relative error
- create **elements_uint16.h** file.
- move **ATOMIC_WEIGHT_FACTOR** to **elements_uint16.h** file.
- create **elements_name.h** file with full element names. Uses ~1670 bytes of RAM.
so will **NOT** work on all boards.
- add related constants
- **AVOGADRO** = 6.02214076e+23; number of particles in one mole.
- **DALTON** = 1.66053907e-24; weight of one nucleon in grams.
- **ELEKTRON_VOLT_JOULE** = 1.602176565e-19; eV in Joule
- **ELEKTRON_VOLT_GRAM** = 1.7826619e-39; eV in grams
- **DALTON_EV** = 931.4940954e12 = DALTON / ELEKTRON_VOLT_GRAM
- **DALTON_JOULE** = 1.036427015e5 = DALTON / ELEKTRON_VOLT_JOULE
- update unit tests.
- update readme.md
- update keywords.txt.
## [0.1.5] - 2023-04-13
- add **moles2grams(formula, moles)**
- add **grams2moles(formula, grams)**
@ -17,7 +35,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- update keywords.txt.
- minor edits.
## [0.1.4] - 2023-04-12
- add **splitElements()** split a formula in an internal list of elements.
- add **element()** access to split elements. See example.

View File

@ -14,45 +14,62 @@ Arduino library for atomic weights, and related functions.
## Description
This library is mainly written to be used for educational purposes.
Of course are other applications possible.
Learning the **periodic table of elements**, the abbreviations and weights.
It also provides the number of electrons, neutrons and protons per element.
It provides the number of electrons, neutrons and protons per element.
Furthermore the library has a **weight()** function, which returns the weight
of either an element or of a formula (e.g. a molecule).
The weight function can also be used to get the weight of a particular element
of either an element or of a formula (e.g. a molecule) in **Daltons**.
One **Dalton** = 1/12th of the weight of a Carbon atom.
The SI abbreviation for **Dalton** == **Da**.
One might think of it as (roughly) the number of nucleons (= protons + neutrons).
The **weight()** function can also be used to get the weight of a particular element
within a formula, e.g. the total weight of all Oxygen atoms in a **H2SO4** molecule.
This latter function allows the library to calculate the **massPercentage()** too.
The library has a **count()** function, to count the atoms in a given formula.
Derived is the **atomPercentage()** function which returns the percentage of atoms
that is of a certain element. Oxygen in H2S04 is 4 in 7 which is about 57%.
that is of a certain element. Oxygen in **H2S04** is 4 in 7 which is about 57.14%.
Since 0.1.5 the library supports conversion from moles to grams and back.
The library supports conversion from moles to grams and back.
This allows one to easily get the amount of grams of some formula one has to weigh
to get a needed amount of moles.
With these functions, in combination with a load cell, one could create a "molar-scale".
Another application for the conversion functions is create lookup-tables, see example.
Note: the library is experimental. More testing is needed. Feedback welcome.
#### Internal
The PTOE class uses a table that has "compressed" weight to save RAM.
- it stores weights as **uint16_t**, 0..65535 instead of floats.
- weight factor = 222.909 = 65535.0 / weight heaviest element(118)
- relative error per element is less than 0.15%
The PTOE class uses a table with "scaled" weights to save RAM.
- it stores weights as **uint16_t**, instead of floats (saves 236 bytes).
- the table (and thus the class) does not handle isotopes of elements.
- the scaling factor is named **ATOMIC_WEIGHT_FACTOR**
**Version 0.2.x**
- ATOMIC_WEIGHT_FACTOR = 1.0 / 201.3868 (found by searching - see example).
- relative error per element is less than 0.09% (~40% better than previous 0.15%).
- **Breaking:** numbers are **not** compatible with 0.1.x version
**Version 0.1.x (for reference)**
- ATOMIC_WEIGHT_FACTOR = 1.0 / 222.909 == weight heaviest element(118) / 65535.
- relative error per element is less than 0.15%.
#### Related
List of formulae to play with.
- https://en.wikipedia.org/wiki/Glossary_of_chemical_formulae
- https://en.wikipedia.org/wiki/Avogadro_constant
Libraries useful to build the "molar-scale"
- https://github.com/RobTillaart/HX711
- https://github.com/RobTillaart/weight
- https://github.com/RobTillaart/HX711 (load cell)
- https://github.com/RobTillaart/weight (conversion)
- https://github.com/RobTillaart/printHelpers (scientific notation of numbers)
## Interface
@ -71,7 +88,7 @@ The parameter size is used in the **find()** and guards some parameters.
- **uint8_t neutrons(uint8_t element)** returns the number of neutrons of the element.
- **uint8_t protons(uint8_t element)** returns the number of protons of the element.
- **float weight(uint8_t element)** returns the weight of the element.
The error is less than 0.15%, as the internal table uses "compression" to save RAM.
The error is less than 0.09%, as the internal table uses "compression" to save RAM.
- **float weight(char \* formula, char \* abbreviation == NULL)** see below.
- if (abbreviation == NULL) returns the weight of the whole formula.
- If (abbreviation != NULL) returns the total weight of one element in a formula.
@ -87,7 +104,6 @@ If the element is out of range **NULL** will be returned.
#### SplitElements
(0.1.4 experimental)
- **uint8_t splitElements(const char \* formula)** split a formula in an internal list of elements.
Returns the number of different elements found.
Maximum number of elements is hardcoded to 20, which is often enough.
@ -95,10 +111,20 @@ Maximum number of elements is hardcoded to 20, which is often enough.
Note: el should be between 0 and the maximum number returned by **splitElements()**.
See example.
```cpp
char formula[24] = "YBa2Cu3O7";
nr = splitElements(formula);
for (int i = 0; i < nr; i++)
{
Serial.print(PTOE.massPercentage(formula, element(i)), 3);
Serial.print("%\t");
Serial.print(element(i));
Seriel.println();
}
```
#### AtomPercentage
(0.1.4 experimental)
- **uint32_t count(const char \* formula, const char \* abbreviation = NULL)**
- If (abbreviation != NULL) returns the total atoms of one element in a formula.
- if (abbreviation == NULL) returns the total atoms of the whole formula.
@ -107,6 +133,11 @@ See example.
Returns atom percentage of the selected element in a formula.
```cpp
ap = PTOE.atomPercentage("H2SO4", "O");
```
#### Conversion grams moles
- **float moles2grams(const char \* formula, float moles = 1.0)**
@ -116,12 +147,19 @@ The default moles == 1, returns the basic conversion factor.
Returns the amount of moles for a given amount of grams.
The default moles == 1, returns the basic conversion factor.
These functions can be used, e.g. if one wants to solve 2 moles of KOH
These functions can be used, e.g. if one wants to solve 2.3 moles of KOH
into 10 litres of water to get a defined pH, now much grams I need to weigh?
```cpp
grams = PTOE.moles2grams("KOH", 2.3);
```
#### Weight
The unit of weight is **Daltons** (Da) or the atomic mass unit (amu), think of it as the number of nucleons.
A **Dalton** is defined as 1/12th of the weight of an Carbon atom.
The **weight(uint8_t element)** call returns the weight of a single atom (by index).
The **weight(formula)** call is meant to calculate the weight of a molecule defined by the formula.
A molecule is defined as one or more atoms.
@ -134,6 +172,10 @@ The **weight(formula, abbreviation)** function is meant to calculate the total w
of one element (by abbreviation) in a molecule.
E.g one can weigh the H atoms in H2O (2 of 18).
```cpp
aw = PTOE.weight("H2O", "H");
```
#### Formulas
@ -154,10 +196,17 @@ Valid formula's might look like:
- "B" = single element, Hydrogen, 1 atom.
- "Na" = single element, Sodium, 1 atom..
- "C6" = single element, multiple times, Benzene.
- "H2SO4" compound molecule, no groups (sulphuric acid).
- "C6(COOH)2" repeating group, (artificial example).
- "YBa2Cu3O7" compound molecule, == some superconductor-ish material.
- "Ba((OH)4(COOH)2)c" recursive repeating groups (artificial example).
- "H2SO4" = compound molecule, no groups (sulphuric acid).
- "C6(COOH)2" = repeating group, (artificial example).
- "(H2O)987" = 987 water molecules.
- "YBa2Cu3O7" = compound molecule, == some superconductor-ish material.
- "Ba((OH)4(COOH)2)3" - recursive repeating groups (artificial example).
Numbers of an element or a group should be between 1 and 2^32-1 (uint32_t).
However in practice values are relative small (< 20).
- zero (0) is mapped upon 1.
- very large numbers cause overflow when printing the output of some functions.
Use - https://github.com/RobTillaart/printHelpers if needed.
#### MassPercentage
@ -166,10 +215,38 @@ The **massPercentage(formula, abbreviation)** function can determine the percent
a selected element has in a formula, e.g. the weight of the Oxygen in **H2SO4**.
This is calculated by dividing the weight of the element by the total weight.
```cpp
mp = PTOE.massPercentage("H2SO4", "O");
```
If you want to do that for all elements it might be more efficient to calculate the weight
of the whole formula once.
#### Avogadro, Dalton, electronVolt.
The library provides the following (SI) constants:
- **const float AVOGADRO = 6.02214076e+23** number of particles in one mole.
- **const float DALTON = 1.66053907e-24** weight of one nucleon in grams.
- relation: DALTON \* AVOGADRO == 1.0
- **const float ELEKTRON_VOLT_JOULE = 1.602176565e-19** eV in Joule
- **const float ELEKTRON_VOLT_GRAM = 1.7826619e-39** eV in grams (E=Mc2)
- **const float DALTON_JOULE = 1.036427015e5** == DALTON / ELEKTRON_VOLT_JOULE.
- **const float DALTON_EV = 931.4940954e12** == DALTON / ELEKTRON_VOLT_GRAM.
Can be used to convert the atomic mass to electron volt.
These constants are not directly used in the library however they fit the scope of the library.
There will be functions based upon these constants in the future.
The **AVOGADRO** constant is the proportionality factor that relates the number of constituent particles
(usually molecules, atoms, or ions) in a sample with the amount of substance in that sample.
From - https://en.wikipedia.org/wiki/Avogadro_constant
Use https://github.com/RobTillaart/printHelpers to print numbers in the scientific notation.
This will prevent printing **OVF** overflow or **0.000**.
#### Debug
- **float weightFactor()** returns the weightFactor, that was used to
@ -182,39 +259,38 @@ minimize the memory used for the elements mass lookup table.
- improve documentation
- reorganize.
- A better weight factor would be 201.3868 (see example)
- relative error is less than 0.09% (which is 40% better than 0.15%)
- for 0.2.0 release.
#### Should
- add examples
- extend formula parser with error codes.
- which ones?
- look for optimizations
- 3x almost same parser
- PROGMEM ?
- overload functions
- **uint32_t protons(formula)** worker, formula can be single element.
- **uint32_t neutrons(formula)** uses protons()
- **uint32_t electrons(formula)** uses protons()
- add weight of electron as constant. for completeness.
- functions around **AVOGADRO**, **DALTON** etc
- **float weightEV(formula)**
- **float dalton2EV(float Dalton)** to express mass in eV.
- **float dalton2Joule(float Dalton)**
#### Could
- extend unit tests
- extend formula parser with error codes.
- which ones?
- look for optimizations
- 3x almost same parser
- PROGMEM ?
- state table
- liquid, gas, solid, unknown (2 bits per element) = ~30 bytes
- room temperature + sea level pressure
- (short) table of English names
- which ones ?
- separate include file?
- rename **name()** to **abbrev()** ?
- add **longName()**?
- separate file like elements_name.h
- performance **find()**
- alphabetical array? tree?
- ==> more memory
- ==> more memory...
- support \[] square brackets too.
- (NH4)2\[Pt(SCN)6]
- optimize weigh-factor
- Arduino sketch
#### Wont (unless)
@ -228,7 +304,7 @@ minimize the memory used for the elements mass lookup table.
- is there a faster data structure?
- search by nr is O(1)
- search by name is O(n)
- only if more RAM is used.
- only if more RAM is used. => not
- parameters element should be less than \_size
- user responsibility
- more information?

View File

@ -0,0 +1,136 @@
#pragma once
//
// FILE: elements_name.h
// AUTHOR: Rob Tillaart
// DATE: 2023-04-15
// PURPOSE: list of long names of elements.
// pretty large table.
// URL: https://github.com/RobTillaart/AtomicWeight
char elements_name[119][14] =
{
"neutronium",
"Hydrogen",
"Helium",
"Lithium",
"Beryllium",
"Boron",
"Carbon",
"Nitrogen",
"Oxygen",
"Fluorine",
"Neon",
"Sodium",
"Magnesium",
"Aluminum",
"Silicon",
"Phosphorus",
"Sulfur",
"Chlorine",
"Argon",
"Potassium",
"Calcium",
"Scandium",
"Titanium",
"Vanadium",
"Chromium",
"Manganese",
"Iron",
"Cobalt",
"Nickel",
"Copper",
"Zinc",
"Gallium",
"Germanium",
"Arsenic",
"Selenium",
"Bromine",
"Krypton",
"Rubidium",
"Strontium",
"Yttrium",
"Zirconium",
"Niobium",
"Molybdenum",
"Technetium",
"Ruthenium",
"Rhodium",
"Palladium",
"Silver",
"Cadmium",
"Indium",
"Tin",
"Antimony",
"Tellurium",
"Iodine",
"Xenon",
"Cesium",
"Barium",
"Lanthanum",
"Cerium",
"Praseodymium",
"Neodymium",
"Promethium",
"Samarium",
"Europium",
"Gadolinium",
"Terbium",
"Dysprosium",
"Holmium",
"Erbium",
"Thulium",
"Ytterbium",
"Lutetium",
"Hafnium",
"Tantalum",
"Tungsten",
"Rhenium",
"Osmium",
"Iridium",
"Platinum",
"Gold",
"Mercury",
"Thallium",
"Lead",
"Bismuth",
"Polonium",
"Astatine",
"Radon",
"Francium",
"Radium",
"Actinium",
"Thorium",
"Protactinium",
"Uranium",
"Neptunium",
"Plutonium",
"Americium",
"Curium",
"Berkelium",
"Californium",
"Einsteinium",
"Fermium",
"Mendelevium",
"Nobelium",
"Lawrencium",
"Rutherfordium",
"Dubnium",
"Seaborgium",
"Bohrium",
"Hassium",
"Meitnerium",
"Darmstadtium",
"Roentgenium",
"Copernicium",
"Nihonium",
"Flerovium",
"Moscovium",
"Livermorium",
"Tennessine",
"Oganesson"
};
// -- END OF FILE --

View File

@ -0,0 +1,149 @@
#pragma once
//
// FILE: elements_uint16.h
// AUTHOR: Rob Tillaart
// DATE: 2023-04-15
// PURPOSE: list of atomic weights (scaled to uint16_t)
// generated with generate_uint16_table.ino
// URL: https://github.com/RobTillaart/AtomicWeight
//////////////////////////////////////////////
//
// list of elements (scaled to uint16_t)
// weight = weight * (1.0 / 201.3868)
//
const float ATOMIC_WEIGHT_FACTOR = (1.0 / 201.3868);
struct element
{
char name[3];
uint16_t weight;
}
elements[119] =
{
{"n", 203},
{"H", 203},
{"He", 806},
{"Li", 1975},
{"Be", 1815},
{"B", 2177},
{"C", 2419},
{"N", 2821},
{"O", 3222},
{"F", 3826},
{"Ne", 4064},
{"Na", 4630},
{"Mg", 4895},
{"Al", 5434},
{"Si", 5656},
{"P", 6238},
{"S", 6457},
{"Cl", 7140},
{"Ar", 8045},
{"K", 7874},
{"Ca", 8071},
{"Sc", 9054},
{"Ti", 9640},
{"V", 10259},
{"Cr", 10471},
{"Mn", 11064},
{"Fe", 11246},
{"Co", 11868},
{"Ni", 11820},
{"Cu", 12797},
{"Zn", 13173},
{"Ga", 14041},
{"Ge", 14629},
{"As", 15088},
{"Se", 15902},
{"Br", 16092},
{"Kr", 16876},
{"Rb", 17212},
{"Sr", 17646},
{"Y", 17904},
{"Zr", 18371},
{"Nb", 18710},
{"Mo", 19321},
{"Tc", 19736},
{"Ru", 20354},
{"Rh", 20724},
{"Pd", 21432},
{"Ag", 21723},
{"Cd", 22638},
{"In", 23123},
{"Sn", 23907},
{"Sb", 24521},
{"Te", 25697},
{"I", 25557},
{"Xe", 26441},
{"Cs", 26765},
{"Ba", 27656},
{"La", 27974},
{"Ce", 28218},
{"Pr", 28377},
{"Nd", 29048},
{"Pm", 29201},
{"Sm", 30281},
{"Eu", 30604},
{"Gd", 31668},
{"Tb", 32005},
{"Dy", 32725},
{"Ho", 33215},
{"Er", 33684},
{"Tm", 34021},
{"Yb", 34848},
{"Lu", 35236},
{"Hf", 35946},
{"Ta", 36441},
{"W", 37023},
{"Re", 37500},
{"Os", 38310},
{"Ir", 38710},
{"Pt", 39286},
{"Au", 39666},
{"Hg", 40396},
{"Tl", 41160},
{"Pb", 41727},
{"Bi", 42086},
{"Po", 42090},
{"At", 42291},
{"Rn", 44708},
{"Fr", 44909},
{"Ra", 45513},
{"Ac", 45715},
{"Th", 46729},
{"Pa", 46528},
{"U", 47936},
{"Np", 47729},
{"Pu", 49138},
{"Am", 48937},
{"Cm", 49743},
{"Bk", 49743},
{"Cf", 50548},
{"Es", 50749},
{"Fm", 51756},
{"Md", 51958},
{"No", 52159},
{"Lr", 52763},
{"Rf", 52562},
{"Db", 52763},
{"Sg", 53569},
{"Bh", 53166},
{"Hs", 55784},
{"Mt", 53972},
{"Ds", 56590},
{"Rg", 54777},
{"Cn", 57395},
{"Nh", 57597},
{"Fl", 58201},
{"Mc", 58201},
{"Lv", 59006},
{"Ts", 59208},
{"Og", 59208},
};
// -- END OF FILE --

View File

@ -3,6 +3,7 @@
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AtomicWeight
#include "Arduino.h"
#include "AtomicWeight.h"
@ -93,4 +94,4 @@ void loop()
// -- END OF FILE --
// -- END OF FILE --

View File

@ -58,7 +58,7 @@ void loop()
}
void test(char * formula)
void test(const char * formula)
{
Serial.println();
Serial.print(formula);

View File

@ -7,11 +7,13 @@
#include "Arduino.h"
#include "AtomicWeight.h"
PTOE ptoe;
char formula[24] = "KOH";
// char formula[24] = "NaCl";
void setup()
{
Serial.begin(115200);

View File

@ -7,12 +7,15 @@
#include "Arduino.h"
#include "AtomicWeight.h"
PTOE ptoe;
uint32_t start, stop;
volatile char *p = 0;
volatile int n;
void setup()
{
Serial.begin(115200);

View File

@ -3,6 +3,7 @@
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AtomicWeight
#include "Arduino.h"
#include "AtomicWeight.h"
@ -17,6 +18,7 @@ char formula3[24] = "CuO2";
// char formula4[24] = "(CH)6O6";
char formula4[24] = "xH2"; // fails => 0;
void setup()
{
Serial.begin(115200);
@ -60,11 +62,25 @@ void setup()
Serial.print(formula4);
Serial.print(" \t");
Serial.println(ptoe.weight(formula4));
Serial.print("()");
Serial.print(" \t");
Serial.println(ptoe.weight("()"));
Serial.print("(H2O)");
Serial.print(" \t");
Serial.println(ptoe.weight("(H2O)"));
Serial.print("(H2O)255");
Serial.print(" \t");
Serial.println(ptoe.weight("(H2O)255"));
}
void loop()
{
}
// -- END OF FILE --
// -- END OF FILE --

View File

@ -0,0 +1,28 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
# - uno
# - due
# - zero
# - leonardo
# - m4
- esp32
- esp8266
- mega2560
- rpipico

View File

@ -0,0 +1,53 @@
// FILE: atomic_weight_print.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// Note: the table of elements_name takes ~1670 bytes.
// So it will NOT work on devices with small memory e.g. UNO.
// URL: https://github.com/RobTillaart/AtomicWeight
#include "Arduino.h"
#include "AtomicWeight.h"
#include "elements_name.h"
PTOE ptoe;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
for (int i = 0; i <= ptoe.size(); i++)
{
printElem(i);
}
}
void loop()
{
}
void printElem(uint8_t i)
{
Serial.print(ptoe.name(i));
Serial.print("\t");
Serial.print(ptoe.electrons(i));
Serial.print("\t");
Serial.print(ptoe.protons(i));
Serial.print("\t");
Serial.print(ptoe.neutrons(i));
Serial.print("\t");
Serial.print(ptoe.weight(i), 3);
Serial.print("\t");
Serial.print(elements_name[i]);
Serial.print("\n");
}
// -- END OF FILE --

View File

@ -10,6 +10,7 @@
PTOE ptoe;
char formula0[24] = "C6H6O6";
char formula1[24] = "((COH)3)2";
char formula2[24] = "H2SO4";
@ -20,9 +21,6 @@ char formula4[24] = "(COH)3(COH)2COH";
char formula5[24] = "YBa2Cu3O7";
uint8_t cnt;
void setup()
{
Serial.begin(115200);
@ -53,7 +51,7 @@ void loop()
}
void test(char * formula)
void test(const char * formula)
{
Serial.println();
Serial.println(formula);

View File

@ -140,4 +140,4 @@ elements[119] =
};
// -- END OF FILE --
// -- END OF FILE --

View File

@ -9,9 +9,14 @@
#include "Arduino.h"
#include "elements_float.h"
// VERSION FACTOR
// 0.1.x 222.909
// 0.2.x 201.3868
// VERSION FACTOR DESCRIPTION
// 0.1.x 222.909 based op mapping max range 65535 == weight(118)
// 0.2.x 201.3868 searched for minimal relative error.
// const float WEIGHT_FACTOR = 222.909;
const float WEIGHT_FACTOR = 201.3868;
void setup()
{
@ -22,11 +27,17 @@ void setup()
// HEADER
Serial.println();
Serial.println();
Serial.println("/////////////////////////////////////////");
Serial.println("//////////////////////////////////////////////");
Serial.println("//");
Serial.println("// list of elements ");
Serial.println("// weight = weight * (1.0/ 222.909)");
Serial.println("// list of elements (scaled to uint16_t)");
Serial.print("// weight = weight * (1.0 / ");
Serial.print(WEIGHT_FACTOR, 4);
Serial.println(")");
Serial.println("//");
Serial.print("const float ATOMIC_WEIGHT_FACTOR = (1.0 / ");
Serial.print(WEIGHT_FACTOR, 4);
Serial.println(");");
Serial.println();
Serial.println("struct element");
Serial.println("{");
Serial.println(" char name[3];");
@ -41,7 +52,7 @@ void setup()
Serial.print(" {\"");
Serial.print(elements[i].name);
Serial.print("\", ");
Serial.print(round(elements[i].weight * 222.909));
Serial.print(round(elements[i].weight * WEIGHT_FACTOR));
Serial.println("},");
}
Serial.println("};");

View File

@ -140,4 +140,4 @@ elements[119] =
};
// -- END OF FILE --
// -- END OF FILE --

View File

@ -14,6 +14,7 @@
#include "Arduino.h"
#include "elements_float.h"
float best = 202;
float error = 10000;
@ -52,8 +53,6 @@ void setup()
Serial.println(val);
}
}
Serial.println("\ndone");
}
@ -64,3 +63,4 @@ void loop()
// -- END OF FILE --

View File

@ -7,6 +7,8 @@ PTOE KEYWORD1
# Methods and Functions (KEYWORD2)
size KEYWORD2
name KEYWORD2
find KEYWORD2
electrons KEYWORD2
protons KEYWORD2
@ -15,8 +17,8 @@ neutrons KEYWORD2
weight KEYWORD2
massPercentage KEYWORD2
name KEYWORD2
find KEYWORD2
moles2grams KEYWORD2
grams2moles KEYWORD2
splitElements KEYWORD2
element KEYWORD2
@ -24,12 +26,17 @@ element KEYWORD2
count KEYWORD2
atomPercentage KEYWORD2
moles2grams KEYWORD2
grams2moles KEYWORD2
# Constants ( LITERAL1)
ATOMIC_WEIGHT_LIB_VERSION LITERAL1
ATOMIC_WEIGHT_MAX_SPLIT_LIST LITERAL1
ATOMIC_WEIGHT_FACTOR LITERAL1
AVOGADRO LITERAL1
DALTON LITERAL1
ELEKTRON_VOLT_JOULE LITERAL1
ELEKTRON_VOLT_GRAM LITERAL1
DALTON_EV LITERAL1
DALTON_JOULE LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "AtomicWeight",
"keywords": "Mass,atoms,weight,PTOE,periodic,elements,massPercentage",
"keywords": "Mass,atoms,weight,PTOE,periodic,elements,massPercentage,atomPercentage,mole,gram",
"description": "Arduino library for atomic weights, calculate massPercentage of elements in a formula.",
"authors":
[
@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/AtomicWeight.git"
},
"version": "0.1.5",
"version": "0.2.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,9 +1,9 @@
name=AtomicWeight
version=0.1.5
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for atomic weights, calculate massPercentage of elements in a formula.
paragraph=Mass,atoms,weight,PTOE,periodic,elements,massPercentage,atomPercentage
paragraph=Mass,atom,weight,PTOE,periodic,elements,massPercentage,atomPercentage,mole,gram
category=Data Processing
url=https://github.com/RobTillaart/AtomicWeight
architectures=*

View File

@ -48,7 +48,14 @@ unittest_teardown()
unittest(test_constants)
{
assertEqualFloat(6.02214076e+23, AVOGADRO, 1e17);
assertEqualFloat(1.66053907e-24, DALTON, 1e-30);
assertEqualFloat(1.0, DALTON * AVOGADRO, 1e-5);
assertEqualFloat(1.602176565e-19, ELEKTRON_VOLT_JOULE, 1e-5);
assertEqualFloat(1.7826619e-39, ELEKTRON_VOLT_GRAM, 1e-5);
assertEqualFloat(DALTON / ELEKTRON_VOLT_GRAM, DALTON_EV, 1e-5);
assertEqualFloat(DALTON / ELEKTRON_VOLT_JOULE, DALTON_JOULE, 1e-5);
}
@ -114,46 +121,76 @@ unittest(test_basic_atom)
unittest(test_weight_formula_I)
{
PTOE ptoe;
assertEqualFloat( 22.9914, ptoe.weight("Na"), 0.1);
assertEqualFloat( 58.4454, ptoe.weight("NaCl"), 0.1);
assertEqualFloat( 100.081, ptoe.weight("CaCO3"), 0.1);
assertEqualFloat( 98.0759, ptoe.weight("H2SO4"), 0.1);
assertEqualFloat( 116.065, ptoe.weight("C2H2(COOH)2"), 0.1);
assertEqualFloat( 666.178, ptoe.weight("YBa2Cu3O7"), 0.1);
assertEqualFloat( 72.1146, ptoe.weight("C(O(H2)2)3"), 0.1);
// fprintf(stderr, "%f\n", ptoe.weight("Na"));
// fprintf(stderr, "%f\n", ptoe.weight("NaCl"));
// fprintf(stderr, "%f\n", ptoe.weight("CaCO3"));
// fprintf(stderr, "%f\n", ptoe.weight("H2SO4"));
// fprintf(stderr, "%f\n", ptoe.weight("C2H2(COOH)2"));
// fprintf(stderr, "%f\n", ptoe.weight("YBa2Cu3O7"));
// fprintf(stderr, "%f\n", ptoe.weight("C(O(H2)2)3"));
assertEqualFloat( 22.990588, ptoe.weight("Na"), 0.1);
assertEqualFloat( 58.444748, ptoe.weight("NaCl"), 0.1);
assertEqualFloat( 100.086006, ptoe.weight("CaCO3"), 0.1);
assertEqualFloat( 98.074951, ptoe.weight("H2SO4"), 0.1);
assertEqualFloat( 116.075134, ptoe.weight("C2H2(COOH)2"), 0.1);
assertEqualFloat( 666.185669, ptoe.weight("YBa2Cu3O7"), 0.1);
assertEqualFloat( 72.105026, ptoe.weight("C(O(H2)2)3"), 0.1);
}
unittest(test_weight_formula_II)
{
PTOE ptoe;
assertEqualFloat( 22.9914, ptoe.weight("NaCl", "Na"), 0.1);
assertEqualFloat( 35.4539, ptoe.weight("NaCl", "Cl"), 0.1);
assertEqualFloat( 40.0791, ptoe.weight("CaCO3", "Ca"), 0.1);
assertEqualFloat( 12.0094, ptoe.weight("CaCO3", "C"), 0.1);
assertEqualFloat( 47.9927, ptoe.weight("CaCO3", "O"), 0.1);
// fprintf(stderr, "%f\n", ptoe.weight("NaCl", "Na"));
// fprintf(stderr, "%f\n", ptoe.weight("NaCl", "Cl"));
// fprintf(stderr, "%f\n", ptoe.weight("CaCO3", "Ca"));
// fprintf(stderr, "%f\n", ptoe.weight("CaCO3", "C"));
// fprintf(stderr, "%f\n", ptoe.weight("CaCO3", "O"));
// fprintf(stderr, "%f\n", ptoe.weight("C(O(H2)2)3", "C"));
// fprintf(stderr, "%f\n", ptoe.weight("C(O(H2)2)3", "O"));
// fprintf(stderr, "%f\n", ptoe.weight("C(O(H2)2)3", "H"));
assertEqualFloat( 12.0094, ptoe.weight("C(O(H2)2)3", "C"), 0.1);
assertEqualFloat( 47.9927, ptoe.weight("C(O(H2)2)3", "O"), 0.1);
assertEqualFloat( 12.1126, ptoe.weight("C(O(H2)2)3", "H"), 0.1);
assertEqualFloat( 22.990583, ptoe.weight("NaCl", "Na"), 0.1);
assertEqualFloat( 35.454163, ptoe.weight("NaCl", "Cl"), 0.1);
assertEqualFloat( 40.077106, ptoe.weight("CaCO3", "Ca"), 0.1);
assertEqualFloat( 12.011711, ptoe.weight("CaCO3", "C"), 0.1);
assertEqualFloat( 47.997189, ptoe.weight("CaCO3", "O"), 0.1);
assertEqualFloat( 12.011711, ptoe.weight("C(O(H2)2)3", "C"), 0.1);
assertEqualFloat( 47.997189, ptoe.weight("C(O(H2)2)3", "O"), 0.1);
assertEqualFloat( 12.096127, ptoe.weight("C(O(H2)2)3", "H"), 0.1);
}
unittest(test_massPercentage)
{
PTOE ptoe;
assertEqualFloat( 100.000, ptoe.massPercentage("Cl2", "Cl"), 0.1);
assertEqualFloat( 39.3383, ptoe.massPercentage("NaCl", "Na"), 0.1);
assertEqualFloat( 60.6617, ptoe.massPercentage("NaCl", "Cl"), 0.1);
assertEqualFloat( 40.0466, ptoe.massPercentage("CaCO3", "Ca"), 0.1);
assertEqualFloat( 11.9996, ptoe.massPercentage("CaCO3", "C"), 0.1);
assertEqualFloat( 47.9537, ptoe.massPercentage("CaCO3", "O"), 0.1);
// fprintf(stderr, "%f\n", ptoe.massPercentage("Cl2", "Cl"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("NaCl", "Na"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("NaCl", "Cl"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("CaCO3", "Ca"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("CaCO3", "C"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("CaCO3", "O"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("C(O(H2)2)3", "C"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("C(O(H2)2)3", "O"));
// fprintf(stderr, "%f\n", ptoe.massPercentage("C(O(H2)2)3", "H"));
assertEqualFloat( 16.6532, ptoe.massPercentage("C(O(H2)2)3", "C"), 0.1);
assertEqualFloat( 66.5505, ptoe.massPercentage("C(O(H2)2)3", "O"), 0.1);
assertEqualFloat( 16.7963, ptoe.massPercentage("C(O(H2)2)3", "H"), 0.1);
assertEqualFloat(100.000000, ptoe.massPercentage("Cl2", "Cl"), 0.1);
assertEqualFloat( 39.337296, ptoe.massPercentage("NaCl", "Na"), 0.1);
assertEqualFloat( 60.662701, ptoe.massPercentage("NaCl", "Cl"), 0.1);
assertEqualFloat( 40.042667, ptoe.massPercentage("CaCO3", "Ca"), 0.1);
assertEqualFloat( 12.001390, ptoe.massPercentage("CaCO3", "C"), 0.1);
assertEqualFloat( 47.955944, ptoe.massPercentage("CaCO3", "O"), 0.1);
assertEqualFloat( 16.658632, ptoe.massPercentage("C(O(H2)2)3", "C"), 0.1);
assertEqualFloat( 66.565666, ptoe.massPercentage("C(O(H2)2)3", "O"), 0.1);
assertEqualFloat( 16.775705, ptoe.massPercentage("C(O(H2)2)3", "H"), 0.1);
}