friedmud / MOOSE.jl

Multiphysics Object Oriented Simulation Environment In Julia

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Extract common fields from similar types

djsegal opened this issue · comments

In class you said types weren't DRY enough in situations where various types share some fields.

Could you direct me to a file that exemplifies this concern? This would be useful even if it is just in the phase where you're saying "if I add another similar type I'm going to have to extract some common stuff"

Sure.

It's currently not very much data... but look at u in Kernel:

https://github.com/friedmud/MOOSE.jl/blob/master/src/kernels/Kernel.jl

And notice that it has to be copied to the concrete Kernel classes:

https://github.com/friedmud/MOOSE.jl/blob/master/src/kernels/Diffusion.jl
https://github.com/friedmud/MOOSE.jl/blob/master/src/kernels/Convection.jl

Now... in this simple implementation that's just one data member... but look at all of the data in the real Kernel class in MOOSE:

https://github.com/idaholab/moose/blob/devel/framework/include/kernels/KernelBase.h

And look at all of the hundreds of member variables inherited in the real Diffusion class in MOOSE through multiple levels of inheritance and from many base classes:

http://mooseframework.org/docs/doxygen/moose/classDiffusion.html

(Scroll down to "Protected Attributes")

If all of those hundreds of member variables had to copy and pasted into every leaf class in MOOSE.jl it would be an absolute disaster...

The workarounds aren't great either. One option is that each of those intermediate base classes could declare a BaseData type that would be included in the leaf classes. But it would still mean that 23 fields would have to be declared in every leaf class (that's the total number of inherited base classes for Diffusion). That's a huge amount of duplication (and plenty of room for error)...

Another option is to go to a system where you just pass in a huge DATA object... and let the leaf functions pull out what they want. This isn't really possible for a number of reasons... the least of which is that it will be incredibly slow (a lot of those data members are being initialized by slow processes at the start of the simulation so that getting the values later is really fast). It's also just not the design I want 😉

Finite elements is just a beast like this. Multiphysics makes it worse. You have a huge amount of data that all needs to trickle down in to (relatively) tiny statements. I mean: look at what the leaf class actually has to implement here:

https://github.com/idaholab/moose/blob/devel/framework/src/kernels/Diffusion.C

That's how I want my Julia code to look too.

Let me know if that makes sense!

(BTW: you can look at any of the inheritance hierarchies in MOOSE.jl and see the same thing. For instance, checkout the DofObject hierarchy:

https://github.com/friedmud/MOOSE.jl/blob/master/src/mesh/DofObject.jl
https://github.com/friedmud/MOOSE.jl/blob/master/src/mesh/Element.jl
https://github.com/friedmud/MOOSE.jl/blob/master/src/mesh/Node.jl

Both Element and Node have to have id and dofs. In C++ those would just be in the base class... as they are here: https://github.com/libMesh/libmesh/blob/master/include/base/dof_object.h#L475
)

BTW: I am planning to work around this in MOOSE.jl... I just haven't
decided exactly how I'm going to alter my design yet.

You can see some of this in the fact that the data is already less "flat"
in MOOSE.jl than it is in MOOSE. u is a Variable type... and to get
the value and gradient you do u.value and u.gradient whereas in MOOSE
those are member variables that you get by doing _u and _grad_u.

So: I'm already starting to workaround this... but it can only go so far
before it gets cumbersome. If you have to do
this.that.something.stuff.junk to get at data... that's not very much
fun. Using inheritance in C++ we can "flatten" the data a lot... to
present the data in a nice way to people extending the objects in MOOSE
(which, are scientists and engineers that frequently would rather not be
creating code at all!).

Just a question before I jump into this. Would I be wrong in assuming that you want a rails-y concern implementation?

something like: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

edit: my bad for getting ahead of myself. no need to respond to this or the next message


require 'active_support/concern'

module Foo
  extend ActiveSupport::Concern
  included do
    def self.method_injected_by_foo
      ...
    end
  end
end

module Bar
  extend ActiveSupport::Concern
  include Foo

  included do
    self.method_injected_by_foo
  end
end

class Host
  include Bar # It works, now Bar takes care of its dependencies
end

Another example would be from my own codebase for ahab:

module Sluggable
  extend ActiveSupport::Concern

  included do
    include Identifiable
    slugged = self.name.constantize::SLUGGED_ON
    friendly_id slugged, use: :slugged
    validates :slug, uniqueness: { case_sensitive: false }, unless: :is_form_resource?

    validates slugged, presence: true
  end

end

class User < ApplicationRecord

  include Sluggable

  ...

end

Sorry, just going to close this. My bad.