For some elements, the code might look very similar when integrating loads, but trying to abstract away those differences always seemed like way more hassle than what it is worth. In most cases, we can support many elements with base class implementations anyway, so there are limited implementations to worry about.
But there is definitely code duplication here, I made a new version for assembling loads through sets, and I'll go through the reasons below.
Sorry for another long reply, but there was a lot to go through here.
First off, "boundary" was an attempt from me to keep things general when considering 2D vs 3D elements, but clearly this was failure.
It is probably a good idea to rename them.
The reason why I avoided using the old code was because I didn't want to risk breaking anything. The base of these "new" methods are in the base class Element, and I needed to make this work with Transport and Structural element at the same time.
The plan was always that these should be merged into the same code path eventually, but I needed to be able to test them side-by-side.
I think the transport elements where mixing up what constitutes an edge and a surface, since they don't actually include the thickness of the cross-section anyway. I could be wrong about the intentions there, since if the thickness is 1.0 it will be identical.
These are the three assemble methods that I developed for the input format using sets. (i.e. elements do not contain the record of loads, instead the loads contain a set of elements):
A. computeLoadVector - [N/m³] integrates given body load. I.e. deadweight on a beam, that would still be a body load, even if it varies over an edge.
Output for the lspace element is a 24 length array.
B. computeBoundaryLoadVector - [N/m²] integrates given surface load. Loads are defined per area N/m^2. 2D elements multiple this by the, possibly varying, thickness.
Output for the lspace element is a 12 length array.
C. computeEdgeLoadVector - [N/m] integrates given edge load. Typically only for beams, bars, or shells/plates. E.g. a plane strain 2D element would not use edge loads. I attached a little diagram trying to show what I mean here. Surface loads as "t" and edge loads as "q", applying to plane stress and axi-symm elements.
Output for the lspace element is a 6 length array.
The integration for each of these routines is different (different integration rules, calls the interpolator in different ways, different size output etc.)
Trying to reuse code in between these is probably not worth it, they are only superficially similar.
---------------------------------------------
Then there are the "old" methods for load integration (computeEdgeLoadVectorAt and computeSurfaceLoadVectorAt).
These are primarily different in 2 aspects:
1. The output for lspace is 24 length arrays (padded out with zeros) for all load types, which means they have to add an additional mapping to the internal edge/surface and up-assemble the results (i.e. giveEdgeDofMapping)
I found it unnecessary, so I did my routines for only the surface/edge nodes.
2. When they are assembled, they are expected to be in the element l.c.s. (because the assembler code will transform all the vectors to global c.s.)
In most cases I could think of, it is easier (or at least just as easy) to just compute the load in global c.s.
Since there was no code existing for element lcs -> global c.s. for *only* edge/surface nodes, I opted to just leave it as global c.s. and let the element deal with any transform that's needed.
We could change this, either way, there are only a few elements with local c.s., so it wouldn't make a huge difference.
There is also the differences on what constitutes a "surface" or "edge" load on 2D elements. I think it's best to define let the units define the behavior here. bulk = N/m³, surf = N/m², edge = N/m.
In the end, I imagine we would have the following routines
virtual void computeBodyLoad(FloatArray, BodyLoad, CharType, ValueModeType, TimeStep);
virtual void computeSurfaceLoad(FloatArray, SurfaceLoad, int, CharType, ValueModeType, TimeStep);
virtual void computeEdgeLoad(FloatArray, EdgeLoad, int, CharType, ValueModeType, TimeStep);
// And for convection/springbed type b.c.s
virtual void computeSurfaceLoadTangent(FloatMatrix, SurfaceLoad, int, TimeStep);
virtual void computeEdgeLoadTagent(FloatMatrix, EdgeLoad, int, TimeStep);
But to have a common assembly code, we must decide on how the output is defined:
1. Should we pad them out with zeros?
2. Should they be in global or element local c.s.?
There is clear best choice here. Merits and downsides to either way you choose.
Post's attachments20160510162121626.pdf 465.64 kb, 5 downloads since 2016-05-10
You don't have the permssions to download the attachments of this post.