Source code: C++ Temperature Conversion and Vapor Pressure Deficit (VPD) šŸ› 

C++ source code for performing temperature conversions and temperature math auto-magically.
C++ source code for calculating vapor pressure deficit (vpd). Uses the above mentioned temperature conversion classes.

This is for the DIY types that have some software background. Iā€™d like to ask forum members to avoid the ā€œwhatā€™s this good forā€ type questions within this thread to avoid clutter. Youā€™ll either know what this is or you wonā€™t (yet).

This is an on-going project with several associated threads with differing objectives. Additional source, documentation, etc can be found by clicking on the ā€œsourcecodeā€ tag. https://overgrow.com/tags/sourcecode

Initial draft 9-1-2018.
Edit: Change VPD internal units to Celcius. Improved precision.
Edit: 9-4-2018 Improve type casting in the temperature conversion classes.
Edit: 9-9-2018 Added additional time specific constants to the Constants.h header.
Edit: 2-9-2020 Updated the conversion classes, see the updated code as posted later in this thread. Leaving the original source posted here in this post to avoid breaking any usage.

Temperature conversion classes:

//
// temperature_types.h
// Version timestamp: 9-4-2018, 4:23 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
//
//
// Some usage wxamples: 
//
//  //Initialize some temperature variables
//  celcius cel_temp = 32.0;
//  celcius cel_temp2(31.0);
//  kelvin kel_temp(280);
//  farenheit far_temp(80);
//  kelvin kel_temp2(cel_temp);
//  farenheit far_temp2(kel_temp);
//
//  //Print the value in celcius with alternatives
//  std::cout << "Temperature celcius(32)" << cel_temp << std::endl;
//  std::cout << "Temperature celcius(32): " << cel_temp.degrees_celcius << std::endl;
//  std::cout << "Temperature celcius(32): " << cel_temp() << std::endl;
//  std::cout << "Temperature F to c: " << (celcius) far_temp << std::endl;
//  std::cout << "Temperature K to c: " << (celcius) kel_temp << std::endl;
//  std::cout << "Temperature F to K: " << (kelvin) far_temp << std::endl;
//  std::cout << "Temperature c to K: " << (kelvin) cel_temp << std::endl;
//  std::cout << "Temperature c to F: " << (farenheit) cel_temp << std::endl;
//  std::cout << "Temperature kelvin to F: " << (farenheit) kel_temp << std::endl;
//	
//  printf("Temperature: %8.3f\n", cel_temp());
//
//  //Print the celcius value in kelvin
//  std::cout << "Temperature c to K" << (kelvin)cel_temp << std::endl;
//
//  //Print the celcius value in farenheit
//  std::cout << "Temperature c to F" << (farenheit)cel_temp << std::endl;
//
//  //Do some math with different units
//  celcius cel_temp3 = 32.0;
//  cel_temp2 = cel_temp3 + far_temp;
//  cel_temp3++;
//  cel_temp += 10;
//  std::cout << "Temperature celcius (33): " << cel_temp3 << std::endl;
//  std::cout << "Temperature celcius (32 + 80F): " << cel_temp2 << std::endl;
//  std::cout << "Temperature celcius (42):" << cel_temp << std::endl;
//  std::cout << "Temperature celcius + kelvin: " << cel_temp + kel_temp << std::endl;
//	
//  if (cel_temp > kel_temp) std::cout << "Celcius Temperature is greater" << std::endl;
//  else if (cel_temp < kel_temp) std::cout << "Kelvin Temperature is greater" << std::endl;
//  else if (cel_temp == kel_temp) std::cout << "Temperatures are equivalent" << std::endl;
//


# pragma once

# include <string>
# include <sstream>
# include <boost/variant.hpp>
# include <iostream>

# include "Constants.h"

class celcius; class kelvin; class Temperature; class farenheit;
typedef boost::variant<celcius, kelvin, farenheit, double> celcius_variant;
typedef boost::variant<celcius, kelvin, farenheit, double> kelvin_variant;
typedef boost::variant<celcius, kelvin, farenheit, double> farenheit_variant;


class farenheit
{
	
public:
	double degrees_farenheit = 0;
	farenheit()
		: degrees_farenheit(0) {}
	farenheit(const farenheit& c) { degrees_farenheit = c.degrees_farenheit; } // Copy contructor
	farenheit(double r) { degrees_farenheit = r; }
	farenheit(farenheit_variant &r);
	void operator=(farenheit_variant &temp2);
	virtual double operator-(farenheit_variant temp2);
	virtual double operator+(farenheit_variant temp2); 
	virtual double operator*(farenheit_variant temp2); 
	virtual double operator/(farenheit_variant temp2); 
	virtual double operator-=(farenheit_variant temp2);
	virtual double operator+=(farenheit_variant temp2); 
	virtual double operator++(int incr);
	virtual double operator--(int incr); 
	virtual double operator*=(farenheit_variant temp2);
	virtual double operator/=(farenheit_variant temp2); 
	virtual double operator()(farenheit_variant temp2);
	virtual double operator()(); 
	virtual double operator>(farenheit_variant temp2); 
	virtual double operator<(farenheit_variant temp2); 
	virtual double operator>=(farenheit_variant temp2); 
	virtual double operator<=(farenheit_variant temp2); 
	virtual double operator==(farenheit_variant temp2); 
	virtual operator celcius();
	virtual operator kelvin();
	friend std::ostream& operator<<(std::ostream& lhs, const farenheit& rhs)
	{
		return (lhs << rhs.degrees_farenheit);
	}
};


class kelvin 
{
	
public:
	double degrees_kelvin = 0;
	kelvin()
		: degrees_kelvin(0) {}
	kelvin(const kelvin& c) { degrees_kelvin = c.degrees_kelvin; } // Copy contructor
	kelvin(double r) { degrees_kelvin = r; }
	kelvin(kelvin_variant &r);
	void operator=(kelvin_variant &temp2);
	virtual double operator-(kelvin_variant temp2);
	virtual double operator+(kelvin_variant temp2); 
	virtual double operator*(kelvin_variant temp2); 
	virtual double operator/(kelvin_variant temp2); 
	virtual double operator-=(kelvin_variant temp2);
	virtual double operator+=(kelvin_variant temp2); 
	virtual double operator++(int incr);
	virtual double operator--(int incr); 
	virtual double operator*=(kelvin_variant temp2);
	virtual double operator/=(kelvin_variant temp2); 
	virtual double operator()(kelvin_variant temp2); 
	virtual double operator()(); 
	virtual double operator>(kelvin_variant temp2); 
	virtual double operator<(kelvin_variant temp2); 
	virtual double operator>=(kelvin_variant temp2); 
	virtual double operator<=(kelvin_variant temp2); 
	virtual double operator==(kelvin_variant temp2); 
	virtual operator celcius();
	virtual operator farenheit();
	friend std::ostream& operator<<(std::ostream& lhs, const kelvin& rhs)
	{
		return (lhs << rhs.degrees_kelvin);
	}
};


class celcius
{
	
public:
	double degrees_celcius = 0;
	celcius()
		: degrees_celcius(0) {}
	celcius(const celcius& c) { degrees_celcius = c.degrees_celcius; } // Copy contructor
	celcius(double r) { degrees_celcius = r; }
	celcius(celcius_variant &r);
	void operator=(celcius_variant &temp2);
	virtual double operator-(celcius_variant temp2);
	virtual double operator+(celcius_variant temp2); 
	virtual double operator*(celcius_variant temp2); 
	virtual double operator/(celcius_variant temp2); 
	virtual double operator-=(celcius_variant temp2);
	virtual double operator+=(celcius_variant temp2); 
	virtual double operator++(int incr);
	virtual double operator--(int incr); 
	virtual double operator*=(celcius_variant temp2);
	virtual double operator/=(celcius_variant temp2); 
	virtual double operator()(celcius_variant temp2); 
	virtual double operator()(); 
	virtual double operator>(celcius_variant temp2); 
	virtual double operator<(celcius_variant temp2); 
	virtual double operator>=(celcius_variant temp2); 
	virtual double operator<=(celcius_variant temp2); 
	virtual double operator==(celcius_variant temp2); 
	virtual operator kelvin();
	virtual operator farenheit();
	friend std::ostream& operator<<(std::ostream& lhs, const celcius& rhs)
	{
		return (lhs << rhs.degrees_celcius);
	}
};



class tcelcius_variant : public boost::static_visitor<double>
{
public:
	double operator()(double temperature) const
	{
		return (temperature);
	}
	
	double operator()(celcius temperature) const
	{
		return (temperature.degrees_celcius);
	}
	
	double operator()(kelvin temperature) const
	{
		double temp = temperature.degrees_kelvin - 273.15;
		return (temp);
	}
	
	double operator()(farenheit temperature) const
	{
		double temp = (temperature.degrees_farenheit - 32.0) * (5.0 / 9.0);
		return (temp);
	}
	
};

class tkelvin_variant : public boost::static_visitor<double>
{
public:
	double operator()(double temperature) const
	{
		return (temperature);
	}
	
	double operator()(kelvin temperature) const
	{
		return (temperature.degrees_kelvin);
	}
	
	double operator()(celcius temperature) const
	{
		double temp = temperature.degrees_celcius + 273.15;
		return (temp);
	}
	
	double operator()(farenheit temperature) const
	{
		double temp = (temperature.degrees_farenheit - 32.0) * (5.0 / 9.0) + 273.15;
		return (temp);
	}
	
};

class tfarenheit_variant : public boost::static_visitor<double>
{
public:
	double operator()(double temperature) const
	{
		return (temperature);
	}
	
	double operator()(farenheit temperature) const
	{
		return (temperature.degrees_farenheit);
	}
	
	double operator()(celcius temperature) const
	{
		double temp = temperature.degrees_celcius * (9.0 / 5.0) + 32.0;
		return (temp);
	}
	
	double operator()(kelvin temperature) const
	{
		double temp = (temperature.degrees_kelvin) * (9.0 / 5.0) - 459.67;
		return (temp);
	}
	
};
//
// Kelvin.cpp
// Version timestamp: 9-4-2018, 4:23 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# include "temperature_types.h"

kelvin::kelvin(kelvin_variant &r)
{
	kelvin_variant c = r;
	this->degrees_kelvin = boost::apply_visitor(tkelvin_variant(), c);
}

kelvin::operator farenheit()
{
	//boost::apply_visitor(tcelcius_variant(), this);
	double temp = (this->degrees_kelvin) * (9.0 / 5.0) - 459.67;
	return (temp);
}

kelvin::operator celcius()
{
	double temp = this->degrees_kelvin - 273.15;
	return (temp);
}

void kelvin::operator=(kelvin_variant &temp2) 
{
	this->degrees_kelvin = boost::apply_visitor(tkelvin_variant(), temp2);
}

double kelvin::operator-(kelvin_variant temp2) 
{
	double temp = this->degrees_kelvin - boost::apply_visitor(tkelvin_variant(), temp2);
	return (temp);
}

double kelvin::operator+(kelvin_variant temp2) 
{
	double temp = this->degrees_kelvin + boost::apply_visitor(tkelvin_variant(), temp2);
	return (temp);
}

double kelvin::operator*(kelvin_variant temp2) 
{
	double temp = this->degrees_kelvin * boost::apply_visitor(tkelvin_variant(), temp2);
	return (temp);
}

double kelvin::operator/(kelvin_variant temp2) 
{
	double temp = this->degrees_kelvin / boost::apply_visitor(tkelvin_variant(), temp2);
	return (temp);
}

double kelvin::operator()(kelvin_variant temp2) 
{
	double temp = boost::apply_visitor(tkelvin_variant(), temp2);
	return (temp);
}

double kelvin::operator()() 
{
	double temp = this->degrees_kelvin;
	return (temp);
}

double kelvin::operator-=(kelvin_variant temp2) 
{
	this->degrees_kelvin = this->degrees_kelvin - boost::apply_visitor(tkelvin_variant(), temp2);
	return (this->degrees_kelvin);
}

double kelvin::operator+=(kelvin_variant temp2) 
{
	this->degrees_kelvin = this->degrees_kelvin + boost::apply_visitor(tkelvin_variant(), temp2);
	return (this->degrees_kelvin);
}

double kelvin::operator++(int incr) 
{
	this->degrees_kelvin +=  1.0;
	return (this->degrees_kelvin);
}

double kelvin::operator--(int incr) 
{
	this->degrees_kelvin -= 1.0;
	return (this->degrees_kelvin);
}

double kelvin::operator*=(kelvin_variant temp2) 
{
	this->degrees_kelvin = this->degrees_kelvin * boost::apply_visitor(tkelvin_variant(), temp2);
	return (this->degrees_kelvin);
}

double kelvin::operator/=(kelvin_variant temp2) 
{
	this->degrees_kelvin = this->degrees_kelvin / boost::apply_visitor(tkelvin_variant(), temp2);
	return (this->degrees_kelvin);
}

double kelvin::operator>(kelvin_variant temp2) 
{
	if (this->degrees_kelvin > boost::apply_visitor(tkelvin_variant(), temp2)) return (1);
	return (0);
}

double kelvin::operator<(kelvin_variant temp2) 
{
	if (this->degrees_kelvin < boost::apply_visitor(tkelvin_variant(), temp2)) return (1);
	return (0);
}

double kelvin::operator==(kelvin_variant temp2) 
{
	if (this->degrees_kelvin == boost::apply_visitor(tkelvin_variant(), temp2)) return (1);
	return (0);
}

double kelvin::operator>=(kelvin_variant temp2) 
{
	if (this->degrees_kelvin >= boost::apply_visitor(tkelvin_variant(), temp2)) return (1);
	return (0);
}
double kelvin::operator<=(kelvin_variant temp2) 
{
	if (this->degrees_kelvin <= boost::apply_visitor(tkelvin_variant(), temp2)) return (1);
	return (0);
}
//
// Farenheit.cpp
// Version timestamp: 9-4-2018, 4:23 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# include "temperature_types.h"

farenheit::farenheit(farenheit_variant &r)
{
	farenheit_variant c = r;
	this->degrees_farenheit = boost::apply_visitor(tfarenheit_variant(), c);
}

farenheit::operator kelvin()
{
	//boost::apply_visitor(tcelcius_variant(), this);
	double temp = (this->degrees_farenheit - 32.0) * (5.0 / 9.0) + 273.15;
	return (temp);
}

farenheit::operator celcius()
{
	double temp = (this->degrees_farenheit - 32.0) * (5.0 / 9.0);
	return (temp);
}

void farenheit::operator=(farenheit_variant &temp2) 
{
	this->degrees_farenheit = boost::apply_visitor(tfarenheit_variant(), temp2);
}

double farenheit::operator-(farenheit_variant temp2) 
{
	double temp = this->degrees_farenheit - boost::apply_visitor(tfarenheit_variant(), temp2);
	return (temp);
}

double farenheit::operator+(farenheit_variant temp2) 
{
	double temp = this->degrees_farenheit + boost::apply_visitor(tfarenheit_variant(), temp2);
	return (temp);
}

double farenheit::operator*(farenheit_variant temp2) 
{
	double temp = this->degrees_farenheit * boost::apply_visitor(tfarenheit_variant(), temp2);
	return (temp);
}

double farenheit::operator/(farenheit_variant temp2) 
{
	double temp = this->degrees_farenheit / boost::apply_visitor(tfarenheit_variant(), temp2);
	return (temp);
}

double farenheit::operator()(farenheit_variant temp2) 
{
	double temp = boost::apply_visitor(tfarenheit_variant(), temp2);
	return (temp);
}

double farenheit::operator()() 
{
	double temp = this->degrees_farenheit;
	return (temp);
}

double farenheit::operator-=(farenheit_variant temp2) 
{
	this->degrees_farenheit = this->degrees_farenheit - boost::apply_visitor(tfarenheit_variant(), temp2);
	return (this->degrees_farenheit);
}

double farenheit::operator+=(farenheit_variant temp2) 
{
	this->degrees_farenheit = this->degrees_farenheit + boost::apply_visitor(tfarenheit_variant(), temp2);
	return (this->degrees_farenheit);
}

double farenheit::operator++(int incr) 
{
	this->degrees_farenheit +=  1.0;
	return (this->degrees_farenheit);
}

double farenheit::operator--(int incr) 
{
	this->degrees_farenheit -=  1.0;
	return (this->degrees_farenheit);
}

double farenheit::operator*=(farenheit_variant temp2) 
{
	this->degrees_farenheit = this->degrees_farenheit * boost::apply_visitor(tfarenheit_variant(), temp2);
	return (this->degrees_farenheit);
}

double farenheit::operator/=(farenheit_variant temp2) 
{
	this->degrees_farenheit = this->degrees_farenheit / boost::apply_visitor(tfarenheit_variant(), temp2);
	return (this->degrees_farenheit);
}

double farenheit::operator>(farenheit_variant temp2) 
{
	if (this->degrees_farenheit > boost::apply_visitor(tfarenheit_variant(), temp2)) return (1);
	return (0);
}

double farenheit::operator<(farenheit_variant temp2) 
{
	if (this->degrees_farenheit < boost::apply_visitor(tfarenheit_variant(), temp2)) return (1);
	return (0);
}

double farenheit::operator==(farenheit_variant temp2) 
{
	if (this->degrees_farenheit == boost::apply_visitor(tfarenheit_variant(), temp2)) return (1);
	return (0);
}

double farenheit::operator>=(farenheit_variant temp2) 
{
	if (this->degrees_farenheit >= boost::apply_visitor(tfarenheit_variant(), temp2)) return (1);
	return (0);
}
double farenheit::operator<=(farenheit_variant temp2) 
{
	if (this->degrees_farenheit <= boost::apply_visitor(tfarenheit_variant(), temp2)) return (1);
	return (0);
}
//
// Celcius.cpp
// Version timestamp: 9-4-2018, 4:23 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# include "temperature_types.h"

celcius::celcius(celcius_variant &r)
{
	celcius_variant c = r;
	this->degrees_celcius = boost::apply_visitor(tcelcius_variant(), c);
}

celcius::operator kelvin()
{
	double temp = this->degrees_celcius + 273.15;
	return (temp);
}

celcius::operator farenheit()
{
	double temp = this->degrees_celcius * (9.0 / 5.0) + 32.0;
	return (temp);
}

void celcius::operator=(celcius_variant &temp2) 
{
	this->degrees_celcius = boost::apply_visitor(tcelcius_variant(), temp2);
}

double celcius::operator-(celcius_variant temp2) 
{
	double temp = this->degrees_celcius - boost::apply_visitor(tcelcius_variant(), temp2);
	return (temp);
}

double celcius::operator+(celcius_variant temp2) 
{
	double temp = this->degrees_celcius + boost::apply_visitor(tcelcius_variant(), temp2);
	return (temp);
}

double celcius::operator++(int incr) 
{
	this->degrees_celcius +=  1.0;
	return (this->degrees_celcius);
}

double celcius::operator--(int incr) 
{
	this->degrees_celcius -=  1.0;
	return (this->degrees_celcius);
}

double celcius::operator*(celcius_variant temp2) 
{
	double temp = this->degrees_celcius * boost::apply_visitor(tcelcius_variant(), temp2);
	return (temp);
}

double celcius::operator/(celcius_variant temp2) 
{
	double temp = this->degrees_celcius / boost::apply_visitor(tcelcius_variant(), temp2);
	return (temp);
}

double celcius::operator()(celcius_variant temp2) 
{
	double temp = boost::apply_visitor(tcelcius_variant(), temp2);
	return (temp);
}

double celcius::operator()() 
{
	double temp = this->degrees_celcius;
	return (temp);
}

double celcius::operator-=(celcius_variant temp2) 
{
	this->degrees_celcius = this->degrees_celcius - boost::apply_visitor(tcelcius_variant(), temp2);
	return (this->degrees_celcius);
}

double celcius::operator+=(celcius_variant temp2) 
{
	this->degrees_celcius = this->degrees_celcius + boost::apply_visitor(tcelcius_variant(), temp2);
	return (this->degrees_celcius);
}

double celcius::operator*=(celcius_variant temp2) 
{
	this->degrees_celcius = this->degrees_celcius * boost::apply_visitor(tcelcius_variant(), temp2);
	return (this->degrees_celcius);
}

double celcius::operator/=(celcius_variant temp2) 
{
	this->degrees_celcius = this->degrees_celcius / boost::apply_visitor(tcelcius_variant(), temp2);
	return (this->degrees_celcius);
}

double celcius::operator>(celcius_variant temp2) 
{
	if (this->degrees_celcius > boost::apply_visitor(tcelcius_variant(), temp2)) return (1);
	return (0);
}

double celcius::operator<(celcius_variant temp2) 
{
	if (this->degrees_celcius < boost::apply_visitor(tcelcius_variant(), temp2)) return (1);
	return (0);
}

double celcius::operator==(celcius_variant temp2) 
{
	if (this->degrees_celcius == boost::apply_visitor(tcelcius_variant(), temp2)) return (1);
	return (0);
}

double celcius::operator>=(celcius_variant temp2) 
{
	if (this->degrees_celcius >= boost::apply_visitor(tcelcius_variant(), temp2)) return (1);
	return (0);
}
double celcius::operator<=(celcius_variant temp2) 
{
	if (this->degrees_celcius <= boost::apply_visitor(tcelcius_variant(), temp2)) return (1);
	return (0);
}

Vapor Pressure Deficit classes:

Moved, see post with the VPD class in thread below (ran out of space to make updates in this post).

Definitions :

//
// Constants.h
// Version timestamp: 9-26-2018, 10:45 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# pragma once

/* Euler's constant */
# define EULER 2.7182818284590452353602875

/* Avagadros Constant SI unit (1/mol) */
# define NA (6.022140758 * pow(10,23))

/* Boltzmann's constant (J/K) */
# define BOLTZMANN (1.38064852 * (pow(10,āˆ’23)))


/* Stefan-Boltzmann's constant SI units (W/(m^2.K^4)) */
# define STEFAN_BOLTZMANN (5.67036713 * (pow(10,-8)))

/* Latent heat of vaporization 1ATM and 0 degrees C SI units (J/g) */
# define L0 2500

/* Latent heat of vaporization 1ATM and 20 degrees C SI units (J/g) */
# define L20 2450

/* Latent heat of vaporization 1ATM and 100 degrees C SI units (J/g) */
# define L100 2260

/* Density of dry air 1ATM and 0 degrees C SI units (kg/m^3) */
# define p_air0 1.2922

/* Isobaric heat capacity of dry air 1ATM and 0 degrees C SI units (J/kg K) */
# define Cp_air0 1010

/* Molecular weight of water (g/mol) */
# define M_H20 18.0


/**************/
/* Conversion */
/**************/
# define TO_DEGREE(radians) ((radians) * 57.295779513082320876798154814105)
# define TO_RADIAN(degrees) ((degrees) * 0.017453292519943295769236907684886)


/*********/
/* Time  */
/********/
/* Julian J2000 */
# define J2000 (2451545.0)

/* Julian UNIX epoch 0h Jan 1, 1970 */
# define UNIX_JULIAN_EPOCH (2440587.5)

/* Julian days per year */
# define JULIAN_DAYS_YEAR (365.25)

/* Julian days per century */
# define JULIAN_DAYS_CENTURY  (JULIAN_DAYS_YEAR) * 100.0

/* Solar days per century */
# define SOLAR_CENTURY (36524.22)

/* Hours per day*/
# define HOURS_DAY (24.0)

/* Minutes per hour */
# define MINUTES_HOUR (60.0)

/* Minutes per day */
# define MINUTES_DAY (MINUTES_HOUR) * (HOURS_DAY)

/* Seconds per minute*/
# define SECONDS_MINUTE (60.0)

/* Seconds per hour (3600) */
# define SECONDS_HOUR ((SECONDS_MINUTE) * (MINUTES_HOUR))

/* Seconds per day (86400) */
# define SECONDS_DAY ((HOURS_DAY) * (SECONDS_HOUR))

/* Mean tropical days per year */
# define MEAN_TROPICAL_DAYS_YEAR (365.242189)

/* Mean sidereal days per year */
# define SIDEREAL_DAYS_YEAR (365.25636)


/**************/
/* Distance   */
/**************/

/* Astronomical units. Kilometers per AU */ 
# define AU_KM (149597870.700) 


/**************/
/*   Solar   */
/*************/

/* Solar constant W/m^2 (averaged of a year) Page, 1986 */
# define SOLAR_CONSTANT 1366.9444

/* Planetary Atmospheric Albedo Mean */
# define ALBEDO_MEAN_Rp 0.3

/* Sun Blackbody Temperature in Kelvin */
# define SUN_BLACKBODY_TEMP 5777

/**************/
/* Pressure   */
/*************/

/* Average atmospheric pressure at sea level (Pascals) */
# define ATM1_AVE 101325.0
	
/* Pascals to millibars */
# define TO_MILLIBAR(pascal) (pascal / 100.0)
	
/* Millibars to pascals */
# define TO_PASCAL(millibars) (millibars * 100.0)

CC BY-SA 4.0

21 Likes

While this is not of help in my world, it didā€™t take me long to realize how helpful, and what a time saver this is for those would need it.

Thanks for posting this @Northern_Loki

4 Likes

The following C++ class serves as an example of how to generate the traditional VPD charts using the classes provided in this thread. Here is an example of the generated VPD charts for the difference stages in a plants life where there is no temperature difference between the leaf and the surrounding atmosphere.

The values will vary depending on the leaf temperatures which hints towards more complexity ahead, transpiration.

Edit: Changed units to celcius
Edit: 9-2 made the text COLOR namespace anonymous to avoid namespace conflicts.

Example generated VPD for propagation and early vegetation with leaf temperature same as environment

Example generated VPD during late vegetation and early flowering with leaf temperature same as environment

Example generated VPD during mid to late flowering with leaf temperature same as environment

//
// vpd_text_table.h
//
// Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# pragma once
# include <ostream>
# include <boost/format.hpp>
# include "vpd.h"

class vpd_text_table : public vpd, farenheit
{
public:
	vpd_text_table();
	vpd_text_table(float leaf_temperature_differential);
	~vpd_text_table();
	void print_table(float temperature_difference, float min, float max);
};
//
// vpd_text_table.cpp
//
// Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//

// This source serves as an example of utilizing the vpd class to generate vpd charts.
//
// Usage example:
//
// // Generate charts for the case where leaf temperature difference is known to be 1 degree greater than the environment.
// vpd_text_table(1);
//
// // Generate charts for the case where leaf temperature difference is unknown or the leaf temperature equals the environment.
// vpd_text_table();



# include "vpd_text_table.h"


namespace
{
	enum Code
	{
		FG_RED     = 31,
		FG_GREEN   = 32,
		FG_YELLOW  = 33,
		FG_BLUE    = 34,
		FG_MAGENTA = 35,
		FG_DEFAULT = 39,
		BG_RED     = 41,
		BG_GREEN   = 42,
		BG_BLUE    = 44,
		BG_DEFAULT = 49
	};
	
	std::ostream& operator<<(std::ostream& os, Code code) 
	{
		return os << "\033[" << static_cast<int>(code) << "m";
	}
}


/* Calculate and print VPD tables for the case where the leaf temperatures are the same as the atmosphere. */
vpd_text_table::vpd_text_table()
{
	/* The following are examples VPD values for different stages of plant growth */
	/* VPD is actually quite a bit more complicated than simply looking at temperature and humidity */
	/* but, this will provide a general goal until you are looking at actual transpiration estimations. Set these to your expectations. */
	float early_veg_min = 0.4;
	float early_veg_max = 0.8;
	float late_veg_min = 0.8;
	float late_veg_max = 1.2;
	float late_flower_min = 1.2;
	float late_flower_max = 1.6;
	
	float leaf_diff = 0;
	
	std::cout << boost::format("%=8i")  % " " << "Suggested VPD during propagation and early vegetation:" << std::endl;
	print_table(leaf_diff, early_veg_min, early_veg_max);
	std::cout << boost::format("%=8i")  % " " << "Suggested VPD during late vegetation and early flowering:" << std::endl;
	print_table(leaf_diff, late_veg_min, late_veg_max);	
	std::cout << boost::format("%=8i")  % " " << "Suggested VPD during mid to late flowering:" << std::endl;
	print_table(leaf_diff, late_flower_min, late_flower_max);	
	
}

/* Calculate and print VPD tables for the case where the leaf temperatures are different than the surrounding atmosphere. */
/* leaf_temperature_differential is the difference between the leaf and the atmosphere in degrees celcius */
vpd_text_table::vpd_text_table(float leaf_temperature_differential)
{
	/* The following are examples VPD values for different stages of plant growth */
	/* VPD is actually quite a bit more complicated than simply looking at temperature and humidity */
	/* but, this will provide a general goal until you are looking at actual transpiration estimations. Set these to your expectations. */
	float early_veg_min = 0.4;
	float early_veg_max = 0.8;
	float late_veg_min = 0.8;
	float late_veg_max = 1.2;
	float late_flower_min = 1.2;
	float late_flower_max = 1.6;
	
	float leaf_diff = leaf_temperature_differential;
	
	std::cout << boost::format("%=8i")  % " " << "Suggested VPD during" << FG_YELLOW << " propagation and early vegetation" << FG_DEFAULT << " with leaf temperature difference of " << leaf_temperature_differential << " degree :" << std::endl;
	print_table(leaf_diff, early_veg_min, early_veg_max);
	std::cout << boost::format("%=8i")  % " " << "Suggested VPD during" << FG_YELLOW << " late vegetation and early flowering" << FG_DEFAULT << " with leaf temperature difference of " << leaf_temperature_differential << " degree :" << std::endl;
	print_table(leaf_diff, late_veg_min, late_veg_max);	
	std::cout << boost::format("%=8i")  % " " << "Suggested VPD during" << FG_YELLOW << " mid to late flowering" << FG_DEFAULT << " with leaf temperature difference of " << leaf_temperature_differential << " degree :" << std::endl;
	print_table(leaf_diff, late_flower_min, late_flower_max);	
	
}


vpd_text_table::~vpd_text_table()
{
}


void vpd_text_table::print_table(float temperature_difference, float min, float max)
{
	float humidity = 0;
	float humidity_step = 5;
	celcius temperature = 0;
	
	std::cout << boost::format("%=8i")  % " " << FG_BLUE << "Relative Humidity (\%) ----->" << FG_DEFAULT << std::endl;
	
	std::cout << boost::format("%=8i|")  % " ";
	for (humidity = 0; humidity <= 100; humidity += humidity_step)
	{
		std::cout << boost::format("%=8s|")  % "--------";
	}
	std::cout << std::endl;
	
	std::cout << boost::format("%=8i|")  % " ";
	for (humidity = 0; humidity <= 100; humidity += humidity_step)
	{
		std::cout << boost::format("%=8i|")  % humidity;
	}
	std::cout << std::endl;
	
	int temp_string_cnt = 0;
	const char temp_string[255] = "                      Temperature degrees celcius                        ";
	
	for (temperature = 5; temperature <= 40; temperature += 1)
	{
		std::cout << FG_BLUE << boost::format("%=1c")  % temp_string[temp_string_cnt++] << FG_DEFAULT;
		std::cout << boost::format("%=7i|")  % " ";
		for (humidity = 0; humidity <= 100; humidity += humidity_step)
		{
			std::cout << boost::format("%=8s|")  % "--------";
		}
		std::cout << std::endl;
	
		std::cout << FG_BLUE << boost::format("%=1c")  % temp_string[temp_string_cnt++] << FG_DEFAULT;
		std::cout << boost::format("%=7i|")  % temperature;

		for (humidity = 0; humidity <= 100; humidity += humidity_step)
		{
			vpd pressure_deficit = vpd(temperature, temperature + temperature_difference, (float)humidity / 100.0);
			if ((pressure_deficit() < min) || (pressure_deficit() > max))
			{
				std::cout << FG_RED << boost::format("%=8.3f")  % pressure_deficit << FG_DEFAULT << "|";
			}
			else
			{
				std::cout << FG_GREEN << boost::format("%=8.3f")  % pressure_deficit << FG_DEFAULT << "|";
			}
			
		}
		std::cout << std::endl;
	}
	
	std::cout << boost::format("%=8i|")  % " ";
	for (humidity = 0; humidity <= 100; humidity += humidity_step)
	{
		std::cout << boost::format("%=8s|")  % "--------";
	}
	std::cout << std::endl << std::endl;
}

CC BY-SA 4.0

10 Likes

You are most certainly welcome.

One day when you dive down this rabbit hole, youā€™ll have a reference to start fromā€¦

6 Likes

These are awesome dude :thumbsup: ā€¦ If I could figure out how to work source code stuff Iā€™d be set lol
Great post my man, you get Hoodinis thread of the month award for sure :trophy:

2 Likes

I canā€™t be thankful for this enough! :grin:
Iā€™ve been itching to use my c++ knowledge somewhere useful. I knew at least learning the basics in HS when I had nothing else to do was a good idea. My teacher just gave me his college books to read as I ran out of stuff in the school to read.

Made for a fun year. 15 minutes of work and 5 hours of play. The other 45 was at the cafeteria.

3 Likes

The following C++ class serves as an example of how to check the VPD values and respond to them for use in a control system using the classes provided in this thread. Here is an example of the checking the state of VPD with user defined parameters.

Nominal:

Out of range:

//
// vpd_runtime_test.h
//
// Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# pragma once
# include <ostream>
# include <boost/format.hpp>
# include "vpd.h"
class vpd_runtime_test : public vpd, celcius
{
public:
	vpd_runtime_test();
	~vpd_runtime_test();
	void print_table(celcius temperature, float actual_humidity, float temperature_difference, float min, float max);
	float print_status(float vapor_pressure_deficit, float min_vpd, float max_vpd);
	float check_vpd(celcius temperature, float humidity, float min_vpd, float max_vpd);
	float check_vpd(celcius temperature, celcius leaf_temperature, float humidity, float min_vpd, float max_vpd);
	float check_vpd_and_print(celcius temperature, float humidity, float min_vpd, float max_vpd);
	float check_vpd_and_print(celcius temperature, celcius leaf_temperature, float humidity, float min_vpd, float max_vpd);
	double calc_boundary(celcius temperature, celcius leaf_temperature, float humidity);
};
//
// vpd_runtime_test.cpp
//
// Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//

// This source serves as an example of utilizing the vpd class for use with control systems to check and respond to VPD conditions.
//
// Usage example:
//
// vpd_runtime_test vpd_runtime;
// farenheit test_temp = 75.0;
//	
// float vpd_result = vpd_runtime.check_vpd(test_temp, test_temp, 60.0, 0.8, 1.2);
// if (vpd_result != 0.0)
// {
//	 /* Do something to alert / correct the VPD */
// }
//	
// vpd_runtime.check_vpd_and_print(test_temp, test_temp, 60.0, 0.8, 1.2);
//	
// test_temp = 90.5;
// vpd_runtime.check_vpd_and_print(test_temp, test_temp, 60.0, 0.8, 1.2);

# include "vpd_runtime_test.h"


namespace
{
	enum Code
	{
		FG_RED     = 31,
		FG_GREEN   = 32,
		FG_YELLOW  = 33,
		FG_BLUE    = 34,
		FG_MAGENTA = 35,
		FG_DEFAULT = 39,
		BG_RED     = 41,
		BG_GREEN   = 42,
		BG_BLUE    = 44,
		BG_DEFAULT = 49
	};
	
	std::ostream& operator<<(std::ostream& os, Code code) 
	{
		return os << "\033[" << static_cast<int>(code) << "m";
	}
}


vpd_runtime_test::vpd_runtime_test()
{
}


vpd_runtime_test::~vpd_runtime_test()
{
}

float vpd_runtime_test::print_status(float vapor_pressure_deficit, float min_vpd, float max_vpd)
{
	std::cout << std::endl;
	
	if (vapor_pressure_deficit > max_vpd)
	{
		std::cout << boost::format("%=8i")  % " " << FG_RED << "WARN :" << FG_DEFAULT << "Vapor pressure deficit is too high (" << vapor_pressure_deficit << ")" << std::endl;
		return (vapor_pressure_deficit - max_vpd);
	}
	else if (vapor_pressure_deficit < min_vpd)
	{
		std::cout << boost::format("%=8i")  % " " << FG_RED << "WARN :" << FG_DEFAULT << "Vapor pressure deficit is too low (" << vapor_pressure_deficit << ")" << std::endl;
		return (vapor_pressure_deficit - min_vpd);
	}
	else
	{
		std::cout << boost::format("%=8i")  % " " << FG_GREEN << "OK :" << FG_DEFAULT << "Vapor pressure deficit is nominal (" << vapor_pressure_deficit << ")" << std::endl;
		return (0.0);
	}
}


float vpd_runtime_test::check_vpd(celcius temperature, float humidity, float min_vpd, float max_vpd)
{
	double vapor_pressure_deficit = calc_boundary(temperature, temperature, humidity);
	return (print_status(vapor_pressure_deficit, min_vpd, max_vpd));
}

float vpd_runtime_test::check_vpd(celcius temperature, celcius leaf_temperature, float humidity, float min_vpd, float max_vpd)
{
	double vapor_pressure_deficit = calc_boundary(temperature, leaf_temperature, humidity);
	return (print_status(vapor_pressure_deficit, min_vpd, max_vpd));
}

float vpd_runtime_test::check_vpd_and_print(celcius temperature, float humidity, float min_vpd, float max_vpd)
{
	double vapor_pressure_deficit = calc_boundary(temperature, temperature, humidity);
	float result = print_status(vapor_pressure_deficit, min_vpd, max_vpd);
	print_table(temperature, humidity, 0, min_vpd, max_vpd);
	return (result);
}

float vpd_runtime_test::check_vpd_and_print(celcius temperature, celcius leaf_temperature, float humidity, float min_vpd, float max_vpd)
{
	double vapor_pressure_deficit = calc_boundary(temperature, leaf_temperature, humidity);
	float result = print_status(vapor_pressure_deficit, min_vpd, max_vpd);
	print_table(temperature, humidity, (leaf_temperature - temperature), min_vpd, max_vpd);
	return (result);
}

double vpd_runtime_test::calc_boundary(celcius temperature, celcius leaf_temperature, float humidity)
{
	vpd pressure_deficit = vpd(temperature, leaf_temperature, (float)humidity / 100.0);
	return (pressure_deficit.vapor_pressure_deficit);
}


void vpd_runtime_test::print_table(celcius actual_temperature, float actual_humidity, float temperature_difference, float min, float max)
{
	float humidity = 0;
	float humidity_step = 5;
	celcius temperature = 0;
	celcius temperature_step = 1;
	
	std::cout << boost::format("%=8i")  % " " << FG_BLUE << "Relative Humidity (\%) ----->" << FG_DEFAULT << std::endl;
	
	std::cout << boost::format("%=8i|")  % " ";
	for (humidity = 0; humidity <= 100; humidity += humidity_step)
	{
		std::cout << boost::format("%=8s|")  % "--------";
	}
	std::cout << std::endl;
	
	std::cout << boost::format("%=8i|")  % " ";
	for (humidity = 0; humidity <= 100; humidity += humidity_step)
	{
		std::cout << boost::format("%=8i|")  % humidity;
	}
	std::cout << std::endl;
	
	int temp_string_cnt = 0;
	const char temp_string[255] = "                      Temperature degrees celcius                        ";
	
	for (temperature = 5; temperature <= 40; temperature += temperature_step)
	{
		std::cout << FG_BLUE << boost::format("%=1c")  % temp_string[temp_string_cnt++] << FG_DEFAULT;
		std::cout << boost::format("%=7i|")  % " ";
		for (humidity = 0; humidity <= 100; humidity += humidity_step)
		{
			std::cout << boost::format("%=8s|")  % "--------";
		}
		std::cout << std::endl;
	
		std::cout << FG_BLUE << boost::format("%=1c")  % temp_string[temp_string_cnt++] << FG_DEFAULT;
		std::cout << boost::format("%=7i|")  % temperature;

		for (humidity = 0; humidity <= 100; humidity += humidity_step)
		{
			if ((actual_humidity >= humidity) && (actual_humidity <= (humidity + humidity_step)) && (actual_temperature >= temperature) && (actual_temperature <= (temperature + temperature_step)))
			{
				vpd pressure_deficit = vpd(actual_temperature, actual_temperature + temperature_difference, (float)actual_humidity / 100.0);
				if ((pressure_deficit() < min) || (pressure_deficit() > max))
				{
					std::cout << BG_RED << FG_YELLOW << boost::format("%=8.3f")  % pressure_deficit << BG_DEFAULT << FG_DEFAULT << "|";
				}
				else
				{
					std::cout << BG_GREEN << boost::format("%=8.3f")  % pressure_deficit << BG_DEFAULT << "|";
				}	
			}
			else
			{
				vpd pressure_deficit = vpd(temperature, temperature + temperature_difference, (float)humidity / 100.0);
				if ((pressure_deficit() < min) || (pressure_deficit() > max))
				{
					std::cout << FG_RED << boost::format("%=8.3f")  % pressure_deficit << FG_DEFAULT << "|";
				}
				else
				{
					std::cout << FG_GREEN << boost::format("%=8.3f")  % pressure_deficit << FG_DEFAULT << "|";
				}
			}
		}
		std::cout << std::endl;
	}
	
	std::cout << boost::format("%=8i|")  % " ";
	for (humidity = 0; humidity <= 100; humidity += humidity_step)
	{
		std::cout << boost::format("%=8s|")  % "--------";
	}
	std::cout << std::endl << std::endl;
}

CC BY-SA 4.0

3 Likes

Thanks.

I certainly understand that software and the associated jazz can appear intimidating. There is a whole bunch of preliminaries that are needed to get going if starting from scratch which leads to ā€œhow the hell do I even get startedā€. But, now days there are quite a few embedded system kits that can be had on the cheap with tons of community support. If you have some ambitions to try building your own thing, you might be pleasantly surprised that itā€™s not as hard as it use to be! Kind-of a learn as you go thingā€¦

4 Likes

Cool! Let us know what you create!

2 Likes

I will be. Just have to fix that PC now. Bought it with a faulty motherboard and ram cards. Iā€™ve already replaced the hard drive and graphics card. Iā€™d rather not write on a PC that can fail at any moment. Iā€™ll see if I canā€™t use my phone for the basics

1 Like

By the way, forgot to mention this is c++11 and uses the Boost libraries (which is fairly common). May work with other c++ versions.

2 Likes

Although VPD and Farenheit concersion are of no use to me
I do appreciate the effort that has gone into this and the fact you CCā€™d them is cool
Nice one

Keep thinking about an arduino contoller but my timer system is working ok right now
when it fails ill probably build a proper controller

1 Like

More to come over time. Perhaps, some future item will give you some ideas and help kickstart your arduino project :wink:

3 Likes

That just reminded me of my buddy who makes those all the time. I could probably ask him to help out. Heā€™s starting a grow for the first time soon

2 Likes

Ah this is awesome. I havenā€™t coded in C++ since 2000 but maybe Iā€™ll convert these over to C# so I can tie it into my dashboard. Nice work!

3 Likes

Vapor Pressure Deficit classes:

edit: 10/1/2018 Added additional algorithms for use in the calculation of the vapor pressure. These can be selected individually when using this class otherwise the default algorithm is utilized. Different algorithms have different accuracy depending on the environmental conditions. The more accurate algorithms can also have greater runtime complexity (bigO). In most cases, the default algorithm will be more than accurate. Error will be primarily due to the sensors.
edit: Addition of dewpoint calculation.
edit: Addition of precipitable water column estimation.
This class is used in conjunction with the aforementioned classes in the OP. I moved this class out of the OP since there is a limit on individual post size and the class has become larger.

//
// vpd.h
// Version timestamp: 10-4-2018, 1:29 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//
# pragma once
# include <boost/variant.hpp>
# include "temperature_types.h"

enum VPD_ALGORITHMS : unsigned short
{
    VPD_BUCK_1981,
    VPD_BUCK_1996,
    VPD_GOFF,
    VPD_WMO
};

class vpd : public celcius, farenheit, kelvin
{
public:
    vpd();
    vpd(celcius_variant temperature, float humidity);
    vpd(celcius_variant temperature, float relative_humidity, VPD_ALGORITHMS algorithm);
    vpd(celcius_variant temperature, celcius_variant leaf_temperature, float humidity);
    vpd(celcius_variant temperature, celcius_variant leaf_temperature, float relative_humidity, VPD_ALGORITHMS algorithm);
    vpd(const vpd& c); 
    ~vpd();
    void calculate_vpd(celcius temperature, celcius temperature_leaf, float relative_humidity, VPD_ALGORITHMS algorithm);
    double calculate_vapor_pressure(celcius temperature, unsigned int method);
    double calculate_buck_1981(celcius temperature);
    double calculate_buck_1996(celcius temperature);
    double calculate_wmo(celcius temperature);
    double calculate_goff_gratch(celcius temperature);
    double calculate_dewpoint(celcius temperature, double relative_humidity);
    double calculate_precipital_water(celcius dewpoint);
    double operator()(celcius_variant temperature, float relative_humidity);
    double operator()(celcius_variant temperature, celcius_variant temperature_leaf, float relative_humidity); 
    double operator()(celcius_variant temperature, float relative_humidity, VPD_ALGORITHMS algorithm);
    double operator()(celcius_variant temperature, celcius_variant temperature_leaf, float relative_humidity, VPD_ALGORITHMS algorithm); 
    double operator()();
    friend std::ostream& operator<<(std::ostream& lhs, const vpd& rhs)
    {
        return (lhs << rhs.vapor_pressure_deficit);
    }
    
    celcius degrees_celcius = 0.0;
    celcius degrees_celcius_leaf = 0.0;
    celcius dewpoint_atmosphere;
    celcius dewpoint_leaf;
    double  precipital_h2o;
    float   humidity = 0.0;
    double saturation_vapor_pressure_atmosphere = 0.0;
    double saturation_vapor_pressure_leaf = 0.0;
    double vapor_pressure_atmosphere = 0.0;
    double vapor_pressure_leaf = 0.0;
    double vapor_pressure_deficit = 0.0;
};

//
// vpd.cpp
// Version timestamp: 10-4-2018, 1:30 PM
//
// Attribution : Copyright (c) 2018 Northern_Loki (sha256::6F290BF833967127BE26C92C8F6B1C1A3949C55A7EABCEF3ECC785CD2D38D30D)
// License is granted under the Creative Commons Attribution-ShareAlike 4.0 International.  https://creativecommons.org/licenses/by-sa/4.0/
//

// This source code may be utilized to calculate the vapor pressure deficit between plant mass and the surrounding environment.
// Requires the temperature conversion classes celcius, farenheit, and kelvin
//
// Some usage examples:
//
//  /* Determine the vapor pressure deficit for 80 degrees farenheit and 60 percent relative humidity. */
//  vpd vapor_pressure_deficit((farenheit) 80, 0.60);
//  std::cout << "Vapor pressure deficit" << vapor_pressure_deficit << std::endl;
//
//  /* Determine the vapor pressure deficit for 23 degrees celcius and 40 percent relative humidity. */
//  vpd vapor_pressure_deficit((celcius) 23, 0.40);
//  std::cout << "Vapor pressure deficit" << vapor_pressure_deficit << std::endl;
//
//  /* Using printf. */
//  vpd vapor_pressure_deficit((celcius) 23, 0.40);
//  printf("Vapor pressure deficit %8.3f \n",vapor_pressure_deficit());
//
//  /* If you know or have estimated the actual leaf temperature. */
//  vpd vapor_pressure_deficit((farenheit) 80, (farenheit) (80+1), 0.60);
//  std::cout << "Vapor pressure deficit" << vapor_pressure_deficit << std::endl;
//

# include "vpd.h"

/* Empty constructor for vapor pressure deficit. */
vpd::vpd()
{
    this->humidity = 0.0;
    this->degrees_celcius = 0.0;
    this->degrees_celcius_leaf = 0.0;
    this->vapor_pressure_atmosphere = 0.0;
    this->vapor_pressure_deficit = 0.0;
    this->vapor_pressure_leaf = 0.0;
}

/* Constructor */
/* Calculate the vapor pressure deficit based on the environment temperature and the relative humidity. */
/* In this case, the temperature of the plant mass is considered to be at the same temperature as the surrounding environment. */
vpd::vpd(celcius_variant temperature, float relative_humidity)
{
    if (relative_humidity > 1)
    {
        this->humidity = 1.0;
    }
    else if (relative_humidity < 0)
    {
        this->humidity = 0.0;
    }
    else
    {
        this->humidity = relative_humidity;
    }
    
    calculate_vpd(temperature, temperature, relative_humidity, VPD_BUCK_1981);
}

/* Constructor */
/* Calculate the vapor pressure deficit based on the environment temperature and the relative humidity. */
/* In this case, the temperature of the plant mass is considered to be at the same temperature as the surrounding environment. */
vpd::vpd(celcius_variant temperature, float relative_humidity, VPD_ALGORITHMS algorithm)
{
    if (relative_humidity > 1)
    {
        this->humidity = 1.0;
    }
    else if (relative_humidity < 0)
    {
        this->humidity = 0.0;
    }
    else
    {
        this->humidity = relative_humidity;
    }
    
    calculate_vpd(temperature, temperature, relative_humidity, algorithm);
}

/* Constructor */
/* Calculate the vapor pressure deficit based on the environment temperature, leaf temperature and the relative humidity. */
/* In this case, the temperature of the plant mass is used in the calculation of the pressure deficit. */
vpd::vpd(celcius_variant temperature, celcius_variant leaf_temperature, float relative_humidity)
{
    if (relative_humidity > 1)
    {
        humidity = 1.0;
    }
    else if (relative_humidity < 0)
    {
        humidity = 0.0;
    }
    else
    {
        humidity = relative_humidity;
    }
    
    calculate_vpd(temperature, leaf_temperature, relative_humidity, VPD_BUCK_1981);
}

/* Constructor */
/* Calculate the vapor pressure deficit based on the environment temperature, leaf temperature and the relative humidity. */
/* In this case, the temperature of the plant mass is used in the calculation of the pressure deficit and the calculation method is specified. */
vpd::vpd(celcius_variant temperature, celcius_variant leaf_temperature, float relative_humidity, VPD_ALGORITHMS algorithm)
{
    if (relative_humidity > 1)
    {
        humidity = 1.0;
    }
    else if (relative_humidity < 0)
    {
        humidity = 0.0;
    }
    else
    {
        humidity = relative_humidity;
    }
    
    calculate_vpd(temperature, leaf_temperature, relative_humidity, algorithm);
}


/* Copy Constructor */
vpd::vpd(const vpd& c) 
{ 
    this->degrees_celcius = c.degrees_celcius;  
    this->degrees_celcius_leaf = c.degrees_celcius_leaf; 
    this->humidity = c.humidity; 
    this->saturation_vapor_pressure_atmosphere = c.saturation_vapor_pressure_atmosphere;
    this->saturation_vapor_pressure_leaf = c.saturation_vapor_pressure_leaf;
    this->vapor_pressure_atmosphere = c.vapor_pressure_atmosphere; 
    this->vapor_pressure_leaf = c.vapor_pressure_leaf; 
    this->vapor_pressure_deficit = c.vapor_pressure_deficit;
} 

/* Destructor */
vpd::~vpd()
{
}

/* Do the VPD calculations. */
/* VPD units are in kPa. A rough estimate in millibars would be to multiply the results in kPa by ten.  */
void vpd::calculate_vpd(celcius temperature, celcius temperature_leaf, float relative_humidity, VPD_ALGORITHMS algorithm) 
{
    /* Save the temperatures and humidity. */
    this->degrees_celcius = temperature;
    this->degrees_celcius_leaf = temperature_leaf;
    this->humidity = relative_humidity;
    
    /* Calculate the water vapor pressure of the air. */
    this->saturation_vapor_pressure_atmosphere = calculate_vapor_pressure(this->degrees_celcius, algorithm);
    this->vapor_pressure_atmosphere = (humidity) * this->saturation_vapor_pressure_atmosphere;
    
    /* Calculate the water vapor pressure in the leaf. */
    
    /* Leaf has 100% humidity */
    this->saturation_vapor_pressure_leaf = calculate_vapor_pressure(this->degrees_celcius_leaf, algorithm);
    this->vapor_pressure_leaf = (1) * this->saturation_vapor_pressure_leaf;
    
    /* Calculate the water vapor pressure deficit of the air. */
    this->vapor_pressure_deficit = vapor_pressure_leaf - vapor_pressure_atmosphere;
	
	/* Calculate the dewpoints */
	this->dewpoint_atmosphere = calculate_dewpoint(temperature, relative_humidity);
	this->dewpoint_leaf = calculate_dewpoint(temperature_leaf, relative_humidity);
	
	/* Estimate the percipital H20 water column (in cm at sea level) */
	this->precipital_h2o = calculate_precipital_water(this->dewpoint_atmosphere);
	
}

double vpd::calculate_vapor_pressure(celcius temperature, unsigned int method)
{
    double vapor_pressure = 0.0;
    
    switch(method)
    {
    case VPD_BUCK_1981:
        vapor_pressure = calculate_buck_1981(temperature);
        break;
    case VPD_BUCK_1996:
        vapor_pressure = calculate_buck_1996(temperature);
        break;
    case VPD_GOFF:
        vapor_pressure = calculate_goff_gratch(temperature);
        break;
    case VPD_WMO:
        vapor_pressure = calculate_wmo(temperature);
        break;
    default: 
        vapor_pressure = calculate_buck_1981(temperature);
        break;
    }
    
    return (vapor_pressure);	
}

double vpd::calculate_buck_1981(celcius temperature)
{
    /* Buck, A. L., New equations for computing vapor pressure and enhancement factor, J. Appl. Meteorol., 20, 1527-1532, 1981. */
    double vapor_pressure = 0.61121 * pow(EULER, (17.502*temperature()) / (temperature() + 240.97));
    return (vapor_pressure);
}

double vpd::calculate_buck_1996(celcius temperature)
{
    double vapor_pressure = 0.61121 * pow(EULER, ((18.678 - temperature() / 234.5)*temperature()) / (temperature() + 257.14));
    return (vapor_pressure);
}

double vpd::calculate_wmo(celcius temperature)
{
    double vapor_pressure = 0.61121 * pow(EULER, (17.62*temperature()) / (temperature() + 243.12));
    return (vapor_pressure);
}

double vpd::calculate_goff_gratch(celcius temperature)
{
    kelvin Tkelvin = temperature;
    
    double a1 = -7.90298 * (373.16 / Tkelvin() - 1.0);
    double a2 = 5.02808 * log10(373.16 / Tkelvin() );
    double a3 = (-1.3816 / 10000000) * (powf(10, 11.344*(1.0 - Tkelvin() / 373.16)) - 1.0);
    double a4 = (8.1328 / 1000) * (powf(10, -3.49149*(373.16 / Tkelvin() - 1.0) - 1.0) );
        
    double vapor_pressure = a1 + a2 + a3 + a4 + log10(1013.246);
    
    return (powf(10.0,vapor_pressure)/10.0);
}

double vpd::calculate_dewpoint(celcius temperature, double relative_humidity)
{
	double dewpoint = 0.0;
	dewpoint = pow((relative_humidity), (1.0 / 8.0)) * (112.0 + (0.9 * temperature())) - 112.0 + (0.1 * temperature());
	
	return (dewpoint);
}

/* Estimate the clearday precipital water in the atomospheric column based on the surface dewpoint. */
/* ā€œModeling Daylight Availability and Irradiance Components from Direct and Global Irradiance,ā€ Perez, et.al. 1990 Solar Energy 44(5) eq. 3. */
/* The coefficients are emperically generated based on monthly averages across 15 sites in the US. */
/* This may have significant error components depending on application and environmental conditions. */
double vpd::calculate_precipital_water(celcius dewpoint)
{
	double prcp = 0.0;
	
	prcp = exp(0.07 * dewpoint() - 0.075);
	
	return (prcp);
}


double vpd::operator()(celcius_variant temperature, float relative_humidity) 
{
    calculate_vpd(temperature, temperature, relative_humidity, VPD_BUCK_1981);
    double temp = this->vapor_pressure_deficit;
    return (temp);
}

double vpd::operator()(celcius_variant temperature, celcius_variant temperature_leaf, float relative_humidity) 
{
    calculate_vpd(temperature, temperature_leaf, relative_humidity, VPD_BUCK_1981);
    double temp = this->vapor_pressure_deficit;
    return (temp);
}

double vpd::operator()(celcius_variant temperature, float relative_humidity, VPD_ALGORITHMS algorithm) 
{
    calculate_vpd(temperature, temperature, relative_humidity, algorithm);
    double temp = this->vapor_pressure_deficit;
    return (temp);
}

double vpd::operator()(celcius_variant temperature, celcius_variant temperature_leaf, float relative_humidity, VPD_ALGORITHMS algorithm) 
{
    calculate_vpd(temperature, temperature_leaf, relative_humidity, algorithm);
    double temp = this->vapor_pressure_deficit;
    return (temp);
}

double vpd::operator()() 
{
    double temp = this->vapor_pressure_deficit;
    return (temp);
}

CC BY-SA 4.0

4 Likes

Is there any chance you could post just the formulas used to calculate VPD?

I donā€™t know C (of any type) and I am wanting to calculate VPD in Python on the fly from live data. I donā€™t need finished code, I can turn formulas into code easily enough.

I have tried to figure it out but it is a bit too alien (only just got my head round OOC in python, it took a long time to remove the virus which is BASIC) for my brain to understand.

3 Likes

There are several VPD algorithms that are essentially estimations applicable to specific circumstances. Here are a couple that are most applicable (with reasonable error) for the temperature ranges weā€™d typically consider. Itā€™s been awhile since I looked at this but from what I recall, three steps, here:

  1. Saturation pressure (aka partial pressure of water)
  2. Vapor pressure
  3. Vapor pressure deficit

(1) Saturation pressure

These calculate the ā€œsaturation pressureā€:

Goff Gratch (1984):

where the equations differ depending if the temperature is > 0 celcius or < 0 celcius, respectively. The variables a(n), b(n), c(n), and g(n) are fixed coefficients:

Note that temperatures (T) input to the Goff-Gratch algorithm is in Kelvin and output is hPa (kPa = hPa / 10).

Another one,

Teten (1960s?):

Accurate for temperatures > 0 Celcius only:

where temperature (t) for Teten is in Celcius and output is kPa.

(2) Vapor Pressure

Then you calculate the vapor pressure of the atmosphere from the saturation pressure:

vapor pressure atmosphere = Rh * Es

and the vapor pressure of the leaf:

vapor pressure leaf = (1) * Es

Es is from the above calculated saturation pressure. Rh is the relative humidity. Calculate both the leaf and the atmosphere vapor pressures.

A leaf has 100% humidity (but the temperature may be different). For simplicity, you can estimate the leaf temperature or just say itā€™s the same as the surrounding air. The leaf temperature does impact the VPD and is an important consideration for determining the leaf transpiration.

(3) Vapor pressure deficit

VPD = (vapor pressure leaf) - (vapor pressure atmosphere)

Done. Pay attention to consistency of the units when applying these algorithms.

Some interesting observations, notice that the vapor pressure (partial pressures) is distilled down to the difference in the relative humidity between the leaf and the surrounding air! A non-dead leaf is considered to be ā€œ100% humidā€ no matter the temperature. If the air happens to also be 100% humid and the temperature of the leaf and the air happen to be the same, VPD would be zero = no transpiration possible.


The specific portion for calculating the saturation pressure in the C / C++ code is (for Goff Gratch):

double a1 = -7.90298 * (373.16 / Tkelvin() - 1.0);
double a2 = 5.02808 * log10(373.16 / Tkelvin() );
double a3 = (-1.3816 / 10000000) * (powf(10, 11.344*(1.0 - Tkelvin() / 373.16)) - 1.0);
double a4 = (8.1328 / 1000) * (powf(10, -3.49149*(373.16 / Tkelvin() - 1.0) - 1.0) );
double vapor_pressure = a1 + a2 + a3 + a4 + log10(1013.246);
return (powf(10.0,vapor_pressure)/10.0);

where TKelvin is the temperature in Kelvin. powf in the above calculates 10^(some number) as a floating point number. log10 is the log base 10 of some number. Other operators are typical mathematical operations. Double means floating point number (not integer). Most of the rest of the code is doing other things such as converting temperatures. Compare this to the above formula for Goff-Gratch.

This would be the ā€œmeatā€ of the problem(step 1). Then apply steps two and three as noted above.

It should be straight forward to convert that portion to over to Python since itā€™s mainly maths. Let me know if you run into trouble.

5 Likes

Iā€™ve been using the VPD class (ported to C#) for quite some time now. I recently went ahead and made a new page that can render the VPD chart around a specific point (Temp/RH). This will be used to render an overlay on my stream showing the real-time VPD using SensorPush data.

Thanks again for this @Northern_Loki ā€“ this was a big help getting started.

8 Likes

Cool! Iā€™m excited that you were able to find it helpful towards adding that feature to your system.

Seeing folk finding use for some of this stuff is encouraging toward future works. Thank you.

3 Likes