@startuml
skinparam {
  shadowing false
  classBackgroundColor white
  classBorderColor black
  noteBackgroundColor white
  noteBorderColor #222
}

package control {

    class ScenarioList {
    - scenario_sets : Scenario [*]
    + add_scenario_set(Scenario)
    + get_scenario_sets()
    + get_scenario_set(name)
    }

    class NarrativeList {
    - narrative_sets : Narrative [*]
    + add_narrative_set(Narrative_Set)
    + get_narrative_sets()
    + get_narrative_set(name)
    }

    NarrativeList -- "*" Narrative
    class Narrative {
    - name : string
    - description : string
    - narratives : Narrative [*]
    }

    class NarrativeData {
    - name : string
    - data
    }
    Narrative "1" -- "*" NarrativeData

    ScenarioList -- "*" Scenario
    class Scenario {
    - name : string
    - description : string
    - scenarios : ScenarioData [*]
    + Scenario(name, description)
    + add_data(ScenarioData)
    + get_scenario(name)
    }
    note right of Scenario : Holds the scenario category\ne.g. 'Population'

    class ScenarioData {
    - name : string
    - description : string
    - data
    - spatial_resolution : RegionSet [1]
    - temporal_resolution : IntervalSet [1]
    + set_name(name)
    + set_description(description)
    + set_data(data)
    + set_spatial_resolution(RegionSet)
    + set_temporal_resolution(IntervalSet)
    }

    abstract class UniqueCombination {
    - combinations : dict
    + {abstract} add_combination(Set, Entity)
    + {abstract} get_entity(Set) : (Entity)
    + {abstract} validate()
    }

    UniqueCombination <|-- ScenarioCombination

    class ScenarioCombination {
    + add_tuple(Scenario, ScenarioData)
    + get_tuple(Scenario)
    + validate()
    }
    note bottom of ScenarioCombination : Holds one ScenarioData in each Scenario

    NarrativeCombination -- "1" Narrative
    NarrativeCombination -- "1" NarrativeData
    class NarrativeCombination {
    + add_tuple(Narrative, Narrative)
    + get_tuple(Narrative)
    + validate()
    }

    UniqueCombination <|-- NarrativeCombination



    ScenarioCombination -- "1" ScenarioData
    ScenarioCombination -- "1" Scenario
    Scenario -- "*" ScenarioData

}

newpage

skinparam {
  shadowing false
  classBackgroundColor white
  classBorderColor black
  noteBackgroundColor white
  noteBorderColor #222
}

package smif.parameter {

class ParameterList {
    - parameters : ModelParameter [*]
    + add_parameter(ModelParameter)
    + linked_model : Model [1]
  }

  abstract class ModelParameter {
  + name : str
  + description : str
  + range : tuple
  + suggested_range : tuple
  + default_value : list
  + units : str
  + parent : str
  + static create_parameter(type): ModelParameter
  }

  class BooleanModelParameter {

  }

  class FloatModelParameter {

  }

  class TupleModelParameter {

  }

  class DiscreteModelParameter {

  }

  ModelParameter <|-- BooleanModelParameter
  ModelParameter <|-- FloatModelParameter
  ModelParameter <|-- TupleModelParameter
  ModelParameter <|-- DiscreteModelParameter

}

package smif.project {

  class Project {
    + model_run_list : ModelRunList [1]
    + scenarios : ScenarioList [1]
    + narratives : NarrativeList [1]
    + region_register : RegionRegister [1]
    + interval_register : IntervalRegister [1]
    + view_model_runs()
    + view_scenarios()
    + view_narratives()
    + view_region_register()
    + view_interval_register()
  }

}

newpage

package smif.model {

  class ModelRunList {
    - model_runs : ModelRun [*]
    + get_model_runs()
    + add_model_run(ModelRun)
  }

  ModelRunList -- "*" ModelRun
  class ModelRun {
    - id : int
    - description : string
    - sosmodel : SosModel [1]
    - timesteps
    - scenarios : ScenarioCombination [*]
    - narratives : NarrativeCombination [*]
    - strategy : 
    -- Building --
    + set_description(description)
    + set_sos_model(SosModel)
    + set_strategy(Strategy)
    + set_scenario(Scenario, scenario)
    + set_narrative(Narrative, narrative)
    + set_modelling_horizon(timestep_list)
    -- Running --
    + run()
    
  }
  note right of ModelRun : We need to set one\n (and only one) scenario\n for each Scenario defined
  note right of ModelRun : We need to set one\n (and only one) narrative\n for each Narrative defined

  ModelRun -- "*" Results
  class Results {
    - model_run_id : int
    + data
  }

  class ModelRunner {
    - modelrun : ModelRun[1]
    + ModelRunner(modelrun)
    + solve() 
  }

  ModelRunner *-- ModelRun
  ModelRun *-- SosModel

  note top of Model : Implement the Composite\ndesign pattern

  abstract class Model {
  -- Static Properties --
  + {static} all_inputs : MetadataList
  + {static} all_outputs : MetadataList
  -- Instance Properties --
  - name : string
  - model_inputs : MetadataList
  - model_outputs : MetadataList
  - parameters : ParameterList
  - dependencies : Dependency [*]
  -- Instance Methods --
  + get_inputs() : MetadataList
  + get_outputs() : MetadataList
  + set_name(name)
  + get_name()
  + add_dependency(data_source, model_input)
  + add_parameter(ModelParameter)
  -- Running --
  - get_input_data(name)
  + {abstract} validate()
  + {abstract} simulate(System, Data) : Data, StateData
  }

  class MetadataList {
    - in_out_puts : ModelInputOutput [*]
    + add_in_out(ModelInputOutput)
  }


  class SosModel {
  - models : Model [1..*] 
  - parameters : ParameterList [1]
  -- Building --
  + add_model(Model)
  + validate()
  -- Running --
  - get_RunOrder(sector_models, dependencies) : ModelSet [1..*]
  + simulate(System, Data) : Data, StateData
  }

  class SectorModel {
  - parameters : ParameterList [1]
  - interventions : Intervention [*]
  - wrapper_path : string
  - wrapper_class : string
  -- Building --
  + add_input(ModelInput)
  + add_output(ModelOutput)
  + add_parameter(ModelParameter)
  + add_intervention(Intervention)
  + add_wrapper(path, classname)
  + validate()
  -- Running --
  + initialise(Parameters)
  + simulate(System, Data) : Data, StateData
  }

  class ScenarioModel {
  - name : string
  - output : ModelOutput [1]
  - scenario : Scenario [1]
  -- Building --
  + ScenarioModel(name, ModelOutput, Scenario)
  + validate()
  -- Running --
  + simulate(System, Data) : Data, StateData
  }
  note bottom of ScenarioModel : simulate() method returns\n scenario data only

  Model <|-- SosModel
  Model <|-- SectorModel
  Model <|-- ScenarioModel
  Model <|-- ModelSet

  Model *-- SosModel
  SosModel *- ModelSet


  class ModelSet {
    sector_models : set of SectorModel
    simulate(System, Data) => Data, StateData
  }

  abstract class ModelInputOutput {
    - name : string
    - spatial_resolution : RegionSet [1]
    - temporal_resolution : IntervalSet [1]
    + validate()
    + get_spatial_resolution()
    + get_temporal_resolution()
  }

  ModelInputOutput <|-- ModelInput 
  ModelInputOutput <|-- ModelOutput

  class ModelInput {
    - scenario_set : Scenario [1]
    + ModelInput(name, region_set, interval_set)
    + set_scenario_set(name)
  }

  class ModelOutput {
  + ModelOutput(name, region_set, interval_set)
  }

  class Dependency {
    - source_model : Model [1]
    - source : ModelOutput [1]
    - conversion_function : func = AreaWeighted
    + Dependency(source_model, source)
    + Dependency(source_model, source, conversion_function)
    + convert(data, model_input)
  }

  SectorModel "1" *-- "*" Dependency

  class Intervention {
  + {static} names
  + {static} attributes 
  - name : string
  - attributes : dict
  + Intervention(name)
  + set_attribute(key, value)
  + get_attribute(key)
  + validate()
  }

}

newpage

skinparam {
  shadowing false
  classBackgroundColor white
  classBorderColor black
  noteBackgroundColor white
  noteBorderColor #222
}

package datalayer {

  abstract class DataReader {
    + {abstract} read_model_run(id)
    + {abstract} read_sos_model(name)
    + {abstract} read_sector_model(name)
    + {abstract} read_scenario_model(name)
    + {abstract} read_dependency(sos_model, name)
  }

  abstract class DataWriter {
    + {abstract} write_model_run(data)
    + {abstract} write_sos_model(data)
    + {abstract} write_sector_model(data)
    + {abstract} write_scenario_model(data)
    + {abstract} write_dependency(data)
  }

DataReader <|-- YamlReader
class YamlReader {
    - filepath : string
    + read_model_run(id)
    + read_sos_model(name)
    + read_sector_model(name)
    + read_scenario_model(name)
    + read_dependency(sos_model, name)
}

}

newpage

skinparam {
  shadowing false
  classBackgroundColor white
  classBorderColor black
  noteBackgroundColor white
  noteBorderColor #222
}

package convertor {

    abstract class Register {
    - entries : ResolutionSet [*]
    - {abstract} compute_matrix(from_set, to_set)
    + register(resolution_set)
    + get_entry(name)
    }

    Register <|-- RegionRegister
    class RegionRegister {
    - entries : RegionSet [*]
    + compute_matrix(from_set, to_set)
    }

    Register <|-- IntervalRegister
    class IntervalRegister {
    - entries : IntervalSet [*]
    + compute_matrix(from_set, to_set)
    }

    class Convertor {
    - conversion_matrix_mapping : array
    - region_register : RegionRegister [1]
    - interval_register : IntervalRegister [1]
    + convert(data, from_space, to_space, from_time, to_time)
    + add_region_set(RegionSet)
    + add_interval_set(IntervalSet)
    }


    abstract class ResolutionSet {
    - name : String
    - {abstract} data
    + get_name() : String
    + set_data()
    + get_data()

    }

    ResolutionSet <|-- RegionSet
    ResolutionSet <|-- IntervalSet

    class RegionSet {
    - data : Area [*]
    }

    RegionSet -- "*" Area

    class IntervalSet {
    - data : Interval [*]
    }

    IntervalSet -- "*" Interval

    class Interval {
    - name : string
    - start : int
    - end : int
    }

    class Area {
    - name : string
    - area : Polygon
    }

}

@enduml
