Skip to content

Commit

Permalink
Merge pull request #5 from stevengj/manychanges
Browse files Browse the repository at this point in the history
many improvements
  • Loading branch information
rahulkp220 authored Mar 15, 2018
2 parents 8ec72ad + 3902e84 commit d3cc0e2
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 184 deletions.
113 changes: 40 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,85 +15,52 @@ julia> Pkg.update("PeriodicTable")
```

## How it works?
PeriodicTable.jl includes a `elements.json` file which acts like a database for this small library.
Working with it is very simple, indeed its just 2 steps before you see the actual data.
PeriodicTable.jl provides a Julia interface to a small database of element
properties for all of the elements in the periodic table. In particular
```jl
using PeriodicTable
```
exports a global variable called `elements`, which is a collection of
`Element` data structures. You can look up elements by name (case-insensitive)
via `elements["oxygen"]`, by symbol via `elements[:O]`, or by number via
`elements[8]`, for example.

Each element has fields `name`, `appearance`, `atomic_mass`, `boil`, `category`, `color`, `density`, `discovered_by`, `melt`, `molar_heat`, `named_by`, `number`, `period`, `phase`, `source`, `spectral_img`, `summary`, `symbol`, `xpos`, `ypos`, `shells`.

This data is pretty-printed when you look up an element in the Julia REPL.
For example:
```jl
julia> elements["oxygen"]
Oxygen (O), number 8:
category: diatomic nonmetal
atomic mass: 15.999 u
density: 1.429 g/cm³
melting point: 54.36 K
boiling point: 90.188 K
phase: Gas
shells: [2, 6]
summary: Oxygen is a chemical element with symbol O and atomic number 8. It is a member of the chalcogen group on the periodic table and is a highly reactive nonmetal and oxidizing agent that readily forms compounds (notably oxides) with most elements. By mass, oxygen is the third-most abundant element in the universe, after hydrogen and helium.
discovered by: Carl Wilhelm Scheele
named by: Antoine Lavoisier
source: https://en.wikipedia.org/wiki/Oxygen
spectral image: https://en.wikipedia.org/wiki/File:Oxygen_spectre.jpg
```

### View the Periodic Table!
This awesome view was added by [Jacob Wikmark](https://github.com/lancebeet) via [#4](https://github.com/rahulkp220/PeriodicTable.jl/pull/4)
```julia

# Initialise the object
julia> p = PeriodicTable.PT()
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 Hf Ta W Re Os Ir Pt Au Hg Tl Pb Bi Po At Rn
Fr Ra Rf Db Sg Bh Hs Mt Ds Rg Cn Nh Fl Mc Lv Ts Og
julia> elements
Elements(119 elements):
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 Hf Ta W Re Os Ir Pt Au Hg Tl Pb Bi Po At Rn
Fr Ra Rf Db Sg Bh Hs Mt Ds Rg Cn Nh Fl Mc Lv Ts Og
Uue
La Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb Lu
Ac Th Pa U Np Pu Am Cm Bk Cf Es Fm Md No Lr
```

### Getting the element and their data
```julia
julia> ele = PeriodicTable.get_element(p, 8)

# to get the complete data
julia> ele.data
Dict{String,Any} with 21 entries:
"number" => 8
"appearance" => ""
"name" => "Oxygen"
"boil" => 90.188
"atomic_mass" => 15.999
"period" => 2
"melt" => 54.36
"shells" => Any[2, 6]
"summary" => "Oxygen is a chemical element with symbol O and atomic number 8. It is a member of the chalcogen group on the periodic …
"molar_heat" => ""
"named_by" => "Antoine Lavoisier"
"xpos" => 16
"symbol" => "O"
"discovered_by" => "Carl Wilhelm Scheele"
"ypos" => 2
"category" => "diatomic nonmetal"
"density" => 1.429
"source" => "https://en.wikipedia.org/wiki/Oxygen"
"color" => ""
"phase" => "Gas"
"spectral_img" => "https://en.wikipedia.org/wiki/File:Oxygen_spectre.jpg"
# exploring these properties via fieldnames
julia> println("$(ele.name) with mass $(ele.atomic_mass)")
Oxygen with mass 15.999
julia> fieldnames(ele)
22-element Array{Symbol,1}:
:data
:name
:appearance
:atomic_mass
:boil
:category
:color
:density
:discovered_by
:melt
:molar_heat
:named_by
:number
:period
:phase
:source
:spectral_img
:summary
:symbol
:xpos
:ypos
:shells
Ac Th Pa U Np Pu Am Cm Bk Cf Es Fm Md No Lr
```

## Data by
Expand Down
242 changes: 141 additions & 101 deletions src/PeriodicTable.jl
Original file line number Diff line number Diff line change
@@ -1,125 +1,165 @@
__precompile__(true)

"""
The PeriodicTable module exports an `elements` variable that stores
data (of type `Element`) on every element in the periodic table.
The data can be looked up by atomic number via `elements[n]`, by name
(case-insensitive) via e.g. `elements["oxygen"]`, or by symbol via
e.g. `elements[:O]`.
"""
module PeriodicTable
export Element, elements

# Import modules
using JSON

# constants
const DATA_FILE = "elements.json"
const PACKAGE_NAME = "PeriodicTable"

"""
Element composite type
"""
type Element
data
name
appearance
atomic_mass
boil
category
color
density
discovered_by
melt
molar_heat
named_by
number
period
phase
source
spectral_img
summary
symbol
xpos
ypos
shells

# inner constructor
Element(data,
name = get(data, "name", nothing),
appearance = get(data, "appearance", nothing),
atomic_mass = get(data, "atomic_mass", nothing),
boil = get(data, "boil", nothing),
category = get(data, "category", nothing),
color = get(data, "color", nothing),
density = get(data, "density", nothing),
discovered_by = get(data, "discovered_by", nothing),
melt = get(data, "melt", nothing),
molar_heat = get(data, "molar_heat", nothing),
named_by = get(data, "named_by", nothing),
number = get(data, "number", nothing),
period = get(data, "period", nothing),
phase = get(data, "phase", nothing),
source = get(data, "source", nothing),
spectral_img = get(data, "spectral_img", nothing),
summary = get(data, "summary", nothing),
symbol = get(data, "symbol", nothing),
xpos = get(data, "xpos", nothing),
ypos = get(data, "ypos", nothing),
shells = get(data, "shells", nothing)) = new(data,name,appearance,atomic_mass,
boil,category,color,density,
discovered_by,melt,molar_heat,
named_by,number,period,phase,source,
spectral_img,summary,symbol,
xpos,ypos,shells)
mutable struct Element
name::String
appearance::String
atomic_mass::Float64
boil::Float64
category::String
color::String
density::Float64
discovered_by::String
melt::Float64
molar_heat::Float64
named_by::String
number::Int
period::Int
phase::String
source::String # url
spectral_img::String # url
summary::String
symbol::String
xpos::Int
ypos::Int
shells::Vector{Int}
end

# like get(data, name, default), but treats
# non-numeric data as missing
function getnum(data, name, default::Number)
v = get(data, name, default)
return v isa Number ? v : default
end

"""
PT composite type
"""
type PT
data

# Helper method, something that I am happy it worked!
function load_data()
output = []
file_path = joinpath(Pkg.dir(PACKAGE_NAME),"src","data", DATA_FILE)

# open the file, convert data, append to Array
open(file_path, "r") do file
data_json = JSON.parse(readstring(file))
for d in data_json
push!(output, Element(d))
end
end

output
end

# inner constructor
function PT()
data = load_data()
new(data)
# constructor from JSON-generated Dict
Element(eldata::Dict) = Element(
get(eldata, "name", ""),
get(eldata, "appearance", ""),
getnum(eldata, "atomic_mass", NaN),
getnum(eldata, "boil", NaN),
get(eldata, "category", ""),
get(eldata, "color", ""),
getnum(eldata, "density", NaN),
get(eldata, "discovered_by", ""),
getnum(eldata, "melt", NaN),
getnum(eldata, "molar_heat", NaN),
get(eldata, "named_by", ""),
getnum(eldata, "number", -1),
getnum(eldata, "period", -1),
get(eldata, "phase", ""),
get(eldata, "source", ""),
get(eldata, "spectral_img", ""),
get(eldata, "summary", ""),
get(eldata, "symbol", ""),
getnum(eldata, "xpos", -1),
getnum(eldata, "ypos", -1),
Vector{Int}(get(eldata, "shells", Int[])))

Base.show(io::IO, el::Element) = print(io, "Element(", el.name, ')')

ispresent(s) = !isempty(s)
ispresent(x::Float64) = !isnan(x)
ispresent(n::Int) = n 0
function printpresent(io::IO, name, val, suffix=""; pad=16)
if ispresent(val)
println(io, lpad(name, pad), ": ", val, suffix)
end
end

function Base.show(io::IO, ::MIME"text/plain", el::Element)
println(io, el.name, " (", el.symbol, "), number ", el.number, ':')
printpresent(io, "category", el.category)
printpresent(io, "atomic mass", el.atomic_mass, " u")
printpresent(io, "density", el.density, " g/cm³")
printpresent(io, "molar heat", el.molar_heat, " J/mol⋅K")
printpresent(io, "melting point", el.melt, " K")
printpresent(io, "boiling point", el.boil, " K")
printpresent(io, "phase", el.phase)
printpresent(io, "shells", el.shells)
printpresent(io, "appearance", el.appearance)
printpresent(io, "color", el.color)
printpresent(io, "summary", el.summary)
printpresent(io, "discovered by", el.discovered_by)
printpresent(io, "named by", el.named_by)
printpresent(io, "source", el.source)
printpresent(io, "spectral image", el.spectral_img)
end

"""
Function to get element by atomic number
Elements composite type
"""
function get_element(pt::PT, atomic_number::Int64)
pt.data[atomic_number]
struct Elements
data::Vector{Element}
bynumber::Dict{Int,Element}
byname::Dict{String,Element}
bysymbol::Dict{Symbol,Element}
Elements(data::Vector{Element}) = new(
sort!(data, by=d->d.number),
Dict{Int,Element}(d.number=>d for d in data),
Dict{String,Element}(lowercase(d.name)=>d for d in data),
Dict{Symbol,Element}(Symbol(d.symbol)=>d for d in data))
end

Base.getindex(e::Elements, i::Integer) = e.bynumber[i]
Base.getindex(e::Elements, i::AbstractString) = e.byname[lowercase(i)]
Base.getindex(e::Elements, i::Symbol) = e.bysymbol[i]
Base.haskey(e::Elements, i::Integer) = haskey(e.bynumber, i)
Base.haskey(e::Elements, i::AbstractString) = haskey(e.byname, lowercase(i))
Base.haskey(e::Elements, i::Symbol) = haskey(e.bysymbol, i)
Base.get(e::Elements, i::Integer, default) = get(e.bynumber, i, default)
Base.get(e::Elements, i::AbstractString, default) = get(e.byname, lowercase(i), default)
Base.get(e::Elements, i::Symbol, default) = get(e.bysymbol, i, default)

"""
Show table
"""
function Base.show(io::IO, ::MIME"text/plain", p::PT) #We can make this static if data is loaded in compile time
table_length = length(p.data)
shape = (10,18)

array_table = fill(" ", shape)
for i in 1:table_length
el = p.data[i].data
array_table[el["ypos"], el["xpos"]] = rpad(el["symbol"], 3)
# support iterating over elements
Base.eltype(e::Elements) = Element
Base.length(e::Elements) = length(e.data)
Base.start(e::Elements) = start(e.data)
Base.next(e::Elements, i) = next(e.data, i)
Base.done(e::Elements, i) = done(e.data, i)

# compact one-line printing
Base.show(io::IO, e::Elements) = print(io, "Elements(…", length(e), " elements…)")

# pretty-printing as a periodic table
function Base.show(io::IO, ::MIME"text/plain", e::Elements)
println(io, e, ':')
table = fill(" ", 10,18)
for el in e
table[el.ypos, el.xpos] = rpad(el.symbol, 3)
end
for i = 1:size(table,1)
for j = 1:size(table, 2)
print(io, table[i,j])
end
println(io)
end
print(join([join(array_table[i,:]) for i in 1:shape[1]], '\n'))
end

# load from JSON data
function Elements(filename = joinpath(dirname(@__FILE__), "data", "elements.json"))
data_json = open(JSON.parse, filename)
data = sizehint!(Element[], length(data_json))
for d in data_json
push!(data, Element(d))
end
return Elements(data)
end

# exports
export Element, PT, get_element
const elements = Elements()

end
Loading

0 comments on commit d3cc0e2

Please sign in to comment.