1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    TestAppendMolecule.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkDataSetAttributes.h"
17 #include "vtkDoubleArray.h"
18 #include "vtkMolecule.h"
19 #include "vtkMoleculeAppend.h"
20 #include "vtkNew.h"
21 #include "vtkUnsignedCharArray.h"
22 #include "vtkUnsignedShortArray.h"
23 
24 #define CheckNumbers(name, first, second)                                                          \
25   if (first != second)                                                                             \
26   {                                                                                                \
27     cerr << "Error : wrong number of " << #name << ". Got " << first << " but expects " << second  \
28          << endl;                                                                                  \
29     return EXIT_FAILURE;                                                                           \
30   }
31 
32 // Used to creates different atoms and data for each molecule
33 static int NB_OF_MOL = 0;
34 
35 // Add two atoms with a bond between them.
InitSimpleMolecule(vtkMolecule * molecule)36 void InitSimpleMolecule(vtkMolecule* molecule)
37 {
38   NB_OF_MOL++;
39   vtkAtom h1 = molecule->AppendAtom(1, 0.5, 1.5, -NB_OF_MOL);
40   vtkAtom h2 = molecule->AppendAtom(1, 0.5, 1.5, NB_OF_MOL);
41   molecule->AppendBond(h1, h2, 1);
42 }
43 
AddAtomData(vtkMolecule * molecule,vtkIdType size)44 void AddAtomData(vtkMolecule* molecule, vtkIdType size)
45 {
46   vtkNew<vtkDoubleArray> data;
47   data->SetName("Data");
48   data->SetNumberOfComponents(1);
49   for (vtkIdType i = 0; i < size; i++)
50   {
51     data->InsertNextValue(NB_OF_MOL * 1.01);
52   }
53   molecule->GetAtomData()->AddArray(data);
54 }
55 
CheckMolecule(vtkMolecule * molecule,int nbAtoms,int nbBonds,int nbArrays,vtkDoubleArray * values,int nbGhostAtoms,int nbGhostBonds)56 int CheckMolecule(vtkMolecule* molecule,
57   int nbAtoms,
58   int nbBonds,
59   int nbArrays,
60   vtkDoubleArray* values,
61   int nbGhostAtoms,
62   int nbGhostBonds)
63 {
64   CheckNumbers("atoms", molecule->GetNumberOfAtoms(), nbAtoms);
65   CheckNumbers("bonds", molecule->GetNumberOfBonds(), nbBonds);
66   CheckNumbers("atom data arrays", molecule->GetAtomData()->GetNumberOfArrays(), nbArrays);
67 
68   vtkDataArray* resultData = molecule->GetAtomData()->GetArray("Data");
69   if (!resultData)
70   {
71     std::cerr << "Error : atoms data array not found in result" << std::endl;
72     return EXIT_FAILURE;
73   }
74   CheckNumbers("atom data array values", resultData->GetNumberOfTuples(), nbAtoms);
75 
76   for (vtkIdType i = 0; i < nbAtoms; i++)
77   {
78     CheckNumbers("data value", resultData->GetTuple1(i), values->GetValue(i));
79   }
80 
81   vtkUnsignedShortArray* bondOrderArray = molecule->GetBondOrdersArray();
82   if (!bondOrderArray)
83   {
84     std::cerr << "Error : bonds data array not found in result" << std::endl;
85     return EXIT_FAILURE;
86   }
87   CheckNumbers("bond data array values", bondOrderArray->GetNumberOfTuples(), nbBonds);
88 
89   vtkUnsignedCharArray* ghostAtoms = molecule->GetAtomGhostArray();
90   int nbOfGhosts = 0;
91   for (vtkIdType id = 0; id < nbAtoms; id++)
92   {
93     if (ghostAtoms->GetValue(id) == 1)
94     {
95       nbOfGhosts++;
96     }
97   }
98   // ghost atom from molecule2 is still ghost in result
99   CheckNumbers("ghost atoms", nbOfGhosts, nbGhostAtoms);
100 
101   vtkUnsignedCharArray* ghostBonds = molecule->GetBondGhostArray();
102   nbOfGhosts = 0;
103   for (vtkIdType id = 0; id < nbBonds; id++)
104   {
105     if (ghostBonds->GetValue(id) == 1)
106     {
107       nbOfGhosts++;
108     }
109   }
110   // ghost bond from molecule2 is still ghost in result
111   CheckNumbers("ghost bonds", nbOfGhosts, nbGhostBonds);
112 
113   return EXIT_SUCCESS;
114 }
115 
TestAppendMolecule(int,char * [])116 int TestAppendMolecule(int, char* [])
117 {
118   // --------------------------------------------------------------------------
119   // Simple test : 2 molecules, no data
120   vtkNew<vtkMolecule> simpleMolecule1;
121   InitSimpleMolecule(simpleMolecule1);
122 
123   vtkNew<vtkMolecule> simpleMolecule2;
124   InitSimpleMolecule(simpleMolecule2);
125 
126   vtkNew<vtkMoleculeAppend> appender;
127   appender->AddInputData(simpleMolecule1);
128   appender->AddInputData(simpleMolecule2);
129   appender->Update();
130   vtkMolecule* resultMolecule = appender->GetOutput();
131 
132   int expectedResult = simpleMolecule1->GetNumberOfAtoms() + simpleMolecule2->GetNumberOfAtoms();
133   CheckNumbers("atoms", resultMolecule->GetNumberOfAtoms(), expectedResult);
134 
135   expectedResult = simpleMolecule1->GetNumberOfBonds() + simpleMolecule2->GetNumberOfBonds();
136   CheckNumbers("bonds", resultMolecule->GetNumberOfBonds(), expectedResult);
137 
138   // --------------------------------------------------------------------------
139   // Full test : ghosts and data
140 
141   /**
142    * Use 3 molecules:
143    *  - fullMolecule1 : 2 atoms and one bond, no ghost
144    *  - fullMolecule2 : 3 atoms and 2 bonds, one ghost atom and one ghost bond
145    *  - fullMolecule3 : 3 atoms and 2 bonds, one ghost atom and one ghost bond
146    */
147 
148   // INIT
149   vtkNew<vtkMolecule> fullMolecule1;
150   InitSimpleMolecule(fullMolecule1);
151   AddAtomData(fullMolecule1, 2);
152   vtkNew<vtkMolecule> fullMolecule2;
153   InitSimpleMolecule(fullMolecule2);
154   AddAtomData(fullMolecule2, 3);
155   vtkNew<vtkMolecule> fullMolecule3;
156   InitSimpleMolecule(fullMolecule3);
157   AddAtomData(fullMolecule3, 3);
158 
159   // duplicate first atom of molecule 2 to be ghost in molecule 3, and vice versa.
160   vtkAtom firstAtom2 = fullMolecule2->GetAtom(0);
161   vtkAtom firstAtom3 = fullMolecule3->GetAtom(0);
162 
163   vtkAtom ghostAtom2 =
164     fullMolecule2->AppendAtom(firstAtom3.GetAtomicNumber(), firstAtom3.GetPosition());
165   vtkBond ghostBond2 = fullMolecule2->AppendBond(firstAtom2, ghostAtom2, 1);
166 
167   vtkAtom ghostAtom3 =
168     fullMolecule3->AppendAtom(firstAtom2.GetAtomicNumber(), firstAtom2.GetPosition());
169   vtkBond ghostBond3 = fullMolecule3->AppendBond(firstAtom3, ghostAtom3, 1);
170 
171   // set ghost flag on relevant atoms and bonds.
172   fullMolecule1->AllocateAtomGhostArray();
173   fullMolecule1->AllocateBondGhostArray();
174 
175   fullMolecule2->AllocateAtomGhostArray();
176   fullMolecule2->GetAtomGhostArray()->SetValue(ghostAtom2.GetId(), 1);
177   fullMolecule2->AllocateBondGhostArray();
178   fullMolecule2->GetBondGhostArray()->SetValue(ghostBond2.GetId(), 1);
179 
180   fullMolecule3->AllocateAtomGhostArray();
181   fullMolecule3->GetAtomGhostArray()->SetValue(ghostAtom3.GetId(), 1);
182   fullMolecule3->AllocateBondGhostArray();
183   fullMolecule3->GetBondGhostArray()->SetValue(ghostBond3.GetId(), 1);
184 
185   // --------------------------------------------------------------------------
186   // First part: 2 molecules, ghost and data
187 
188   // APPEND 1 with 2
189   vtkNew<vtkMoleculeAppend> appender2;
190   appender2->AddInputData(fullMolecule1);
191   appender2->AddInputData(fullMolecule2);
192   appender2->Update();
193   vtkMolecule* resultFullMolecule = appender2->GetOutput();
194 
195   // CHECK RESULT
196   int nbOfExpectedAtoms = fullMolecule1->GetNumberOfAtoms() + fullMolecule2->GetNumberOfAtoms();
197   int nbOfExpectedBonds = fullMolecule1->GetNumberOfBonds() + fullMolecule2->GetNumberOfBonds();
198   int nbOfExpectedArrays = fullMolecule1->GetAtomData()->GetNumberOfArrays();
199   vtkNew<vtkDoubleArray> expectedResultValues;
200   expectedResultValues->InsertNextValue(
201     fullMolecule1->GetAtomData()->GetArray("Data")->GetTuple1(0));
202   expectedResultValues->InsertNextValue(
203     fullMolecule1->GetAtomData()->GetArray("Data")->GetTuple1(1));
204   expectedResultValues->InsertNextValue(
205     fullMolecule2->GetAtomData()->GetArray("Data")->GetTuple1(0));
206   expectedResultValues->InsertNextValue(
207     fullMolecule2->GetAtomData()->GetArray("Data")->GetTuple1(1));
208   expectedResultValues->InsertNextValue(
209     fullMolecule2->GetAtomData()->GetArray("Data")->GetTuple1(2));
210 
211   int res = CheckMolecule(resultFullMolecule,
212     nbOfExpectedAtoms,
213     nbOfExpectedBonds,
214     nbOfExpectedArrays,
215     expectedResultValues,
216     1,
217     1);
218   if (res == EXIT_FAILURE)
219   {
220     return EXIT_FAILURE;
221   }
222 
223   // --------------------------------------------------------------------------
224   // Second part: 3 molecules, ghost and data, no merge
225   // APPEND third molecule
226   appender2->MergeCoincidentAtomsOff();
227   appender2->AddInputData(fullMolecule3);
228   appender2->Update();
229   resultFullMolecule = appender2->GetOutput();
230 
231   // CHECK RESULT
232   nbOfExpectedAtoms = fullMolecule1->GetNumberOfAtoms() + fullMolecule2->GetNumberOfAtoms() +
233     fullMolecule3->GetNumberOfAtoms();
234   nbOfExpectedBonds = fullMolecule1->GetNumberOfBonds() + fullMolecule2->GetNumberOfBonds() +
235     fullMolecule3->GetNumberOfBonds();
236   nbOfExpectedArrays = fullMolecule1->GetAtomData()->GetNumberOfArrays();
237 
238   // Result contains data of non ghost atom.
239   expectedResultValues->InsertNextValue(
240     fullMolecule3->GetAtomData()->GetArray("Data")->GetTuple1(0));
241   expectedResultValues->InsertNextValue(
242     fullMolecule3->GetAtomData()->GetArray("Data")->GetTuple1(1));
243   expectedResultValues->InsertNextValue(
244     fullMolecule3->GetAtomData()->GetArray("Data")->GetTuple1(2));
245 
246   res = CheckMolecule(resultFullMolecule,
247     nbOfExpectedAtoms,
248     nbOfExpectedBonds,
249     nbOfExpectedArrays,
250     expectedResultValues,
251     2,
252     2);
253   if (res == EXIT_FAILURE)
254   {
255     return EXIT_FAILURE;
256   }
257 
258   // --------------------------------------------------------------------------
259   // Third part: 3 molecules, ghost and data, merge coincident atoms
260 
261   appender2->MergeCoincidentAtomsOn();
262   appender2->Update();
263   resultFullMolecule = appender2->GetOutput();
264   // the ghost atoms are not duplicated in output.
265   nbOfExpectedAtoms = fullMolecule1->GetNumberOfAtoms() + fullMolecule2->GetNumberOfAtoms() +
266     fullMolecule3->GetNumberOfAtoms() - 2;
267   // the ghost bond is not duplicated in output.
268   nbOfExpectedBonds = fullMolecule1->GetNumberOfBonds() + fullMolecule2->GetNumberOfBonds() +
269     fullMolecule3->GetNumberOfBonds() - 1;
270   expectedResultValues->Resize(nbOfExpectedAtoms);
271   expectedResultValues->InsertValue(
272     4, fullMolecule3->GetAtomData()->GetArray("Data")->GetTuple1(0));
273   expectedResultValues->InsertValue(
274     4, fullMolecule3->GetAtomData()->GetArray("Data")->GetTuple1(1));
275 
276   return CheckMolecule(resultFullMolecule,
277     nbOfExpectedAtoms,
278     nbOfExpectedBonds,
279     nbOfExpectedArrays,
280     expectedResultValues,
281     0,
282     0);
283 }
284