Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 41 additions & 119 deletions src/data-model.typ
Original file line number Diff line number Diff line change
@@ -1,21 +1,53 @@
#import "utils.typ": is-sequence, is-kind, is-heading, is-metadata, padright, get-all-children, hydrates, elements, shell-capacities, orbital-capacities, get-element-dict, get-molecule-dict, to-string
#import "utils.typ": is-sequence, is-kind, is-heading, is-metadata, padright, get-all-children, hydrates, elements, get-element-dict, get-molecule-dict, to-string
#import "regex.typ": patterns

#import "formula-parser.typ": ce
#let get-element(
symbol: auto,
atomic-number:auto,
common-name:auto,
cas:auto,
)={
let element = if symbol != auto {
elements.find(x=> x.symbol == symbol)
} else if atomic-number != auto{
elements.find(x=> x.atomic-number == atomic-number)
} else if common-name != auto{
elements.find(x=> x.common-name == common-name)
} else if cas != auto{
elements.find(x=> x.cas == cas)
}
return metadata(element)
}

#let validate-element(element)={
let type = type(element)
if type == str{
if element.len() > 2{
return get-element(common-name:element)
} else {
return get-element(symbol:element)
}
} else if type == int{
return get-element(atomic-number:element)
} else if type == content{
return get-element-dict(element)
} else if type == dictionary{
return element
}
}

//TODO: properly parse bracket contents
// maybe recursively with a bracket regex, passing in the bracket content and multiplier(?)
//TODO: Properly apply stochiometry
#let get-element-counts(molecule)={

let found-elements = (:)
let remaining = molecule.trim()
while remaining.len() > 0 {
let match = remaining.match(regex_patterns.at("element"))
let match = remaining.match(patterns.element)
if match != none {
remaining = remaining.slice(match.end)
let element = match.captures.at(0)
let count = int(if match.captures.at(1, default: "") == "" {1} else{match.captures.at(1)})
let count = 1 //int(if match.captures.at(1, default: "") == "" {1} else{match.captures.at(1)})
let current = found-elements.at(element, default: 0)
found-elements.insert(element, count)
}
Expand Down Expand Up @@ -43,116 +75,6 @@
return weight
}

#let get-shell-configuration(element)={
element = get-element-dict(element)
let charge = element.at("charge", default:0)
let electron-amount = element.atomic-number - charge

let result = ()
for value in shell-capacities {
if electron-amount <= 0{
break
}

if electron-amount >= value.at(1){
result.push(value)
electron-amount -= value.at(1)
} else {
result.push((value.at(0), electron-amount))
electron-amount = 0
}
}
return result
}

//TODO: fix Cr and Mo
#let get-electron-configuration(element)={
element = get-element-dict(element)
let charge = element.at("charge", default:0)
let electron-amount = element.atomic-number - charge

let result = ()
for value in orbital-capacities {
if electron-amount <= 0{
break
}
if electron-amount >= value.at(1){
result.push(value)
electron-amount -= value.at(1)
} else {
result.push((value.at(0), electron-amount))
electron-amount = 0
}
}
return result
}

#let display-electron-configuration(element, short: false)={
let configuration = get-electron-configuration(element)

if short{
let prefix = ""
if configuration.at(14, default: (0,0)).at(1) == 6{
configuration = configuration.slice(15)
prefix = "[Rn]"
} else if configuration.at(10, default: (0,0)).at(1) == 6{
configuration = configuration.slice(11)
prefix = "[Xe]"
} else if configuration.at(7, default: (0,0)).at(1) == 6{
configuration = configuration.slice(8)
prefix = "[Kr]"
} else if configuration.at(4, default: (0,0)).at(1) == 6{
configuration = configuration.slice(5)
prefix = "[Ar]"
} else if configuration.at(2, default: (0,0)).at(1) == 6{
configuration = configuration.slice(3)
prefix = "[Ne]"
} else if configuration.at(0, default: (0,0)).at(1) == 2{
configuration = configuration.slice(1)
prefix = "[He]"
}

return prefix + configuration.map(x=> $#x.at(0)^#str(x.at(1))$).sum()
} else{
return configuration.map(x=> $#x.at(0)^#str(x.at(1))$).sum()
}
}

#let get-element(
symbol: auto,
atomic-number:auto,
common-name:auto,
cas:auto,
)={
let element = if symbol != auto {
elements.find(x=> x.symbol == symbol)
} else if atomic-number != auto{
elements.find(x=> x.atomic-number == atomic-number)
} else if common-name != auto{
elements.find(x=> x.common-name == common-name)
} else if cas != auto{
elements.find(x=> x.cas == cas)
}
return metadata(element)
}

#let validate-element(element)={
let type = type(element)
if type == str{
if element.len() > 2{
return get-element(common-name:element)
} else {
return get-element(symbol:element)
}
} else if type == int{
return get-element(atomic-number:element)
} else if type == content{
return get-element-dict(element)
} else if type == dictionary{
return element
}
}

#let define-ion(
element,
charge: 0,
Expand Down Expand Up @@ -241,18 +163,18 @@
formula = smiles
}

if CAS == ""{
CAS = none
if cas == ""{
cas = none
}

found-elements = get-element-counts(formula)

if InChI != ""{
if inchi != ""{
// TODO: create InChI keys from provided InChI:
// https://typst.app/universe/package/jumble
// https://www.inchi-trust.org/download/104/InChI_TechMan.pdf
}else{
InChI = none
inchi = none
}
}

Expand Down
108 changes: 108 additions & 0 deletions src/display-intermediate-representation.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#import "utils.typ": try-at, count-to-content, charge-to-content, get-bracket, get-arrow, phase-to-content

#let display-element(data) = {
let isotope = data.at("isotope", default: none)
math.attach(
data.symbol,
t: data.at("oxidation-number", default: none),
tr: charge-to-content(data.at("charge", default: none), radical: data.at("radical", default: false)),
br: count-to-content(data.at("count", default: none)),
tl: try-at(isotope, "mass-number"),
bl: try-at(isotope, "atomic-number"),
)
}

#let display-group(data) = {
let children = data.at("children", default:())
let kind = data.at("kind", default: 1)
math.attach(
math.lr({
get-bracket(kind, open:true)
for child in children {
if child.type == "content" {
child.body
} else if child.type == "element" {
display-element(child)
}else if data.type == "align"{
$&$
} else if child.type == "group" {
display-group(child)
}
}
get-bracket(kind, open:false)
}),
tr: charge-to-content(data.at("charge", default: none)),
br: count-to-content(data.at("count", default: none)),
)
}

#let display-molecule(data) = {
count-to-content(data.at("count", default: none))
math.attach(
[
#let children = data.at("children", default:())
#for child in children {
if child.type == "content"{
child.body
} else if data.type == "align"{
$&$
} else if child.type == "element"{
display-element(child)
} else if child.type == "group" {
display-group(child)
}
}
],
tr: charge-to-content(data.at("charge", default: none)),
// br: phase-to-content(data.at("phase", default:none)),
)
context {
text(phase-to-content(data.at("phase", default:none)), size: text.size * 0.75)
}
}

#let display-ir(data) = {
if data== none{
none
} else if type(data) == array{
for value in data {
display-ir(value)
//this removes spacing for groups that have long charges (looks better)
if value.type == "molecule" {
let last = value.children.last()
if last.type == "group" and (last.at("charge", default: none) != none or last.at("count", default: none) != none){
h(-0.4em)
}
}
}
}else{
if data.type == "molecule" {
display-molecule(data)
} else if data.type == "+"{
h(0.4em, weak: true)
math.plus
h(0.4em, weak: true)
} else if data.type == "group"{
display-group(data)
} else if data.type == "element"{
display-element(data)
} else if data.type == "content"{
data.body
}else if data.type == "align"{
$&$
} else if data.type == "arrow"{
h(0.4em, weak: true)
let top = display-ir(data.at("top", default:none))
let bottom = display-ir(data.at("bottom", default:none))
math.attach(
math.stretch(
get-arrow(data.at("kind", default:0)),
size: 100% + 2em
),
t: top,
b: bottom,
)
h(0.4em, weak: true)
}
}
}
76 changes: 76 additions & 0 deletions src/display-shell-configuration.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#import "utils.typ": get-element-dict, shell-capacities, orbital-capacities

#let get-shell-configuration(element)={
element = get-element-dict(element)
let charge = element.at("charge", default:0)
let electron-amount = element.atomic-number - charge

let result = ()
for value in shell-capacities {
if electron-amount <= 0{
break
}

if electron-amount >= value.at(1){
result.push(value)
electron-amount -= value.at(1)
} else {
result.push((value.at(0), electron-amount))
electron-amount = 0
}
}
return result
}

//TODO: fix Cr and Mo
#let get-electron-configuration(element)={
element = get-element-dict(element)
let charge = element.at("charge", default:0)
let electron-amount = element.atomic-number - charge

let result = ()
for value in orbital-capacities {
if electron-amount <= 0{
break
}
if electron-amount >= value.at(1){
result.push(value)
electron-amount -= value.at(1)
} else {
result.push((value.at(0), electron-amount))
electron-amount = 0
}
}
return result
}

#let display-electron-configuration(element, short: false)={
let configuration = get-electron-configuration(element)

if short{
let prefix = ""
if configuration.at(14, default: (0,0)).at(1) == 6{
configuration = configuration.slice(15)
prefix = "[Rn]"
} else if configuration.at(10, default: (0,0)).at(1) == 6{
configuration = configuration.slice(11)
prefix = "[Xe]"
} else if configuration.at(7, default: (0,0)).at(1) == 6{
configuration = configuration.slice(8)
prefix = "[Kr]"
} else if configuration.at(4, default: (0,0)).at(1) == 6{
configuration = configuration.slice(5)
prefix = "[Ar]"
} else if configuration.at(2, default: (0,0)).at(1) == 6{
configuration = configuration.slice(3)
prefix = "[Ne]"
} else if configuration.at(0, default: (0,0)).at(1) == 2{
configuration = configuration.slice(1)
prefix = "[He]"
}

return prefix + configuration.map(x=> $#x.at(0)^#str(x.at(1))$).sum()
} else{
return configuration.map(x=> $#x.at(0)^#str(x.at(1))$).sum()
}
}
Loading
Loading