Risk Management

Risk management identifies what may go wrong in a project, assesses how likely it is and how hard it would hit, and tracks the measures against it. A risk register collects the risks of a project: each with its probability, its impact, its state, the implications it has, if it occurs, and the mitigations reducing it.

Data Model

The risk management model of pyEDAA.Reports.RiskManagement is a risk register: the root, RiskManagement, holds every risk, implication and mitigation by its ID. A risk references its implications and mitigations, so several risks can share one.

  • RiskManagementthe risk register - version, name, key-value pairs
    • Riske.g. R1 - title, description, probability, impact, state, owner, next review
      • Linke.g. R1 causes R2 - source, destination, kind
    • Implicatione.g. I1 - a consequence, if a risk occurs
    • Mitigatione.g. M1 - a measure reducing a risk or an implication - kind, state
  • A risk’s Probability says how likely it occurs: from low (identified, but sleeping) to occurred (the risk is active and causes heavy effects). Its Impact says how much delay it causes, if it occurs: from low (a few days) to critical (multiple weeks).

  • A risk’s Score is the rank of its probability times the rank of its impact, 1 to 16. The score classifies the risk by a RiskLevel - see the matrix below -, e.g. to iterate the critical risks.

  • A risk’s RiskState follows its life cycle: open or on hold while it is tracked; then either solved - its mitigations removed it - or closed - it isn’t tracked anymore, without being solved, e.g. because it was accepted or its time has passed.

  • An Implication can have implications of its own, and it can be mitigated like a risk.

  • A Mitigation can have implications too: its side effects. So the same mitigation reduces a risk and a second-order implication, and its side effects are visible in the register.

  • A risk’s second-order implications (Risk.SecondOrderImplications) are the implications of its implications and the side effects of its mitigations.

  • A mitigation’s MitigationKind says how it reduces a risk: it avoids its cause, prevents it - reduces its probability -, is a contingency plan applied, when the risk occurs, or transfers the impact to a third party. Its MitigationState says whether it is applied: on hold - waiting to be planned -, planned, active, done or abandoned.

  • A risk names its owner - the person responsible for it - and the date of its next review; a date without a time zone is a local time. The owner is a Person - user ID, name, e-mail address -, which the register refers to (RiskManagement.AddPerson(), RiskManagement.Persons). The same person may be registered in several registers, e.g. a register of risks and a specification of requirements.

  • A Link relates two risks of the same register: from its source to its destination, of a LinkKind - e.g. causes, amplifies, depends on, duplicates. A link has no parent: both risks list it - Risk.OutboundLinks and Risk.InboundLinks -, and the register lists it in RiskManagement.Links. A source has at most one link of a kind to a destination. Risk.LinkTo() and Risk.LinkFrom() create an outbound or inbound link of a risk, with the parameters of the link’s constructor.

  • An element joins the register by naming it as its parent, in its constructor or by Parent. An ID is unique among the elements of its kind; a second element of the same ID raises a duplicate error of its kind, e.g. DuplicateRiskError - each a DuplicateElementError and a RiskManagementError. An element already part of a register raises an AlreadyInRiskRegisterError - an AlreadyInHierarchyError.

  • An element of the register references only implications and mitigations of the same register, and a link connects risks of the same register: referenced elements and linked risks join the register first. Otherwise a NotInSameRiskRegisterError - a NotInSameHierarchyError - is raised: the element is valid, but not part of the register. A risk’s owner is a person of the register too.

  • Every element - the register too - has key-value pairs for data the model doesn’t name, e.g. a ticket: risk["ticket"] = "#42".

  • The register iterates its risks, implications, mitigations and links, optionally filtered by a predicate, e.g. RiskManagement.IterateRisks().

Risk level by probability and impact - the score in brackets

Probability

Impact low

medium

high

critical

low

low (1)

low (2)

low (3)

medium (4)

medium

low (2)

medium (4)

medium (6)

high (8)

high

low (3)

medium (6)

high (9)

critical (12)

occurred

medium (4)

high (8)

critical (12)

critical (16)

from pyTooling.Versioning import SemanticVersion
from pyEDAA.Reports import Person
from pyEDAA.Reports.RiskManagement import Impact, Implication, LinkKind, Mitigation, MitigationKind
from pyEDAA.Reports.RiskManagement import Probability, Risk, RiskLevel, RiskManagement

register = RiskManagement(SemanticVersion(1, 0, 0), "Wafer tape-out")
paebbels = Person("paebbels", "Patrick Lehmann", "paebbels@example.com")
register.AddPerson(paebbels)
budget =   Implication("I3", "The budget rises.", parent=register)
shuttle =  Mitigation("M1", "Order a second shuttle run.", [budget], MitigationKind.Contingency, parent=register)
launch =   Implication("I2", "The product launch is delayed.", parent=register)
tapeOut =  Implication("I1", "The tape-out is delayed.", implications=[launch], parent=register)
foundry =  Risk(
  "R1", "Late silicon", "The foundry delays the wafers.", Probability.High, Impact.High,
  implications=[tapeOut], mitigations=[shuttle], owner=paebbels, parent=register
)
foundry["ticket"] = "#42"
board = Risk("R2", "Late board", "The board waits.", Probability.Medium, Impact.High, parent=register)
foundry.LinkTo(board, LinkKind.Causes)

for risk in register.IterateRisks(lambda risk: risk.Level in (RiskLevel.High, RiskLevel.Critical)):
  causes = [link.Destination.ID for link in risk.OutboundLinks if link.Kind is LinkKind.Causes]
  print(f"{risk.ID} {risk.Title} ({risk.Level}, owner {risk.Owner.Name}): causes {', '.join(causes)}")
  print(f"  second-order implications: {', '.join(risk.SecondOrderImplications)}")