From 43dbd7a5b220f5c68254d29fb13695fffb4fc165 Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 24 Nov 2022 06:34:54 -0800 Subject: [PATCH] Add units::power (watts) (#62155) * units::power / watts definition * Operators between power, energy and time_duration --- src/units.cpp | 69 +++++++++++++++++++++++++++++++- src/units.h | 93 ++++++++++++++++++++++++++++++++++++++++++++ src/units_fwd.h | 6 +++ tests/units_test.cpp | 38 ++++++++++++++++++ 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/src/units.cpp b/src/units.cpp index 9c6644479cad4..79fd8733006f2 100644 --- a/src/units.cpp +++ b/src/units.cpp @@ -1,6 +1,8 @@ +#include "units.h" + +#include "calendar.h" #include "json.h" #include "string_formatter.h" -#include "units.h" namespace units { @@ -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 { @@ -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( 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( 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( power ); + const int64_t seconds = to_seconds( 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( 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 diff --git a/src/units.h b/src/units.h index 4417ab7d8aa60..c1e33cf271351 100644 --- a/src/units.h +++ b/src/units.h @@ -19,6 +19,8 @@ #include "translations.h" #include "units_fwd.h" // IWYU pragma: export +class time_duration; + namespace units { @@ -499,6 +501,62 @@ inline constexpr value_type to_kilojoule( const quantity::min(), + units::power::unit_type{} ); + +const power power_max = units::power( std::numeric_limits::max(), + units::power::unit_type{} ); + +template +inline constexpr quantity from_milliwatt( + const value_type v ) +{ + return quantity( v, power_in_milliwatt_tag{} ); +} + +template +inline constexpr quantity from_watt( const value_type v ) +{ + constexpr value_type max_power_watts = std::numeric_limits::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( power * 1000 ); +} + +template +inline constexpr quantity from_kilowatt( const value_type v ) +{ + constexpr value_type max_power_watts = std::numeric_limits::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( power ); +} + +template +inline constexpr value_type to_milliwatt( const quantity &v ) +{ + return v / from_milliwatt( 1 ); +} + +template +inline constexpr value_type to_watt( const quantity &v ) +{ + return to_milliwatt( v ) / 1000.0; +} + +template +inline constexpr value_type to_kilowatt( const quantity &v ) +{ + return to_watt( v ) / 1000.0; +} + +// Money(cents) + const money money_min = units::money( std::numeric_limits::min(), units::money::unit_type{} ); @@ -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"; @@ -692,6 +755,8 @@ inline std::string quantity_to_string( const quantity &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! @@ -796,6 +861,21 @@ inline constexpr units::quantity 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 ); @@ -957,6 +1037,12 @@ static const std::vector> energy_units = { { { "kJ", 1_kJ }, } }; +static const std::vector> power_units = { { + { "mW", 1_mW }, + { "W", 1_W }, + { "kW", 1_kW }, + } +}; static const std::vector> mass_units = { { { "mg", 1_milligram }, { "g", 1_gram }, @@ -991,6 +1077,13 @@ static const std::vector> 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 diff --git a/src/units_fwd.h b/src/units_fwd.h index 4c525536e8c99..bd73d976c1630 100644 --- a/src/units_fwd.h +++ b/src/units_fwd.h @@ -41,6 +41,12 @@ class energy_in_millijoule_tag using energy = quantity; +class power_in_milliwatt_tag +{ +}; + +using power = quantity; + class money_in_cent_tag { }; diff --git a/tests/units_test.cpp b/tests/units_test.cpp index 2af27950eb746..7de736b6a16df 100644 --- a/tests/units_test.cpp +++ b/tests/units_test.cpp @@ -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 ) @@ -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( 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 );