Skip to content

Commit

Permalink
Add carbonateconversion!, clean up other metal/oxide conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
brenhinkeller committed Mar 6, 2024
1 parent 70a5081 commit 7bd7280
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 62 deletions.
119 changes: 75 additions & 44 deletions src/utilities/Geochemistry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,84 +206,115 @@

"""
```julia
dataset = oxideconversion(dataset::Dict; unitratio::Number=10000)
converted_dataset = oxideconversion(dataset::Union{Dict,NamedTuple}; unitratio::Number=10000)
```
Convert major elements (Ti, Al, etc.) into corresponding oxides (TiO2, Al2O3, ...).
If metals are as PPM, set unitratio=10000 (default); if metals are as wt%,
set unitratio = 1
As `oxideconversion!`, but returning a copy rather than modifying in-place
"""
function oxideconversion(dataset::Dict; unitratio::Number=10000)
result = copy(dataset)
return oxideconversion!(result)
end
oxideconversion(ds::Union{Dict,NamedTuple}; kwargs...) = oxideconversion!(deepcopy(ds); kwargs...)
export oxideconversion

"""
```julia
dataset = oxideconversion!(dataset::Dict; unitratio::Number=10000)
oxideconversion!(dataset::Dict; unitratio::Number=10000)
```
Convert major elements (Ti, Al, etc.) into corresponding oxides (TiO2, Al2O3, ...) in place.
Convert major elements (Ti, Al, etc.) into corresponding oxides (TiO2, Al2O3, ...) in place if extant.
If metals are as PPM, set unitratio=10000 (default); if metals are as wt%,
If metals are expected as PPM, set unitratio=10000 (default); if metals are as wt%,
set unitratio = 1
"""
function oxideconversion!(dataset::Dict; unitratio::Number=10000)
# Convert major elements (Ti, Al, etc.) into corresponding oxides (TiO2, Al2O3)...
# for i ∈ eachindex(source)
# conversionfactor(i)=mass.percation.(dest[i])./mass.(source[i]);
# end
# Array of elements to convert
source = ("Si","Ti","Al","Fe","Fe","Mg","Ca","Mn","Li","Na","K","P","Cr","Ni","Co","C","S","H")
dest = ("SiO2","TiO2","Al2O3","FeOT","Fe2O3T","MgO","CaO","MnO","Li2O","Na2O","K2O","P2O5","Cr2O3","NiO","CoO","CO2","SO2","H2O")
See also `oxideconversion`, c.f. `metalconversion!`
"""
function oxideconversion!(dataset::NamedTuple; unitratio::Number=10000)
# List of elements to convert
source = (:Si, :Ti, :Al, :Fe, :Fe, :Mg, :Ca, :Mn, :Li, :Na, :K, :P, :Cr, :Ni, :Co, :C, :S, :H)
dest = (:SiO2, :TiO2, :Al2O3, :FeOT, :Fe2O3T, :MgO, :CaO, :MnO, :Li2O, :Na2O, :K2O, :P2O5, :Cr2O3, :NiO, :CoO, :CO2, :SO2, :H2O)
conversionfactor = (2.13932704290547,1.66847584248889,1.88944149488507,1.28648836426407,1.42973254639611,1.65825961736268,1.39919258253823,1.29121895771597,2.1526657060518732,1.34795912485574,1.20459963614796,2.29133490474735,1.46154369861159,1.27258582901258,1.27147688434143,3.66405794688203,1.99806612601372,8.93601190476191)

# If source field exists, fill in destination from source
for i eachindex(source)
if haskey(dataset, source[i])
if ~haskey(dataset, dest[i]) # If destination field doesn't exist, make it.
dataset[dest[i]] = fill(NaN, size(dataset[source[i]]))
if haskey(dataset, "elements")
dataset["elements"] = dataset["elements"] (dest[i],)
end
if haskey(dataset, dest[i]) # If destination field doesn't exist, make it.
oxide, metal = dataset[dest[i]], dataset[source[i]]
fillifnan!(oxide, metal, conversionfactor[i]/unitratio)
end
oxide = dataset[dest[i]]
metal = dataset[source[i]]
fillifnan!(oxide, metal, conversionfactor[i]/unitratio)
end
end

return dataset
end
oxideconversion!(ds::Dict; kwargs...) = (oxideconversion!(TupleDataset(ds); kwargs...); ds)
export oxideconversion!

function oxideconversion!(dataset::NamedTuple; unitratio::Number=10000)
# Convert major elements (Ti, Al, etc.) into corresponding oxides (TiO2, Al2O3)...
# for i ∈ eachindex(source)
# conversionfactor(i)=mass.percation.(dest[i])./mass.(source[i]);
# end

# Array of elements to convert
source = (:Si, :Ti, :Al, :Fe, :Fe, :Mg, :Ca, :Mn, :Li, :Na, :K, :P, :Cr, :Ni, :Co, :C, :S, :H)
dest = (:SiO2, :TiO2, :Al2O3, :FeOT, :Fe2O3T, :MgO, :CaO, :MnO, :Li2O, :Na2O, :K2O, :P2O5, :Cr2O3, :NiO, :CoO, :CO2, :SO2, :H2O)
conversionfactor = (2.13932704290547,1.66847584248889,1.88944149488507,1.28648836426407,1.42973254639611,1.65825961736268,1.39919258253823,1.29121895771597,2.1526657060518732,1.34795912485574,1.20459963614796,2.29133490474735,1.46154369861159,1.27258582901258,1.27147688434143,3.66405794688203,1.99806612601372,8.93601190476191)
"""
```julia
converted_dataset = metalconversion(dataset::Union{Dict,NamedTuple}; unitratio::Number=10000)
```
As `metalconversion!`, but returning a copy rather than modifying in-place
"""
metalconversion(ds::Union{Dict,NamedTuple}; kwargs...) = metalconversion!(copy(ds); kwargs...)
export metalconversion

"""
```julia
dataset = metalconversion!(dataset::Union{Dict,NamedTuple}; unitratio::Number=10000)
```
Convert minor element oxides (MnO, Cr2O3, NiO, ...) into corresponding metals (Mn, Cr, Ni, ...) in place if extant.
If metals are expected as parts per million (ppm), set unitratio=10000 (default); if metals are as wt%, set unitratio = 1
See also `metalconversion`, c.f. `oxideconversion!`
"""
function metalconversion!(dataset::NamedTuple; unitratio::Number=10000)
# List of elements to convert
dest = (:Mn, :P, :Cr, :Ni, :Co, :Sr, :Ba, :Li, :S,)
source = (:MnO, :P2O5, :Cr2O3, :NiO, :CoO, :SrO, :BaO, :Li2O, :SO3)
conversionfactor = (0.7744619872751028, 0.4364268173666496, 0.6842080746199798, 0.785801615263874, 0.786486968277016, 0.8455993051534453, 0.8956541815613328, 0.46454031259412965, 0.4004646689233921)

# If source field exists, fill in destination from source
for i eachindex(source)
if haskey(dataset, source[i])
if haskey(dataset, dest[i]) # If destination field doesn't exist, make it.
oxide = dataset[dest[i]]
metal = dataset[source[i]]
fillifnan!(oxide, metal, conversionfactor[i]/unitratio)
metal, oxide = dataset[dest[i]], dataset[source[i]]
fillifnan!(metal, oxide, conversionfactor[i]*unitratio)
end
end
end

return dataset
end
export oxideconversion!
metalconversion!(ds::Dict; kwargs...) = (metalconversion!(TupleDataset(ds); kwargs...); ds)
export metalconversion!



"""
```julia
carbonateconversion!(dataset::NamedTuple)
```
Convert carbonates (CaCO3, MgCO3) into corresponding metal oxides and CO2 if extant, in place.
"""
function carbonateconversion!(ds::NamedTuple)
source = (:CaCO3, :CaCO3, :MgCO3, :MgCO3, :TIC,)
dest = (:CaO, :CO2, :MgO, :CO2, :CO2)
conversionfactor = (0.5602899095181764, 0.43971009048182363, 0.4780282993132732, 0.5219717006867268, 3.664057946882025)
if haskey(ds, :CaCO3) && haskey(ds, :MgCO3)
t = .!(isnan.(ds.CaCO3) .| isnan.(ds.MgCO3))
if haskey(ds, :CO2)
fillifnan!(ds.CO2, ds.CaCO3*0.43971009048182363 .+ ds.MgCO3*0.5219717006867268, 1)
end
end
for i in eachindex(source)
if haskey(ds, source[i])
if haskey(ds, dest[i])
d, s = ds[dest[i]], ds[source[i]]
fillifnan!(d, s, conversionfactor[i])
end
end
end
return ds
end
carbonateconversion!(ds::Dict) = (carbonateconversion!(TupleDataset(ds)); ds)
export carbonateconversion!

## --- Chemical Index of Alteration

# Chemical Index of Alteration as defined by Nesbitt and Young, 1982
Expand Down
10 changes: 5 additions & 5 deletions src/utilities/Import.jl
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@
La = Vector{Float64}(100,) [0.44094669199955616 ... 0.5371416189174069]
```
"""
function TupleDataset(d::Dict, elements=keys(d))
function TupleDataset(d::Dict, elements=haskey(d,"elements") ? d["elements"] : keys(d))
symbols = symboltuple(sanitizevarname.(elements))
return NamedTuple{symbols}(d[e] for e in elements)
end
Expand Down Expand Up @@ -794,14 +794,14 @@
```
"""
concatenatedatasets(args...; kwargs...) = concatenatedatasets((args...,); kwargs...)
function concatenatedatasets(dst::Tuple; elements=Symbol[])
function concatenatedatasets(dst::Tuple; kwargs...)
if length(dst) == 1
only(dst)
elseif length(dst) == 2
concatenatedatasets(dst[1], dst[2]; elements)
concatenatedatasets(dst[1], dst[2]; kwargs...)
else
c = concatenatedatasets(dst[1], dst[2]; elements)
concatenatedatasets((c, dst[3:end]...); elements)
c = concatenatedatasets(dst[1], dst[2]; kwargs...)
concatenatedatasets((c, dst[3:end]...); kwargs...)
end
end
function concatenatedatasets(d1::AbstractDict, d2::AbstractDict; elements=String[])
Expand Down
39 changes: 26 additions & 13 deletions test/testGeochemistry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
@test eustar(34.7773, 6.5433, 5.9037, 0.8904) 2.0825737578695205

# Iron oxide conversions

@test feoconversion(3.5, NaN, NaN, NaN) == 3.5
@test feoconversion(3.5, NaN, 7.5, NaN) == 7.5
@test feoconversion(3.5, NaN, 7.5, 10) == 7.5
Expand All @@ -15,24 +14,38 @@
@test isnan(feoconversion(NaN, NaN, NaN, NaN))

# Other oxide conversion
D = elementify(["Fe" "Mg" "Ca" "P"; 10000 10000 10000 10000; 10000 10000 10000 10000], importas=:Dict)
D = oxideconversion(D)
@test all(D["FeOT"] .≈ (molarmass["Fe"]+molarmass["O"])/molarmass["Fe"])
@test all(D["MgO"] .≈ (molarmass["Mg"]+molarmass["O"])/molarmass["Mg"])
@test all(D["CaO"] .≈ (molarmass["Ca"]+molarmass["O"])/molarmass["Ca"])
@test all(D["P2O5"] .≈ (molarmass["P"]+2.5*molarmass["O"])/molarmass["P"])


DT = elementify(unelementify(D), importas=:Tuple)
for e in (:FeOT, :MgO, :CaO, :P2O5)
DT[e] .= NaN
end
D = ["Fe" "Mg" "Ca" "P" "FeOT" "MgO" "CaO" "P2O5"; 10000 10000 10000 10000 NaN NaN NaN NaN; 10000 10000 10000 10000 NaN NaN NaN NaN]
M = elementify(D, importas=:Dict)
O = oxideconversion(M)
@test all(O["FeOT"] .≈ (molarmass["Fe"]+molarmass["O"])/molarmass["Fe"])
@test all(O["MgO"] .≈ (molarmass["Mg"]+molarmass["O"])/molarmass["Mg"])
@test all(O["CaO"] .≈ (molarmass["Ca"]+molarmass["O"])/molarmass["Ca"])
@test all(O["P2O5"] .≈ (molarmass["P"]+2.5*molarmass["O"])/molarmass["P"])

DT = deepcopy(TupleDataset(M))
oxideconversion!(DT)
@test all(DT.FeOT .≈ (molarmass["Fe"]+molarmass["O"])/molarmass["Fe"])
@test all(DT.MgO .≈ (molarmass["Mg"]+molarmass["O"])/molarmass["Mg"])
@test all(DT.CaO .≈ (molarmass["Ca"]+molarmass["O"])/molarmass["Ca"])
@test all(DT.P2O5 .≈ (molarmass["P"]+2.5*molarmass["O"])/molarmass["P"])

D = ["NiO" "CoO" "BaO" "SO3" "Ni" "Co" "Ba" "S"; 1 1 1 1 NaN NaN NaN NaN; 1 1 1 1 NaN NaN NaN NaN]
O = elementify(D, importas=:Dict)
M = metalconversion(O)
@test all(M["Ni"] .≈ 10000molarmass["Ni"]/(molarmass["Ni"]+molarmass["O"]))
@test all(M["Co"] .≈ 10000molarmass["Co"]/(molarmass["Co"]+molarmass["O"]))
@test all(M["Ba"] .≈ 10000molarmass["Ba"]/(molarmass["Ba"]+molarmass["O"]))
@test all(M["S"] .≈ 10000molarmass["S"]/(molarmass["S"]+3molarmass["O"]))


# Carbonate conversions
D = ["CaCO3" "MgCO3" "CaO" "MgO" "CO2"; 1 1 NaN NaN NaN; 1 1 NaN NaN NaN]
C = elementify(D, importas=:Dict)
carbonateconversion!(C)
@test all(C["MgO"] .≈ (molarmass["Mg"]+molarmass["O"])/(molarmass["Mg"]+molarmass["C"]+3molarmass["O"]))
@test all(C["CaO"] .≈ (molarmass["Ca"]+molarmass["O"])/(molarmass["Ca"]+molarmass["C"]+3molarmass["O"]))
@test all(C["CO2"] .≈ (molarmass["C"]+2molarmass["O"])/(molarmass["Ca"]+molarmass["C"]+3molarmass["O"]) + (molarmass["C"]+2molarmass["O"])/(molarmass["Mg"]+molarmass["C"]+3molarmass["O"]))

# Weathering indices
@test CIA(14.8577, 4.5611, 3.29641, 2.3992) 47.66582778067264
@test WIP(3.2964, 4.5611, 2.3992, 5.9121) 78.40320264846837
Expand Down

0 comments on commit 7bd7280

Please sign in to comment.