Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F90363248
yaml_reader.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, Oct 31, 23:57
Size
27 KB
Mime Type
text/x-c
Expires
Sat, Nov 2, 23:57 (2 d)
Engine
blob
Format
Raw Data
Handle
22062458
Attached To
rSPECMICP SpecMiCP / ReactMiCP
yaml_reader.cpp
View Options
/* =============================================================================
Copyright (c) 2014 - 2016
F. 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. *
============================================================================= */
#include "config_database.hpp"
#include "yaml_reader.hpp"
#include "reader_common.hpp"
#include "section_name.hpp"
#include "specmicp_common/io/yaml.hpp"
#include "specmicp_common/log.hpp"
#include "specmicp_common/compat.hpp"
#include <yaml-cpp/yaml.h>
#include <fstream>
#include <iostream>
namespace
specmicp
{
namespace
database
{
struct
DataReaderYaml
::
DataReaderYamlElementData
{
bool
check_compo
{
true
};
std
::
vector
<
element_map
>
components
;
bool
is_init
{
false
};
};
DataReaderYaml
::
DataReaderYaml
()
:
m_elem_data
(
make_unique
<
DataReaderYamlElementData
>
())
{}
DataReaderYaml
::~
DataReaderYaml
()
=
default
;
//! \brief Constructor
DataReaderYaml
::
DataReaderYaml
(
RawDatabasePtr
data
,
bool
check_compo
)
:
DatabaseModule
(
data
),
m_elem_data
(
make_unique
<
DataReaderYamlElementData
>
())
{
m_elem_data
->
check_compo
=
check_compo
;
}
//! \brief Constructor
//!
//! @param filepath string containing the path to the database
DataReaderYaml
::
DataReaderYaml
(
const
std
::
string
&
filepath
,
bool
check_compo
)
:
DatabaseModule
(),
m_elem_data
(
make_unique
<
DataReaderYamlElementData
>
())
{
m_elem_data
->
check_compo
=
check_compo
;
parse
(
filepath
);
}
//! \brief Constructor
//!
//! @param input input stream that contains the database
DataReaderYaml
::
DataReaderYaml
(
std
::
istream
&
input
,
bool
check_compo
)
:
DatabaseModule
(),
m_elem_data
(
make_unique
<
DataReaderYamlElementData
>
())
{
m_elem_data
->
check_compo
=
check_compo
;
parse
(
input
);
}
// safe access to values
//! \brief Return a label
std
::
string
obtain_label
(
const
YAML
::
Node
&
species
,
const
std
::
string
&
section
);
//! \brief Set a composition
void
obtain_composition
(
const
YAML
::
Node
&
species
,
std
::
map
<
std
::
string
,
scalar_t
>&
compo
,
const
std
::
string
&
label
);
//! \brief Return an equilibrium constant
scalar_t
obtain_logk
(
const
YAML
::
Node
&
species
,
const
std
::
string
&
label
);
//! \brief Return true if the
bool
is_kinetic
(
const
YAML
::
Node
&
species
,
const
std
::
string
&
label
);
void
DataReaderYaml
::
parse
(
const
std
::
string
&
filepath
)
{
std
::
ifstream
datafile
(
filepath
,
std
::
ifstream
::
binary
);
if
(
not
datafile
.
is_open
())
{
ERROR
<<
"Database not found : "
<<
filepath
;
std
::
string
message
(
"Database not found : "
+
filepath
);
throw
std
::
invalid_argument
(
message
);
}
data
->
metadata
.
path
=
filepath
;
parse
(
datafile
);
datafile
.
close
();
}
void
DataReaderYaml
::
parse
(
std
::
istream
&
input
)
{
const
YAML
::
Node
root
=
io
::
parse_yaml
(
input
);
const
char
*
indb_main
=
INDB_MAIN
;
io
::
check_mandatory_yaml_node
(
root
,
INDB_SECTION_METADATA
,
indb_main
);
parse_metadata
(
root
[
INDB_SECTION_METADATA
]);
// Basis
// ------
io
::
check_mandatory_yaml_node
(
root
,
INDB_SECTION_BASIS
,
indb_main
);
parse_basis
(
root
[
INDB_SECTION_BASIS
]);
// Elements
// --------
io
::
check_mandatory_yaml_node
(
root
,
INDB_SECTION_ELEMENTS
,
indb_main
);
ElementList
elements
;
parse_elements
(
root
[
INDB_SECTION_ELEMENTS
],
elements
);
data
->
elements
=
std
::
move
(
elements
);
// Aqueous species
// ---------------
io
::
check_mandatory_yaml_node
(
root
,
INDB_SECTION_AQUEOUS
,
indb_main
);
AqueousList
alist
;
parse_aqueous
(
root
[
INDB_SECTION_AQUEOUS
],
alist
);
data
->
aqueous
=
std
::
move
(
alist
);
// Minerals
// --------
io
::
check_mandatory_yaml_node
(
root
,
INDB_SECTION_MINERALS
,
indb_main
);
MineralList
minerals
,
minerals_kinetic
;
parse_minerals
(
root
[
INDB_SECTION_MINERALS
],
minerals
,
minerals_kinetic
);
data
->
minerals
=
std
::
move
(
minerals
);
data
->
minerals_kinetic
=
std
::
move
(
minerals_kinetic
);
// Gas
// ----
GasList
glist
(
0
,
data
->
nb_component
());
if
(
root
[
INDB_SECTION_GAS
])
parse_gas
(
root
[
INDB_SECTION_GAS
],
glist
);
else
glist
.
set_valid
();
data
->
gas
=
std
::
move
(
glist
);
// Sorbed species
// --------------
SorbedList
slist
(
0
,
data
->
nb_component
());
if
(
root
[
INDB_SECTION_SORBED
])
parse_sorbed
(
root
[
INDB_SECTION_SORBED
],
slist
);
else
slist
.
set_valid
();
data
->
sorbed
=
std
::
move
(
slist
);
// Compounds
// ---------
CompoundList
clist
(
0
,
data
->
nb_component
());
if
(
root
[
INDB_SECTION_COMPOUNDS
])
parse_compounds
(
root
[
INDB_SECTION_COMPOUNDS
],
clist
);
else
clist
.
set_valid
();
data
->
compounds
=
std
::
move
(
clist
);
}
void
DataReaderYaml
::
parse_metadata
(
const
YAML
::
Node
&
root
)
{
data
->
metadata
.
name
=
io
::
get_yaml_mandatory
<
std
::
string
>
(
root
,
INDB_ATTRIBUTE_NAME
,
INDB_SECTION_METADATA
);
data
->
metadata
.
version
=
io
::
get_yaml_mandatory
<
std
::
string
>
(
root
,
INDB_ATTRIBUTE_VERSION
,
INDB_SECTION_METADATA
);
m_elem_data
->
check_compo
=
io
::
get_yaml_optional
<
bool
>
(
root
,
INDB_ATTRIBUTE_CHECKCOMPO
,
INDB_SECTION_METADATA
,
m_elem_data
->
check_compo
);
}
void
DataReaderYaml
::
init_elemental_composition
()
{
if
(
m_elem_data
->
is_init
)
return
;
m_elem_data
->
components
.
reserve
(
data
->
nb_component
());
// Parse label to find elemental composition
for
(
auto
id:
data
->
range_component
())
{
element_map
elem_compo
;
element_composition_from_label
(
data
->
get_label_component
(
id
),
elem_compo
);
m_elem_data
->
components
.
emplace_back
(
elem_compo
);
}
m_elem_data
->
is_init
=
true
;
}
void
DataReaderYaml
::
parse_basis
(
const
YAML
::
Node
&
basis
)
{
DEBUG
<<
"Size basis : "
<<
basis
.
size
();
data
->
components
=
ComponentList
(
basis
.
size
());
if
(
data
->
nb_component
()
<=
3
)
throw
std
::
invalid_argument
(
"The basis is too small."
);
index_t
starting_point
=
2
;
for
(
int
id:
data
->
components
.
range
())
{
index_t
id_in_db
=
starting_point
;
if
(
id_in_db
>=
data
->
nb_component
())
{
throw
std
::
invalid_argument
(
"The basis should contain H2O and E[-]."
);
}
const
YAML
::
Node
&
species
=
basis
[
id
];
std
::
string
label
=
obtain_label
(
species
,
INDB_SECTION_BASIS
);
if
(
label
==
water_label
)
{
id_in_db
=
0
;
//throw invalid_database("The first component should be "+water_label+".");
}
else
if
(
label
==
electron_label
)
{
id_in_db
=
1
;
//throw invalid_database("The second component should be "+electron_label+".");
}
else
{
++
starting_point
;
}
auto
is_already_in_the_database
=
data
->
components
.
get_id
(
label
);
if
(
is_already_in_the_database
!=
no_species
)
throw
invalid_database
(
"Component : "
+
label
+
"is already in the database."
);
SPAM
<<
"Parsing component : "
<<
label
;
ComponentValues
values
{};
values
.
label
=
label
;
// activity coeeficients
values
.
ionic_values
.
charge
=
charge_from_label
(
label
);
if
(
species
[
INDB_ATTRIBUTE_ACTIVITY
])
{
values
.
ionic_values
.
a_debye
=
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
[
INDB_ATTRIBUTE_ACTIVITY
],
INDB_ATTRIBUTE_ACTIVITY_A
,
label
);
values
.
ionic_values
.
b_debye
=
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
[
INDB_ATTRIBUTE_ACTIVITY
],
INDB_ATTRIBUTE_ACTIVITY_B
,
label
);
}
else
{
values
.
ionic_values
.
a_debye
=
0
;
values
.
ionic_values
.
b_debye
=
0
;
}
// molar mass
values
.
molar_mass
=
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
,
INDB_ATTRIBUTE_MOLARMASS
,
label
);
data
->
components
.
set_values
(
id_in_db
,
std
::
move
(
values
));
}
if
(
m_elem_data
->
check_compo
)
init_elemental_composition
();
// after this the basis is ready to use
data
->
components
.
set_valid
();
}
void
DataReaderYaml
::
parse_aqueous
(
const
YAML
::
Node
&
aqueous
,
AqueousList
&
alist
)
{
DEBUG
<<
"Parse aqueous species"
;
alist
=
AqueousList
(
aqueous
.
size
(),
data
->
nb_component
());
//const auto size = data->nb_aqueous();
for
(
auto
id:
alist
.
range
())
{
const
YAML
::
Node
&
species
=
aqueous
[
static_cast
<
int
>
(
id
)];
AqueousValues
values
;
values
.
label
=
obtain_label
(
species
,
INDB_SECTION_AQUEOUS
);
values
.
ionic_values
.
charge
=
charge_from_label
(
values
.
label
);
if
(
species
[
INDB_ATTRIBUTE_ACTIVITY
])
{
values
.
ionic_values
.
a_debye
=
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
[
INDB_ATTRIBUTE_ACTIVITY
],
INDB_ATTRIBUTE_ACTIVITY_A
,
values
.
label
);
values
.
ionic_values
.
b_debye
=
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
[
INDB_ATTRIBUTE_ACTIVITY
],
INDB_ATTRIBUTE_ACTIVITY_B
,
values
.
label
);
}
values
.
logk
=
obtain_logk
(
species
,
values
.
label
);
alist
.
set_values
(
id
,
values
);
// equation
std
::
map
<
std
::
string
,
scalar_t
>
compo
;
obtain_composition
(
species
,
compo
,
values
.
label
);
scalar_t
test_charge
{
0.0
};
std
::
map
<
index_t
,
scalar_t
>
to_canonicalize
;
for
(
const
auto
&
it:
compo
)
{
// component ?
const
auto
search
=
data
->
components
.
get_id
(
it
.
first
);
if
(
search
!=
no_species
)
{
alist
.
set_nu_ji
(
id
,
search
,
it
.
second
);
test_charge
+=
it
.
second
*
data
->
charge_component
(
search
);
}
// aqueous species ?
else
{
// in the document we parse ?
const
auto
id_aq
=
alist
.
get_id
(
it
.
first
);
if
(
id_aq
!=
no_species
)
{
to_canonicalize
.
insert
({
id_aq
,
it
.
second
});
test_charge
+=
it
.
second
*
alist
.
charge
(
id_aq
);
}
// cannot be found...
else
{
throw
db_invalid_syntax
(
"Unknown species : '"
+
it
.
first
+
"' while parsing equations for "
+
alist
.
get_label
(
id
)
+
"."
);
}
}
}
if
(
std
::
abs
(
test_charge
-
alist
.
charge
(
id
))
>
EPS_TEST_CHARGE
)
{
throw
db_invalid_data
(
"Total charge is not zero for aqueous species : '"
+
alist
.
get_label
(
id
)
+
"' - Charge-decomposition - charge species : "
+
std
::
to_string
(
test_charge
-
alist
.
charge
(
id
)
)
+
"."
);
}
for
(
const
auto
&
tocanon:
to_canonicalize
)
{
alist
.
canonicalize
(
id
,
tocanon
.
first
,
tocanon
.
second
);
}
// check composition
if
(
m_elem_data
->
check_compo
)
{
if
(
species
[
INDB_ATTRIBUTE_FORMULA
])
check_composition
(
species
[
INDB_ATTRIBUTE_FORMULA
].
as
<
std
::
string
>
(),
alist
,
id
);
else
check_composition
(
alist
.
get_label
(
id
),
alist
,
id
);
}
}
// valid set of aqueous species
alist
.
set_valid
();
}
void
DataReaderYaml
::
parse_minerals
(
const
YAML
::
Node
&
minerals
,
MineralList
&
minerals_list
,
MineralList
&
minerals_kinetic_list
)
{
// this one is a little bit more complicated since we need to detect
// if it is a solid phase governed by equilibrium or kinetic
DEBUG
<<
"Parse minerals"
;
index_t
nb_mineral
=
minerals
.
size
();
// find kinetic minerals
int
nb_kin
=
0
;
for
(
int
id
=
0
;
id
<
nb_mineral
;
++
id
)
{
if
(
is_kinetic
(
minerals
[
id
],
INDB_SECTION_MINERALS
))
++
nb_kin
;
}
minerals_list
=
MineralList
(
nb_mineral
-
nb_kin
,
data
->
nb_component
());
minerals_kinetic_list
=
MineralList
(
nb_kin
,
data
->
nb_component
());
// id for each class of minerals
index_t
id_in_eq
=
0
;
index_t
id_in_kin
=
0
;
for
(
index_t
id
=
0
;
id
<
nb_mineral
;
++
id
)
{
const
YAML
::
Node
&
species
=
minerals
[
static_cast
<
int
>
(
id
)];
//check if material is at equilibrium or governed by kinetics
MineralValue
value
;
value
.
label
=
obtain_label
(
species
,
INDB_SECTION_MINERALS
);
value
.
logk
=
obtain_logk
(
species
,
value
.
label
);
bool
is_kin
=
is_kinetic
(
species
,
value
.
label
);
// ###FIXME
value
.
molar_volume
=
io
::
get_yaml_optional
<
scalar_t
>
(
species
,
INDB_ATTRIBUTE_MOLARVOLUME
,
value
.
label
,
-
1
);
auto
&
mlist
=
is_kin
?
minerals_kinetic_list:
minerals_list
;
auto
&
true_id
=
is_kin
?
id_in_kin:
id_in_eq
;
mlist
.
set_values
(
true_id
,
value
);
// equation
// --------
std
::
map
<
std
::
string
,
scalar_t
>
compo
;
obtain_composition
(
species
,
compo
,
value
.
label
);
double
test_charge
=
0
;
std
::
map
<
index_t
,
scalar_t
>
to_canonicalize
;
for
(
const
auto
&
it:
compo
)
{
auto
idc
=
data
->
components
.
get_id
(
it
.
first
);
if
(
idc
!=
no_species
)
{
// this is a component
mlist
.
set_nu_ji
(
true_id
,
idc
,
it
.
second
);
test_charge
+=
it
.
second
*
data
->
charge_component
(
idc
);
}
else
{
// this is an aqueous species
auto
idaq
=
data
->
aqueous
.
get_id
(
it
.
first
);
if
(
idaq
!=
no_species
)
{
to_canonicalize
.
insert
({
idaq
,
it
.
second
});
test_charge
+=
it
.
second
*
data
->
charge_aqueous
(
idaq
);
}
else
// or something we don't know
{
throw
db_invalid_syntax
(
"Unknown species : '"
+
it
.
first
+
"' while parsing equations for "
+
mlist
.
get_label
(
true_id
)
+
"."
);
}
}
}
if
(
std
::
abs
(
test_charge
)
>
EPS_TEST_CHARGE
)
{
throw
db_invalid_data
(
"Total charge is not zero for mineral : '"
+
mlist
.
get_label
(
true_id
)
+
"' - total charge : "
+
std
::
to_string
(
test_charge
)
+
"."
);
}
// canonicalise
for
(
const
auto
&
tocanon:
to_canonicalize
)
{
mlist
.
canonicalize
(
true_id
,
data
->
aqueous
,
tocanon
.
first
,
tocanon
.
second
);
}
// check composition
if
(
m_elem_data
->
check_compo
)
{
if
(
species
[
INDB_ATTRIBUTE_FORMULA
])
check_composition
(
species
[
INDB_ATTRIBUTE_FORMULA
].
as
<
std
::
string
>
(),
mlist
,
true_id
);
else
check_composition
(
mlist
.
get_label
(
true_id
),
mlist
,
true_id
);
}
// update index
++
true_id
;
}
specmicp_assert
(
id_in_kin
==
minerals_kinetic_list
.
size
());
specmicp_assert
(
id_in_eq
==
minerals_list
.
size
());
// ok after that point
minerals_list
.
set_valid
();
minerals_kinetic_list
.
set_valid
();
}
void
DataReaderYaml
::
parse_gas
(
const
YAML
::
Node
&
gas
,
GasList
&
glist
)
{
DEBUG
<<
"Parse gas"
;
glist
=
GasList
(
gas
.
size
(),
data
->
nb_component
());
for
(
auto
id:
glist
.
range
())
{
const
YAML
::
Node
&
species
=
gas
[
static_cast
<
int
>
(
id
)];
GasValues
values
;
values
.
label
=
obtain_label
(
species
,
INDB_SECTION_GAS
);
auto
is_already_in_the_database
=
data
->
gas
.
get_id
(
values
.
label
);
if
(
is_already_in_the_database
!=
no_species
)
throw
invalid_database
(
"Component : "
+
values
.
label
+
"is already in the database."
);
values
.
logk
=
obtain_logk
(
species
,
values
.
label
);
glist
.
set_values
(
id
,
values
);
// equation
std
::
map
<
std
::
string
,
scalar_t
>
compo
;
obtain_composition
(
species
,
compo
,
values
.
label
);
scalar_t
test_charge
=
0.0
;
std
::
map
<
index_t
,
scalar_t
>
to_canonicalize
;
for
(
auto
it:
compo
)
{
const
auto
idc
=
data
->
components
.
get_id
(
it
.
first
);
// component
if
(
idc
!=
no_species
)
{
glist
.
set_nu_ji
(
id
,
idc
,
it
.
second
);
test_charge
+=
it
.
second
*
data
->
charge_component
(
idc
);
}
// aqueous species
else
{
const
auto
idaq
=
data
->
aqueous
.
get_id
(
it
.
first
);
if
(
idaq
!=
no_species
)
{
to_canonicalize
.
insert
({
idaq
,
it
.
second
});
test_charge
+=
it
.
second
*
data
->
charge_aqueous
(
idaq
);
}
else
{
throw
db_invalid_syntax
(
"Unknown species : '"
+
it
.
first
+
"' while parsing equations for "
+
data
->
gas
.
get_label
(
id
)
+
"."
);
}
}
}
if
(
std
::
abs
(
test_charge
)
>
EPS_TEST_CHARGE
)
{
throw
db_invalid_data
(
"Total charge is not zero for gas '"
+
data
->
gas
.
get_label
(
id
)
+
"' : "
+
std
::
to_string
(
test_charge
)
+
"."
);
}
// canonicalise
for
(
const
auto
&
tocanon:
to_canonicalize
)
{
glist
.
canonicalize
(
id
,
data
->
aqueous
,
tocanon
.
first
,
tocanon
.
second
);
}
// check composition
if
(
m_elem_data
->
check_compo
)
{
if
(
species
[
INDB_ATTRIBUTE_FORMULA
])
check_composition
(
species
[
INDB_ATTRIBUTE_FORMULA
].
as
<
std
::
string
>
(),
glist
,
id
);
else
check_composition
(
glist
.
get_label
(
id
),
glist
,
id
);
}
}
glist
.
set_valid
();
}
void
DataReaderYaml
::
parse_sorbed
(
const
YAML
::
Node
&
sorbed
,
SorbedList
&
slist
)
{
DEBUG
<<
"Parse sorbed species"
;
slist
=
SorbedList
(
sorbed
.
size
(),
data
->
nb_component
());
for
(
auto
id:
slist
.
range
())
{
const
YAML
::
Node
&
species
=
sorbed
[
static_cast
<
int
>
(
id
)];
SorbedValues
values
;
values
.
label
=
obtain_label
(
species
,
INDB_SECTION_SORBED
);
auto
is_already_in_the_database
=
slist
.
get_id
(
values
.
label
);
if
(
is_already_in_the_database
!=
no_species
)
throw
invalid_database
(
"Sorbed species : "
+
values
.
label
+
"is already in the database."
);
values
.
logk
=
obtain_logk
(
species
,
values
.
label
);
const
scalar_t
nb_site_occupied
=
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
,
INDB_ATTRIBUTE_NBSITEOCCUPIED
,
values
.
label
);
if
(
nb_site_occupied
<
0
)
{
throw
db_invalid_data
(
"The number of sites occupied by a sorbed species must be positive. (Species : "
+
values
.
label
+
", number of sites : "
+
std
::
to_string
(
nb_site_occupied
)
+
")"
);
}
values
.
sorption_site_occupied
=
nb_site_occupied
;
slist
.
set_values
(
id
,
values
);
std
::
map
<
std
::
string
,
scalar_t
>
compo
;
obtain_composition
(
species
,
compo
,
values
.
label
);
double
test_charge
=
0
;
std
::
map
<
index_t
,
scalar_t
>
to_canonicalize
;
for
(
auto
it:
compo
)
{
const
auto
idc
=
data
->
components
.
get_id
(
it
.
first
);
if
(
idc
!=
no_species
)
{
slist
.
set_nu_ji
(
id
,
idc
,
it
.
second
);
test_charge
+=
it
.
second
*
data
->
charge_component
(
idc
);
}
else
{
const
auto
idaq
=
data
->
aqueous
.
get_id
(
it
.
first
);
if
(
idaq
!=
no_species
)
{
to_canonicalize
.
insert
({
idaq
,
it
.
second
});
test_charge
+=
it
.
second
*
data
->
charge_aqueous
(
idaq
);
}
else
{
throw
db_invalid_syntax
(
"Unknown species : '"
+
it
.
first
+
"' while parsing equations for "
+
slist
.
get_label
(
id
)
+
"."
);
}
}
}
if
(
std
::
abs
(
test_charge
)
>
EPS_TEST_CHARGE
)
{
throw
db_invalid_data
(
"Total charge is not zero for gas '"
+
slist
.
get_label
(
id
)
+
"' : "
+
std
::
to_string
(
test_charge
)
+
"."
);
}
// canonicalise
for
(
const
auto
&
tocanon:
to_canonicalize
)
{
slist
.
canonicalize
(
id
,
data
->
aqueous
,
tocanon
.
first
,
tocanon
.
second
);
}
}
slist
.
set_valid
();
}
void
DataReaderYaml
::
parse_compounds
(
const
YAML
::
Node
&
compounds
,
CompoundList
&
clist
)
{
DEBUG
<<
"Parse compounds"
;
clist
=
CompoundList
(
compounds
.
size
(),
data
->
nb_component
());
for
(
auto
id:
clist
.
range
())
{
const
YAML
::
Node
&
species
=
compounds
[
static_cast
<
int
>
(
id
)];
std
::
string
label
=
obtain_label
(
species
,
INDB_SECTION_COMPOUNDS
);
auto
is_already_in_the_database
=
clist
.
get_id
(
label
);
if
(
is_already_in_the_database
!=
no_species
)
throw
invalid_database
(
"Compounds : "
+
label
+
"is already in the database."
);
clist
.
set_values
(
id
,
label
);
std
::
map
<
std
::
string
,
scalar_t
>
compo
;
obtain_composition
(
species
,
compo
,
label
);
double
test_charge
=
0
;
std
::
map
<
index_t
,
scalar_t
>
to_canonicalize
;
for
(
auto
it:
compo
)
{
const
auto
idc
=
data
->
components
.
get_id
(
it
.
first
);
if
(
idc
!=
no_species
)
{
clist
.
set_nu_ji
(
id
,
idc
,
it
.
second
);
test_charge
+=
it
.
second
*
data
->
charge_component
(
idc
);
}
else
{
const
auto
idaq
=
data
->
aqueous
.
get_id
(
it
.
first
);
if
(
idaq
!=
no_species
)
{
to_canonicalize
.
insert
({
idaq
,
it
.
second
});
test_charge
+=
it
.
second
*
data
->
charge_aqueous
(
idaq
);
}
else
{
throw
db_invalid_syntax
(
"Unknown species : '"
+
it
.
first
+
"' while parsing equations for "
+
clist
.
get_label
(
id
)
+
"."
);
}
}
}
if
(
std
::
abs
(
test_charge
)
>
EPS_TEST_CHARGE
)
{
throw
db_invalid_data
(
"Total charge is not zero for gas '"
+
clist
.
get_label
(
id
)
+
"' : "
+
std
::
to_string
(
test_charge
)
+
"."
);
}
// canonicalise
for
(
const
auto
&
tocanon:
to_canonicalize
)
{
clist
.
canonicalize
(
id
,
data
->
aqueous
,
tocanon
.
first
,
tocanon
.
second
);
}
// check composition
if
(
m_elem_data
->
check_compo
)
{
if
(
species
[
INDB_ATTRIBUTE_FORMULA
])
check_composition
(
species
[
INDB_ATTRIBUTE_FORMULA
].
as
<
std
::
string
>
(),
clist
,
id
);
else
check_composition
(
clist
.
get_label
(
id
),
clist
,
id
);
}
}
clist
.
set_valid
();
}
//! \brief Check that composition from formula and equations are equivalent
void
check_element_map
(
const
element_map
&
formula
,
const
element_map
&
composition
,
const
std
::
string
&
label
);
void
DataReaderYaml
::
check_composition
(
const
std
::
string
&
formula
,
ReactiveSpeciesList
&
rlist
,
index_t
id
)
{
if
(
not
m_elem_data
->
is_init
)
init_elemental_composition
();
// parse the formula
element_map
compo_from_label
;
element_composition_from_label
(
formula
,
compo_from_label
);
// parse the composition
element_map
compo_from_compo
;
for
(
auto
idc:
data
->
range_component
())
{
if
(
rlist
.
nu_ji
(
id
,
idc
)
==
0.0
)
continue
;
add_to_element_map
(
compo_from_compo
,
m_elem_data
->
components
[
idc
],
rlist
.
nu_ji
(
id
,
idc
)
);
}
// clean the composition
for
(
auto
it
=
compo_from_compo
.
begin
();
it
!=
compo_from_compo
.
end
();
)
{
if
(
it
->
first
==
"E"
or
it
->
second
==
0.0
)
it
=
compo_from_compo
.
erase
(
it
);
else
++
it
;
}
// check that both map are equal
check_element_map
(
compo_from_label
,
compo_from_compo
,
rlist
.
get_label
(
id
));
}
void
check_element_map
(
const
element_map
&
formula
,
const
element_map
&
composition
,
const
std
::
string
&
label
)
{
for
(
const
auto
&
it1:
formula
)
{
const
auto
it2
=
composition
.
find
(
it1
.
first
);
if
(
it2
==
composition
.
cend
())
{
throw
db_invalid_data
(
"Species '"
+
label
+
"' : element '"
+
it1
.
first
+
"' is in the formula but not in the composition"
);
}
if
(
std
::
abs
(
it1
.
second
-
it2
->
second
)
>
EPS_TEST_COMPOSITION
)
{
throw
db_invalid_data
(
"Species '"
+
label
+
"' : element '"
+
it1
.
first
+
"' has a different stoichiometry in the formula ("
+
std
::
to_string
(
it1
.
second
)
+
") than in the composition ("
+
std
::
to_string
(
it2
->
second
)
+
")."
);
}
}
for
(
const
auto
&
it1:
composition
)
{
const
auto
it2
=
formula
.
find
(
it1
.
first
);
if
(
it2
==
composition
.
cend
())
{
throw
db_invalid_data
(
"Species '"
+
label
+
"' : element '"
+
it1
.
first
+
"' is in the composition but not in the formula"
);
}
}
}
void
DataReaderYaml
::
parse_elements
(
const
YAML
::
Node
&
elements
,
ElementList
&
elist
)
{
if
(
elements
.
size
()
!=
static_cast
<
std
::
size_t
>
(
data
->
nb_component
()))
throw
db_invalid_data
(
"The number of elements does not corresponds to the number of components"
);
for
(
index_t
id:
data
->
range_component
())
{
const
YAML
::
Node
&
elem
=
elements
[
static_cast
<
int
>
(
id
)];
const
std
::
string
elem_label
=
io
::
get_yaml_mandatory
<
std
::
string
>
(
elem
,
INDB_ATTRIBUTE_ELEMENT
,
INDB_SECTION_ELEMENTS
);
const
std
::
string
comp_label
=
io
::
get_yaml_mandatory
<
std
::
string
>
(
elem
,
INDB_ATTRIBUTE_COMPONENT
,
elem_label
);
index_t
id_comp
=
data
->
get_id_component
(
comp_label
);
if
(
id_comp
==
no_species
)
{
throw
db_invalid_label
(
"'"
+
comp_label
+
"' is not a valid component"
);
}
elist
.
add_element
(
elem_label
,
id_comp
);
}
}
std
::
string
obtain_label
(
const
YAML
::
Node
&
species
,
const
std
::
string
&
section
)
{
return
io
::
get_yaml_mandatory
<
std
::
string
>
(
species
,
INDB_ATTRIBUTE_LABEL
,
section
);
}
void
obtain_composition
(
const
YAML
::
Node
&
species
,
std
::
map
<
std
::
string
,
scalar_t
>&
compo
,
const
std
::
string
&
label
)
{
const
std
::
string
compo_string
=
io
::
get_yaml_mandatory
<
std
::
string
>
(
species
,
INDB_ATTRIBUTE_COMPOSITION
,
label
);
parse_equation
(
compo_string
,
compo
);
}
scalar_t
obtain_logk
(
const
YAML
::
Node
&
species
,
const
std
::
string
&
label
)
{
return
io
::
get_yaml_mandatory
<
scalar_t
>
(
species
,
INDB_ATTRIBUTE_LOGK
,
label
);
}
bool
is_kinetic
(
const
YAML
::
Node
&
species
,
const
std
::
string
&
label
)
{
return
io
::
get_yaml_optional
<
bool
>
(
species
,
INDB_ATTRIBUTE_FLAG_KINETIC
,
label
,
false
);
}
}
//end namespace database
}
//end namespace specmicp
Event Timeline
Log In to Comment