-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from stevengj/manychanges
many improvements
- Loading branch information
Showing
3 changed files
with
188 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.