Page MenuHomec4science

database.pyx
No OneTemporary

File Metadata

Created
Mon, Jun 10, 16:27

database.pyx

#Copyright (c) 2014,2015 Fabien Georget <fabieng@princeton.edu>, Princeton
#University #All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions are met:
#
#1. Redistributions of source code must retain the above copyright notice, this
#list of conditions and the following disclaimer.
#
#2. Redistributions in binary form must reproduce the above copyright notice,
#this list of conditions and the following disclaimer in the documentation
#and/or other materials provided with the distribution.
#
#3. Neither the name of the copyright holder nor the names of its contributors
#may be used to endorse or promote products derived from this software without
#specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
#FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from libcpp.map cimport map as cmap
from libcpp.pair cimport pair
from memory cimport shared_ptr
from database cimport Database, DataContainer
# Used to indicate that something does not exist
cdef enum:
no_species = -1
cdef class DatabaseManager:
""" The Python database handler
Use this class for checking values in the database
or switching basis/dropping minerals or components...
"""
def __cinit__(self, bytes filepath):
"""filepath is the path to the database file"""
if filepath:
self.database = new Database(<string> filepath)
else:
self.database = new Database()
self.container = self.database.get_database()
def __dealloc__(self):
del self.database
cdef void init_database(self, shared_ptr[DataContainer] raw_db):
del self.database
self.database = new Database(raw_db)
self.container = self.database.get_database()
cdef shared_ptr[DataContainer] get_raw_db(self):
return self.container
cdef DataContainer* _get(self):
return self.container.get()
def is_valid(self):
"""Return true of the database is valid.
If it isn't valid, something went wrong.
"""
return self.database.is_valid();
def remove_sorbed_species(self):
"""Remove all the sorbed species in the database."""
self.database.remove_sorbed_species()
def add_sorbed_species(self, bytes input_sorbed):
""" Add sorbed species in the database
'input_sporbed' is a JSON list of sorbed species formatted as in the
database
"""
self.database.add_sorbed_species(input_sorbed)
# --------- #
# Component #
# --------- #
property nb_component:
"""The number of component in the database."""
def __get__(self): return self._get().nb_component()
def component_check_bounds(self, int idc):
"""Check if 'idc' is a correct index for a component.
Raises a ValueError if it is not correct."""
if (idc >= self.nb_component or idc < 0):
raise ValueError("'"+str(idc)+
"' is not a valid index for a component")
# labels and id
# --------------
def component_label_to_id(self, bytes comp):
""" Return the id of a component."""
cdef int ans = self.database.component_label_to_id(comp)
if (ans == no_species):
raise ValueError("Species : '"+comp.decode()+"' is not a component.")
return ans
def component_id_to_label(self, int idc):
""" Return the label of a component."""
if (idc >= self.nb_component):
raise ValueError("'"+str(idc)+
"' is not a valid index for a component")
return bytes(self._get().get_label_component(idc))
def print_basis(self):
"""Print the components in the basis, with their ID"""
print("The basis is :")
cdef int i
for i in range(self.nb_component):
print(" - "+str(i)+" : "+bytes(self._get().get_label_component(i)).decode())
# basis switch
# ------------
def swap_components(self, dict swapping):
"""Swap components in the basis
swapping is a dictionnary where the keys are the labels of the
components to replace and the values are the labels of the secondary
species to add in the basis.
Raise value errors if the labels are invalid
"""
cdef cmap[string,string] input_map
cdef int idsp
for (key, value) in swapping.items():
idsp = self.component_label_to_id(key)
if (idsp == no_species):
raise ValueError("'"+key.decode()+"' is not a valid component.")
idsp = self.aqueous_label_to_id(value)
if (idsp == no_species):
raise ValueError("'"+key.decode()+
"' is not a valid secondary aqueous species.")
input_map.insert(pair[string, string](key, value))
self.database.swap_components(input_map)
# --------------- #
# Aqueous species #
# --------------- #
property nb_aqueous:
"""The number of secondary aqueous species in the database"""
def __get__(self): return self._get().nb_aqueous()
def aqueous_check_bounds(self, int ida):
"""Check if 'ida' is a correct index for a secondary aqueous species.
Raises a ValueError if it is not correct."""
if (ida >= self.nb_aqueous or ida < 0):
raise ValueError("'"+str(ida)+
"' is not a valid index for a secondary aqueous species")
# labels and id
# -------------
def aqueous_id_to_label(self, int ids):
self.aqueous_check_bounds(ids)
return bytes(self._get().get_label_aqueous(ids))
def aqueous_label_to_id(self, bytes aqueous):
""" Return the id of a secondary aqueous species"""
cdef int ans = self.database.aqueous_label_to_id(aqueous)
if (ans == no_species):
raise ValueError("Species : '"+aqueous.decode()+
"' is not a secondary aqueous species.")
return ans
def print_aqueous(self):
""" Print the secondary species"""
print("The secondary aqueous species are :")
cdef int i
for i in range(self.nb_aqueous):
print(" - "+str(i)+" : "+bytes(self._get().get_label_aqueous(i)).decode())
# properties
# ----------
def nu_aqueous(self, int ids, int idc):
"""Return stoichiometric coefficient of a secondary aqueous species"""
self.aqueous_check_bounds(ids)
self.component_check_bounds(idc)
return self._get().nu_aqueous(ids, idc)
def l_nu_aqueous(self, bytes aqueous, bytes component):
"""Return stoichiometric coefficient of a secondary aqueous species"""
return self._get().nu_aqueous(self.aqueous_label_to_id(aqueous),
self.component_label_to_id(component))
def logk_aqueous(self, int ids):
"""Return the equilibrium constant of a secondary aqueous species"""
self.aqueous_check_bounds(ids)
return self._get().logk_aqueous(ids)
def l_logk_aqueous(self, bytes aqueous):
"""Return the equilibrium constant of a secondary aqueous species"""
return self._get().logk_aqueous(self.aqueous_label_to_id(aqueous))
# ------------ #
# Solid phases #
# ------------ #
def remove_solid_phases(self):
"""Remove all the solid phases in the database.
"""
self.database.remove_solid_phases()
def add_solid_phases(self, bytes input_solid_phases):
"""Add some solid phases in the database.
'input_sorbed' is a JSON list of minerals formatted as in the database
"""
self.database.add_solid_phases(input_solid_phases)
# Equilibrium
# =======
property nb_mineral:
"""The number of mineral in the database"""
def __get__(self):
return self._get().nb_mineral()
def mineral_check_bounds(self, int idm):
"""Check if 'idm' is a correct index for a mineral.
Raises a ValueError if it is not correct."""
if (idm >= self.nb_mineral or idm < 0):
raise ValueError("'"+str(idm)+
"' is not a valid index for a mineral")
# labels and id
# -------------
def mineral_id_to_label(self, int idm):
"""Return the label of a mineral."""
self.mineral_check_bounds(idm)
return bytes(self._get().get_label_mineral(idm))
def mineral_label_to_id(self, bytes mineral):
""" Return the id of a mineral"""
ans = self.database.mineral_label_to_id(mineral)
if (ans == no_species):
raise ValueError("Species : '"+mineral.decode()+
"' is not a solid phase at equilibrium in the database.")
return ans
def print_minerals(self):
""" Print the solid phases at equilibrium"""
print("The solid phases at equilibrium are :")
cdef int i
for i in range(self.nb_mineral):
print(" - "+str(i)+" : "+bytes(self._get().get_label_mineral(i)).decode())
def minerals_keep_only(self, list_mineral_to_keep):
"""Keep only the solid phases in 'list_mineral_to_keep'"""
# we first verify that the list is valid
for label in list_mineral_to_keep:
self.mineral_label_to_id(label) # just check that it is not raising any error
self.database.minerals_keep_only(list_mineral_to_keep)
# properties
# ----------
def logk_mineral(self, int idm):
"""Return the equilibrium constant of a solid phase"""
self.mineral_check_bounds(idm)
return self._get().logk_mineral(idm)
def l_logk_mineral(self, bytes mineral):
"""Return the equilibrium constant of a solid phase"""
return self._get().logk_mineral(self.mineral_label_to_id(mineral))
def nu_mineral(self, int idm, int idc):
"""Return stoichometric coefficient of a mineral"""
self.mineral_check_bounds(idm)
self.component_check_bounds(idc)
return self._get().nu_mineral(idm, idc)
def l_nu_mineral(self, bytes mineral, bytes component):
"""Return stoichometric coefficient of a mineral"""
return self._get().nu_mineral(self.mineral_label_to_id(mineral),
self.component_label_to_id(component))
def molar_mass_mineral(self, int idm):
"""Return the molar mass (kg/mol) of mineral 'idm'"""
self.mineral_check_bounds(idm)
return self._get().molar_mass_mineral(idm)
def l_molar_mass_mineral(self, bytes mineral):
"""Return the molar mass (kg/mol) of 'mineral'"""
return self.molar_mass_mineral(self.mineral_label_to_id(mineral))
def molar_volume_mineral(self, int idm):
"""Return the molar volume (m^3/mol) of mineral 'idm'"""
self.mineral_check_bounds(idm)
return self._get().molar_volume_mineral(idm)
def l_molar_volume_mineral(self, bytes mineral):
"""Return the molar volume (m^3/mol) of 'mineral'"""
return self.molar_volume_mineral(self.mineral_label_to_id(mineral))
def density_mineral(self, int idm):
"""Return the density (kg/m^3) of a mineral 'idm'"""
return (self._get().molar_mass_mineral(idm)
/ self._get().molar_volume_mineral(idm))
def l_density_mineral(self, bytes mineral):
"""Return the density (kg/m^3) of a mineral 'idm'"""
return self.density_mineral(
self.mineral_label_to_id(mineral))
# Kinetic
# ===========
property nb_mineral_kinetic:
"""The number of mineral governed by kinetics in the database"""
def __get__(self):
return self._get().nb_mineral_kinetic()
def mineral_kinetic_check_bounds(self, int idm):
"""Check if 'idm' is a correct index for a kinetic mineral.
Raises a ValueError if it is not correct."""
if (idm >= self.nb_mineral_kinetic or idm < 0):
raise ValueError("'"+str(idm)+
"' is not a valid index for a kinetic mineral")
# labels and id
# -------------
def mineral_kinetic_id_to_label(self, int idm):
"""Return the label of a mineral."""
self.mineral_kinetic_check_bounds(idm)
return bytes(self._get().get_label_mineral_kinetic(idm))
def mineral_kinetic_label_to_id(self, bytes mineral):
""" Return the id of a mineral governed by kinetics"""
ans = self.database.mineral_kinetic_label_to_id(mineral)
if (ans == no_species):
raise ValueError("Species : '"+mineral.decode()+
"' is not a solid phase at equilibrium in the database.")
return ans
def print_minerals_kinetic(self):
""" Print the solid phases governed by kinetic"""
print("The solid phases governed by kinetics law are :")
cdef int i
for i in range(self.nb_mineral_kinetic):
print(" - "+str(i)+" : "+bytes(self._get().get_label_mineral_kinetic(i)).decode())
# properties
# ----------
def logk_mineral_kinetic(self, int idm):
"""Return the equilibrium constant of a solid phase"""
self.mineral_kinetic_check_bounds(idm)
return self._get().logk_mineral_kinetic(idm)
def l_logk_mineral_kinetic(self, bytes mineral):
"""Return the equilibrium constant of a solid phase"""
return self._get().logk_mineral_kinetic(
self.mineral_kinetic_label_to_id(mineral))
def nu_mineral_kinetic(self, int idm, int idc):
"""Return stoichometric coefficient of a mineral"""
self.mineral_kinetic_check_bounds(idm)
self.component_check_bounds(idc)
return self._get().nu_mineral_kinetic(idm, idc)
def l_nu_mineral_kinetic(self, bytes mineral, bytes component):
"""Return stoichometric coefficient of a mineral"""
return self._get().nu_mineral_kinetic(
self.mineral_kinetic_label_to_id(mineral),
self.component_label_to_id(component))
def molar_mass_mineral_kinetic(self, int idm):
"""Return the molar mass (kg/mol) of mineral 'idm'"""
self.mineral_kinetic_check_bounds(idm)
return self._get().molar_mass_mineral_kinetic(idm)
def l_molar_mass_mineral_kinetic(self, bytes mineral):
"""Return the molar mass (kg/mol) of 'mineral'"""
return self.molar_mass_mineral_kinetic(
self.mineral_kinetic_label_to_id(mineral))
def molar_volume_mineral_kinetic(self, int idm):
"""Return the molar volume (m^3/mol) of mineral 'idm'"""
self.mineral_kinetic_check_bounds(idm)
return self._get().molar_volume_mineral_kinetic(idm)
def l_molar_volume_mineral_kinetic(self, bytes mineral):
"""Return the molar volume (m^3/mol) of 'mineral'"""
return self.molar_volume_mineral_kinetic(
self.mineral_kinetic_label_to_id(mineral))
def density_mineral_kinetic(self, int idm):
"""Return the density (kg/m^3) of a mineral 'idm'"""
return (self._get().molar_mass_mineral_kinetic(idm)
/ self._get().molar_volume_mineral_kinetic(idm))
def l_density_mineral_kinetic(self, bytes mineral):
"""Return the density (kg/m^3) of a mineral 'idm'"""
return self.density_mineral_kinetic(
self.mineral_kinetic_label_to_id(mineral))
# ========
# Gas
# ========
property nb_gas:
"""The number of gas in the database"""
def __get__(self):
return self._get().nb_gas()
def gas_check_bounds(self, int idg):
"""Check if idg is a correct index for a gas"""
if (idg >= self.nb_gas or idg < 0):
raise ValueError("'"+str(idg)+
"' is not a valid index for a gas")
def gas_id_to_label(self, int idg):
"""Return the label of a gas."""
self.gas_check_bounds(idg)
return bytes(self._get().get_label_gas(idg))
def gas_label_to_id(self, bytes gas):
""" Return the id of a mineral"""
ans = self.database.gas_label_to_id(gas)
if (ans == no_species):
raise ValueError("Species : '"+gas.decode()+
"' is not a gas in the database.")
return ans
def remove_gas_phases(self):
"""Remove all the gas species in the database."""
self.database.remove_gas_phases()
def add_gas_phases(self, bytes input_gas_phases):
"""Add some gas into the database.
'input_gas_phases' is a JSON list of gas formatted as in the database.
"""
self.database.add_gas_phases(input_gas_phases)
def nu_gas(self, int idg, int idc):
"""Return the stoichiometric coefficient of 'idc' in 'idg' dissolution
reaction."""
self.gas_check_bounds(idg)
self.component_check_bounds(idc)
return self._get().nu_gas(idg, idc)
def l_nu_gas(self, bytes gas, bytes component):
"""Return the stoihiometric coefficient of 'component' in the 'gas'
dissolution reaction."""
return self._get().nu_gas(self.gas_label_to_id(gas),
self.component_label_to_id(component))
def logk_gas(self, int idg):
"""Return the equilibrium constant of a gas"""
self.gas_check_bounds(idg)
if (idg >= self.nb_gas or idg < 0):
raise ValueError("Index : '"+str(idg)
+"'is not valid for a gas.")
return self._get().logk_mineral(idg)
def l_logk_gas(self, bytes gas):
"""Return the equilibrium constant of a gas"""
return self._get().logk_gas(self.gas_label_to_id(gas))

Event Timeline