Xmipp  v3.23.11-Nereus
volume_to_pseudoatoms.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Carlos Oscar S. Sorzano (coss@cnb.csic.es)
4  *
5  * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307 USA
21  *
22  * All comments concerning this program package may be sent to the
23  * e-mail address 'xmipp@cnb.csic.es'
24  ***************************************************************************/
25 
26 #include "volume_to_pseudoatoms.h"
27 #include <data/pdb.h>
28 #include <algorithm>
29 #include <stdio.h>
30 #include <list>
31 
32 /* Pseudo atoms ------------------------------------------------------------ */
34 {
36  intensity=0;
37 }
38 
39 bool operator <(const PseudoAtom &a, const PseudoAtom &b)
40 {
41  return a.intensity<b.intensity;
42 }
43 
44 std::ostream& operator << (std::ostream &o, const PseudoAtom &a)
45 {
46  o << a.location.transpose() << " " << a.intensity;
47  return o;
48 }
49 
50 /* I/O --------------------------------------------------------------------- */
52 {
53  fnVol = getParam("-i");
54  fnOut = getParam("-o");
55  mask_prm.allowed_data_types = INT_MASK;
56  if ((useMask = checkParam("--mask")))
57  mask_prm.readParams(this);
58  sigma = getDoubleParam("--sigma");
59  targetError = getDoubleParam("--targetError")/100.0;
60  stop = getDoubleParam("--stop");
61  initialSeeds = getIntParam("--initialSeeds");
62  growSeeds = getDoubleParam("--growSeeds");
63  allowMovement = !checkParam("--dontAllowMovement");
64  allowIntensity = !checkParam("--dontAllowIntensity");
65  if (!allowIntensity)
66  intensityFraction = getDoubleParam("--dontAllowIntensity",1);
67  intensityColumn = getParam("--intensityColumn");
68  Nclosest = getIntParam("--Nclosest");
69  minDistance = getDoubleParam("--minDistance");
70  penalty = getDoubleParam("--penalty");
71  numThreads = getIntParam("--thr");
72  sampling = getDoubleParam("--sampling_rate");
73  dontScale = checkParam("--dontScale");
74  binarize = checkParam("--binarize");
75  if (binarize)
76  threshold=getDoubleParam("--binarize");
77  else
78  threshold=0;
79 }
80 
82 {
83  if (verbose==0)
84  return;
85  std::cout << "Input volume: " << fnVol << std::endl
86  << "Output volume: " << fnOut << std::endl
87  << "Sigma: " << sigma << std::endl
88  << "Initial seeds: " << initialSeeds << std::endl
89  << "Grow seeds: " << growSeeds << std::endl
90  << "Target error: " << targetError << std::endl
91  << "Stop: " << stop << std::endl
92  << "AllowMovement: " << allowMovement << std::endl
93  << "AllowIntensity: " << allowIntensity << std::endl
94  << "Intensity Frac: " << intensityFraction << std::endl
95  << "Intensity Col: " << intensityColumn << std::endl
96  << "Nclosest: " << Nclosest << std::endl
97  << "Min. Distance: " << minDistance << std::endl
98  << "Penalty: " << penalty << std::endl
99  << "Threads: " << numThreads << std::endl
100  << "Sampling Rate: " << sampling << std::endl
101  << "Don't scale: " << dontScale << std::endl
102  << "Binarize: " << binarize << std::endl
103  << "Threshold: " << threshold << std::endl
104  ;
105  if (useMask)
106  mask_prm.show();
107  else
108  std::cout << "No mask\n";
109 }
110 
112 {
113  addUsageLine("Creates a set of pseudoatoms representing the density of an EM volume. ");
114  addUsageLine("+This is useful for the vector quantization process needed in problems ");
115  addUsageLine("+like docking, approximation of structures by Alpha Shapes, Normal Mode ");
116  addUsageLine("+Analysis, etc.");
117  addUsageLine("+");
118  addUsageLine("+The volume is approximated by Gaussians of a desired size. The user can ");
119  addUsageLine("+specify whether the Gaussians can have different intensities or not, as well ");
120  addUsageLine("+as the level of precision with which she desires to approximate the input ");
121  addUsageLine("+EM volume.");
122  addParamsLine(" -i <volume> : Input volume");
123  addParamsLine(" [-o <rootname=\"\">] : Output rootname. If not given, the rootname of the input volume is taken.");
124  addParamsLine(" : The output of the program is: [rootname].pdb");
125  addParamsLine(" : (PDB file with the pseudo atoms).");
126  addParamsLine(" :+If verbose is set to 2, then also [rootname].vol ");
127  addParamsLine(" :+(the approximation volume), [rootname].hist ");
128  addParamsLine(" :+(histogram of the Gaussian intensities), ");
129  addParamsLine(" :+[rootname]_rawDiff.vol (difference between the ");
130  addParamsLine(" :+input volume and its approximation), ");
131  addParamsLine(" :+[rootname]_relativeDiff.vol (the raw difference ");
132  addParamsLine(" :+divided by the input volume at that location; this ");
133  addParamsLine(" :+gives an idea of how much the error represents with ");
134  addParamsLine(" :+respect to the input");
135  addParamsLine(" [--sigma <s=1.5>] : Sigma of gaussians (in Angstroms)");
136  addParamsLine(" : It should be comparable to the sampling rate");
137  addParamsLine(" [--initialSeeds+ <N=300>] : Initial number of pseudoatoms");
138  addParamsLine(" [--growSeeds+ <percentage=30>] : Percentage of growth");
139  addParamsLine(" :+At each iteration the smallest percentage/2 ");
140  addParamsLine(" :+pseudoatoms will be removed, and percentage new pseudoatoms will be created.");
141  addParamsLine(" [--stop+ <p=0.001>] : Stop criterion (0<p<1) for inner iterations");
142  addParamsLine(" :+At each iteration the current number of gaussians will be optimized until ");
143  addParamsLine(" :+the average error does not decrease at least this amount relative to the previous iteration.");
144  addParamsLine(" [--targetError+ <e=2>] : Finish when the average representation");
145  addParamsLine(" : error is below this threshold (in percentage; by default, 2%)");
146  addParamsLine(" [--dontAllowMovement+] : Don't allow pseudoatoms to move");
147  addParamsLine(" [--dontAllowIntensity+ <f=0.01>] : Don't allow pseudoatoms to change intensity");
148  addParamsLine(" : f determines the fraction of intensity");
149  addParamsLine(" : held by each pseudoatom");
150  addParamsLine(" [--intensityColumn+ <s=Bfactor>] : Where to write the intensity in the PDB file");
151  addParamsLine(" where <s>");
152  addParamsLine(" occupancy");
153  addParamsLine(" Bfactor");
154  addParamsLine(" [--Nclosest+ <N=3>] : N closest atoms, it is used only for the");
155  addParamsLine(" : distance histogram");
156  addParamsLine(" [--minDistance+ <d=0.001>] : Minimum distance between two pseudoatoms (in Angstroms)");
157  addParamsLine(" : Set it to -1 to disable");
158  addParamsLine(" [--penalty+ <p=10>] : Penalty for overshooting");
159  addParamsLine(" [--sampling_rate <Ts=1>] : Sampling rate Angstroms/pixel");
160  addParamsLine(" [--dontScale+] : Don't scale atom weights in the PDB");
161  addParamsLine(" [--binarize+ <threshold>] : Binarize the volume for a more uniform distribution");
162  addParamsLine(" [--thr <n=1>] : Number of threads");
163  mask_prm.defineParams(this,INT_MASK,nullptr,"Statistics restricted to the mask area.",true);
164  addExampleLine("Convert volume to pseudoatoms, each pseudoatom with a different weight",false);
165  addExampleLine("xmipp_volume_to_pseudoatoms -i volume.vol -o pseudoatoms");
166  addExampleLine("Convert volume to pseudoatoms, all pseudoatoms with the same weight",false);
167  addExampleLine("xmipp_volume_to_pseudoatoms -i volume.vol -o pseudoatoms --dontAllowIntensity");
168 }
169 
171 {
172  sigma/=sampling;
173  minDistance/=sampling;
174 
175  if (intensityColumn!="occupancy" && intensityColumn!="Bfactor")
176  REPORT_ERROR(ERR_VALUE_INCORRECT,(std::string)"Unknown column: "+intensityColumn);
177 
178  Vin.read(fnVol);
179  Vin().setXmippOrigin();
180  if (binarize)
181  Vin().binarize(threshold,0);
182 
183  if (fnOut=="")
184  fnOut=fnVol.withoutExtension();
185 
186  Vcurrent().initZeros(Vin());
187  mask_prm.generate_mask(Vin());
188 
189  sigma3=3*sigma;
190  gaussianTable.resize(CEIL(sigma3*sqrt(3.0)*1000));
191  FOR_ALL_ELEMENTS_IN_ARRAY1D(gaussianTable)
192  gaussianTable(i)=gaussian1D(i/1000.0,sigma);
193 
194  energyOriginal=0;
195  double N=0;
196  double minval=1e38, maxval=-1e38;
197  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
199  {
200  if (useMask && iMask3D(k,i,j)==0)
201  continue;
202  double v=Vin(k,i,j);
203  energyOriginal+=v*v;
204  minval=XMIPP_MIN(minval,v);
205  maxval=XMIPP_MAX(maxval,v);
206  N++;
207  }
208  energyOriginal/=N;
209 
210  Histogram1D hist;
211  if (useMask)
212  compute_hist_within_binary_mask(iMask3D, Vin(), hist,
213  minval, maxval, 200);
214  else
215  compute_hist(Vin(), hist, minval, maxval, 200);
216  percentil1=hist.percentil(1);
217  if (percentil1<=0)
218  percentil1=maxval/500;
219  range = hist.percentil(99)-percentil1;
220 
221  if (XMIPP_EQUAL_ZERO(range))
222  REPORT_ERROR(ERR_VALUE_INCORRECT, "Range cannot be zero.");
223 
224  smallAtom=range*intensityFraction;
225 
226  // Create threads
227  barrier_init(&barrier,numThreads+1);
228  threadIds=(pthread_t *)malloc(numThreads*sizeof(pthread_t));
230  malloc(numThreads*sizeof(Prog_Convert_Vol2Pseudo_ThreadParams));
231  for (int i=0; i<numThreads; i++)
232  {
233  threadArgs[i].myThreadID=i;
234  threadArgs[i].parent=this;
235  pthread_create( (threadIds+i), nullptr, optimizeCurrentAtomsThread,
236  (void *) (threadArgs+i));
237  }
238 
239  // Filter for the difference volume
240  Filter.FilterBand=LOWPASS;
241  Filter.FilterShape=REALGAUSSIAN;
242  Filter.w1=sigma;
243  Filter.generateMask(Vin());
244  Filter.do_generate_3dmask=false;
245 }
246 
247 #ifdef NEVER_DEFINED
248 //#define DEBUG
250 {
251  // Convolve the difference with the Gaussian to know
252  // where it would be better to put a Gaussian
253  FourierFilter Filter;
254  Filter.FilterBand=LOWPASS;
255  Filter.FilterShape=REALGAUSSIAN;
256  Filter.w1=sigma;
257  Filter.generateMask(Vin());
258  Filter.do_generate_3dmask=false;
259 
260  MultidimArray<double> Vdiff=Vin();
261  Vdiff-=Vcurrent();
262  Filter.applyMaskSpace(Vdiff);
263 
264  // Place all seeds
265  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
266  for (int n=0; n<Nseeds; n++)
267  {
268  // Look for the maximum error
269  bool first=true;
270  int kmax=0, imax=0, jmax=0;
271  double maxVal=0.;
273  {
274  if (useMask && A3D_ELEM(iMask3D,k,i,j)==0)
275  continue;
276  double voxel=A3D_ELEM(Vdiff,k,i,j);
277  if (first || voxel>maxVal)
278  {
279  kmax=k;
280  imax=i;
281  jmax=j;
282  maxVal=voxel;
283  first=false;
284  }
285  }
286 
287  // Keep this as an atom
288  PseudoAtom a;
289  VEC_ELEM(a.location,0)=kmax;
290  VEC_ELEM(a.location,1)=imax;
291  VEC_ELEM(a.location,2)=jmax;
292  if (allowIntensity)
293  a.intensity=maxVal;
294  else
295  {
296  if (maxVal<smallAtom)
297  break;
298  a.intensity=smallAtom;
299  }
300  atoms.push_back(a);
301 
302  // Remove this density from the difference
303  drawGaussian(kmax,imax,jmax,Vdiff,-a.intensity);
304 
305 #ifdef DEBUG
306 
307  std::cout << "New atom: " << a << std::endl;
308  VolumeXmipp save;
309  save()=Vdiff;
310  save.write("PPPDiff.vol");
311  std::cout << "Press any key\n";
312  char c;
313  std::cin >> c;
314 #endif
315 
316  }
317 }
318 #undef DEBUG
319 #endif
320 
322 {
323 public:
324  int k,i,j;
325  double v;
326  bool operator < (const SeedCandidate& c) const {return v>c.v;}
327 };
328 
330 {
331  // Convolve the difference with the Gaussian to know
332  // where it would be better to put a Gaussian
333  MultidimArray<double> Vdiff=Vin();
334  Vdiff-=Vcurrent();
335  Filter.applyMaskSpace(Vdiff);
336 
337  // Look for the Nseeds
338  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
339  size_t idx=0;
340  std::list<SeedCandidate> candidateList;
341  size_t listSize=0;
342  double lastValue=0;
344  {
345  if (!useMask || useMask && DIRECT_MULTIDIM_ELEM(iMask3D,idx))
346  {
347  double v=A3D_ELEM(Vdiff,k,i,j);
348  if (listSize==0 || v>lastValue)
349  {
350  // Check if there is any other candidate around
351  bool found=false;
352  for (std::list<SeedCandidate>::iterator iter=candidateList.begin(); iter!=candidateList.end(); iter++)
353  if (std::abs(iter->k-k)<sigma && std::abs(iter->i-i)<sigma && std::abs(iter->j-j)<sigma)
354  {
355  found=true;
356  break;
357  }
358  if (!found)
359  {
360  SeedCandidate aux;
361  aux.v=v;
362  aux.k=k;
363  aux.i=i;
364  aux.j=j;
365  candidateList.push_back(aux);
366  candidateList.sort();
367  listSize++;
368  if (listSize>Nseeds)
369  {
370  candidateList.pop_back();
371  listSize--;
372  }
373  lastValue=candidateList.back().v;
374  }
375  }
376  }
377  idx++;
378  }
379 
380  // Place atoms
381  for (std::list<SeedCandidate>::iterator iter=candidateList.begin(); iter!=candidateList.end(); iter++)
382  {
383  PseudoAtom a;
384  VEC_ELEM(a.location,0)=iter->k;
385  VEC_ELEM(a.location,1)=iter->i;
386  VEC_ELEM(a.location,2)=iter->j;
387  if (allowIntensity)
388  a.intensity=iter->v;
389  else
390  {
391  if (iter->v<smallAtom)
392  break;
393  a.intensity=smallAtom;
394  }
395  atoms.push_back(a);
396 
397  // Remove this density from the difference
398  drawGaussian(iter->k,iter->i,iter->j,Vdiff,-a.intensity);
399  }
400 }
401 
402 /* Remove seeds ------------------------------------------------------------ */
404 {
405  int fromNegative=ROUND(Nseeds*0.5);
406  int fromSmall=Nseeds-fromNegative;
407 
408  if (allowIntensity)
409  {
410  // Remove too small atoms
411  std::sort(atoms.begin(),atoms.end());
412  atoms.erase(atoms.begin(),atoms.begin()+fromSmall);
413  }
414  else
415  {
416  fromNegative=Nseeds;
417  fromSmall=0;
418  }
419 
420  // Remove atoms from regions in which the error is too negative
421  MultidimArray<double> Vdiff=Vin();
422  Vdiff-=Vcurrent();
423  int alreadyRemoved=0;
424  double vmin=Vdiff.computeMin();
425  if (vmin<0)
426  {
427  for (double v=vmin+vmin/20; v<0; v-=vmin/20)
428  {
429  size_t oldListSize;
430  do
431  {
432  oldListSize=atoms.size();
433 
434  // Search for a point within a negative region
435  bool found=false;
436  int kneg=0, ineg=0, jneg=0;
437  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
438  for (int k=STARTINGZ(Vdiff); k<=FINISHINGZ(Vdiff) && !found; k++)
439  for (int i=STARTINGY(Vdiff); i<=FINISHINGY(Vdiff) && !found; i++)
440  for (int j=STARTINGX(Vdiff); j<=FINISHINGX(Vdiff) && !found; j++)
441  {
442  if (useMask && iMask3D(k,i,j)==0)
443  continue;
444  if (A3D_ELEM(Vdiff,k,i,j)<v)
445  {
446  kneg=k;
447  ineg=i;
448  jneg=j;
449  A3D_ELEM(Vdiff,k,i,j)=0;
450  found=true;
451  }
452  }
453 
454  // If found such a point, search for a nearby atom
455  if (found)
456  {
457  // Search for atom
458  int nmax=atoms.size();
459  for (int n=0; n<nmax; n++)
460  {
461  double r=
462  (kneg-atoms[n].location(0))*(kneg-atoms[n].location(0))+
463  (ineg-atoms[n].location(1))*(ineg-atoms[n].location(1))+
464  (jneg-atoms[n].location(2))*(jneg-atoms[n].location(2));
465  r=sqrt(r);
466  if (r<sigma3)
467  {
468  drawGaussian(atoms[n].location(0),
469  atoms[n].location(1),
470  atoms[n].location(2),
471  Vdiff,
472  atoms[n].intensity);
473  atoms.erase(atoms.begin()+n);
474  alreadyRemoved++;
475  break;
476  }
477  }
478  }
479  }
480  while (oldListSize>atoms.size() && alreadyRemoved<fromNegative);
481  if (alreadyRemoved==fromNegative)
482  break;
483  }
484  }
485 
486  removeTooCloseSeeds();
487 }
488 
490 {
491  // Remove atoms that are too close to each other
492  if (minDistance>0 && allowIntensity)
493  {
494  std::vector<int> toRemove;
495  int nmax=atoms.size();
496  double minDistance2=minDistance*minDistance;
497  for (int n1=0; n1<nmax; n1++)
498  {
499  bool found=false;
500  int nn=0, nnmax=toRemove.size();
501  while (nn<nnmax)
502  {
503  if (toRemove[nn]==n1)
504  {
505  found=true;
506  break;
507  }
508  else if (toRemove[nn]>n1)
509  break;
510  nn++;
511  }
512  if (found)
513  continue;
514  for (int n2=n1+1; n2<nmax; n2++)
515  {
516  nn=0;
517  found=false;
518  while (nn<nnmax)
519  {
520  if (toRemove[nn]==n2)
521  {
522  found=true;
523  break;
524  }
525  else if (toRemove[nn]>n2)
526  break;
527  nn++;
528  }
529  if (found)
530  continue;
531  double diffZ=atoms[n1].location(0)-atoms[n2].location(0);
532  double diffY=atoms[n1].location(1)-atoms[n2].location(1);
533  double diffX=atoms[n1].location(2)-atoms[n2].location(2);
534  double d2=diffZ*diffZ+diffY*diffY+diffX*diffX;
535  if (d2<minDistance2)
536  {
537  if (atoms[n1].intensity<atoms[n2].intensity)
538  {
539  toRemove.push_back(n1);
540  break;
541  }
542  else
543  toRemove.push_back(n2);
544  std::sort(toRemove.begin(),toRemove.end());
545  }
546  }
547  }
548  for (int n=toRemove.size()-1; n>=0; n--)
549  atoms.erase(atoms.begin()+toRemove[n]);
550  }
551 }
552 
553 /* Draw approximation ------------------------------------------------------ */
555 {
556  Vcurrent().initZeros(Vin());
557  int nmax=atoms.size();
558  for (int n=0; n<nmax; n++)
559  drawGaussian(atoms[n].location(0),atoms[n].location(1),
560  atoms[n].location(2),Vcurrent(),atoms[n].intensity);
561 
562  energyDiff=0;
563  double N=0;
564  percentageDiff=0;
565  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
566  const MultidimArray<double> &mVcurrent=Vcurrent();
567  const MultidimArray<double> &mVin=Vin();
569  {
570  if (useMask && DIRECT_MULTIDIM_ELEM(iMask3D,n)==0)
571  continue;
572  double Vinv=DIRECT_MULTIDIM_ELEM(mVin,n);
573  if (Vinv<=0)
574  continue;
575  double vdiff=Vinv-DIRECT_MULTIDIM_ELEM(mVcurrent,n);
576  double vperc=fabs(vdiff);
577  energyDiff+=vdiff*vdiff;
578  percentageDiff+=vperc;
579  N++;
580  }
581  energyDiff/=N;
582  percentageDiff/=(N*range);
583 }
584 
585 /* Gaussian operations ----------------------------------------------------- */
588 {
589  int k0=std::max(STARTINGZ(V),(int)floor(k-sigma3));
590  int i0=std::max(STARTINGY(V),(int)floor(i-sigma3));
591  int j0=std::max(STARTINGX(V),(int)floor(j-sigma3));
592  int kF=std::min(FINISHINGZ(V),(int)ceil(k+sigma3));
593  int iF=std::min(FINISHINGY(V),(int)ceil(i+sigma3));
594  int jF=std::min(FINISHINGX(V),(int)ceil(j+sigma3));
595  double sum=0;
596  for (int kk=k0; kk<=kF; kk++)
597  for (int ii=i0; ii<=iF; ii++)
598  for (int jj=j0; jj<=jF; jj++)
599  sum+=V(kk,ii,jj);
600  return sum/((kF-k0+1)*(iF-i0+1)*(jF-j0+1));
601 }
602 
603 void ProgVolumeToPseudoatoms::drawGaussian(double k, double i, double j,
604  MultidimArray<double> &V, double intensity)
605 {
606  int k0=CEIL(XMIPP_MAX(STARTINGZ(V),k-sigma3));
607  int i0=CEIL(XMIPP_MAX(STARTINGY(V),i-sigma3));
608  int j0=CEIL(XMIPP_MAX(STARTINGX(V),j-sigma3));
609  int kF=FLOOR(XMIPP_MIN(FINISHINGZ(V),k+sigma3));
610  int iF=FLOOR(XMIPP_MIN(FINISHINGY(V),i+sigma3));
611  int jF=FLOOR(XMIPP_MIN(FINISHINGX(V),j+sigma3));
612  for (int kk=k0; kk<=kF; kk++)
613  {
614  double aux=kk-k;
615  double diffkk2=aux*aux;
616  for (int ii=i0; ii<=iF; ii++)
617  {
618  aux=ii-i;
619  double diffiikk2=aux*aux+diffkk2;
620  for (int jj=j0; jj<=jF; jj++)
621  {
622  aux=jj-j;
623  double r=sqrt(diffiikk2+aux*aux);
624  aux=r*1000;
625  long iaux=lround(aux);
626  A3D_ELEM(V,kk,ii,jj)+=intensity*DIRECT_A1D_ELEM(gaussianTable,iaux);
627  }
628  }
629  }
630 }
631 
633  MultidimArray<double> &region, bool extended) const
634 {
635  double k=atoms[idxGaussian].location(0);
636  double i=atoms[idxGaussian].location(1);
637  double j=atoms[idxGaussian].location(2);
638 
639  double sigma3ToUse=sigma3;
640  if (extended)
641  sigma3ToUse+=1.5;
642 
643  int k0=CEIL(XMIPP_MAX(STARTINGZ(Vcurrent()),k-sigma3ToUse));
644  int i0=CEIL(XMIPP_MAX(STARTINGY(Vcurrent()),i-sigma3ToUse));
645  int j0=CEIL(XMIPP_MAX(STARTINGX(Vcurrent()),j-sigma3ToUse));
646  int kF=FLOOR(XMIPP_MIN(FINISHINGZ(Vcurrent()),k+sigma3ToUse));
647  int iF=FLOOR(XMIPP_MIN(FINISHINGY(Vcurrent()),i+sigma3ToUse));
648  int jF=FLOOR(XMIPP_MIN(FINISHINGX(Vcurrent()),j+sigma3ToUse));
649 
650  region.resizeNoCopy(kF-k0+1,iF-i0+1,jF-j0+1);
651  STARTINGZ(region)=k0;
652  STARTINGY(region)=i0;
653  STARTINGX(region)=j0;
654  const MultidimArray<double> &mVcurrent=Vcurrent();
655  for (int k=k0; k<=kF; k++)
656  for (int i=i0; i<=iF; i++)
657  for (int j=j0; j<=jF; j++)
658  A3D_ELEM(region,k,i,j)=A3D_ELEM(mVcurrent,k,i,j);
659 }
660 
661 //#define DEBUG
663 const
664 {
665  double avgDiff=0;
666  double N=0;
667  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
668  const MultidimArray<double> &mVin=Vin();
669 #ifdef DEBUG
670  Image<double> save, save2;
671  save().initZeros(region);
672  save2().initZeros(region);
673 #endif
675  {
676  double Vinv=A3D_ELEM(mVin,k,i,j);
677  if (Vinv<=0 || (useMask && A3D_ELEM(iMask3D,k,i,j)==0))
678  continue;
679 #ifdef DEBUG
680  save(k,i,j)=Vinv;
681  save2(k,i,j)=A3D_ELEM(region,k,i,j);
682 #endif
683  double vdiff=A3D_ELEM(region,k,i,j)-Vinv;
684  double vperc=(vdiff<0)?-vdiff:penalty*vdiff;
685  avgDiff+=vperc;
686 #ifdef DEBUG
687  std::cout << "(k,i,j)=(" << k << "," << i << "," << j << ") toSimulate=" << Vinv << " simulated=" << A3D_ELEM(region,k,i,j) << " vdiff=" << vdiff << " vperc=" << vperc << std::endl;
688 #endif
689  ++N;
690  }
691 #ifdef DEBUG
692  save.write("PPPtoSimulate.vol");
693  save2.write("PPPsimulated.vol");
694  std::cout << "Error=" << avgDiff/(N*range) << std::endl;
695  std::cout << "Press any key\n";
696  char c; std::cin >> c;
697 #endif
698  return avgDiff/(N*range);
699 }
700 #undef DEBUG
701 
703 {
704  const MultidimArray<double> &mVcurrent=Vcurrent();
706  A3D_ELEM(mVcurrent,k,i,j)=A3D_ELEM(region,k,i,j);
707 }
708 
709 /* Optimize ---------------------------------------------------------------- */
710 static pthread_mutex_t mutexUpdateVolume=PTHREAD_MUTEX_INITIALIZER;
711 
712 //#define DEBUG
714  void * threadArgs)
715 {
716  auto *myArgs=(Prog_Convert_Vol2Pseudo_ThreadParams *) threadArgs;
717  ProgVolumeToPseudoatoms *parent=myArgs->parent;
718  std::vector< PseudoAtom > &atoms=parent->atoms;
719  bool allowIntensity=parent->allowIntensity;
720  bool allowMovement=parent->allowMovement;
721  MultidimArray<double> region, regionBackup;
722 
723  barrier_t *barrier=&(parent->barrier);
724  do
725  {
726  barrier_wait( barrier );
727  if (parent->threadOpCode==KILLTHREAD)
728  return nullptr;
729 
730  myArgs->Nintensity=0;
731  myArgs->Nmovement=0;
732  int nmax=atoms.size();
733  for (int n=0; n<nmax; n++)
734  {
735  if ((n+1)%parent->numThreads!=myArgs->myThreadID)
736  continue;
737 
738  parent->extractRegion(n,region,true);
739  double currentRegionEval=parent->evaluateRegion(region);
740  parent->drawGaussian(atoms[n].location(0), atoms[n].location(1),
741  atoms[n].location(2),region,-atoms[n].intensity);
742  regionBackup=region;
743 
744 #ifdef DEBUG
745  std::cout << "Atom n=" << n << " current intensity=" << atoms[n].intensity << " -> " << currentRegionEval << std::endl;
746 #endif
747  // Change intensity
748  if (allowIntensity)
749  {
750  // Try with a Gaussian that is of different intensity
751  double tryCoeffs[8]={0, 0.1, 0.2, 0.5, 0.9, 0.99, 1.01, 1.1};
752  double bestRed=0;
753  int bestT=-1;
754  for (int t=0; t<8; t++)
755  {
756  region=regionBackup;
757  parent->drawGaussian(atoms[n].location(0),
758  atoms[n].location(1), atoms[n].location(2),region,
759  tryCoeffs[t]*atoms[n].intensity);
760  double trialRegionEval=parent->evaluateRegion(region);
761  double reduction=trialRegionEval-currentRegionEval;
762  if (reduction<bestRed)
763  {
764  bestRed=reduction;
765  bestT=t;
766 #ifdef DEBUG
767  std::cout << " better -> " << trialRegionEval << " (factor=" << tryCoeffs[t] << ")" << std::endl;
768 #endif
769  }
770  }
771  if (bestT!=-1)
772  {
773  atoms[n].intensity*=tryCoeffs[bestT];
774  region=regionBackup;
775  parent->drawGaussian(atoms[n].location(0), atoms[n].location(1),
776  atoms[n].location(2),region,atoms[n].intensity);
777  pthread_mutex_lock(&mutexUpdateVolume);
778  parent->insertRegion(region);
779  pthread_mutex_unlock(&mutexUpdateVolume);
780  currentRegionEval=parent->evaluateRegion(region);
781  parent->drawGaussian(atoms[n].location(0),
782  atoms[n].location(1), atoms[n].location(2),region,
783  -atoms[n].intensity);
784  regionBackup=region;
785 #ifdef DEBUG
786  std::cout << " finally -> " << currentRegionEval << " (intensity=" << atoms[n].intensity << ")" << std::endl;
787 #endif
788  myArgs->Nintensity++;
789  }
790  }
791 
792  // Change location
793  if (allowMovement && atoms[n].intensity>0)
794  {
795  double tryX[6]={-0.45,0.5, 0.0 ,0.0, 0.0 ,0.0};
796  double tryY[6]={ 0.0 ,0.0,-0.45,0.5, 0.0 ,0.0};
797  double tryZ[6]={ 0.0 ,0.0, 0.0 ,0.0,-0.45,0.5};
798  double bestRed=0;
799  int bestT=-1;
800  for (int t=0; t<6; t++)
801  {
802  region=regionBackup;
803  parent->drawGaussian(atoms[n].location(0)+tryZ[t],
804  atoms[n].location(1)+tryY[t],
805  atoms[n].location(2)+tryX[t],
806  region,atoms[n].intensity);
807  double trialRegionEval=parent->evaluateRegion(region);
808  double reduction=trialRegionEval-currentRegionEval;
809  if (reduction<bestRed)
810  {
811  bestRed=reduction;
812  bestT=t;
813  }
814  }
815  if (bestT!=-1)
816  {
817  atoms[n].location(0)+=tryZ[bestT];
818  atoms[n].location(1)+=tryY[bestT];
819  atoms[n].location(2)+=tryX[bestT];
820  region=regionBackup;
821  parent->drawGaussian(atoms[n].location(0),
822  atoms[n].location(1), atoms[n].location(2),region,
823  atoms[n].intensity);
824  pthread_mutex_lock(&mutexUpdateVolume);
825  parent->insertRegion(region);
826  pthread_mutex_unlock(&mutexUpdateVolume);
827  myArgs->Nmovement++;
828  }
829  }
830  }
831 
832  barrier_wait( barrier );
833  }
834  while (true);
835 }
836 
838 {
839  if (!allowIntensity && !allowMovement)
840  return;
841  bool finished=false;
842  int iter=0;
843  do
844  {
845  double oldError=percentageDiff;
846 
847  threadOpCode=WORKTHREAD;
848  // Launch workers
850  // Wait for workers to finish
852 
853  // Retrieve results
854  int Nintensity=0;
855  int Nmovement=0;
856  for (int i=0; i<numThreads; i++)
857  {
858  Nintensity+=threadArgs[i].Nintensity;
859  Nmovement+=threadArgs[i].Nmovement;
860  }
861 
862  // Remove all the removed atoms
863  int nmax=atoms.size();
864  for (int n=nmax-1; n>=0; n--)
865  if (atoms[n].intensity==0)
866  atoms.erase(atoms.begin()+n);
867 
868  drawApproximation();
869  if (verbose>0)
870  std::cout << "Iteration " << iter << " error= " << percentageDiff
871  << " Natoms= " << atoms.size()
872  << " Intensity= " << Nintensity
873  << " Location= " << Nmovement
874  << std::endl;
875 
876  if (iter>0)
877  if ((oldError-percentageDiff)/oldError<stop)
878  finished=true;
879  iter++;
880  }
881  while (!finished);
882 }
883 
884 /* Write ------------------------------------------------------------------- */
886 {
887  // Compute the histogram of intensities
888  MultidimArray<double> intensities;
889  intensities.initZeros(atoms.size());
890  FOR_ALL_ELEMENTS_IN_ARRAY1D(intensities)
891  intensities(i)=atoms[i].intensity;
892  Histogram1D hist;
893  compute_hist(intensities, hist, 0, intensities.computeMax(), 100);
894 
895  if (verbose>=2)
896  {
897  Vcurrent.write(fnOut+"_approximation.vol");
898  hist.write(fnOut+"_approximation.hist");
899 
900  // Save the difference
901  Image<double> Vdiff;
902  Vdiff()=Vin()-Vcurrent();
903  const MultidimArray<int> &iMask3D=mask_prm.get_binary_mask();
904  if (useMask && XSIZE(iMask3D)!=0)
906  if (!iMask3D(k,i,j))
907  Vdiff(k,i,j)=0;
908  Vdiff.write(fnOut+"_rawDiff.vol");
909 
910  Vdiff()/=range;
911  Vdiff.write(fnOut+"_relativeDiff.vol");
912  }
913 
914  // Write the PDB
915  double minIntensity=intensities.computeMin();
916  double maxIntensity=intensities.computeMax();
917  double a=0.99/(maxIntensity-minIntensity);
918  if (dontScale)
919  a=1;
920 
921  FILE *fhOut=nullptr;
922  fhOut=fopen((fnOut+".pdb").c_str(),"w");
923  if (!fhOut)
925  int nmax=atoms.size();
926  int col=1;
927  if (intensityColumn=="Bfactor")
928  col=2;
929  fprintf(fhOut,"REMARK xmipp_volume_to_pseudoatoms\n");
930  fprintf(fhOut,"REMARK fixedGaussian %f\n",sigma*sampling);
931  fprintf(fhOut,"REMARK intensityColumn %s\n",intensityColumn.c_str());
932  for (int n=0; n<nmax; n++)
933  {
934  double intensity=1.0;
935  if (allowIntensity)
936  intensity=0.01+ROUND(100*a*(atoms[n].intensity-minIntensity))/100.0;
937  if (col==1)
938  fprintf(fhOut,
939  "ATOM %5d DENS DENS%5d %8.3f%8.3f%8.3f%6.2f 1 DENS\n",
940  n+1,n+1,
941  (float)(atoms[n].location(2)*sampling),
942  (float)(atoms[n].location(1)*sampling),
943  (float)(atoms[n].location(0)*sampling),
944  (float)intensity);
945  else
946  fprintf(fhOut,
947  "ATOM %5d DENS DENS%5d %8.3f%8.3f%8.3f 1%6.2f DENS\n",
948  n+1,n+1,
949  (float)(atoms[n].location(2)*sampling),
950  (float)(atoms[n].location(1)*sampling),
951  (float)(atoms[n].location(0)*sampling),
952  (float)intensity);
953  }
954  fclose(fhOut);
955 
956  if (verbose>=2)
957  {
958  PDBPhantom pdb;
959  pdb.read(fnOut+".pdb");
960  distanceHistogramPDB(pdb,Nclosest,-1,200,hist);
961  hist.write(fnOut+"_distance.hist");
962  }
963 }
964 
965 /* Run --------------------------------------------------------------------- */
967 {
968  produceSideInfo();
969  int iter=0;
970  double previousNAtoms=0;
971  percentageDiff=1;
972  double actualGrowSeeds=0.;
973  do
974  {
975  // Place seeds
976  if (iter==0)
977  placeSeeds(initialSeeds);
978  else
979  {
980  double Natoms=atoms.size();
981  actualGrowSeeds=growSeeds*std::min(1.0,0.1+(percentageDiff-targetError)/targetError);
982  removeSeeds(FLOOR(Natoms*(actualGrowSeeds/2)/100));
983  placeSeeds(FLOOR(Natoms*actualGrowSeeds/100));
984  }
985  drawApproximation();
986 
987  if (iter==0 && verbose>0)
988  std::cout << "Initial error with " << atoms.size()
989  << " pseudo-atoms " << percentageDiff << std::endl;
990 
991  // Optimize seeds until convergence
992  optimizeCurrentAtoms();
993  if (verbose>0)
994  std::cout << "Error with " << atoms.size() << " pseudo-atoms "
995  << percentageDiff << std::endl;
996  writeResults();
997  iter++;
998 
999  if (fabs(previousNAtoms-atoms.size())/atoms.size()<0.01*actualGrowSeeds/100)
1000  {
1001  std::cout << "The required precision cannot be attained\n"
1002  << "Suggestion: Reduce sigma and/or minDistance\n"
1003  << "Writing best approximation with current parameters\n";
1004 
1005  break;
1006  }
1007  previousNAtoms=atoms.size();
1008  }
1009  while (percentageDiff>targetError);
1010  removeTooCloseSeeds();
1011  writeResults();
1012 
1013  // Kill threads
1014  threadOpCode=KILLTHREAD;
1016  free(threadIds);
1017  free(threadArgs);
1018 }
void read(const FileName &fnPDB)
Read phantom from either a PDB of CIF file.
Definition: pdb.cpp:503
int numThreads
Number of threads.
int barrier_init(barrier_t *barrier, int needed)
int * nmax
void min(Image< double > &op1, const Image< double > &op2)
#define VEC_ELEM(v, i)
Definition: matrix1d.h:245
#define XMIPP_MAX(x, y)
Definition: xmipp_macros.h:193
void removeSeeds(int Nseeds)
Remove seeds.
__host__ __device__ float2 floor(const float2 v)
Pseudoatom class.
#define FINISHINGX(v)
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
doublereal * c
void resizeNoCopy(const MultidimArray< T1 > &v)
void sqrt(Image< double > &op)
Couldn&#39;t write to file.
Definition: xmipp_error.h:140
void writeResults()
Write results.
void show() const
show parameters
void write(const FileName &name="", size_t select_img=ALL_IMAGES, bool isStack=false, int mode=WRITE_OVERWRITE, CastWriteMode castMode=CW_CAST, int _swapWrite=0)
void abs(Image< double > &op)
double percentil(double percent_mass)
Definition: histogram.cpp:160
#define FINISHINGZ(v)
glob_prnt iter
std::vector< PseudoAtom > atoms
void drawGaussian(double k, double i, double j, MultidimArray< double > &V, double intensity)
Draw a Gaussian on a volume.
#define WORKTHREAD
void produceSideInfo()
Prepare side info.
#define STARTINGX(v)
void insertRegion(const MultidimArray< double > &region)
Insert region.
#define i
ql0001_ & k(htemp+1),(cvec+1),(atemp+1),(bj+1),(bl+1),(bu+1),(x+1),(clamda+1), &iout, infoqp, &zero,(w+1), &lenw,(iw+1), &leniw, &glob_grd.epsmac
Matrix1D< T > transpose() const
Definition: matrix1d.cpp:644
#define STARTINGY(v)
PseudoAtom()
Empty constructor.
void threshold(double *phi, unsigned long nvox, double limit)
Definition: lib_vwk.cpp:524
#define A3D_ELEM(V, k, i, j)
glob_log first
#define DIRECT_A1D_ELEM(v, i)
Barrier * barrier
#define FOR_ALL_ELEMENTS_IN_ARRAY3D(V)
doublereal * b
#define FLOOR(x)
Definition: xmipp_macros.h:240
T computeMin() const
void compute_hist(const MultidimArrayGeneric &array, Histogram1D &hist, int no_steps)
Definition: histogram.cpp:572
void distanceHistogramPDB(const PDBPhantom &phantomPDB, size_t Nnearest, double maxDistance, int Nbins, Histogram1D &hist)
Definition: pdb.cpp:1627
void extractRegion(int idxGaussian, MultidimArray< double > &region, bool extended=false) const
Extract region around a Gaussian.
#define CEIL(x)
Definition: xmipp_macros.h:225
void optimizeCurrentAtoms()
Optimize current atoms.
FileName fnOut
void readParams()
Read parameters from command line.
#define sampling
#define XSIZE(v)
friend std::ostream & operator<<(std::ostream &o, const PseudoAtom &f)
Show pseudo atom.
void write(const FileName &fn) const
void max(Image< double > &op1, const Image< double > &op2)
#define FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(v)
double intensity
Intensity.
free((char *) ob)
#define DIRECT_MULTIDIM_ELEM(v, n)
bool allowIntensity
Allow gaussians to vary intensity.
#define ROUND(x)
Definition: xmipp_macros.h:210
#define XMIPP_EQUAL_ZERO(x)
Definition: xmipp_macros.h:123
void initZeros()
Definition: matrix1d.h:592
void sort(struct DCEL_T *dcel)
Definition: sorting.cpp:18
void defineParams()
define parameters
Matrix1D< double > location
Location.
#define XMIPP_MIN(x, y)
Definition: xmipp_macros.h:181
bool allowMovement
Allow gaussians to move.
#define j
#define FINISHINGY(v)
#define FOR_ALL_ELEMENTS_IN_ARRAY1D(v)
FileName withoutExtension() const
void placeSeeds(int Nseeds)
Place seeds.
void write(const FileName &fn, MDLabel=MDL_X, MDLabel=MDL_COUNT)
Definition: histogram.cpp:129
void drawApproximation()
Draw approximation.
#define INT_MASK
Definition: mask.h:385
bool operator<(const PseudoAtom &a, const PseudoAtom &b)
Comparison between pseudo atoms.
fprintf(glob_prnt.io, "\)
#define REALGAUSSIAN
void initZeros(const MultidimArray< T1 > &op)
double computeAverage(int k, int i, int j, MultidimArray< double > &V)
Compute average of a volume.
static void * optimizeCurrentAtomsThread(void *threadArgs)
Optimize current atoms (thread)
int barrier_wait(barrier_t *barrier)
Incorrect value received.
Definition: xmipp_error.h:195
#define KILLTHREAD
void generateMask(MultidimArray< double > &v)
void removeTooCloseSeeds()
Remove too close seeds.
double evaluateRegion(const MultidimArray< double > &region) const
Evaluate region.
#define STARTINGZ(v)
int * n
void compute_hist_within_binary_mask(const MultidimArray< int > &mask, MultidimArray< T > &v, Histogram1D &hist, int no_steps)
Definition: mask.h:906
doublereal * a
#define LOWPASS
void applyMaskSpace(MultidimArray< double > &v)
T computeMax() const
double gaussian1D(double x, double sigma, double mu)