///|
/// Two-state continuous-time Markov availability model.
pub struct AvailabilityPoint {
time : Double
availability : Double
unavailability : Double
expected_failures : Double
}
///|
pub fn availability_point(
time~ : Double,
availability~ : Double,
unavailability~ : Double,
expected_failures~ : Double,
) -> AvailabilityPoint {
{ time, availability, unavailability, expected_failures }
}
///|
pub fn markov_availability(
failure_rate : Double,
repair_rate : Double,
time : Double,
) -> Double {
if failure_rate < 0.0 || repair_rate <= 0.0 || time < 0.0 {
abort("invalid Markov rates")
}
let steady = repair_rate / (failure_rate + repair_rate)
let transient = failure_rate / (failure_rate + repair_rate)
steady + transient * @math.exp(-(failure_rate + repair_rate) * time)
}
///|
pub fn markov_availability_curve(
failure_rate : Double,
repair_rate : Double,
horizon : Double,
steps : Int,
) -> Array[AvailabilityPoint] {
let grid = linspace(0.0, horizon, steps)
grid.map(time => {
let availability = markov_availability(failure_rate, repair_rate, time)
availability_point(
time~,
availability~,
unavailability=1.0 - availability,
expected_failures=failure_rate * time,
)
})
}
///|
pub fn semi_markov_cycle_time(uptime : Double, downtime : Double) -> Double {
if uptime < 0.0 || downtime < 0.0 {
abort("cycle durations must be non-negative")
}
uptime + downtime
}
///|
pub fn alternating_renewal_availability(
uptime : Double,
downtime : Double,
) -> Double {
let cycle = semi_markov_cycle_time(uptime, downtime)
if cycle == 0.0 {
0.0
} else {
uptime / cycle
}
}
///|
pub fn cold_standby_reliability(
primary : ReliabilityModel,
standby : ReliabilityModel,
switch_probability : Double,
time : Double,
) -> Double {
if switch_probability < 0.0 || switch_probability > 1.0 {
abort("switch probability outside [0, 1]")
}
let primary_survival = primary.survival(time)
let standby_path = (1.0 - primary_survival) *
switch_probability *
standby.survival(time)
primary_survival + standby_path
}
///|
pub fn warm_standby_reliability(
primary : ReliabilityModel,
standby : ReliabilityModel,
time : Double,
) -> Double {
1.0 - (1.0 - primary.survival(time)) * (1.0 - standby.survival(time))
}
///|
pub fn expected_downtime(model : ReliabilityModel, horizon : Double) -> Double {
horizon * (1.0 - model.survival(horizon))
}
///|
pub fn expected_failures_in_window(
model : ReliabilityModel,
start : Double,
stop : Double,
) -> Double {
let first = model_hazard(model, start)
let second = model_hazard(model, stop)
(first + second) / 2.0 * (stop - start)
}