Skip to content

Commit 01da471

Browse files
authored
fix(postgres): Store capacity as bigint (#92)
1 parent 3ad4d5d commit 01da471

File tree

8 files changed

+91
-292
lines changed

8 files changed

+91
-292
lines changed

.github/pull_request_template.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
### Contribution Checklist
2+
13
- [ ] Have you followed the Open Climate Fix [Contribution Guidelines](https://github.com/openclimatefix#github-contributions)?
2-
- [ ] Have you referenced the [Issue](https://github.com/openclimatefix/data-platform/issues) this PR addresses?
4+
- [ ] Have you referenced the [Issue](https://github.com/openclimatefix/data-platform/issues) this PR addresses, where applicable?
35
- [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/Homebrew/brew/pulls) for the same change?
46
- [ ] Have you added a summary of the changes?
5-
- [ ] Have you written new tests for your changes?.
7+
- [ ] Have you written new tests for your changes, where applicable?
68
- [ ] Have you successfully run `make lint` with your changes locally?
7-
- [ ] Have you successfully run `make gen` with your changes locally?
89
- [ ] Have you successfully run `make test` with your changes locally?
10+
11+
> [!Warning]
12+
> PRs may be closed if all the above boxes are not checked.
13+
14+
15+
### Changes in this Pull Request
16+

internal/server/postgres/dataserverimpl.go

Lines changed: 55 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -30,59 +30,6 @@ import (
3030

3131
// --- Reuseable Functions for Route Logic -------------------------------------------------------
3232

33-
// capacityToValueMultiplier return a number, plus the index to raise 10 to the power to
34-
// to get the resultant number of Watts, to the closest power of 3.
35-
// This is an important function which tries to preserve accuracy whilst also enabling a
36-
// large range of values to be represented by two 16 bit integers.
37-
func capacityToValueMultiplier(capacityWatts uint64) (int16, int16, error) {
38-
if capacityWatts == 0 {
39-
return 0, 0, nil
40-
}
41-
42-
currentValue := capacityWatts
43-
exponent := int16(0)
44-
45-
const maxExponent = 18 // Limit to ExaWatts - current generation is ~20PW for the whole world!
46-
47-
// Keep scaling up as long as the value exceeds the int16 limit
48-
for currentValue > math.MaxInt16 {
49-
if exponent >= maxExponent {
50-
return 0, exponent, fmt.Errorf(
51-
"input represents a value greater than %d ExaWatts, which is not supported",
52-
math.MaxInt16,
53-
)
54-
}
55-
56-
// Divide by 1000 to get to the next SI unit prefix
57-
// * add on 500 to round up numbers that are over halfway to the next 10^3
58-
nextValue := (currentValue + 500) / 1000
59-
60-
// Check we haven't accidentally rounded to 0
61-
if nextValue == 0 && currentValue > 0 {
62-
return 0, exponent + 3, fmt.Errorf(
63-
"scaled value rounded to zero from large input %d at potential exponent %d",
64-
capacityWatts, exponent+3)
65-
}
66-
67-
currentValue = nextValue
68-
69-
exponent += 3
70-
}
71-
72-
// This is safe as currentValue is now less than or equal to int16 max
73-
// but I've put a check to really be as safe as possible
74-
if currentValue > math.MaxInt16 {
75-
return 0, exponent, fmt.Errorf(
76-
"scaled value %d exceeds int16 max %d at exponent %d",
77-
currentValue, math.MaxInt16, exponent,
78-
)
79-
}
80-
81-
resultValue := int16(currentValue)
82-
83-
return resultValue, exponent, nil
84-
}
85-
8633
// timeWindowToPgWindow converts a TimeWindow protobuf message to a pair of pgtype.Timestamp values.
8734
func timeWindowToPgWindow(
8835
window *pb.TimeWindow,
@@ -636,11 +583,7 @@ func (s *DataPlatformDataServiceServerImpl) StreamForecastData(
636583
P50Fraction: float32(pred.P50Sip) / 30000.0,
637584
OtherStatisticsFractions: otherStatistics,
638585
CreatedTimestampUtc: timestamppb.New(forecast.CreatedAtUtc.Time),
639-
EffectiveCapacityWatts: uint64(
640-
pred.CapacityIncLimit,
641-
) * uint64(
642-
math.Pow10(int(pred.CapacityUnitPrefixFactor)),
643-
),
586+
EffectiveCapacityWatts: uint64(pred.CapacityWatts),
644587
})
645588
if err != nil {
646589
return err
@@ -741,10 +684,8 @@ func (s *DataPlatformDataServiceServerImpl) GetWeekAverageDeltas(
741684
DeltaFraction: float32(delta.AvgDeltaSip) / 30000.0,
742685
HorizonMins: uint32(delta.HorizonMins),
743686
EffectiveCapacityWatts: uint64(
744-
dbSource.CapacityIncLimit,
745-
) * uint64(
746-
math.Pow10(int(dbSource.CapacityUnitPrefixFactor)),
747-
), // Should this be done over time? I'm not sure how important this is to the response
687+
dbSource.CapacityWatts,
688+
), // Should this be done over time?
748689
}
749690
}
750691

@@ -805,13 +746,9 @@ func (s *DataPlatformDataServiceServerImpl) GetObservationsAsTimeseries(
805746
values := make([]*pb.GetObservationsAsTimeseriesResponse_Value, len(dbObs))
806747
for i, obs := range dbObs {
807748
values[i] = &pb.GetObservationsAsTimeseriesResponse_Value{
808-
ValueFraction: float32(obs.ValueSip) / 30000.0,
809-
TimestampUtc: timestamppb.New(obs.ObservationTimestampUtc.Time),
810-
EffectiveCapacityWatts: uint64(
811-
obs.EffectiveCapacity,
812-
) * uint64(
813-
math.Pow10(int(obs.CapacityUnitPrefixFactor)),
814-
),
749+
ValueFraction: float32(obs.ValueSip) / 30000.0,
750+
TimestampUtc: timestamppb.New(obs.ObservationTimestampUtc.Time),
751+
EffectiveCapacityWatts: uint64(obs.CapacityWatts),
815752
}
816753
}
817754

@@ -945,14 +882,10 @@ func (s *DataPlatformDataServiceServerImpl) GetLatestObservations(
945882
observations := make([]*pb.GetLatestObservationsResponse_Observation, len(dbObs))
946883
for i, obs := range dbObs {
947884
observations[i] = &pb.GetLatestObservationsResponse_Observation{
948-
LocationUuid: obs.GeometryUuid.String(),
949-
TimestampUtc: timestamppb.New(obs.ObservationTimestampUtc.Time),
950-
ValueFraction: float32(obs.ValueSip) / 30000.0,
951-
EffectiveCapacityWatts: uint64(
952-
obs.CapacityIncLimit,
953-
) * uint64(
954-
math.Pow10(int(obs.CapacityUnitPrefixFactor)),
955-
),
885+
LocationUuid: obs.GeometryUuid.String(),
886+
TimestampUtc: timestamppb.New(obs.ObservationTimestampUtc.Time),
887+
ValueFraction: float32(obs.ValueSip) / 30000.0,
888+
EffectiveCapacityWatts: uint64(obs.CapacityWatts),
956889
}
957890
}
958891

@@ -1091,14 +1024,10 @@ func (s *DataPlatformDataServiceServerImpl) GetForecastAtTimestamp(
10911024
values := make([]*pb.GetForecastAtTimestampResponse_Value, len(dbPredictions))
10921025
for i, value := range dbPredictions {
10931026
values[i] = &pb.GetForecastAtTimestampResponse_Value{
1094-
ValueFraction: float32(value.P50Sip) / 30000.0,
1095-
EffectiveCapacityWatts: uint64(
1096-
value.CapacityIncLimit,
1097-
) * uint64(
1098-
math.Pow10(int(value.CapacityUnitPrefixFactor)),
1099-
),
1100-
LocationUuid: value.GeometryUuid.String(),
1101-
LocationName: value.GeometryName,
1027+
ValueFraction: float32(value.P50Sip) / 30000.0,
1028+
EffectiveCapacityWatts: uint64(value.CapacityWatts),
1029+
LocationUuid: value.GeometryUuid.String(),
1030+
LocationName: value.GeometryName,
11021031
Latlng: &pb.LatLng{
11031032
Latitude: value.Latitude,
11041033
Longitude: value.Longitude,
@@ -1144,7 +1073,7 @@ func (s *DataPlatformDataServiceServerImpl) GetLocation(
11441073
l.Debug().
11451074
Str("dp.source.geometry_uuid", dbSource.GeometryUuid.String()).
11461075
Int16("dp.source.type_id", dbSource.SourceTypeID).
1147-
Int64("dp.source.capacity", int64(dbSource.CapacityIncLimit)*int64(math.Pow10(int(dbSource.CapacityUnitPrefixFactor)))).
1076+
Int64("dp.source.capacity", int64(dbSource.CapacityWatts)).
11481077
Str("dp.source.valid_from_utc", dbSource.SysPeriod.Lower.Time.String()).
11491078
Msg("found source")
11501079

@@ -1166,12 +1095,8 @@ func (s *DataPlatformDataServiceServerImpl) GetLocation(
11661095
Latitude: dbSource.Latitude,
11671096
Longitude: dbSource.Longitude,
11681097
},
1169-
EffectiveCapacityWatts: uint64(
1170-
dbSource.CapacityIncLimit,
1171-
) * uint64(
1172-
math.Pow10(int(dbSource.CapacityUnitPrefixFactor)),
1173-
),
1174-
Metadata: metadata,
1098+
EffectiveCapacityWatts: uint64(dbSource.CapacityWatts),
1099+
Metadata: metadata,
11751100
}, nil
11761101
}
11771102

@@ -1217,28 +1142,17 @@ func (s *DataPlatformDataServiceServerImpl) CreateLocation(
12171142
)
12181143
}
12191144

1220-
cp, ex, err := capacityToValueMultiplier(req.EffectiveCapacityWatts)
1221-
if err != nil {
1222-
l.Err(err).Msgf("capacityMwToValueMultiplier(%d)", req.EffectiveCapacityWatts)
1223-
1224-
return nil, status.Error(
1225-
codes.InvalidArgument,
1226-
"Invalid capacity. Ensure capacity is non-negative.",
1227-
)
1228-
}
1229-
12301145
// Set valid from time to now if not provided
12311146
if req.ValidFromUtc == nil {
12321147
req.ValidFromUtc = timestamppb.New(time.Now().UTC().Truncate(time.Minute))
12331148
}
12341149

12351150
csprms := db.CreateSourceEntryParams{
1236-
GeometryUuid: dbLocation.GeometryUuid,
1237-
SourceTypeID: int16(req.EnergySource),
1238-
Capacity: cp,
1239-
CapacityUnitPrefixFactor: ex,
1240-
Metadata: metadata,
1241-
ValidFromUtc: pgtype.Timestamp{Time: req.ValidFromUtc.AsTime(), Valid: true},
1151+
GeometryUuid: dbLocation.GeometryUuid,
1152+
SourceTypeID: int16(req.EnergySource),
1153+
CapacityWatts: int64(req.EffectiveCapacityWatts),
1154+
Metadata: metadata,
1155+
ValidFromUtc: pgtype.Timestamp{Time: req.ValidFromUtc.AsTime(), Valid: true},
12421156
}
12431157

12441158
dbSource, err := querier.CreateSourceEntry(ctx, csprms)
@@ -1254,7 +1168,7 @@ func (s *DataPlatformDataServiceServerImpl) CreateLocation(
12541168
l.Debug().
12551169
Str("dp.source.geometry_uuid", dbSource.GeometryUuid.String()).
12561170
Int16("dp.source.type_id", dbSource.SourceTypeID).
1257-
Int64("dp.source.capacity", int64(dbSource.Capacity)*int64(math.Pow10(int(dbSource.CapacityUnitPrefixFactor)))).
1171+
Int64("dp.source.capacity", int64(dbSource.CapacityWatts)).
12581172
Str("dp.source.valid_from_utc", dbSource.ValidFromUtc.Time.String()).
12591173
Msg("created source entry for location")
12601174

@@ -1267,13 +1181,9 @@ func (s *DataPlatformDataServiceServerImpl) CreateLocation(
12671181
l.Debug().Msg("refreshed sources materialised view")
12681182

12691183
return &pb.CreateLocationResponse{
1270-
LocationUuid: dbLocation.GeometryUuid.String(),
1271-
LocationName: dbLocation.GeometryName,
1272-
EffectiveCapacityWatts: uint64(
1273-
dbSource.Capacity,
1274-
) * uint64(
1275-
math.Pow10(int(dbSource.CapacityUnitPrefixFactor)),
1276-
),
1184+
LocationUuid: dbLocation.GeometryUuid.String(),
1185+
LocationName: dbLocation.GeometryName,
1186+
EffectiveCapacityWatts: uint64(dbSource.CapacityWatts),
12771187
}, nil
12781188
}
12791189

@@ -1319,24 +1229,13 @@ func (s *DataPlatformDataServiceServerImpl) UpdateLocation(
13191229
l.Debug().
13201230
Str("dp.source.geometry_uuid", dbSource.GeometryUuid.String()).
13211231
Int16("dp.source.type_id", dbSource.SourceTypeID).
1322-
Int64("dp.source.capacity", int64(dbSource.Capacity)*int64(math.Pow10(int(dbSource.CapacityUnitPrefixFactor)))).
1232+
Int64("dp.source.capacity", int64(dbSource.CapacityWatts)).
13231233
Str("dp.source.valid_from_utc", dbSource.SysPeriod.Lower.Time.String()).
13241234
Msg("fetched source")
13251235

1326-
// Use existing capacity, unless a new capacity is provided
1327-
cp := dbSource.Capacity
1328-
1329-
ex := dbSource.CapacityUnitPrefixFactor
1236+
capacity := dbSource.CapacityWatts
13301237
if req.NewEffectiveCapacityWatts != nil {
1331-
cp, ex, err = capacityToValueMultiplier(req.GetNewEffectiveCapacityWatts())
1332-
if err != nil {
1333-
l.Err(err).Msgf("capacityMwToValueMultiplier(%d)", req.NewEffectiveCapacityWatts)
1334-
1335-
return nil, status.Error(
1336-
codes.InvalidArgument,
1337-
"Invalid capacity. Ensure new capacity is non-negative.",
1338-
)
1339-
}
1238+
capacity = int64(*req.NewEffectiveCapacityWatts)
13401239
}
13411240

13421241
// Use existing metadata, unless new metadata is provided
@@ -1373,13 +1272,12 @@ func (s *DataPlatformDataServiceServerImpl) UpdateLocation(
13731272

13741273
// Update the source history with a new entry
13751274
csprms := db.CreateSourceEntryParams{
1376-
GeometryUuid: dbSource.GeometryUuid,
1377-
SourceTypeID: dbSource.SourceTypeID,
1378-
Capacity: cp,
1379-
CapacityUnitPrefixFactor: ex,
1380-
CapacityLimitSip: dbSource.CapacityLimitSip, // TODO: Enable updating this
1381-
ValidFromUtc: pgtype.Timestamp{Time: validFrom, Valid: true},
1382-
Metadata: metadata,
1275+
GeometryUuid: dbSource.GeometryUuid,
1276+
SourceTypeID: dbSource.SourceTypeID,
1277+
CapacityWatts: capacity,
1278+
CapacityLimitSip: dbSource.CapacityLimitSip, // TODO: Enable updating this
1279+
ValidFromUtc: pgtype.Timestamp{Time: validFrom, Valid: true},
1280+
Metadata: metadata,
13831281
}
13841282

13851283
dbNewSource, err := querier.CreateSourceEntry(ctx, csprms)
@@ -1395,7 +1293,7 @@ func (s *DataPlatformDataServiceServerImpl) UpdateLocation(
13951293
l.Debug().
13961294
Str("dp.source.geometry_uuid", dbSource.GeometryUuid.String()).
13971295
Int16("dp.source.type_id", dbSource.SourceTypeID).
1398-
Int64("dp.source.new_capacity", int64(dbNewSource.Capacity)*int64(math.Pow10(int(dbNewSource.CapacityUnitPrefixFactor)))).
1296+
Int64("dp.source.new_capacity", int64(dbNewSource.CapacityWatts)).
13991297
Str("dp.source.valid_from_utc", dbNewSource.ValidFromUtc.Time.String()).
14001298
Msg("updated source")
14011299

@@ -1408,13 +1306,9 @@ func (s *DataPlatformDataServiceServerImpl) UpdateLocation(
14081306
l.Debug().Msg("refreshed sources materialised view")
14091307

14101308
return &pb.UpdateLocationResponse{
1411-
LocationUuid: req.LocationUuid,
1412-
LocationName: dbSource.GeometryName,
1413-
EffectiveCapacityWatts: uint64(
1414-
dbNewSource.Capacity,
1415-
) * uint64(
1416-
math.Pow10(int(dbNewSource.CapacityUnitPrefixFactor)),
1417-
),
1309+
LocationUuid: req.LocationUuid,
1310+
LocationName: dbSource.GeometryName,
1311+
EffectiveCapacityWatts: uint64(dbNewSource.CapacityWatts),
14181312
}, nil
14191313
}
14201314

@@ -1555,14 +1449,10 @@ func (s *DataPlatformDataServiceServerImpl) GetForecastAsTimeseries(
15551449
}
15561450

15571451
values[i] = &pb.GetForecastAsTimeseriesResponse_Value{
1558-
TargetTimestampUtc: timestamppb.New(value.TargetTimeUtc.Time),
1559-
P50ValueFraction: float32(value.P50Sip) / 30000.0,
1560-
OtherStatisticsFractions: otherStats,
1561-
EffectiveCapacityWatts: uint64(
1562-
value.CapacityIncLimit,
1563-
) * uint64(
1564-
math.Pow10(int(value.CapacityUnitPrefixFactor)),
1565-
),
1452+
TargetTimestampUtc: timestamppb.New(value.TargetTimeUtc.Time),
1453+
P50ValueFraction: float32(value.P50Sip) / 30000.0,
1454+
OtherStatisticsFractions: otherStats,
1455+
EffectiveCapacityWatts: uint64(value.CapacityWatts),
15661456
InitializationTimestampUtc: timestamppb.New(value.InitTimeUtc.Time),
15671457
CreatedTimestampUtc: timestamppb.New(value.CreatedAtUtc.Time),
15681458
}
@@ -1637,14 +1527,10 @@ func (s *DataPlatformDataServiceServerImpl) ListLocations(
16371527
Latitude: loc.Latitude,
16381528
Longitude: loc.Longitude,
16391529
},
1640-
EffectiveCapacityWatts: uint64(
1641-
loc.Capacity,
1642-
) * uint64(
1643-
math.Pow10(int(loc.CapacityUnitPrefixFactor)),
1644-
),
1645-
EnergySource: pb.EnergySource(loc.SourceTypeID),
1646-
LocationType: pb.LocationType(loc.GeometryTypeID),
1647-
Metadata: metadata,
1530+
EffectiveCapacityWatts: uint64(loc.CapacityWatts),
1531+
EnergySource: pb.EnergySource(loc.SourceTypeID),
1532+
LocationType: pb.LocationType(loc.GeometryTypeID),
1533+
Metadata: metadata,
16481534
})
16491535
}
16501536
}
@@ -1677,14 +1563,10 @@ func (s *DataPlatformDataServiceServerImpl) ListLocations(
16771563
Latitude: loc.Latitude,
16781564
Longitude: loc.Longitude,
16791565
},
1680-
EffectiveCapacityWatts: uint64(
1681-
loc.Capacity,
1682-
) * uint64(
1683-
math.Pow10(int(loc.CapacityUnitPrefixFactor)),
1684-
),
1685-
EnergySource: pb.EnergySource(loc.SourceTypeID),
1686-
LocationType: pb.LocationType(loc.GeometryTypeID),
1687-
Metadata: metadata,
1566+
EffectiveCapacityWatts: uint64(loc.CapacityWatts),
1567+
EnergySource: pb.EnergySource(loc.SourceTypeID),
1568+
LocationType: pb.LocationType(loc.GeometryTypeID),
1569+
Metadata: metadata,
16881570
})
16891571
}
16901572
}
@@ -1716,14 +1598,10 @@ func (s *DataPlatformDataServiceServerImpl) ListLocations(
17161598
Latitude: loc.Latitude,
17171599
Longitude: loc.Longitude,
17181600
},
1719-
EffectiveCapacityWatts: uint64(
1720-
loc.Capacity,
1721-
) * uint64(
1722-
math.Pow10(int(loc.CapacityUnitPrefixFactor)),
1723-
),
1724-
EnergySource: pb.EnergySource(loc.SourceTypeID),
1725-
LocationType: pb.LocationType(loc.GeometryTypeID),
1726-
Metadata: metadata,
1601+
EffectiveCapacityWatts: uint64(loc.CapacityWatts),
1602+
EnergySource: pb.EnergySource(loc.SourceTypeID),
1603+
LocationType: pb.LocationType(loc.GeometryTypeID),
1604+
Metadata: metadata,
17271605
})
17281606
}
17291607
}

0 commit comments

Comments
 (0)