Skip to content

Commit

Permalink
Use more numpy types and computations
Browse files Browse the repository at this point in the history
Signed-off-by: Sylvain Leclerc <[email protected]>
  • Loading branch information
sylvlecl committed Jul 28, 2024
1 parent cb0442b commit 6187aad
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 104 deletions.
2 changes: 1 addition & 1 deletion src/antares/tsgen/cluster_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def import_thermal_cluster(path: Path, days_per_year: int = 365) -> ThermalClust
return ThermalCluster(
unit_count=int(array[1][1]),
nominal_power=float(array[2][1]),
modulation=[float(i) for i in array[3][1 : 24 + 1]],
modulation=array[3][1 : 24 + 1].astype(int),
fo_law=law_dict[array[4][1]],
fo_volatility=float(array[5][1]),
po_law=law_dict[array[6][1]],
Expand Down
14 changes: 7 additions & 7 deletions src/antares/tsgen/cluster_resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ def resolve_thermal_cluster(
return ThermalCluster(
unit_count=parsed_yaml.unit_count,
nominal_power=parsed_yaml.nominal_power,
modulation=modulation["modulation"].to_list(),
modulation=modulation["modulation"].to_numpy(dtype=float),
fo_law=law_dict[parsed_yaml.fo_law],
fo_volatility=parsed_yaml.fo_volatility,
po_law=law_dict[parsed_yaml.po_law],
po_volatility=parsed_yaml.po_volatility,
fo_duration=parameters_ts["FOD"].to_list(),
fo_rate=parameters_ts["FOR"].to_list(),
po_duration=parameters_ts["POD"].to_list(),
po_rate=parameters_ts["POR"].to_list(),
npo_min=parameters_ts["POMax"].to_list(),
npo_max=parameters_ts["POMin"].to_list(),
fo_duration=parameters_ts["FOD"].to_numpy(dtype=int),
fo_rate=parameters_ts["FOR"].to_numpy(dtype=float),
po_duration=parameters_ts["POD"].to_numpy(dtype=int),
po_rate=parameters_ts["POR"].to_numpy(dtype=float),
npo_min=parameters_ts["POMax"].to_numpy(dtype=int),
npo_max=parameters_ts["POMin"].to_numpy(dtype=int),
)
17 changes: 12 additions & 5 deletions src/antares/tsgen/duration_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from abc import ABC, abstractmethod
from enum import Enum
from math import log, sqrt
from typing import List

import numpy as np
import numpy.typing as npt

from .random_generator import RNG

Expand All @@ -41,7 +41,10 @@ class GeneratorWrapper(DurationGenerator):
"""

def __init__(
self, delegate: DurationGenerator, volatility: float, expecs: List[int]
self,
delegate: DurationGenerator,
volatility: float,
expecs: npt.NDArray[np.int_],
) -> None:
self.volatility = volatility
self.expectations = expecs
Expand All @@ -59,7 +62,9 @@ def generate_duration(self, day: int) -> int:


class UniformDurationGenerator(DurationGenerator):
def __init__(self, rng: RNG, volatility: float, expecs: List[int]) -> None:
def __init__(
self, rng: RNG, volatility: float, expecs: npt.NDArray[np.int_]
) -> None:
self.rng = rng
self.a = np.empty(len(expecs), dtype=float)
self.b = np.empty(len(expecs), dtype=float)
Expand All @@ -77,7 +82,9 @@ def generate_duration(self, day: int) -> int:


class GeometricDurationGenerator(DurationGenerator):
def __init__(self, rng: RNG, volatility: float, expecs: List[int]) -> None:
def __init__(
self, rng: RNG, volatility: float, expecs: npt.NDArray[np.int_]
) -> None:
self.rng = rng
self.a = np.empty(len(expecs), dtype=float)
self.b = np.empty(len(expecs), dtype=float)
Expand All @@ -100,7 +107,7 @@ def generate_duration(self, day: int) -> int:


def make_duration_generator(
rng: RNG, law: ProbabilityLaw, volatility: float, expectations: List[int]
rng: RNG, law: ProbabilityLaw, volatility: float, expectations: npt.NDArray[np.int_]
) -> DurationGenerator:
"""
return a DurationGenerator for the given law
Expand Down
107 changes: 58 additions & 49 deletions src/antares/tsgen/ts_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,18 @@ class ThermalCluster:
nominal_power: float
# modulation of the nominal power for a certain hour in the day (between 0 and 1)
# TODO: check that it should be 24 or 8760 ?
modulation: List[float] ### maybe group nominal_power and modulation in one vaiable
modulation: npt.NDArray[
np.int_
] ### maybe group nominal_power and modulation in one vaiable

# forced and planed outage parameters
# indexed by day of the year
fo_duration: List[int]
fo_rate: List[float]
po_duration: List[int]
po_rate: List[float]
npo_min: List[int] # number of planed outage min in a day
npo_max: List[int] # number of planed outage max in a day
fo_duration: npt.NDArray[np.int_]
fo_rate: npt.NDArray[np.float_]
po_duration: npt.NDArray[np.int_]
po_rate: npt.NDArray[np.float_]
npo_min: npt.NDArray[np.int_] # number of planed outage min in a day
npo_max: npt.NDArray[np.int_] # number of planed outage max in a day

# forced and planed outage probability law and volatility
# volatility characterizes the distance from the expect at which the value drawn can be
Expand Down Expand Up @@ -96,48 +98,55 @@ def generate_time_series(

# --- precalculation ---
# cached values for (1-lf)**k and (1-lp)**k
self.FPOW: List[List[float]] = []
self.PPOW: List[List[float]] = []

for day in range(self.days):
# lf and lp represent the forced and programed failure rate
# failure rate means the probability to enter in outage each day
# its value is given by: OR / [OR + OD * (1 - OR)]
FOR = cluster.fo_rate[day]
FOD = cluster.fo_duration[day]
self.lf[day] = FOR / (FOR + FOD * (1 - FOR))

POR = cluster.po_rate[day]
POD = cluster.po_duration[day]
self.lp[day] = POR / (POR + POD * (1 - POR))

if self.lf[day] < 0:
raise ValueError(f"forced failure rate is negative on day {day}")
if self.lp[day] < 0:
raise ValueError(f"programed failure rate is negative on day {day}")

## i dont understand what these calulations are for
## consequently reduce the lower failure rate
if self.lf[day] < self.lp[day]:
self.lf[day] *= (1 - self.lp[day]) / (1 - self.lf[day])
if self.lp[day] < self.lf[day]:
self.lp[day] *= (1 - self.lf[day]) / (1 - self.lp[day])

a = 0
b = 0
if self.lf[day] <= FAILURE_RATE_EQ_1:
a = 1 - self.lf[day]
self.ff[day] = self.lf[day] / a
if self.lp[day] <= FAILURE_RATE_EQ_1:
b = 1 - self.lp[day]
self.pp[day] = self.lp[day] / b

# pre calculating power values
self.FPOW.append([])
self.PPOW.append([])
for k in range(cluster.unit_count + 1):
self.FPOW[-1].append(pow(a, k))
self.PPOW[-1].append(pow(b, k))
# TODO: why +1 ?
self.FPOW = np.zeros(shape=(self.days, cluster.unit_count + 1))
self.PPOW = np.zeros(shape=(self.days, cluster.unit_count + 1))

# lf and lp represent the forced and programed failure rate
# failure rate means the probability to enter in outage each day
# its value is given by: OR / [OR + OD * (1 - OR)]
self.lf = cluster.fo_rate / (
cluster.fo_rate + cluster.fo_duration * (1 - cluster.fo_rate)
)
self.lp = cluster.po_rate / (
cluster.po_rate + cluster.po_duration * (1 - cluster.po_rate)
)

invalid_days = self.lf < 0
if invalid_days.any():
raise ValueError(
f"forced failure rate is negative on days {invalid_days.nonzero()[0].tolist()}"
)
invalid_days = self.lp < 0
if invalid_days.any():
raise ValueError(
f"planned failure rate is negative on days {invalid_days.nonzero()[0].tolist()}"
)

## i dont understand what these calulations are for
## consequently reduce the lower failure rate
mask = self.lf < self.lp
self.lf[mask] *= (1 - self.lp[mask]) / (1 - self.lf[mask])
mask = self.lp < self.lf
self.lp[mask] *= (1 - self.lf[mask]) / (1 - self.lp[mask])

a = np.zeros(shape=self.days, dtype=float)
b = np.zeros(shape=self.days, dtype=float)
mask = self.lf <= FAILURE_RATE_EQ_1
a[mask] = 1 - self.lf[mask]
self.ff[mask] = self.lf[mask] / a

mask = self.lp <= FAILURE_RATE_EQ_1
b[mask] = 1 - self.lp[mask]
self.pp[mask] = self.lp[mask] / b

# change dimensions to get fpow and ppow with right shape
k = np.arange(cluster.unit_count + 1)
k.shape = (1, len(k))
a.shape = (self.days, 1)
b.shape = (self.days, 1)
self.FPOW = pow(a, k)
self.PPOW = pow(b, k)

fod_generator = make_duration_generator(
self.rng, cluster.fo_law, cluster.fo_volatility, cluster.fo_duration
Expand Down
30 changes: 16 additions & 14 deletions tests/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#
# This file is part of the Antares project.

import numpy as np
import numpy.testing as npt
import pytest

from antares.tsgen.cluster_parsing import parse_cluster_ts, parse_yaml_cluster
Expand All @@ -24,17 +26,17 @@ def cluster() -> ThermalCluster:
return ThermalCluster(
unit_count=1,
nominal_power=500,
modulation=[1 for i in range(24)],
modulation=np.ones(dtype=float, shape=24),
fo_law=ProbabilityLaw.UNIFORM,
fo_volatility=0,
po_law=ProbabilityLaw.UNIFORM,
po_volatility=0,
fo_duration=[2 for i in range(NB_OF_DAY)],
fo_rate=[0.2 for i in range(NB_OF_DAY)],
po_duration=[1 for i in range(NB_OF_DAY)],
po_rate=[0.1 for i in range(NB_OF_DAY)],
npo_min=[0 for i in range(NB_OF_DAY)],
npo_max=[1 for i in range(NB_OF_DAY)],
fo_duration=np.ones(dtype=int, shape=NB_OF_DAY) * 2,
fo_rate=np.ones(dtype=float, shape=NB_OF_DAY) * 0.2,
po_duration=np.ones(dtype=int, shape=NB_OF_DAY),
po_rate=np.ones(dtype=float, shape=NB_OF_DAY) * 0.1,
npo_min=np.zeros(dtype=int, shape=NB_OF_DAY),
npo_max=np.ones(dtype=int, shape=NB_OF_DAY),
)


Expand All @@ -47,14 +49,14 @@ def test(cluster, data_directory):

assert cld.unit_count == cluster.unit_count
assert cld.nominal_power == cluster.nominal_power
assert cld.modulation == cluster.modulation
npt.assert_equal(cld.modulation, cluster.modulation)
assert cld.fo_law == cluster.fo_law
assert cld.fo_volatility == cluster.fo_volatility
assert cld.po_law == cluster.po_law
assert cld.po_volatility == cluster.po_volatility
assert cld.fo_duration == cluster.fo_duration
assert cld.fo_rate == cluster.fo_rate
assert cld.po_duration == cluster.po_duration
assert cld.po_rate == cluster.po_rate
assert cld.npo_min == cluster.npo_min
assert cld.npo_max == cluster.npo_max
npt.assert_equal(cld.fo_duration, cluster.fo_duration)
npt.assert_equal(cld.fo_rate, cluster.fo_rate)
npt.assert_equal(cld.po_duration, cluster.po_duration)
npt.assert_equal(cld.po_rate, cluster.po_rate)
npt.assert_equal(cld.npo_min, cluster.npo_min)
npt.assert_equal(cld.npo_max, cluster.npo_max)
56 changes: 28 additions & 28 deletions tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ def test_forced_outages(rng):
cluster = ThermalCluster(
unit_count=10,
nominal_power=100,
modulation=[1 for i in range(24)],
modulation=np.ones(dtype=float, shape=24),
fo_law=ProbabilityLaw.UNIFORM,
fo_volatility=0,
po_law=ProbabilityLaw.UNIFORM,
po_volatility=0,
fo_duration=[10 for i in range(days)],
fo_rate=[0.2 for i in range(days)],
po_duration=[10 for i in range(days)],
po_rate=[0 for i in range(days)],
npo_min=[0 for i in range(days)],
npo_max=[10 for i in range(days)],
fo_duration=10 * np.ones(dtype=int, shape=days),
fo_rate=0.2 * np.ones(dtype=float, shape=days),
po_duration=10 * np.ones(dtype=int, shape=days),
po_rate=np.zeros(dtype=float, shape=days),
npo_min=np.zeros(dtype=int, shape=days),
npo_max=10 * np.ones(dtype=int, shape=days),
)
cluster.modulation[12] = 0.5

Expand All @@ -60,17 +60,17 @@ def test_planned_outages(rng):
cluster = ThermalCluster(
unit_count=10,
nominal_power=100,
modulation=[1 for i in range(24)],
modulation=np.ones(dtype=float, shape=24),
fo_law=ProbabilityLaw.UNIFORM,
fo_volatility=0,
po_law=ProbabilityLaw.UNIFORM,
po_volatility=0,
fo_duration=[10 for i in range(days)],
fo_rate=[0 for i in range(days)],
po_duration=[10 for i in range(days)],
po_rate=[0.2 for i in range(days)],
npo_min=[0 for i in range(days)],
npo_max=[10 for i in range(days)],
fo_duration=10 * np.ones(dtype=int, shape=days),
fo_rate=np.zeros(dtype=float, shape=days),
po_duration=10 * np.ones(dtype=int, shape=days),
po_rate=0.2 * np.ones(dtype=float, shape=days),
npo_min=np.zeros(dtype=int, shape=days),
npo_max=10 * np.ones(dtype=int, shape=days),
)
cluster.modulation[12] = 0.5

Expand All @@ -94,17 +94,17 @@ def test_planned_outages_limitation(rng):
cluster = ThermalCluster(
unit_count=10,
nominal_power=100,
modulation=[1 for i in range(24)],
modulation=np.ones(dtype=float, shape=24),
fo_law=ProbabilityLaw.UNIFORM,
fo_volatility=0,
po_law=ProbabilityLaw.UNIFORM,
po_volatility=0,
fo_duration=[10 for i in range(days)],
fo_rate=[0 for i in range(days)],
po_duration=[2 for i in range(days)],
po_rate=[0.2 for i in range(days)],
npo_min=[0 for i in range(days)],
npo_max=[1 for i in range(days)],
fo_duration=10 * np.ones(dtype=int, shape=days),
fo_rate=np.zeros(dtype=float, shape=days),
po_duration=2 * np.ones(dtype=int, shape=days),
po_rate=0.2 * np.ones(dtype=float, shape=days),
npo_min=np.zeros(dtype=int, shape=days),
npo_max=1 * np.ones(dtype=int, shape=days),
)

generator = ThermalDataGenerator(rng=rng, days=days)
Expand All @@ -127,17 +127,17 @@ def test_planned_outages_min_limitation(rng):
cluster = ThermalCluster(
unit_count=10,
nominal_power=100,
modulation=[1 for i in range(24)],
modulation=np.ones(dtype=float, shape=24),
fo_law=ProbabilityLaw.UNIFORM,
fo_volatility=0,
po_law=ProbabilityLaw.UNIFORM,
po_volatility=0,
fo_duration=[10 for i in range(days)],
fo_rate=[0 for i in range(days)],
po_duration=[10 for i in range(days)],
po_rate=[0.2 for i in range(days)],
npo_min=[2 for i in range(days)],
npo_max=[5 for i in range(days)],
fo_duration=10 * np.ones(dtype=int, shape=days),
fo_rate=np.zeros(dtype=float, shape=days),
po_duration=10 * np.ones(dtype=int, shape=days),
po_rate=0.2 * np.ones(dtype=float, shape=days),
npo_min=2 * np.ones(dtype=int, shape=days),
npo_max=5 * np.ones(dtype=int, shape=days),
)

generator = ThermalDataGenerator(rng=rng, days=days)
Expand Down

0 comments on commit 6187aad

Please sign in to comment.