Loading...
Searching...
No Matches
Handling units

In this tutorial, we explore the role of units in SAMSON by developing an app that performs energy conversions. Please, refer to the Units section for an extensive description of the units mechanism in SAMSON.

Note: For the sake of the tutorial, we demonstrate here a simplified implementation of the units converter.

The source code for an Extension created in this tutorial can be found at https://github.com/1A-OneAngstrom/SAMSON-Developer-Tutorials.

Generating an Extension

We will start by creating a new SAMSON Extension called EnergyConverter thanks to the SAMSON Extension generator (please, refer to the SAMSON Extension generator tutorial for a reminder on how to use it):

The SAMSON Extension generator generates an app class that implements the functionality of the app and is derived from the SBDApp class, and a GUI class that implements the user interface of the app and is derived from the SBGApp class.

Setting the description of the Extension

Open the header file of the app descriptor (file: SEEnergyConverterAppDescriptor.hpp) and modify the class description:

SB_CLASS_DESCRIPTION("DevTutorial: Energy converter");
#define SB_CLASS_DESCRIPTION(CLASS_DESCRIPTION)
Declares the description of the class.
Definition: SBCClassProxy.hpp:202

Open the source file of the app GUI (file: SEEnergyConverterAppGUI.cpp) and modify the getName function to have a user-friendly description of the app inside SAMSON:

QString SEEnergyConverterAppGUI::getName() const {
return "DevTutorial: Energy converter";
}

Now, try building the Extension and when you launch SAMSON you should be able to find it in Home > Apps. For now, it has no interface and does nothing, but we will now add functionality to it.

Setting up the interface

Let's start by adding the interface for the energy conversion.

Modify the user interface SEEnergyConverterAppGUI.ui to include two line edits (QLineEdit), two combo boxes (QComboBox), and a label.

Add widgets to the interface

Note how two vertical layouts and a horizontal one are used to align widgets in the interface. One good strategy is to create the first vertical layout and its contents and then copy it and rename widgets. You might also need to set the minimal width for the label to e.g. 10.

Please, name the added widgets as on an image above, so that you can copy and paste code later in this tutorial.

Now, enter the following entries (eV, Eh, kcal/mol, zJ) in each combo box (right clicl on a combo box and click on Edit items...):

Add entries to each combo box

These are the different units supported by our app to perform conversions.

Add two slots called onLeftChanged() and onRightChanged() to the interface:

Add slots

We want to implement the following behavior for the interface:

  • when the user modifies the value in the left line edit or changes the current index in any of the combo boxes, the value in the right line edit should be updated;
  • when the user modifies the value in the right line edit, the value in the left line edit should be updated.

For that, we connect the textEdited signal from the left line edit and the currentIndexChanged signal from both combo boxes to the onLeftChanged() slot, and the textEdited signal from the right line editto the onRightChanged() slot as shown on an image below:

Connect signals to slots

Note: We use the textEdited signal for the line edits because it is emitted when the text was edited by the user; the textChanged signal, on the other hand, is also emitted when the text was changed programatically.

Converting energies

Let's add these slots to the GUI class (class: SEEnergyConverterAppGUI, file: SEEnergyConverterAppGUI.hpp):

class SEEnergyConverterAppGUI : public SBGApp {
// ...
public slots:
void onLeftChanged();
void onRightChanged();
// ...
};
This class is the base class for App's GUI.
Definition: SBGApp.hpp:15

For a clean organization, we separate the functionality of the app from its interface. As a result, the SEEnergyConverterAppGUI class is going to delegate the actual energy conversion work to the app (SEEnergyConverterApp class).

Let's now implement a function for energy conversion in the app. Add in the app class (class: SEEnergyConverterApp, file: SEEnergyConverterApp.hpp) the following function declaration:

class SEEnergyConverterApp : public SBDApp {
// ...
/// \name Conversion
//@{
double convert(double sourceEnergy, const unsigned int sourceUnit, const unsigned int destinationUnit);
//@}
};
This class is the base class for apps.
Definition: SBDApp.hpp:18

In the source file of the app (SEEnergyConverterApp.cpp) add the header SBQuantity.hpp:

#include "SBQuantity.hpp"

And implement the convert function as follows:

double SEEnergyConverterApp::convert(double sourceEnergy, const unsigned int sourceUnit, const unsigned int destinationUnit) {
if (sourceUnit == 0) source = SBQuantity::eV(sourceEnergy);
if (sourceUnit == 1) source = SBQuantity::Eh(sourceEnergy);
if (sourceUnit == 2) source = SBQuantity::kcalPerMol(sourceEnergy);
if (sourceUnit == 3) source = SBQuantity::zJ(sourceEnergy);
if (destinationUnit == 0) return (SBQuantity::eV(source)).getValue();
if (destinationUnit == 1) return (SBQuantity::Eh(source)).getValue();
if (destinationUnit == 2) return (SBQuantity::kcalPerMol(source)).getValue();
if (destinationUnit == 3) return (SBQuantity::zJ(source)).getValue();
return 0.0;
}
SBDQuantityType< SBDQuantityUnitType< SBUnitSystemElectronvolt, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > > eV
The electronvolt type.
Definition: SBDQuantity.hpp:663
SBDQuantityType< SBDQuantityUnitType< SBUnitSystemKilocaloriePerMole, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > > kcalPerMol
The kilocalorie per mole type.
Definition: SBDQuantity.hpp:671
SBDQuantityType< SBDQuantityUnitType< SBUnitSystemSI, -12, 2, -27, 1, -15, -2, 0, 0, 0, 0, 0, 0, 0, 0 > > zJ
The zeptojoule type.
Definition: SBDQuantity.hpp:466
SBDQuantityType< SBDQuantityUnitType< SBUnitSystemAU, 0, 1, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0 > > Eh
The hartree type.
Definition: SBDQuantity.hpp:635
This template class defines physical quantity types.
Definition: SBDQuantityType.hpp:43

Here, we first declare a physical quantity with energy units (zeptoJoules by default in SAMSON). Then we convert from the source energy to zeptoJoules and then we convert from zeptoJoules to the destination unit, and extract the value as a double from the result. Note, that all the conversions are done internaly by SAMSON. You can check the units page for more information.

Now, we program the slots. Open the source file for the GUI of the app (SEEnergyConverterAppGUI.cpp) and implement the onLeftChanged() slot as follows:

void SEEnergyConverterAppGUI::onLeftChanged() {
// convert the string from the line edit to a double
// with checking for valid numerical characters (including the scientific notation)
bool ok;
double source = ui.lineEditLeft->text().toDouble(&ok);
if (ok) {
// convert units
unsigned int sourceUnit = ui.comboBoxLeft->currentIndex();
unsigned int destinationUnit = ui.comboBoxRight->currentIndex();
double destination = getApp()->convert(source, sourceUnit, destinationUnit);
// set the destination value
// QString::number will use float or scientific notation format, whichever is the most concise
ui.lineEditRight->setText(QString::number(destination));
}
else {
// in the case when the line edit contained invalid characters clear another line edit
ui.lineEditRight->setText("");
}
}

Here, we first try to convert the string from the line edit into a number: the toDouble function from the QString class takes care of checking the validity of the numeric string, including the support for scientific notation (i.e. numbers in the format 1.5e+5 are supported). Then, if the string has a valid number we do a conversion, else we empty the other line edit.

Implement the onRightChanged() slot in the same way by exchanging the source and destination widgets:

void SEEnergyConverterAppGUI::onRightChanged() {
// convert the string from the line edit to a double
// with checking for valid numerical characters (including the scientific notation)
bool ok;
double source = ui.lineEditRight->text().toDouble(&ok);
if (ok) {
// convert units
unsigned int sourceUnit = ui.comboBoxRight->currentIndex();
unsigned int destinationUnit = ui.comboBoxLeft->currentIndex();
double destination = getApp()->convert(source, sourceUnit, destinationUnit);
// set the destination value
// QString::number will use float or scientific notation format, whichever is the most concise
ui.lineEditLeft->setText(QString::number(destination));
}
else {
// in the case when the line edit contained invalid characters clear another line edit
ui.lineEditLeft->setText("");
}
}

Now, you have a simple energy converter implementation. Build the Extension, launch SAMSON, open the Extension, and try converting values.

Converting energies