2

I want to create a finite element object oriented program. I have a class Node. Since the nodes in a finite element mesh (represented by class Mesh) are distinct, I created the Node class to be a value class. When I instantiate the array of objects from class Node, I assign that object array to the nodes property of Mesh. I have an Element class too, representing a finite element. I also create an object array from this class and assign it to the element property of Mesh. It is clear up to now.

Since the finite element nodes also belong to the elements, I want to assign some of the nodes to the appropriate elements. But copying the nodes results in data redundancy, therefore I want to assign pointers to the Node objects so that the localNodes property of Element contains an array of pointers to the specific nodes. How should I modify my classes below to achieve it?

The Node class:

classdef Node

   properties
      coordinate;
   end

   methods
     % Not interesting for this example
   end

end

The Element class:

classdef Element

   properties
      localNodes; % the object instantiated from the class Element
                  % will store an array of pointers to the
                  % appropriate elements of the object array stored
                  % in Mesh.nodes. How can I assign these pointers
                  % to Element.localNodes?
   end

   methods
     % Not interesting for this example
   end

end

The Mesh class:

classdef Mesh

   properties
      nodes;    % its object will contain an object array of Node
      elements; % its object will contain an object array of Element
   end

   methods
     % Not interesting for this example
   end

end
Zoltán Csáti
  • 679
  • 5
  • 17

2 Answers2

3

Finally, following some discussion here is a starting point how I would solve this:

classdef Node < handle

   properties
      coordinate;
   end

   methods
       function obj=Node(id)
           obj.coordinate=id;
       end
   end

end

.

classdef Mesh < handle

   properties
      nodes;    % its object will contain an object array of Node
      elements; % its object will contain an object array of Element
   end

   methods
       function obj=Mesh(nodes,elements)
           pnodes=cell(1,nodes);
           for idx=1:nodes
               pnodes{idx}=Node(idx);
           end
           obj.nodes=[pnodes{:}];
           pelements=cell(1,numel(elements));
           for idx=1:numel(elements)
               pelements{idx}=Element(obj.nodes(elements{idx}));
           end
           obj.elements=[pelements{:}];
       end
       function non_deleted_nodes=get.nodes(obj)
           %getter to return only not-deleted nodes
           obj.nodes=obj.nodes(arrayfun(@isvalid,(obj.nodes)));
           non_deleted_nodes=obj.nodes;
       end
       function non_deleted_nodes=get.elements(obj)
           %getter to return only not-deleted nodes
           obj.elements=obj.elements(arrayfun(@isvalid,(obj.elements)));
           non_deleted_nodes=obj.elements;
       end

   end

end

.

classdef Element < handle

   properties
      localNodes; % the object instantiated from the class Element
                  % will store an array of pointers to the
                  % appropriate elements of the object array stored
                  % in Mesh.nodes. How can I assign these pointers
                  % to Element.localNodes?
   end

   methods
       function obj=Element(localNodes)
           obj.localNodes=localNodes;
       end
       function non_deleted_nodes=get.localNodes(obj)
           %getter to return only not-deleted nodes
           obj.localNodes=obj.localNodes(arrayfun(@isvalid,(obj.localNodes)));
           non_deleted_nodes=obj.localNodes
       end
       function delete(obj)
           for ix=1:numel(obj.localNodes)
               %The 1 is not a typo, we will delete always the first
               %element until the list is empty
               obj.localNodes(1).delete();
           end
           delete@handle(obj);
       end
   end

end

And finally a short demonstration:

m=Mesh(10,{[1,2],[2,3],[3,4]})
m.elements(1).localNodes
m.elements(1).localNodes(1).delete()
%now the node is deleted from the element and the mesh
m.elements(2).delete()
%now element 2 together with the nodes is deleted.
Daniel
  • 36,610
  • 3
  • 36
  • 69
  • Thank you for your classes. Two questions: 1) Why is `delete@handle(obj);` needed? 2) Correct me if I'm wrong. The **Element** class needs to be a handle class, because if I delete an element, its two nodes must be removed from both the Element object AND the Mesh object. If I wanted to delete just nodes, then it would be enough the **Node** class to be handle class, since in that case I wanted to remove that Node object both from the Element AND the Mesh but the element would remain. Reformulating: which condition demands the **Element** class to be a handle class? 3) See my next comment. – Zoltán Csáti Nov 05 '15 at 18:31
  • 3) Can we delete a specific Node or Element object permanently, not just its handle so that the getter methods are not required in **Mesh**? Or in case of a handle class, the object cannot be deleted just its handle? – Zoltán Csáti Nov 05 '15 at 18:31
  • Perhaps I have the answer for the first question: if you delete an element, first you delete its nodes in a loop, than the element itself. But why is the command `delete@handle(obj)` needed? When the function exits, it automatically deletes the element, doesn't it? The delete function is just needed to ensure that the nodes are also deleted, right? – Zoltán Csáti Nov 05 '15 at 18:49
  • 1
    1) The call is not necessary, I thought it would be required to call `delete` in handle but it is called in any case. – Daniel Nov 05 '15 at 19:00
  • 3) To permanently remove the references, use `obj.localNodes=obj.localNodes(arrayfun(@isvalid,(obj.localNodes)));` and similar for the other attributes. – Daniel Nov 05 '15 at 19:08
  • 2) It is possible without using handle, but then you can no longer implement a delete in the `Element` class, you must modify the `Mesh` properties to delete an `Element` – Daniel Nov 05 '15 at 19:11
  • 2) I understand now. Which one do you recommend (speed, ease of use) to to able to delete an element by removing its reference from **Mesh** or delete right from the object of **Mesh**? 3) Is it necessary to remove the reference permanently? What do you advise? – Zoltán Csáti Nov 05 '15 at 19:16
  • 1
    3) I have updated the code, it will now delete references permanently when calling a getter. This prevents unnecessary large data structures. – Daniel Nov 05 '15 at 19:20
  • 1
    2) Use the version with `handle` otherwise you can not use references to elements. – Daniel Nov 05 '15 at 19:23
  • All right, thank you for help very much, I learnt a lot from you. – Zoltán Csáti Nov 05 '15 at 19:36
1

You have to use the superclass handle which let's you use references instead of copying the data.

x=MyValueClass(); %assume handle not superclass
y=x; %creates a copy
x=MyHandleClass(); %assume handle is superclass
y=x; %creates a reference to the same instance
Daniel
  • 36,610
  • 3
  • 36
  • 69
  • Don't you need `< handle` in the class definition to create a handle class? Or can you define a handle to a non-handle object? – Andras Deak -- Слава Україні Nov 05 '15 at 12:18
  • Daniel, I know that in that case classdef Node < handle should be used. However, class Node must also function as a value class, because of my reasoning above. How can it be both a value and a handle class? @AndrasDeak, that's a good idea to define a handle to the non-handle object. But how can I do that? – Zoltán Csáti Nov 05 '15 at 12:54
  • @AndrasDeak: Yes, that's exactly what I suggested. – Daniel Nov 05 '15 at 13:00
  • @ZoltánCsáti: I don't see your problem using `handle`, could you try to further explain it? – Daniel Nov 05 '15 at 13:00
  • 1
    @ZoltánCsáti You can just create a new object of the handle class for each Node, so effectively, you've got a single copy for each Node with the advantage of being able to pass it around more efficiently. – lhcgeneva Nov 05 '15 at 13:11
  • The problem occurs here: myMesh = Mesh(); myElemArray = repmat(Element(), 3, 1); myElemArray = repmat(Node(), 4, 1); myElemArray = repmat(Element(), 3, 1); myNodeArray = repmat(Node(), 4, 1); myNodeArray(1).coordinate = 0; myNodeArray(2).coordinate = 1; myNodeArray(3).coordinate = 2; myNodeArray(4).coordinate = 3; myMesh.nodes = myNodeArray; As you can see, all elements of myNodeArray will have the property value coordinate=3. This is not what I want. Therefore, I have to stick to the value class. But if I define the Node class to be a value class, then it cannot serve as a pointer. – Zoltán Csáti Nov 05 '15 at 13:16
  • 1
    Ahha, so perhaps the problem in the code I attached in my last comment is that repmat copied the reference to the same object and that's why I gave identically 3. But then how can I initialize an array of objects from a handle class so as to avoid them being pointed to the same single object? – Zoltán Csáti Nov 05 '15 at 13:23
  • Your understanding of the problem is right, you can not use `repmat`, instead you have to use a for loop or similar. – Daniel Nov 05 '15 at 13:50
  • This [solution](http://stackoverflow.com/questions/7879726/how-to-initialize-a-vector-of-class-handles/7879959#7879959) is suitable for preallocation, right? – Zoltán Csáti Nov 05 '15 at 14:07
  • I have added an answer to the question, in case you don't have a default constructor. – Daniel Nov 05 '15 at 14:25
  • Do you think, it is good to create the constructor of class **Node** as `function obj = Node(nNodes) if nargin ~= 0 obj(nNodes) = Node(); end end` ? – Zoltán Csáti Nov 05 '15 at 14:39
  • @ZoltánCsáti: No! This way you create a `Node` object which holds a `Node` object. If you intend to have an array, make `Mesh` being an array which holds nodes. I don't fully understand what a Mesh and an Element is in your context, so I can not really recommend a finished architecture. You may update your question with more details (What structure you actually want to instantiate), then I could give more advice how to implement it. – Daniel Nov 05 '15 at 14:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94346/discussion-between-zoltan-csati-and-daniel). – Zoltán Csáti Nov 05 '15 at 15:09