class Modelling::Model

Model Class

Attributes

name[RW]

The name of the model

notes[RW]

notes about the model

parameters[RW]

a hash of parameters

rules[RW]

an array of rules

sassy_extra[RW]

extra comment for sassy

species[RW]

a hash of species

Public Class Methods

new(_name = "Model") click to toggle source
# File lib/modelling/model.rb, line 34
def initialize(_name = "Model")
    @rules = []
    @parameters = {}
    @species = {}
    @name = _name
    @parser = Modelling::SassyParser.new
    @sassy_extra = ""
    @notes = ""
end

Public Instance Methods

add_parameter(name, value = 0, description = "") click to toggle source

add a new parameter

# File lib/modelling/model.rb, line 225
def add_parameter(name, value = 0, description = "")
    if (@parameters.key? name) or (@species.key? name)
        raise "duplicate parameter #{name}"
    end

    @parameters[name] = Parameter.new(name, value, description)

    self
end
add_rule(output, equation, type = "scalar", comment = "") click to toggle source

Make a rule

# File lib/modelling/model.rb, line 251
def add_rule(output, equation, type = "scalar", comment = "")
    @rules.push(Rule.new(get_symbol(output), Equation.new(equation, comment), type))

    self.validate
end
add_species(name, initvalue = 0, description = "") click to toggle source

add a new parameter

# File lib/modelling/model.rb, line 236
def add_species(name, initvalue = 0, description = "")
    if (@parameters.key? name) or (@species.key? name)
        raise "duplicate symbol #{name}"
    end

    max_matlab_no = @species.inject(1) do |mval, s|
        [mval, s[1].matlab_no].max
    end

    @species[name] = Species.new(name, initvalue, max_matlab_no+1)

    self.validate
end
combine_with(m2) click to toggle source

Combine with another model

# File lib/modelling/model.rb, line 178
def combine_with(m2)
    m2 = m2.make_copy

    @name = @name + " & " + m2.name

    max_matlab_no = @species.inject(1) do |mval, s|
        [mval, s[1].matlab_no].max
    end

    m2.species.each do |n,sp|
        if @species.key?(n)
            raise "Cannot combine models, duplicate species #{n}"
        end

        # if we have a parameter by this name, turn it into a species
        if @parameters.key?(n)
            @parameters.delete(n)
        end

        sp.matlab_no=(sp.matlab_no + max_matlab_no)
        @species[n] = sp
    end

    m2.parameters.each do |n, p|
        # We only get parameters which are not already species
        if not @species.key?(n)
            if @parameters.key?(n)
                raise "Cannot combine models, duplicate parameter #{n}"
            end
            @parameters[n] = p
        else
            puts "[N] Parameter #{n} is converted to Species\n"
        end
    end              

    # keep only rules for things which haven't been turned from parameter to species
    @rules = @rules | m2.rules.reject { |r| m2.parameters.key?(r.output.name) and @species.key?(r.output.name) }

    self.validate
end
delete_symbol(sym) click to toggle source
# File lib/modelling/model.rb, line 145
def delete_symbol(sym)
    if @parameters.key?(sym)
        @parameters.delete sym
    elsif @species.key?(sym)
        @species.delete sym
    else
        raise "Symbol #{sym} not found in model"
    end
    self.validate
end
get_symbol(sym) click to toggle source

get a symbol (species or parameter) object

# File lib/modelling/model.rb, line 130
def get_symbol(sym)
    if @parameters.key?(sym)
        return @parameters[sym]
    elsif @species[sym]
        return @species[sym]
    else
        raise "Symbol #{sym} not found in model"
    end
end
get_symbol_output_rule(sym, type = 'scalar') click to toggle source

get the rule which outputs to a symbol

# File lib/modelling/model.rb, line 258
def get_symbol_output_rule(sym, type = 'scalar')
    rule = nil
    @rules.each do |r|
        if r.output.name == sym 
            if r.type != type
                raise "Conflicting rule types for symbol #{sym} : Type #{type} is needed, but a rule of type #{r.type} is present"
            end
            rule = r
            return rule
        end
    end
    rule
end
has_symbol?(sym) click to toggle source

Test if model has a certain symbol

# File lib/modelling/model.rb, line 141
def has_symbol?(sym)
    @parameters.key?(sym) or @species.key?(sym)
end
make_copy() click to toggle source

Deep copy via Marshalling

# File lib/modelling/model.rb, line 220
def make_copy
    Marshal.load(Marshal.dump(self))
end
parameter_to_species(pname) click to toggle source

Turn a parameter into a species Returns: the Species object of the newly generated species

# File lib/modelling/model.rb, line 116
def parameter_to_species(pname)
    raise "Unknown parameter #{pname}" if not @parameters.key?(pname)
    matlab_number = @species.values.inject (1) { |mem, var| mem < var.matlab_no ? var.matlab_no + 1 : mem }
    raise "Species #{pname} already exists, cannot replace." if @species.key?(pname)

    p = @parameters.delete pname
    spec = Species.new(pname, p.value, matlab_number)
    @species[pname] = spec

    self.validate
    spec
end
replace_symbol(sym, new_sym) click to toggle source

Replace a symbol in all equations Removes the object for sym and returns it

# File lib/modelling/model.rb, line 158
def replace_symbol(sym, new_sym)
    syo = get_symbol(sym)
    syn = get_symbol(new_sym)

    @rules.each do |r|
        r.equation.replace_ident(sym, new_sym)
    end

    if @parameters[sym]
        @parameters.delete(sym)
    else
        @species.delete(sym)
    end

    self.validate

    syo
end
unparameterize(pname) click to toggle source

replace all occurrances of a parameter by its numeric value and remove the parameter

# File lib/modelling/model.rb, line 97
def unparameterize(pname)
    par = nil
    if @parameters.key?(pname)
        par = @parameters[pname]
    else
        raise "Parameter #{pname} not found."
    end

    @rules.each do |r|
        if r.output.name == pname
            raise "Parameter #{pname} has an output rule associated with it, cannot unparameterize."
        end
        r.equation.replace_ident(pname, "#{par.value}")
    end
    delete_symbol(pname)
end
validate() click to toggle source

validate a model, i.e. make sure we all species and parameters

# File lib/modelling/model.rb, line 45
def validate
    # time always exists
    syms = { 't' => Parameter.new('t', 0) }
    idents = []
    @species.each {|k, v| syms[k] = v}
    @parameters.each do |k, v| 
        raise "Parameter and species share id: #{k}" if syms[k]
        syms[k] = v
    end
    syms.each do |k, v|
        raise "Symbol #{v.to_s} mapped to wrong id: #{k}" if v.name != k
    end

    rule_output_names = {}

    @rules.each do |rule|
        if rule_output_names.key? (rule.output.name)
            raise "Two rules for one output (#{rule.output.name})"
        end
        rule_output_names[rule.output.name] = 1

        if !syms.key?(rule.output.name)
            raise "rule #{rule.to_s} has undefined output: #{rule.output.name}"
        end

        if syms[rule.output.name] != rule.output
            raise "Inconsistent model: rule #{rule.to_s} does not output to the correct variable #{syms[rule.output.name]}"
        end

        rule.equation.all_idents.each do |id|
            idents = idents | [id]
            if !syms.key?(id)
                raise "rule #{rule.to_s} has undefined input: #{id}"
            # else
            #  puts "#{id} used in #{rule.to_s}\n"
            end
        end
    end

    unused = idents.reject { |e| syms.key?(e) }
    unused = unused | (syms.keys.reject { |e| idents.index(e) })
    # dawn dusk and force are in every model (at least after sassy opens it)
    unused = unused.reject { |e| e == 't' || e == 'dawn' || e == 'dusk' || e == 'force'                  || rule_output_names.key?(e) || @species.key?(e) }
    if unused.length > 0
        puts "[W] Model has unused symbols: #{unused}\n"
    end
    self
end