SedSat3 1.1.6
Sediment Source Apportionment Tool - Advanced statistical methods for environmental pollution research
Loading...
Searching...
No Matches
/home/runner/work/SedSat3/SedSat3/src/elemental_profile.cpp

Create profile containing only elements included in analysis.

Create profile containing only elements included in analysisFilters the profile based on element_information metadata, keeping only elements that should be included in statistical analysis. Excludes:

The marked status of elements is preserved in the filtered profile.

Parameters
element_infoPointer to element metadata map (nullptr to include all)
Returns
New profile containing only analysis-appropriate elements
Postcondition
If element_info == nullptr: returns copy of entire profile
If element_info != nullptr: returns filtered subset
Marked status preserved for included elements
IsIncludedInAnalysis() state copied to output
Note
Original profile is unchanged (const method)
Elemental_Profile full_profile;
full_profile.AppendElement("Al", 8.5);
full_profile.AppendElement("OC", 2.5); // Organic carbon
full_profile.AppendElement("D50", 125); // Particle size
map<string, element_information> info;
info["Al"].include_in_analysis = true;
// Get only analysis elements
Elemental_Profile analysis = full_profile.CreateAnalysisProfile(&info);
// analysis contains only "Al", not "OC" or "D50"
Container for elemental concentration data of a single sediment sample.
Elemental_Profile CreateAnalysisProfile(const map< string, element_information > *elementinfo=nullptr) const
Create profile with only analyzed elements.
bool AppendElement(const string &name, double val=0.0)
Add new element to profile.
@ element
Standard chemical element concentration.
@ organic_carbon
Organic carbon content.
@ particle_size
Particle size distribution parameter.
#include <iostream>
#include "Utilities.h"
#include <QStringList>
#include <QFile>
// =============================================================================
// CONSTRUCTION AND ASSIGNMENT
// =============================================================================
: map<string, double>(),
included_in_analysis_(true),
marked_elements_()
{
// All initialization done in initializer list
// Empty body is fine - demonstrates proper C++ style
}
: map<string, double>(other), // Copy base class (element concentrations)
Interface(other), // Copy Interface base class
included_in_analysis_(other.included_in_analysis_),
marked_elements_(other.marked_elements_)
{
// All copying done in initializer list - efficient and exception-safe
}
/*/
*@brief Copy assignment operator
*
* Assigns the contents of another profile to this one, replacing all
* existing data.Handles self - assignment safely.
*
*@param other Profile to assign from
* @return Reference to * this for chaining(e.g., a = b = c)
*
* @post* this contains copy of other's data
* @post Previous contents of * this are replaced
* @post Self - assignment(a = a) is safe
*
*@note Assignment order is important : Interface first, then members,
* then base class to ensure proper cleanup if exceptions occur
*/
{
// Check for self-assignment (important for performance and correctness)
if (this == &other) {
return *this;
}
// Assign in proper order:
// 1. Interface base class
// 2. Member variables
// 3. map base class (element concentrations)
// Done last because it might throw, and we want other data copied first
map<string, double>::operator=(other);
return *this;
}
const map<string, element_information>* element_info) const
{
// If no metadata provided, return complete copy
if (element_info == nullptr) {
return *this;
}
// Create filtered profile
Elemental_Profile filtered_profile;
// Copy analysis inclusion flag
filtered_profile.included_in_analysis_ = this->included_in_analysis_;
// Iterate through all elements in current profile
for (const auto& element_entry : *this) {
const string& element_name = element_entry.first;
const double concentration = element_entry.second;
// Check if element exists in metadata
auto info_iter = element_info->find(element_name);
if (info_iter == element_info->end()) {
// Element not in metadata - skip it
continue;
}
const element_information& info = info_iter->second;
// Apply inclusion criteria
bool should_include = info.include_in_analysis &&
if (should_include) {
// Add element to filtered profile
filtered_profile[element_name] = concentration;
// Preserve marked status if element was marked
auto marked_iter = marked_elements_.find(element_name);
if (marked_iter != marked_elements_.end() && marked_iter->second) {
filtered_profile.marked_elements_[element_name] = true;
}
}
}
return filtered_profile;
}
bool exclude_elements,
bool apply_corrections,
const vector<double>& reference_om_size,
const MultipleLinearRegressionSet* regression_models,
const map<string, element_information>* element_info) const
{
// Start with current profile
Elemental_Profile result = *this;
// Step 1: Apply OM/size corrections if requested
if (apply_corrections) {
if (regression_models == nullptr) {
// Log warning or throw exception in production code
std::cerr << "Warning: apply_corrections=true but regression_models is null. "
<< "Skipping corrections." << std::endl;
}
else {
reference_om_size,
regression_models,
element_info
);
}
}
// Step 2: Filter elements if requested
if (exclude_elements) {
if (element_info == nullptr) {
// Log warning or throw exception in production code
std::cerr << "Warning: exclude_elements=true but element_info is null. "
<< "Skipping filtering." << std::endl;
}
else {
result = result.CreateAnalysisProfile(element_info);
}
}
return result;
}
const map<string, element_information>* element_info,
bool include_isotopes) const
{
// Validate input - element_info is required
if (element_info == nullptr) {
// For now, log error and return empty profile
// Future: throw std::invalid_argument("element_info cannot be null")
std::cerr << "Error: ExtractChemicalElements called with null element_info. "
<< "Returning empty profile." << std::endl;
}
Elemental_Profile extracted_profile;
// Iterate through all elements in current profile
for (const auto& [element_name, concentration] : *this) {
// Check if element exists in metadata
auto info_iter = element_info->find(element_name);
if (info_iter == element_info->end()) {
// Element not in metadata - skip it
// Could log warning: std::cerr << "Warning: Element '" << element_name
// << "' not found in metadata. Skipping.\n";
continue;
}
const element_information& info = info_iter->second;
// Check if element should be included based on role
bool should_include = false;
// Always include chemical elements
should_include = true;
}
// Include isotopes only if requested
should_include = include_isotopes;
}
// All other roles (organic_carbon, particle_size, do_not_include) are excluded
if (should_include) {
extracted_profile[element_name] = concentration;
}
}
return extracted_profile;
}
const vector<string>& element_names) const
{
Elemental_Profile extracted_profile;
// Reserve space if we expect most elements to exist (optimization)
// Note: Can't reserve in map, but this shows intent
// Iterate through requested elements
for (const string& element_name : element_names) {
// Check if element exists in current profile
auto it = find(element_name);
if (it != end()) {
// Element exists - add to extracted profile
extracted_profile[element_name] = it->second;
}
// Element doesn't exist - silently skip
}
return extracted_profile;
}
double Elemental_Profile::GetValue(const string& element_name) const
{
auto it = find(element_name);
if (it == end()) {
throw std::out_of_range(
"Element '" + element_name + "' does not exist in profile"
);
}
return it->second;
}
bool Elemental_Profile::SetValue(const string& element_name, double value)
{
auto it = find(element_name);
if (it == end()) {
return false;
}
it->second = value;
return true;
}
bool Elemental_Profile::SetMarked(const string& element_name, bool marked)
{
if (find(element_name) == end()) {
return false;
}
marked_elements_[element_name] = marked;
return true;
}
bool Elemental_Profile::IsMarked(const string& element_name) const
{
if (find(element_name) == end()) {
return false;
}
auto it = marked_elements_.find(element_name);
return (it != marked_elements_.end() && it->second);
}
bool Elemental_Profile::AppendElement(const string& element_name, double value)
{
if (find(element_name) != end()) {
return false;
}
(*this)[element_name] = value;
marked_elements_[element_name] = false;
return true;
}
vector<double> Elemental_Profile::GetAllValues() const
{
vector<double> values;
values.reserve(size());
for (const auto& [element_name, concentration] : *this) {
values.push_back(concentration);
}
return values;
}
{
string output;
for (const auto& [element_name, concentration] : *this) {
output += element_name + ":" + aquiutils::numbertostring(concentration) + "\n";
}
return output;
}
{
QTableWidget* table = new QTableWidget();
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setColumnCount(1);
table->setRowCount(size());
QStringList headers;
QStringList element_names;
int row = 0;
for (const auto& [element_name, concentration] : *this) {
element_names << QString::fromStdString(element_name);
table->setItem(row, 0, new QTableWidgetItem(QString::number(concentration)));
// Highlight values outside limits if enabled
(concentration > highlimit || concentration < lowlimit)) {
table->item(row, 0)->setForeground(QColor(Qt::red));
}
row++;
}
headers << YAxisLabel();
table->setHorizontalHeaderLabels(headers);
table->setVerticalHeaderLabels(element_names);
return table;
}
QJsonObject Elemental_Profile::toJsonObject() const
{
QJsonObject json_object;
QJsonObject element_contents;
json_object["Include"] = IsIncludedInAnalysis();
for (const auto& [element_name, concentration] : *this) {
element_contents[QString::fromStdString(element_name)] = concentration;
}
json_object["Element Contents"] = element_contents;
json_object["XAxisLabel"] = XAxisLabel();
json_object["YAxisLabel"] = YAxisLabel();
return json_object;
}
bool Elemental_Profile::ReadFromJsonObject(const QJsonObject& json_object)
{
clear();
if (json_object.contains("Include")) {
// Full format with metadata
SetIncludedInAnalysis(json_object["Include"].toBool());
QJsonObject contents = json_object["Element Contents"].toObject();
for (const QString& key : contents.keys()) {
(*this)[key.toStdString()] = contents[key].toDouble();
}
}
else {
// Legacy format - elements directly in root
for (const QString& key : json_object.keys()) {
(*this)[key.toStdString()] = json_object[key].toDouble();
}
}
if (json_object.contains("XAxisLabel")) {
SetXAxisLabel(json_object["XAxisLabel"].toString());
}
if (json_object.contains("YAxisLabel")) {
SetYAxisLabel(json_object["YAxisLabel"].toString()); // Fixed: was SetXAxisLabel
}
return true;
}
bool Elemental_Profile::Read(const QStringList& string_list)
{
clear();
for (const QString& line : string_list) {
QStringList parts = line.split(":");
if (parts.size() > 1) {
AppendElement(parts[0].toStdString(), parts[1].toDouble());
}
}
return true;
}
{
file->write(QString::fromStdString(ToString()).toUtf8());
return true;
}
{
if (empty()) {
return -1e12;
}
double max_value = -1e12;
for (const auto& [element_name, concentration] : *this) {
if (concentration > max_value) {
max_value = concentration;
}
}
return max_value;
}
{
if (empty()) {
return 1e12;
}
double min_value = 1e12;
for (const auto& [element_name, concentration] : *this) {
if (concentration < min_value) {
min_value = concentration;
}
}
return min_value;
}
vector<string> Elemental_Profile::GetElementNames() const
{
vector<string> names;
names.reserve(size());
for (const auto& [element_name, concentration] : *this) {
names.push_back(element_name);
}
return names;
}
bool Elemental_Profile::Contains(const string& element_name) const
{
return (find(element_name) != end());
}
const vector<double>& reference_om_size,
const MultipleLinearRegressionSet* regression_models,
const map<string, element_information>* element_info) const
{
Elemental_Profile corrected_profile;
for (const auto& [element_name, concentration] : *this) {
const element_information& info = element_info->at(element_name);
// Skip organic carbon and particle size indicators
continue;
}
// Start with original concentration
corrected_profile[element_name] = concentration;
// Apply corrections if regression model exists for this element
if (regression_models->count(element_name) == 0) {
continue;
}
const MultipleLinearRegression* mlr = &regression_models->at(element_name);
const auto& var_names = mlr->GetIndependentVariableNames();
const auto& coefficients = mlr->CoefficientsIntercept();
bool is_linear = (mlr->Equation() == regression_form::linear);
// Apply first correction (typically organic matter)
if (mlr->Effective(0) && var_names[0] != element_name) {
double reference = reference_om_size[0];
double measured = this->at(var_names[0]);
if (is_linear) {
corrected_profile[element_name] += (reference - measured) * coefficients[1];
}
else {
corrected_profile[element_name] *= pow(reference / measured, coefficients[1]);
}
}
// Apply second correction (typically particle size) if available
if (var_names.size() > 1 && mlr->Effective(1) && var_names[1] != element_name) {
double reference = reference_om_size[1];
double measured = this->at(var_names[1]);
if (is_linear) {
corrected_profile[element_name] += (reference - measured) * coefficients[2];
}
else {
corrected_profile[element_name] *= pow(reference / measured, coefficients[2]);
}
}
// Warn about negative corrected values (except for isotopes)
if (corrected_profile[element_name] < 0 &&
std::cerr << "Warning: Corrected value for " << element_name
<< " is negative: " << corrected_profile[element_name] << std::endl;
}
}
return corrected_profile;
}
{
if (size() != weights.getsize()) {
return -999;
}
double sum = 0.0;
int index = 0;
for (const auto& [element_name, concentration] : *this) {
sum += concentration * weights[index];
index++;
}
return sum;
}
{
CMBVector sorted_result;
vector<string> processed_elements;
for (size_t i = 0; i < size(); i++) {
double target_value = ascending ? 1e6 : -1e6;
string target_element;
for (const auto& [element_name, concentration] : *this) {
// Skip already processed elements
if (lookup(processed_elements, element_name) != -1) {
continue;
}
// Find min (ascending) or max (descending)
bool is_better = ascending ? (concentration < target_value)
: (concentration > target_value);
if (is_better) {
target_element = element_name;
target_value = concentration;
}
}
processed_elements.push_back(target_element);
sorted_result.append(target_element, target_value);
}
return sorted_result;
}
vector<string> Elemental_Profile::SelectTopElements(int n, bool ascending) const
{
CMBVector sorted = SortByConcentration(ascending);
vector<string> top_elements;
for (int i = 0; i < n && i < sorted.num; i++) {
top_elements.push_back(sorted.Label(i));
}
return top_elements;
}
Vector class with string labels for Chemical Mass Balance analysis.
Definition cmbvector.h:17
void append(const string &label, const double &val)
Appends a labeled element to the vector.
string Label(int i) const
Gets label at specified index.
Definition cmbvector.h:118
Elemental_Profile & operator=(const Elemental_Profile &other)
Copy assignment operator.
Elemental_Profile ExtractChemicalElements(const map< string, element_information > *elementinfo, bool include_isotopes=false) const
Extract chemical elements only (exclude isotopes, metadata)
Elemental_Profile ExtractElements(const vector< string > &element_names) const
Extract subset of specified elements.
Elemental_Profile ApplyOrganicMatterAndSizeCorrections(const vector< double > &reference_om_size, const MultipleLinearRegressionSet *regression_models, const map< string, element_information > *elementinfo=nullptr) const
Apply organic matter and particle size corrections.
bool IsMarked(const string &name) const
Check if element is marked.
map< string, bool > marked_elements_
Map tracking marked elements for quality control.
double GetMaximum() const
Get maximum concentration value.
vector< string > GetElementNames() const
Get list of all element names.
double CalculateDotProduct(const CMBVector &weights) const
Calculate dot product with weight vector.
CMBVector SortByConcentration(bool ascending=true) const
Sort elements by concentration value.
bool included_in_analysis_
Whether profile should be included in analysis.
double GetMinimum() const
Get minimum concentration value.
vector< double > GetAllValues() const
Get all concentration values as vector.
bool SetMarked(const string &name, bool marked)
Set marked status for an element.
QTableWidget * ToTable() override
Create Qt table widget representation.
void SetIncludedInAnalysis(bool included)
Set analysis inclusion status.
Elemental_Profile CreateCorrectedProfile(bool exclude_elements, bool apply_corrections, const vector< double > &reference_om_size, const MultipleLinearRegressionSet *regression_models=nullptr, const map< string, element_information > *elementinfo=nullptr) const
Create corrected profile with filtering and corrections.
double GetValue(const string &name) const
Get element concentration by name.
vector< string > SelectTopElements(int n, bool ascending=true) const
Select top N elements by concentration.
Elemental_Profile()
Default constructor - creates empty profile.
string ToString() const override
Convert profile to string representation.
bool writetofile(QFile *file) override
Write profile to file.
bool ReadFromJsonObject(const QJsonObject &json) override
Deserialize from JSON object.
QJsonObject toJsonObject() const override
Serialize to JSON object.
bool IsIncludedInAnalysis() const
Check if profile is included in analysis.
bool SetValue(const string &name, double val)
Set concentration value for existing element.
bool Read(const QStringList &string_list) override
Read profile from string list.
bool Contains(const string &name) const
Check if element exists in profile.
Abstract base class providing common serialization and visualization interface.
Definition interface.h:65
double lowlimit
Lower threshold for value highlighting (when enabled)
Definition interface.h:211
Interface & operator=(const Interface &intf)
Assignment operator.
Definition interface.cpp:14
QString XAxisLabel() const
Get the X-axis label.
Definition interface.h:287
bool SetXAxisLabel(const QString &label)
Set the X-axis label.
Definition interface.h:307
QString YAxisLabel() const
Get the Y-axis label.
Definition interface.h:298
bool highlightoutsideoflimit
Flag indicating whether to highlight values outside defined limits.
Definition interface.h:221
bool SetYAxisLabel(const QString &label)
Set the Y-axis label.
Definition interface.h:317
double highlimit
Upper threshold for value highlighting (when enabled)
Definition interface.h:212
Elemental concentration profile for sediment source fingerprinting.
Metadata describing an element's role and properties in analysis.
enum element_information::role Role
@ isotope
Isotopic ratio (e.g., 206Pb/207Pb)
@ do_not_include
Exclude from all analyses.
bool include_in_analysis
Whether to include in statistical analyses.