2121package org .biojava .nbio .structure .contact ;
2222
2323import java .io .Serializable ;
24- import java .util .*;
24+ import java .util .ArrayList ;
25+ import java .util .Collections ;
26+ import java .util .Iterator ;
27+ import java .util .List ;
28+ import java .util .Map ;
29+ import java .util .Optional ;
30+ import java .util .Set ;
31+ import java .util .TreeMap ;
2532
2633import org .biojava .nbio .core .util .SingleLinkageClusterer ;
2734import org .biojava .nbio .structure .Atom ;
@@ -72,6 +79,8 @@ public class StructureInterfaceList implements Serializable, Iterable<StructureI
7279 private List <StructureInterfaceCluster > clusters = null ;
7380 private List <StructureInterfaceCluster > clustersNcs = null ;
7481
82+ private Map <String , String > chainOrigNamesMap ;
83+
7584 public StructureInterfaceList () {
7685 this .list = new ArrayList <>();
7786 }
@@ -129,8 +138,20 @@ public void calcAsas(int nSpherePoints, int nThreads, int cofactorSizeToUse) {
129138 Map <String , Atom []> uniqAsaChains = new TreeMap <>();
130139 Map <String , double []> chainAsas = new TreeMap <>();
131140
141+ List <StructureInterface > redundancyReducedList ;
142+ if (clustersNcs != null ) {
143+ redundancyReducedList = new ArrayList <>();
144+ for (StructureInterfaceCluster ncsCluster : clustersNcs ) {
145+ // we use the first one in list as the only one for which we calculate ASAs
146+ redundancyReducedList .add (ncsCluster .getMembers ().get (0 ));
147+ }
148+
149+ } else {
150+ redundancyReducedList = list ;
151+ }
152+
132153 // first we gather rotation-unique chains (in terms of AU id and transform id)
133- for (StructureInterface interf :list ) {
154+ for (StructureInterface interf :redundancyReducedList ) {
134155 String molecId1 = interf .getMoleculeIds ().getFirst ()+interf .getTransforms ().getFirst ().getTransformId ();
135156 String molecId2 = interf .getMoleculeIds ().getSecond ()+interf .getTransforms ().getSecond ().getTransformId ();
136157
@@ -159,12 +180,12 @@ public void calcAsas(int nSpherePoints, int nThreads, int cofactorSizeToUse) {
159180
160181 logger .debug ("Calculated uncomplexed ASA for {} orientation-unique chains. Time: {} s" , uniqAsaChains .size (), ((end -start )/1000.0 ));
161182
162- logger .debug ("Will calculate complexed ASA for {} pairwise complexes." , list .size ());
183+ logger .debug ("Will calculate complexed ASA for {} pairwise complexes." , redundancyReducedList .size ());
163184
164185 start = System .currentTimeMillis ();
165186
166187 // now we calculate the ASAs for each of the complexes
167- for (StructureInterface interf :list ) {
188+ for (StructureInterface interf :redundancyReducedList ) {
168189
169190 String molecId1 = interf .getMoleculeIds ().getFirst ()+interf .getTransforms ().getFirst ().getTransformId ();
170191 String molecId2 = interf .getMoleculeIds ().getSecond ()+interf .getTransforms ().getSecond ().getTransformId ();
@@ -176,15 +197,53 @@ public void calcAsas(int nSpherePoints, int nThreads, int cofactorSizeToUse) {
176197 }
177198 end = System .currentTimeMillis ();
178199
179- logger .debug ("Calculated complexes ASA for {} pairwise complexes. Time: {} s" , list .size (), ((end -start )/1000.0 ));
200+ logger .debug ("Calculated complexes ASA for {} pairwise complexes. Time: {} s" , redundancyReducedList .size (), ((end -start )/1000.0 ));
180201
202+ // now let's populate the interface area value for the NCS-redundant ones from the reference interface (first one in list)
203+ if (clustersNcs !=null ) {
204+ if (chainOrigNamesMap ==null ) {
205+ logger .warn ("No chainOrigNamesMap is set. Considering NCS interfaces in same order as reference. This is likely a bug." );
206+ }
207+ for (StructureInterfaceCluster ncsCluster : clustersNcs ) {
208+ StructureInterface refInterf = ncsCluster .getMembers ().get (0 );
209+ String refMolecId1 = refInterf .getMoleculeIds ().getFirst ();
210+ for (int i =1 ;i <ncsCluster .getMembers ().size ();i ++) {
211+ StructureInterface member = ncsCluster .getMembers ().get (i );
212+ member .setTotalArea (refInterf .getTotalArea ());
213+ String molecId1 = member .getMoleculeIds ().getFirst ();
214+ if (areMolecIdsSameOrder (refMolecId1 , molecId1 )) {
215+ // we add the reference interface GroupAsas as the GroupAsas for all other members, like that
216+ // ResidueNumbers won't match in their chain ids, but otherwise all info is there without using a lot of memory
217+ member .setFirstGroupAsas (refInterf .getFirstGroupAsas ());
218+ member .setSecondGroupAsas (refInterf .getSecondGroupAsas ());
219+ } else {
220+ member .setFirstGroupAsas (refInterf .getSecondGroupAsas ());
221+ member .setSecondGroupAsas (refInterf .getFirstGroupAsas ());
222+ }
223+ }
224+ }
225+ }
181226
182227 // finally we sort based on the ChainInterface.comparable() (based in interfaceArea)
183228 sort ();
184229 }
185230
231+ private boolean areMolecIdsSameOrder (String refMolecId , String molecId ) {
232+
233+ if (chainOrigNamesMap ==null ) {
234+ // we've already warned above
235+ return true ;
236+ }
237+
238+ String refMolecIdOrig = chainOrigNamesMap .get (refMolecId );
239+ String molecIdOrig = chainOrigNamesMap .get (molecId );
240+
241+ return (refMolecIdOrig .equals (molecIdOrig ));
242+ }
243+
186244 /**
187245 * Sorts the interface list and reassigns ids based on new sorting
246+ *
188247 */
189248 public void sort () {
190249 Collections .sort (list );
@@ -251,9 +310,21 @@ public void addNcsEquivalent(StructureInterface interfaceNew, StructureInterface
251310 clustersNcs .add (newCluster );
252311 }
253312
313+ /**
314+ * Sets a map with mapping from NCS chain names to original chain names.
315+ * Necessary when {@link #addNcsEquivalent(StructureInterface, StructureInterface)} is used and NCS equivalent
316+ * interfaces exist in this list and their names need mapping when setting ASAs.
317+ * @param chainOrigNamesMap a map of NCS chain name to original chain name
318+ */
319+ public void setChainOrigNamesMap (Map <String , String > chainOrigNamesMap ) {
320+ this .chainOrigNamesMap = chainOrigNamesMap ;
321+ }
322+
254323 /**
255324 * Removes from this interface list all interfaces with areas
256- * below the default cutoff area
325+ * below the default cutoff area.
326+ * Note that this must be called after {@link #calcAsas(int, int, int)}, otherwise all areas would
327+ * be 0 and thus all removed.
257328 * @see #DEFAULT_MINIMUM_INTERFACE_AREA
258329 */
259330 public void removeInterfacesBelowArea () {
@@ -262,17 +333,18 @@ public void removeInterfacesBelowArea() {
262333
263334 /**
264335 * Removes from this interface list all interfaces with areas
265- * below the given cutoff area
266- * @param area
336+ * below the given cutoff area.
337+ * Note that this must be called after {@link #calcAsas(int, int, int)}, otherwise all areas would
338+ * be 0 and thus all removed.
339+ * @param area the minimum interface buried surface area to keep. Interfaces below this value will be removed.
267340 */
268341 public void removeInterfacesBelowArea (double area ) {
269- Iterator <StructureInterface > it = iterator ();
270- while (it .hasNext ()) {
271- StructureInterface interf = it .next ();
272- if (interf .getTotalArea ()<area ) {
273- it .remove ();
274- }
275- }
342+
343+ list .removeIf (interf -> interf .getTotalArea () < area );
344+
345+ if (clustersNcs != null ) {
346+ clustersNcs .removeIf (ncsCluster -> ncsCluster .getMembers ().get (0 ).getTotalArea () < area );
347+ }
276348 }
277349
278350 /**
@@ -291,6 +363,7 @@ public List<StructureInterfaceCluster> getClusters() {
291363 * Calculate the interface clusters for this StructureInterfaceList
292364 * using a contact overlap score to measure the similarity of interfaces.
293365 * Subsequent calls will use the cached value without recomputing the clusters.
366+ * The clusters will be assigned ids by sorting descending by {@link StructureInterfaceCluster#getTotalArea()}
294367 * @param contactOverlapScoreClusterCutoff the contact overlap score above which a pair will be
295368 * clustered
296369 * @return
@@ -300,7 +373,7 @@ public List<StructureInterfaceCluster> getClusters(double contactOverlapScoreClu
300373 return clusters ;
301374 }
302375
303- clusters = new ArrayList <StructureInterfaceCluster >();
376+ clusters = new ArrayList <>();
304377
305378 // nothing to do if we have no interfaces
306379 if (list .size ()==0 ) return clusters ;
@@ -324,9 +397,9 @@ public List<StructureInterfaceCluster> getClusters(double contactOverlapScoreClu
324397
325398 SingleLinkageClusterer slc = new SingleLinkageClusterer (matrix , true );
326399
327- Map <Integer ,Set <Integer >> clusteredIndices = slc .getClusters (contactOverlapScoreClusterCutoff );
400+ Map <Integer , Set <Integer >> clusteredIndices = slc .getClusters (contactOverlapScoreClusterCutoff );
328401 for (int clusterIdx :clusteredIndices .keySet ()) {
329- List <StructureInterface > members = new ArrayList <StructureInterface >();
402+ List <StructureInterface > members = new ArrayList <>();
330403 for (int idx :clusteredIndices .get (clusterIdx )) {
331404 members .add (list .get (idx ));
332405 }
@@ -335,8 +408,9 @@ public List<StructureInterfaceCluster> getClusters(double contactOverlapScoreClu
335408 double averageScore = 0.0 ;
336409 int countPairs = 0 ;
337410 for (int i =0 ;i <members .size ();i ++) {
411+ int iIdx = list .indexOf (members .get (i ));
338412 for (int j =i +1 ;j <members .size ();j ++) {
339- averageScore += matrix [members . get ( i ). getId ()- 1 ][ members .get (j ). getId ()- 1 ];
413+ averageScore += matrix [iIdx ][ list . indexOf ( members .get (j )) ];
340414 countPairs ++;
341415 }
342416 }
@@ -358,19 +432,15 @@ public List<StructureInterfaceCluster> getClusters(double contactOverlapScoreClu
358432 }
359433
360434 // now we sort by areas (descending) and assign ids based on that sorting
361- Collections .sort (clusters , new Comparator <StructureInterfaceCluster >() {
362- @ Override
363- public int compare (StructureInterfaceCluster o1 , StructureInterfaceCluster o2 ) {
364- return Double .compare (o2 .getTotalArea (), o1 .getTotalArea ()); //note we invert so that sorting is descending
365- }
435+ clusters .sort ((o1 , o2 ) -> {
436+ return Double .compare (o2 .getTotalArea (), o1 .getTotalArea ()); //note we invert so that sorting is descending
366437 });
367438 int id = 1 ;
368439 for (StructureInterfaceCluster cluster :clusters ) {
369440 cluster .setId (id );
370441 id ++;
371442 }
372443
373-
374444 return clusters ;
375445 }
376446
0 commit comments