Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Mimi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ end

# Components are defined here to allow pre-compilation to work
function __init__()
compdir = joinpath(dirname(@__FILE__), "components")
compdir = joinpath(@__DIR__, "components")
load_comps(compdir)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need to do this in `init``? With these fixes, shouldn't we just be able to include these files in the normal way now? We don't have to change this in this PR, just wondering...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I quickly tried to change this but MimiDice2013 complained about loading Mimi from cache. Rather than debug this now, I'll merge what we have.

end

Expand Down
3 changes: 1 addition & 2 deletions src/core/connections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,7 @@ function add_connector_comps(md::ModelDef)
end

# Fetch the definition of the appropriate connector commponent
conn_name = num_dims == 1 ? :ConnectorCompVector : :ConnectorCompMatrix
conn_comp_def = compdef(conn_name)
conn_comp_def = (num_dims == 1 ? Mimi.ConnectorCompVector : Mimi.ConnectorCompMatrix)
conn_comp_name = connector_comp_name(i)

# Add the connector component before the user-defined component that required it
Expand Down
27 changes: 13 additions & 14 deletions src/core/defcomp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,14 @@ macro defcomp(comp_name, ex)
comp_name = cmpname
end

# We'll return a block of expressions that will define the component. First,
# save the ComponentId to a variable with the same name as the component.
# We'll return a block of expressions that will define the component.
# @__MODULE__ is evaluated when the expanded macro is interpreted
result = :(
let current_module = @__MODULE__
global const $comp_name = Mimi.ComponentId(nameof(current_module), $(QuoteNode(comp_name)))
let current_module = @__MODULE__,
comp_id = Mimi.ComponentId(nameof(current_module), $(QuoteNode(comp_name))),
comp = Mimi.ComponentDef(comp_id)

global $comp_name = comp
end
)

Expand All @@ -151,9 +153,6 @@ macro defcomp(comp_name, ex)
push!(let_block, expr)
end

newcomp = :(comp = new_comp($comp_name, $defcomp_verbosity))
addexpr(newcomp)

for elt in elements
@debug "elt: $elt"

Expand Down Expand Up @@ -241,22 +240,22 @@ macro defcomp(comp_name, ex)

@debug " index $(Tuple(dimensions)), unit '$unit', desc '$desc'"

dflt = eval(dflt)
if (dflt !== nothing && length(dimensions) != ndims(dflt))
error("Default value has different number of dimensions ($(ndims(dflt))) than parameter '$name' ($(length(dimensions)))")
if dflt !== nothing
dflt = Base.eval(Main, dflt)
if length(dimensions) != ndims(dflt)
error("Default value has different number of dimensions ($(ndims(dflt))) than parameter '$name' ($(length(dimensions)))")
end
end

vartype = vartype === nothing ? Number : eval(vartype)
vartype = (vartype === nothing ? Number : Base.eval(Main, vartype))
addexpr(_generate_var_or_param(elt_type, name, vartype, dimensions, dflt, desc, unit))

else
error("Unrecognized element type: $elt_type")
end
end

# addexpr(:($comp_name))
addexpr(:(nothing)) # reduces noise

return esc(result)
end

Expand Down Expand Up @@ -297,7 +296,7 @@ macro defmodel(model_name, ex)
addexpr(expr)

name = (alias === nothing ? comp_name : alias)
expr = :(add_comp!($model_name, eval(comp_mod_name).$comp_name, $(QuoteNode(name))))
expr = :(add_comp!($model_name, Mimi.ComponentId(comp_mod_name, $(QuoteNode(comp_name))), $(QuoteNode(name))))

# TBD: extend comp.var syntax to allow module name, e.g., FUND.economy.ygross
elseif (@capture(elt, src_comp_.src_name_[arg_] => dst_comp_.dst_name_) ||
Expand Down
70 changes: 10 additions & 60 deletions src/core/defs.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,4 @@
# Global component registry: @defcomp stores component definitions here
global const _compdefs = Dict{ComponentId, ComponentDef}()

compdefs() = collect(values(_compdefs))

compdef(comp_id::ComponentId) = _compdefs[comp_id]

function compdef(comp_name::Symbol)
matches = collect(Iterators.filter(obj -> name(obj) == comp_name, values(_compdefs)))
count = length(matches)

if count == 1
return matches[1]
elseif count == 0
error("Component $comp_name was not found in the global registry")
else
error("Multiple components named $comp_name were found in the global registry")
end
end
compdef(comp_id::ComponentId) = getfield(getfield(Main, comp_id.module_name), comp_id.comp_name)

compdefs(md::ModelDef) = values(md.comp_defs)

Expand All @@ -27,10 +9,8 @@ hascomp(md::ModelDef, comp_name::Symbol) = haskey(md.comp_defs, comp_name)
compdef(md::ModelDef, comp_name::Symbol) = md.comp_defs[comp_name]

function reset_compdefs(reload_builtins=true)
empty!(_compdefs)

if reload_builtins
compdir = joinpath(dirname(@__FILE__), "..", "components")
compdir = joinpath(@__DIR__, "..", "components")
load_comps(compdir)
end
end
Expand All @@ -43,11 +23,14 @@ last_period(md::ModelDef, comp_def::ComponentDef) = last_period(comp_def) === no

# Return the module object for the component was defined in
compmodule(comp_id::ComponentId) = comp_id.module_name

compname(comp_id::ComponentId) = comp_id.comp_name

compmodule(comp_def::ComponentDef) = compmodule(comp_def.comp_id)
compname(comp_def::ComponentDef) = compname(comp_def.comp_id)


function Base.show(io::IO, comp_id::ComponentId)
print(io, "$(comp_id.module_name).$(comp_id.comp_name)")
print(io, "<ComponentId $(comp_id.module_name).$(comp_id.comp_name)>")
end

"""
Expand All @@ -61,40 +44,6 @@ number_type(md::ModelDef) = md.number_type

numcomponents(md::ModelDef) = length(md.comp_defs)


function dump_components()
for comp in compdefs()
println("\n$(name(comp))")
for (tag, objs) in ((:Variables, variables(comp)), (:Parameters, parameters(comp)), (:Dimensions, dimensions(comp)))
println(" $tag")
for obj in objs
println(" $(obj.name) = $obj")
end
end
end
end

"""
new_comp(comp_id::ComponentId, verbose::Bool=true)

Add an empty `ComponentDef` to the global component registry with the given
`comp_id`. The empty `ComponentDef` must be populated with calls to `addvariable`,
`addparameter`, etc.
"""
function new_comp(comp_id::ComponentId, verbose::Bool=true)
if verbose
if haskey(_compdefs, comp_id)
@warn "Redefining component $comp_id"
else
@info "new component $comp_id"
end
end

comp_def = ComponentDef(comp_id)
_compdefs[comp_id] = comp_def
return comp_def
end

"""
delete!(m::ModelDef, component::Symbol

Expand Down Expand Up @@ -529,13 +478,14 @@ function _add_anonymous_dims!(md::ModelDef, comp_def::ComponentDef)
end

"""
add_comp!(md::ModelDef, comp_def::ComponentDef; first=nothing, last=nothing, before=nothing, after=nothing)
add_comp!(md::ModelDef, comp_def::ComponentDef, comp_name::Symbol=comp_def.comp_id.comp_name;
first=nothing, last=nothing, before=nothing, after=nothing)

Add the component indicated by `comp_def` to the model indcated by `md`. The component is added at the
end of the list unless one of the keywords, `first`, `last`, `before`, `after`. If the `comp_name`
differs from that in the `comp_def`, a copy of `comp_def` is made and assigned the new name.
"""
function add_comp!(md::ModelDef, comp_def::ComponentDef, comp_name::Symbol;
function add_comp!(md::ModelDef, comp_def::ComponentDef, comp_name::Symbol=comp_def.comp_id.comp_name;
first::NothingInt=nothing, last::NothingInt=nothing,
before::NothingSymbol=nothing, after::NothingSymbol=nothing)

Expand Down
14 changes: 14 additions & 0 deletions src/core/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ function add_comp!(m::Model, comp_id::ComponentId, comp_name::Symbol=comp_id.com
return ComponentReference(m, comp_name)
end

function add_comp!(m::Model, comp_def::ComponentDef, comp_name::Symbol=comp_def.comp_id.comp_name;
first=nothing, last=nothing, before=nothing, after=nothing)
add_comp!(m.md, comp_def, comp_name; first=first, last=last, before=before, after=after)
decache(m)
return ComponentReference(m, comp_name)
end

"""
replace_comp!(m::Model, comp_id::ComponentId, comp_name::Symbol=comp_id.comp_name;
first::NothingSymbol=nothing, last::NothingSymbol=nothing,
Expand All @@ -183,6 +190,13 @@ function replace_comp!(m::Model, comp_id::ComponentId, comp_name::Symbol=comp_id
return ComponentReference(m, comp_name)
end

function replace_comp!(m::Model, comp_def::ComponentDef, comp_name::Symbol=comp_def.comp_id.comp_name;
first::NothingSymbol=nothing, last::NothingSymbol=nothing,
before::NothingSymbol=nothing, after::NothingSymbol=nothing,
reconnect::Bool=true)
replace_comp!(m, comp_def.comp_id, comp_name; first=first, last=last, before=before, after=after, reconnect=reconnect)
end

"""
components(m::Model)

Expand Down
5 changes: 2 additions & 3 deletions src/core/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -357,16 +357,15 @@ mutable struct ComponentInstance{TV <: ComponentInstanceVariables, TP <: Compone

comp_name = comp_id.comp_name
module_name = comp_id.module_name
comp_module = Base.eval(Main, module_name)
comp_module = getfield(Main, module_name)

# TBD: use FunctionWrapper here?
function get_func(name)
func_name = Symbol("$(name)_$(comp_name)")
try
Base.eval(comp_module, func_name)
getfield(comp_module, func_name)
catch err
# No need to warn about this...
# @warn "Failed to evaluate function name $func_name in module $comp_module"
nothing
end
end
Expand Down
17 changes: 4 additions & 13 deletions test/test_components.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ using Mimi
using Test

import Mimi:
reset_compdefs, compdefs, compdef, compkeys, hascomp, _compdefs, first_period,
last_period, compmodule, compname, numcomponents, dump_components,
reset_compdefs, compdefs, compdef, compkeys, hascomp, first_period,
last_period, compmodule, compname, numcomponents,
dim_keys, dim_values, dimensions

reset_compdefs()

@test length(_compdefs) == 3 # adder, ConnectorCompVector, ConnectorCompMatrix

my_model = Model()

# Try running model with no components
Expand Down Expand Up @@ -81,8 +79,7 @@ comps = collect(compdefs(my_model))
# Test compdefs, compdef, compkeys, etc.
@test comps == collect(compdefs(my_model.md))
@test length(comps) == 3
@test compdef(:testcomp3) == comps[3]
@test_throws ErrorException compdef(:testcomp4) #this component does not exist
@test testcomp3 == comps[3]
@test [compkeys(my_model.md)...] == [:testcomp1, :testcomp2, :testcomp3]
@test hascomp(my_model.md, :testcomp1) == true && hascomp(my_model.md, :testcomp4) == false

Expand All @@ -93,12 +90,6 @@ comps = collect(compdefs(my_model))
add_comp!(my_model, testcomp3, :testcomp3_v2)
@test numcomponents(my_model) == 4

# Test reset_compdefs methods
reset_compdefs()
@test length(_compdefs) == 3 # adder, ConnectorCompVector, ConnectorCompMatrix
reset_compdefs(false)
@test length(_compdefs) == 0


#------------------------------------------------------------------------------
# Tests for component run periods when resetting the model's time dimension
Expand All @@ -115,7 +106,7 @@ end

# 1. Test resetting the time dimension without explicit first/last values

cd = compdef(testcomp1)
cd = testcomp1
@test cd.first === nothing # original component definition's first and last values are unset
@test cd.last === nothing

Expand Down
2 changes: 0 additions & 2 deletions test/test_main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ end
foo1.par1 = 5.0
end

@test length(compdefs()) == 4 # adder, 2 connectors, and foo1

# x1 = foo1(Float64, Dict{Symbol, Int}(:time=>10, :index1=>3))
# x1 = foo1(Float64, Val{1}, Val{1}, Val{10}, Val{1}, Val{1}, Val{1}, Val{1}, Dict{Symbol, Int}(:time=>10, :index1=>3))

Expand Down
2 changes: 0 additions & 2 deletions test/test_main_variabletimestep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ end
# foo1.par2 = []
end

@test length(compdefs()) == 4 # adder, 2 connectors, and foo1

# x1 = foo1(Float64, Dict{Symbol, Int}(:time=>10, :index1=>3))
# x1 = foo1(Float64, Val{1}, Val{1}, Val{10}, Val{1}, Val{1}, Val{1}, Val{1}, Dict{Symbol, Int}(:time=>10, :index1=>3))

Expand Down
4 changes: 2 additions & 2 deletions test/test_metainfo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ end
c1 = compdef(test_model, :ch4forcing1)
c2 = compdef(test_model, :ch4forcing2)

@test c1 == compdef(:ch4forcing1)
@test_throws ErrorException compdef(:missingcomp)
@test c1 == compdef(test_model, :ch4forcing1)
@test_throws KeyError compdef(test_model, :missingcomp)

@test c2.comp_id.module_name == :TestMetaInfo
@test c2.comp_id.comp_name == :ch4forcing1
Expand Down
4 changes: 2 additions & 2 deletions test/test_metainfo_variabletimestep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ end
c1 = compdef(test_model, :ch4forcing1)
c2 = compdef(test_model, :ch4forcing2)

@test c1 == compdef(:ch4forcing1)
@test_throws ErrorException compdef(:missingcomp)
@test c1 == ch4forcing1
@test_throws KeyError compdef(test_model, :missingcomp)

@test c2.comp_id.module_name == :TestMetaInfo_VariableTimestep
@test c2.comp_id.comp_name == :ch4forcing1
Expand Down