Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add units::power (watts) #62155

Merged
merged 2 commits into from
Nov 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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