I’m trying to create a mini materials database. In this database, I would have materials which can have a varying number of properties: optical, electrical, thermal …
Each material can have only one of those, some of those or all of those.
I thought to create a type Material in which the data is kept in a dict:
struct Material{K,V <: Description}
data :: Dict{K,V}
end
where depending on the property I can have a description that can change (using a model or data)
abstract type Description end
struct Model <: Description end
struct SomeData <: Description end
I would then describe silicon in the following way
Si = Material( Dict(:optical => Model(),
:electrical => SomeData()) )
If you don’t need to add properties after instantiation, you could wrap a NamedTuple instead of a Dict, i.e.
struct Material{K,V}
data::NamedTuple{K,V}
end
Material(; kwargs...) = Material(kwargs.data)
Si = Material(optical = Model(), electrical = SomeData())
function permitivitty(material :: Material{K,V}) where {K,V}
if :optical in K
permitivitty(material.data.optical)
else
@warn "$material has no propery optical"
end
end
# Union for descriptions: either a description, or nothing
DescriptionUnion = Union{Description, Nothing}
struct Material
optical::DescriptionUnion
electrical::DescriptionUnion
end
# keyword-arguments constructor (could be generated by a macro for convenience...)
Material(;optical=nothing, electrical=nothing) = Material(optical, electrical)
# possible usage
Material(nothing, SomeData()) # default constructor
Material() # all fields are "nothing"
Material(optical=Model()) # set some of the data fields
Then you’d define functions on material properties as:
IMO @asprionj’s solution is a lot better, since it comes with type safety… if you use a dictionary or named tuple, a typo in one of your properties will result in a bug (as if the material is missing that property), vs refusing to create the material in the first place. (Of course, you could detect that even with a dictionary by adding verification code in the constructor, but that seems a lot messier.)
If you think this won’t be an issue in practice, consider that you already have a typo in the word “permitivitty” in your code
struct Material{To,Te}
optical::To
electrical::Te
end
Look e.g. at m = Material(optical=Model()); @code_warntype permitivitty(m).
If there are only two properties, I would also go with this approach. But I understood the OP that there can be many more properties, and then I see a trade-off between convenience (the NamedTuple-approach) and typo-safety (the explicit parametric struct approach). In terms of performance they should be equivalent.
But I understood the OP that there can be many more properties, and then I see a trade-off between convenience (the NamedTuple -approach) and typo-safety
I agree 200%
In my case I don’t have too many properties, but if for some reason I have a material for which I want to add other properties, the NamedTuple allows me to keep the types as they are.
Thanks to all of you for looking into this.
I learned a lot again.