MercuryDPM  Alpha
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
LiquidMigrationWilletInteraction.cc
Go to the documentation of this file.
1 //Copyright (c) 2013-2014, The MercuryDPM Developers Team. All rights reserved.
2 //For the list of developers, see <http://www.MercuryDPM.org/Team>.
3 //
4 //Redistribution and use in source and binary forms, with or without
5 //modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name MercuryDPM nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 //DISCLAIMED. IN NO EVENT SHALL THE MERCURYDPM DEVELOPERS TEAM BE LIABLE FOR ANY
19 //DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 
30 #include "InteractionHandler.h"
31 #include "DPMBase.h"
32 #include <iomanip>
33 #include <fstream>
34 
41  : BaseInteraction(P, I, timeStamp)
42 {
43  liquidBridgeVolume_ = 0.0;
44  wasInContact_ = false;
45 #ifdef DEBUG_CONSTRUCTOR
46  std::cout << "LiquidMigrationWilletInteraction::LiquidMigrationWilletInteraction() finished" << std::endl;
47 #endif
48 }
49 
54  : BaseInteraction(p)
55 {
58 #ifdef DEBUG_CONSTRUCTOR
59  std::cout << "LiquidMigrationWilletInteraction::LiquidMigrationWilletInteraction(const LiquidMigrationWilletInteraction &p finished" << std::endl;
60 #endif
61 }
62 
67 {
68 #ifdef DEBUG_DESTRUCTOR
69  std::cout << "LiquidMigrationWilletInteraction::~LiquidMigrationWilletInteraction() finished" << std::endl;
70 #endif
71 }
72 
74 {
75  rupture();
76 };
77 
81 void LiquidMigrationWilletInteraction::write(std::ostream& os) const
82 {
83  os
84  << " wasInContact " << wasInContact_
85  << " liquidBridgeVolume " << liquidBridgeVolume_;
86 }
87 
92 {
93  std::string dummy;
94  is >> dummy >> wasInContact_;
95  is >> dummy >> liquidBridgeVolume_;
96 }
97 
102 {
103  // Adding no capillary force for liquid bridge volume = 0
104  if (getLiquidBridgeVolume() == 0) return;
105 
106  if (getOverlap() >= 0)
107  {
108  // if particles are in contact
109  const LiquidMigrationWilletSpecies* species = getSpecies();
110  const Mdouble effectiveRadius = 2.0*getEffectiveRadius();
111  const Mdouble fdotn = -2.0 * constants::pi * effectiveRadius * species->getSurfaceTension()
112  * std::cos(species->getContactAngle());
113  addForce(getNormal() * fdotn);
114  } else if (wasInContact_) {
115  // if particles are not in contact, but within their interaction distance
116  const LiquidMigrationWilletSpecies* species = getSpecies();
117  const Mdouble effectiveRadius = 2.0*getEffectiveRadius();
118  const Mdouble s_c = -getOverlap() * std::sqrt(effectiveRadius / getLiquidBridgeVolume());
119  const Mdouble fdotn = -2.0 * constants::pi * effectiveRadius * species->getSurfaceTension()
120  * std::cos(species->getContactAngle()) / (1 + (1.05 + 2.5 * s_c) * s_c);
121  addForce(getNormal() * fdotn);
122  }
123 }
124 
127 {
128  if (wasInContact_)
129  {
130  if (-getOverlap() >= getRuptureDistance())
131  {
132  rupture();
133  }
134  } else {
135  if (getOverlap() >= 0)
136  {
137  form();
138  }
139  }
140 }
141 
143 {
144  //form a bridge
145  //todo: extend to neighbours
146 
147  wasInContact_ = true;
148  const LiquidMigrationWilletSpecies* species = getSpecies();
149  LiquidFilmParticle* IParticle = dynamic_cast<LiquidFilmParticle*>(getI());
150  LiquidFilmParticle* PParticle = dynamic_cast<LiquidFilmParticle*>(getP());
151  if (IParticle == 0) //if I is a wall
152  {
153  //consider max bridge volume and add the rest to particles
154  if (PParticle->getLiquidVolume() <= species->getLiquidBridgeVolumeMax())
155  {
156  liquidBridgeVolume_ = PParticle->getLiquidVolume();
157  PParticle->setLiquidVolume(0.0);
158  }
159  else
160  {
162  PParticle->setLiquidVolume(PParticle->getLiquidVolume() - species->getLiquidBridgeVolumeMax());
163  }
164  }
165  else if (PParticle == 0) //if P is a wall
166  {
167  //consider max bridge volume and add the rest to particles
168  if (IParticle->getLiquidVolume() <= species->getLiquidBridgeVolumeMax())
169  {
170  liquidBridgeVolume_ = IParticle->getLiquidVolume();
171  IParticle->setLiquidVolume(0.0);
172  }
173  else
174  {
176  IParticle->setLiquidVolume(IParticle->getLiquidVolume() - species->getLiquidBridgeVolumeMax());
177  }
178  }
179  else //if P and I are particles
180  {
181  //if I is a ghost particle, apply volume change only to real particles (this removes the possibility that contacts are established only on one side of the periodic boundary; the same problem could exist for ruptures!)
182  LiquidFilmParticle* IParticleReal;
183  if (IParticle->getPeriodicFromParticle())
184  {
185  IParticleReal = dynamic_cast<LiquidFilmParticle*>(IParticle->getPeriodicFromParticle());
186  } else {
187  IParticleReal = IParticle;
188  }
189 
190  Mdouble distributableLiquidVolume =
191  PParticle->getLiquidVolume() + IParticleReal->getLiquidVolume();
192  //assign all liquid of the contacting particles to the bridge,
193  //if the total volume does not exceed LiquidBridgeVolumeMax
195  if (distributableLiquidVolume <= 0)
196  {
197  return;
198  }
199  else if (distributableLiquidVolume <= species->getLiquidBridgeVolumeMax())
200  {
201  liquidBridgeVolume_ = distributableLiquidVolume;
202  if (!IParticle->getPeriodicFromParticle() || PParticle->getIndex()<IParticle->getPeriodicFromParticle()->getIndex())
203  {
204  PParticle->setLiquidVolume(0.0);
205  IParticleReal->setLiquidVolume(0.0);
206  }
207  }
208  else //if the total volume exceeds LiquidBridgeVolumeMax, only distribute the max value
209  {
211  Mdouble pFraction =
212  PParticle->getLiquidVolume() / distributableLiquidVolume;
213  if (!IParticle->getPeriodicFromParticle() || PParticle->getIndex()<IParticle->getPeriodicFromParticle()->getIndex())
214  {
215  PParticle->addLiquidVolume(-pFraction * species->getLiquidBridgeVolumeMax());
216  IParticleReal->addLiquidVolume(-(1.0 - pFraction) * species->getLiquidBridgeVolumeMax());
217  }
218  }
219  }
220 }
221 
223 {
224  int numContacts = 0;
225  for (auto i : interactable->getInteractions())
226  {
228  LiquidFilmParticle* jIParticle = dynamic_cast<LiquidFilmParticle*>(i->getI());
229  if (j != this && jIParticle != nullptr && j->getLiquidBridgeVolume() != 0.0)
230  numContacts++;
231  }
232  return numContacts;
233 }
234 
236 {
237  // remove the contact history
238  wasInContact_ = false;
239 
240  //if the bridge is already empty, do nothing
241  if (getLiquidBridgeVolume() == 0.0)
242  return;
243 
244  //logger(INFO,"rupture % cp % p % i %",getIndex(),getContactPoint(),getP()->getIndex(),getI()->getIndex());
245 
246  //else rupture a bridge
247  const LiquidMigrationWilletSpecies* species = getSpecies();
248  LiquidFilmParticle* IParticle = dynamic_cast<LiquidFilmParticle*>(getI());
249  LiquidFilmParticle* PParticle = dynamic_cast<LiquidFilmParticle*>(getP());
250  if (IParticle == 0) //if I is a wall
251  {
252  int numContactsP = 0;
253  for (auto i : getP()->getInteractions())
254  {
256  if (j != this && j != nullptr && j->getLiquidBridgeVolume() != 0.0)
257  {
258  numContactsP++;
259  }
260 
261  }
262  if (numContactsP > 0)
263  {
264  // Updating new volume with distribution less than critical volume
265  Mdouble ExcessBridgeVolume = 0.0;
266  Mdouble newVolume = liquidBridgeVolume_ *species->getDistributionCoefficient()/ (numContactsP);
267 
268  for (auto i : getP()->getInteractions())
269  {
271  dynamic_cast<LiquidMigrationWilletInteraction*>(i);
272  if (j != this && j != nullptr && j->getLiquidBridgeVolume() != 0.0)
273  {
275  j->getLiquidBridgeVolume() + newVolume);
276  if (j->getLiquidBridgeVolume() >=
277  species->getLiquidBridgeVolumeMax())
278  {
279  ExcessBridgeVolume +=
281  - species->getLiquidBridgeVolumeMax();
283  species->getLiquidBridgeVolumeMax());
284  }
285  }
286  }
287  PParticle->setLiquidVolume(
288  PParticle->getLiquidVolume() + ExcessBridgeVolume + liquidBridgeVolume_ *(1-species->getDistributionCoefficient()));
289 
291  }
292  else
293  {
294  PParticle->setLiquidVolume(PParticle->getLiquidVolume() + liquidBridgeVolume_);
295  }
296  liquidBridgeVolume_ = 0.0;
297  for (auto i : *getHandler())
298  {
300  dynamic_cast<LiquidMigrationWilletInteraction*>(i);
301  }
302  for (auto i : getHandler()->getDPMBase()->particleHandler)
303  {
304  LiquidFilmParticle* j = dynamic_cast<LiquidFilmParticle*>(i);
305  }
306  }
307  else if (PParticle == 0) //if P is a wall
308  {
309  int numContactsI = 0;
310  for (auto i : getI()->getInteractions())
311  {
313  dynamic_cast<LiquidMigrationWilletInteraction*>(i);
314  if (j != this && j != nullptr && j->getLiquidBridgeVolume() != 0.0)
315  numContactsI++;
316  }
317  if (numContactsI > 0)
318  {
319  // Updating new volume with distribution less than critical volume
320  Mdouble ExcessBridgeVolume = 0.0;
321  Mdouble newVolume = liquidBridgeVolume_*species->getDistributionCoefficient() / (numContactsI);
322  for (auto i : getI()->getInteractions())
323  {
325  dynamic_cast<LiquidMigrationWilletInteraction*>(i);
326  if (j != this && j != nullptr && j->getLiquidBridgeVolume() != 0.0)
327  {
329  j->getLiquidBridgeVolume() + newVolume);
330  if (j->getLiquidBridgeVolume() >=
331  species->getLiquidBridgeVolumeMax())
332  {
333  ExcessBridgeVolume += j->getLiquidBridgeVolume()
334  - species->getLiquidBridgeVolumeMax();
336  }
337  }
338  }
339  IParticle->setLiquidVolume(IParticle->getLiquidVolume() + ExcessBridgeVolume + liquidBridgeVolume_ *(1-species->getDistributionCoefficient()));
340 
342  }
343  else
344  {
345  IParticle->setLiquidVolume(IParticle->getLiquidVolume() + liquidBridgeVolume_);
346  }
347  liquidBridgeVolume_ = 0.0;
348  }
349  else //if P and I are particles
350  {
351  //count interaction partners of p (this contact, ghosts and interaction without liquid bridge dont count)
352  int numContactsP = getNumberOfContacts(getP());
353  if (numContactsP < 1) //if P has only one contact (the one that gets ruptured), pass the fluid into it)
354  {
355  PParticle->addLiquidVolume(0.5 * liquidBridgeVolume_);
356  }
357  else //if P has only multiple contacts pass the fluid into it)
358  {
359  // move fluid to neighbouring contacts
360  Mdouble perContactVolume = 0.5 * liquidBridgeVolume_*species->getDistributionCoefficient() / numContactsP;
361  for (auto i : getP()->getInteractions())
362  {
364  dynamic_cast<LiquidMigrationWilletInteraction*>(i);
365  LiquidFilmParticle* jIParticle =
366  dynamic_cast<LiquidFilmParticle*>(i->getI());
367  if (j != this && jIParticle != nullptr && j->getLiquidBridgeVolume() != 0.0)
368  {
369  Mdouble excessVolume = perContactVolume - (species->getLiquidBridgeVolumeMax() - j->getLiquidBridgeVolume());
370  if (excessVolume < 0.0)
371  {
372  j->addLiquidBridgeVolume(perContactVolume);
373  PParticle->addLiquidVolume(0.5 * liquidBridgeVolume_*(1-species->getDistributionCoefficient()) / numContactsP);
374  }
375  else
376  {
378  PParticle->addLiquidVolume(excessVolume);
379  PParticle->addLiquidVolume(0.5 * liquidBridgeVolume_*(1-species->getDistributionCoefficient()) / numContactsP);
380  }
381  }
382  }
383  Mdouble PParticle_fin = PParticle->getLiquidVolume();
384 
385  }
386 
387  //count interaction partners of i (ghosts and interaction without liquid bridge dont count)
388  int numContactsI = getNumberOfContacts(getI());
389  if (numContactsI < 1)
390  {
391  IParticle->addLiquidVolume(0.5 * liquidBridgeVolume_);
392  //std::cout << "added " << 0.5 * liquidBridgeVolume_
393  // << " to particle #" << IParticle->getIndex()
394  //<< ", V=" << IParticle->getLiquidVolume() << std::endl;
395  }
396  else
397  {
398  // move fluid to neighbouring contacts
399  Mdouble perContactVolume = 0.5 * liquidBridgeVolume_*species->getDistributionCoefficient() / (numContactsI);
400  for (BaseInteraction* i : getI()->getInteractions())
401  {
403  LiquidFilmParticle* jIParticle = dynamic_cast<LiquidFilmParticle*>(i->getI());
404  if (j != this && jIParticle != nullptr && j->getLiquidBridgeVolume() != 0.0)
405  {
406  Mdouble excessVolume =
407  perContactVolume
408  - species->getLiquidBridgeVolumeMax()
409  + j->getLiquidBridgeVolume();
410  if (excessVolume < 0.0)
411  {
412  j->addLiquidBridgeVolume(perContactVolume);
413  IParticle->addLiquidVolume(0.5 * liquidBridgeVolume_*(1-species->getDistributionCoefficient()) / (numContactsI));
414  }
415  else
416  {
417  //std::cout << "excess i-Volume " << excessVolume << std::endl;
419  IParticle->addLiquidVolume(excessVolume);
420  IParticle->addLiquidVolume(0.5 * liquidBridgeVolume_*(1-species->getDistributionCoefficient()) / (numContactsI));
421  }
422  //~ std::cout << "added " << perContactVolume
423  //~ << " to i-contact #" << j->getIndex()
424  //~ << ", V=" << j->getLiquidBridgeVolume() << std::endl;
425  }
426  }
427  }
428 
429  //~ std::cout << "ruptured liquid bridge #" << getId()
430  //~ << " between particles #"
431  //~ << PParticle->getIndex()
432  //~ << " and #"
433  //~ << IParticle->getIndex()
434  //~ << ": V" << liquidBridgeVolume_
435  //~ << " -> Vp" << PParticle->getLiquidVolume()
436  //~ << " Vi" << IParticle->getLiquidVolume()
437  //~ << " Np" << numContactsP
438  //~ << " Ni" << numContactsI
439  //~ << std::endl;
440 
441  //to balance the added volume, remove the liquid from the bridge
442  liquidBridgeVolume_ = 0.0;
443 
444  }
445 }
446 
451 {
453  return 0.0;
454 }
455 
460 {
461  return dynamic_cast<const LiquidMigrationWilletSpecies *>(getBaseSpecies());
462 }
463 
468 {
469  return "LiquidMigrationWillet";
470 }
471 
473 {
474  return liquidBridgeVolume_;
475 }
476 
478 {
479  liquidBridgeVolume_ = liquidBridgeVolume;
480 }
481 
483 {
484  liquidBridgeVolume_ += liquidBridgeVolume;
485 }
486 
488 {
489  return wasInContact_;
490 }
491 
493 {
494  wasInContact_ = wasInContact;
495 }
496 
498 {
499  const LiquidMigrationWilletSpecies* species = getSpecies();
500  return (1.0 + 0.5 * species->getContactAngle())*cbrt(liquidBridgeVolume_);
501 }
502 
504  return 1;
505 }
506 
507 std::string LiquidMigrationWilletInteraction::getTypeVTK(unsigned i) const {
508  return "Float32";
509 }
510 
511 std::string LiquidMigrationWilletInteraction::getNameVTK(unsigned i) const {
512  return "liquidBridgeRadius";
513 
514 }
515 
516 std::vector<Mdouble> LiquidMigrationWilletInteraction::getFieldVTK(unsigned i) const {
517  return std::vector<Mdouble>(1, cbrt(liquidBridgeVolume_));
518 }
std::string getTypeVTK(unsigned i) const override
unsigned int getIndex() const
Returns the index of the object in the handler.
Definition: BaseObject.cc:108
unsigned getNumberOfFieldsVTK() const override
writes extra information to the VTK output
LiquidMigrationWilletSpecies contains the parameters used to describe a short-range force caused by l...
Mdouble getEffectiveRadius() const
Returns a Mdouble to the effective radius of the interaction. (Not corrected for the overlap) ...
int getNumberOfContacts(BaseInteractable *interactable)
Defines the liquid bridge willet interaction between two particles or walls.
void addLiquidVolume(Mdouble liquidVolume)
InteractionHandler * getHandler() const
Gets a point to the interaction handlers to which this interaction belongs.
Mdouble getElasticEnergy() const
Returns the amount of Elastic energy involved in an interaction. Basically used in case you want to w...
double Mdouble
Mdouble getContactAngle() const
used to access the contact angle between particle and liquid bridge surface.
Stores information about interactions between two interactable objects; often particles but could be ...
Mdouble getLiquidBridgeVolumeMax() const
used to access the Volume of the liquid bridge.
LiquidMigrationWilletInteraction(BaseInteractable *P, BaseInteractable *I, Mdouble timeStamp)
Constructor.
std::vector< Mdouble > getFieldVTK(unsigned i) const override
bool wasInContact_
A history parameter to store if the particles were in contact or not. Useful to compute adhesive forc...
Mdouble cos(Mdouble x)
Definition: ExtendedMath.cc:60
BaseParticle * getPeriodicFromParticle() const
Returns the 'original' particle this one's a periodic copy of.
Mdouble getDistributionCoefficient() const
used to access the surface tension of the liquid.
const BaseSpecies * getBaseSpecies() const
Return a constant point to BaseSpecies of the interaction.
const Mdouble pi
Definition: ExtendedMath.h:42
virtual void actionsOnErase()
If an interaction needs to do something before it gets erased, add it here. E.g. Liquid bridges ruptu...
ParticleHandler particleHandler
An object of the class ParticleHandler, contains the pointers to all the particles created...
Definition: DPMBase.h:1001
void setLiquidVolume(Mdouble liquidVolume)
const Vec3D & getNormal() const
Gets the normal vector between the two interacting objects.
std::string getBaseName() const
Returns the name of the interaction, see Interaction.h.
void read(std::istream &is)
Interaction read function, which accepts an std::istream as input.
Mdouble getOverlap() const
Returns a Mdouble with the current overlap between the two interacting objects.
void addForce(Vec3D force)
add an force increment to the total force.
std::string getNameVTK(unsigned i) const override
void addLiquidBridgeVolume(Mdouble liquidBridgeVolume)
BaseInteractable * getI()
Mdouble getLiquidVolume() const
void setLiquidBridgeVolume(Mdouble liquidBridgeVolume)
Defines the basic properties that a interactable object can have.
const std::list< BaseInteraction * > & getInteractions() const
Returns a reference to the list of interactions in this BaseInteractable.
void computeAdhesionForce()
Computes the adhesive forces for liquid bridge Willet type of interaction.
BaseInteractable * getP()
Returns a pointer to first object involved in the interaction (normally a particle).
DPMBase * getDPMBase()
Gets the problem that is solved using this handler.
Definition: BaseHandler.h:543
Mdouble getSurfaceTension() const
used to access the surface tension of the liquid.
void write(std::ostream &os) const
Interaction print function, which accepts an std::ostream as input.
void actionsAfterTimeStep()
test if particle needs to be ruptured
const LiquidMigrationWilletSpecies * getSpecies() const
A dynamic_cast of BaseSpecies type pointer to a pointer of type LiquidMigrationWilletSpecies.