Skip to content

Commit

Permalink
Add units::power (watts) (#62155)
Browse files Browse the repository at this point in the history
* units::power / watts definition

* Operators between power, energy and time_duration
  • Loading branch information
irwiss authored Nov 24, 2022
1 parent 817e77e commit 43dbd7a
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 1 deletion.
69 changes: 68 additions & 1 deletion src/units.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "units.h"

#include "calendar.h"
#include "json.h"
#include "string_formatter.h"
#include "units.h"

namespace units
{
Expand Down Expand Up @@ -84,6 +86,29 @@ void energy::deserialize( const JsonValue &jv )
*this = read_from_json_string( jv, units::energy_units );
}

template<>
void power::serialize( JsonOut &jsout ) const
{
if( value_ % 1000000 == 0 ) {
jsout.write( string_format( "%d kW", value_ / 1000000 ) ); //NOLINT
} else if( value_ % 1000 == 0 ) {
jsout.write( string_format( "%d W", value_ / 1000 ) ) ; //NOLINT
} else {
jsout.write( string_format( "%d mW", value_ ) ); //NOLINT
}
}

template<>
void power::deserialize( const JsonValue &jv )
{
if( jv.test_int() ) {
// Compatibility with old 0.F saves
*this = from_watt( jv.get_int() );
return;
}
*this = read_from_json_string( jv, units::power_units );
}

template<>
void angle::serialize( JsonOut &jsout ) const
{
Expand Down Expand Up @@ -112,4 +137,46 @@ std::string display( const units::energy v )
return std::to_string( mj ) + ' ' + pgettext( "energy unit: millijoule", "mJ" );
}

std::string display( const units::power v )
{
const int kw = units::to_kilowatt( v );
const int w = units::to_watt( v );
// at least 1 kW and there is no fraction
if( kw >= 1 && static_cast<float>( w ) / kw == 1000 ) {
return std::to_string( kw ) + ' ' + pgettext( "energy unit: kilowatt", "kW" );
}
const int mw = units::to_milliwatt( v );
// at least 1 W and there is no fraction
if( w >= 1 && static_cast<float>( mw ) / w == 1000 ) {
return std::to_string( w ) + ' ' + pgettext( "energy unit: watt", "W" );
}
return std::to_string( mw ) + ' ' + pgettext( "energy unit: milliwatt", "mW" );
}

units::energy operator*( const units::power &power, const time_duration &time )
{
const int64_t mW = units::to_milliwatt<int64_t>( power );
const int64_t seconds = to_seconds<int64_t>( time );
return units::from_millijoule( mW * seconds );
}

units::energy operator*( const time_duration &time, const units::power &power )
{
return power * time;
}

units::power operator/( const units::energy &energy, const time_duration &time )
{
const int64_t mj = to_millijoule( energy );
const int64_t seconds = to_seconds<int64_t>( time );
return from_milliwatt( mj / seconds );
}

time_duration operator/( const units::energy &energy, const units::power &power )
{
const int64_t mj = to_millijoule( energy );
const int64_t mw = to_milliwatt( power );
return time_duration::from_seconds( mj / mw );
}

} // namespace units
93 changes: 93 additions & 0 deletions src/units.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "translations.h"
#include "units_fwd.h" // IWYU pragma: export

class time_duration;

namespace units
{

Expand Down Expand Up @@ -499,6 +501,62 @@ inline constexpr value_type to_kilojoule( const quantity<value_type, energy_in_m
return to_joule( v ) / 1000.0;
}

// Power (watts)

const power power_min = units::power( std::numeric_limits<units::power::value_type>::min(),
units::power::unit_type{} );

const power power_max = units::power( std::numeric_limits<units::power::value_type>::max(),
units::power::unit_type{} );

template<typename value_type>
inline constexpr quantity<value_type, power_in_milliwatt_tag> from_milliwatt(
const value_type v )
{
return quantity<value_type, power_in_milliwatt_tag>( v, power_in_milliwatt_tag{} );
}

template<typename value_type>
inline constexpr quantity<value_type, power_in_milliwatt_tag> from_watt( const value_type v )
{
constexpr value_type max_power_watts = std::numeric_limits<value_type>::max() / 1000;
// Check for overflow - if the power provided is greater than max power, then it
// will overflow when converted to milliwatt
const value_type power = v > max_power_watts ? max_power_watts : v;
return from_milliwatt<value_type>( power * 1000 );
}

template<typename value_type>
inline constexpr quantity<value_type, power_in_milliwatt_tag> from_kilowatt( const value_type v )
{
constexpr value_type max_power_watts = std::numeric_limits<value_type>::max() / 1000;
// This checks for value_type overflow - if the power we are given in watts is greater
// than the max power in watts, overflow will occur when it is converted to milliwatts
// The value we are given is in kilowatts, multiply by 1000 to convert it to watts, for use in from_watt
value_type power = v * 1000 > max_power_watts ? max_power_watts : v * 1000;
return from_watt<value_type>( power );
}

template<typename value_type>
inline constexpr value_type to_milliwatt( const quantity<value_type, power_in_milliwatt_tag> &v )
{
return v / from_milliwatt<value_type>( 1 );
}

template<typename value_type>
inline constexpr value_type to_watt( const quantity<value_type, power_in_milliwatt_tag> &v )
{
return to_milliwatt( v ) / 1000.0;
}

template<typename value_type>
inline constexpr value_type to_kilowatt( const quantity<value_type, power_in_milliwatt_tag> &v )
{
return to_watt( v ) / 1000.0;
}

// Money(cents)

const money money_min = units::money( std::numeric_limits<units::money::value_type>::min(),
units::money::unit_type{} );

Expand Down Expand Up @@ -661,6 +719,11 @@ inline std::ostream &operator<<( std::ostream &o, energy_in_millijoule_tag )
return o << "mJ";
}

inline std::ostream &operator<<( std::ostream &o, power_in_milliwatt_tag )
{
return o << "mW";
}

inline std::ostream &operator<<( std::ostream &o, money_in_cent_tag )
{
return o << "cent";
Expand Down Expand Up @@ -692,6 +755,8 @@ inline std::string quantity_to_string( const quantity<value_type, tag_type> &v )

std::string display( units::energy v );

std::string display( units::power v );

} // namespace units

// Implicitly converted to volume, which has int as value_type!
Expand Down Expand Up @@ -796,6 +861,21 @@ inline constexpr units::quantity<double, units::energy_in_millijoule_tag> operat
return units::from_kilojoule( v );
}

inline constexpr units::power operator"" _mW( const unsigned long long v )
{
return units::from_milliwatt( v );
}

inline constexpr units::power operator"" _W( const unsigned long long v )
{
return units::from_watt( v );
}

inline constexpr units::power operator"" _kW( const unsigned long long v )
{
return units::from_kilowatt( v );
}

inline constexpr units::money operator"" _cent( const unsigned long long v )
{
return units::from_cent( v );
Expand Down Expand Up @@ -957,6 +1037,12 @@ static const std::vector<std::pair<std::string, energy>> energy_units = { {
{ "kJ", 1_kJ },
}
};
static const std::vector<std::pair<std::string, power>> power_units = { {
{ "mW", 1_mW },
{ "W", 1_W },
{ "kW", 1_kW },
}
};
static const std::vector<std::pair<std::string, mass>> mass_units = { {
{ "mg", 1_milligram },
{ "g", 1_gram },
Expand Down Expand Up @@ -991,6 +1077,13 @@ static const std::vector<std::pair<std::string, temperature>> temperature_units
{ "K", 1_K }
}
};

units::energy operator*( const units::power &power, const time_duration &time );
units::energy operator*( const time_duration &time, const units::power &power );

units::power operator/( const units::energy &energy, const time_duration &time );
time_duration operator/( const units::energy &energy, const units::power &power );

} // namespace units

namespace detail
Expand Down
6 changes: 6 additions & 0 deletions src/units_fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class energy_in_millijoule_tag

using energy = quantity<std::int64_t, energy_in_millijoule_tag>;

class power_in_milliwatt_tag
{
};

using power = quantity<std::int64_t, power_in_milliwatt_tag>;

class money_in_cent_tag
{
};
Expand Down
38 changes: 38 additions & 0 deletions tests/units_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ TEST_CASE( "units_have_correct_ratios", "[units]" )
CHECK( M_PI * 1_radians == 1_pi_radians );
CHECK( 2_pi_radians == 360_degrees );
CHECK( 60_arcmin == 1_degrees );

CHECK( 1_mW == units::from_milliwatt( 1 ) );
CHECK( 1_W == units::from_watt( 1 ) );
CHECK( 1_kW == units::from_kilowatt( 1 ) );

CHECK( 1_W * 1_seconds == 1_J );
CHECK( 1_seconds * 1_W == 1_J );
CHECK( 1_J / 1_seconds == 1_W );
CHECK( 1_J / 1_W == 1_seconds );
CHECK( 5_W * 5_minutes == 1.5_kJ );
CHECK( -5_W * 5_minutes == -1.5_kJ );
CHECK( ( 5_kJ / 5_W ) == ( 16_minutes + 40_seconds ) );
CHECK( ( -5_kJ / 5_W ) == -( 16_minutes + 40_seconds ) );
CHECK( ( 5_kJ / -5_W ) == -( 16_minutes + 40_seconds ) );
}

static units::energy parse_energy_quantity( const std::string &json )
Expand All @@ -70,6 +84,30 @@ TEST_CASE( "energy parsing from JSON", "[units]" )
CHECK( parse_energy_quantity( "\"1 mJ -4 J 1 kJ\"" ) == 1_mJ - 4_J + 1_kJ );
}

static units::power parse_power_quantity( const std::string &json )
{
JsonValue jsin = json_loader::from_string( json );
return read_from_json_string<units::power>( jsin, units::power_units );
}

TEST_CASE( "power parsing from JSON", "[units]" )
{
CHECK_THROWS( parse_power_quantity( "\"\"" ) ); // empty string
CHECK_THROWS( parse_power_quantity( "27" ) ); // not a string at all
CHECK_THROWS( parse_power_quantity( "\" \"" ) ); // only spaces
CHECK_THROWS( parse_power_quantity( "\"27\"" ) ); // no energy unit

CHECK( parse_power_quantity( "\"1 mW\"" ) == 1_mW );
CHECK( parse_power_quantity( "\"1 W\"" ) == 1_W );
CHECK( parse_power_quantity( "\"1 kW\"" ) == 1_kW );
CHECK( parse_power_quantity( "\"+1 mW\"" ) == 1_mW );
CHECK( parse_power_quantity( "\"+1 W\"" ) == 1_W );
CHECK( parse_power_quantity( "\"+1 kW\"" ) == 1_kW );

CHECK( parse_power_quantity( "\"1 mW 1 W 1 kW\"" ) == 1_mW + 1_W + 1_kW );
CHECK( parse_power_quantity( "\"1 mW -4 W 1 kW\"" ) == 1_mW - 4_W + 1_kW );
}

static time_duration parse_time_duration( const std::string &json )
{
JsonValue jsin = json_loader::from_string( json );
Expand Down

0 comments on commit 43dbd7a

Please sign in to comment.