1 /* 2 This file is part of MADNESS. 3 4 Copyright (C) 2007,2010 Oak Ridge National Laboratory 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 20 For more information please contact: 21 22 Robert J. Harrison 23 Oak Ridge National Laboratory 24 One Bethel Valley Road 25 P.O. Box 2008, MS-6367 26 27 email: harrisonrj@ornl.gov 28 tel: 865-241-3937 29 fax: 865-572-0680 30 31 $Id: sdf_shape.h 1792 2010-01-26 12:58:18Z rjharrison $ 32 */ 33 34 /*! 35 \file mra/sdf_domainmask.h 36 \brief Defines abstract interfaces and concrete classes signed distance 37 functions and domain masks. 38 39 Interfaces for a signed distance function (sdf) and a domain mask are 40 made. The original conception of these interfaces was for doing 41 shapes and interior boundary conditions in MADNESS; however, the 42 interfaces were abstracted for other applications. 43 44 The domain mask builds on a sdf such that \f$0 <= \mbox{mask(sdf)} <= 1\f$ 45 for smooth switching between domains. That said, a mask needs a sdf. 46 47 A general-purpose functor is given that combines a sdf and mask 48 to produce MADNESS functions for various entities including 49 - The domain mask (trace is the volume) 50 - The gradient of mask (related to the surface) 51 - The surface (trace is the surface area) 52 - The normal derivative of the surface 53 54 \ingroup mrabcint 55 */ 56 57 #ifndef MADNESS_MRA_SDF_DOMAINMASK_H__INCLUDED 58 #define MADNESS_MRA_SDF_DOMAINMASK_H__INCLUDED 59 60 #include <madness/mra/mra.h> 61 62 namespace madness { 63 64 /** \brief The interface for a signed distance function (sdf). 65 66 A class implementing this interface will need to provide the sdf 67 given a point in coordinate space, and also the gradient of the 68 sdf at this point. 69 70 \c NDIM is the dimensionality of the coordinate space. 71 72 \ingroup mrabcint */ 73 template <std::size_t NDIM> 74 class SignedDFInterface { 75 public: 76 /** \brief Returns the signed distance from the surface, 77 - Positive is ``inside'' 78 - Negative is ``outside'' 79 80 \param[in] x The coordinate 81 \return The signed distance */ 82 virtual double sdf(const Vector<double,NDIM>& x) const = 0; 83 84 /** \brief Returns the gradient of the signed distance from the 85 surface (i.e., \c dsdf(x)/dx[i] ) 86 87 \param[in] x The coordinate 88 \return The gradient */ 89 virtual Vector<double,NDIM> grad_sdf(const Vector<double,NDIM>& x) 90 const = 0; 91 ~SignedDFInterface()92 virtual ~SignedDFInterface() {} 93 }; 94 95 /** \brief The interface for masking functions defined by signed distance 96 functions. 97 98 This interface was initially conceived for using shapes in MADNESS 99 to specify internal boundary conditions. The signed distance function 100 defines the shape, and this mask allows calculations of volumes, 101 surfaces, etc. 102 103 \ingroup mrabcint */ 104 class DomainMaskInterface { 105 public: 106 /** \brief Returns the characteristic mask function 107 - 1 in interior 108 - 0 in exterior 109 - 1/2 on boundary 110 111 \param[in] d The signed distance from the surface 112 \return The mask or characteristic function */ 113 virtual double mask(double d) const = 0; 114 115 /** \brief Returns the derivative of the characteristic mask function 116 with respect to the distance (from the surface) 117 118 \param[in] d The signed normal distance from the surface 119 \return Derivative of the characteristic mask function */ 120 virtual double dmask(double d) const = 0; 121 122 /** \brief Returns the value of the normalized surface layer function 123 124 Normalized means the volume integral of this function should 125 converge to the surface area in the limit of a either an infinitely 126 thin surface or zero curvature. The function thus acts as a 127 ``delta function'' located at the boundary. 128 129 \param[in] d The signed normal distance from the surface 130 \return Normalized surface layer function */ 131 virtual double surface(double d) const = 0; 132 133 /** \brief Returns the derivative of the normalized surface layer 134 function 135 136 \param[in] d The signed normal distance from the surface 137 \return Derivative of the normalized surface layer function */ 138 virtual double dsurface(double d) const = 0; 139 ~DomainMaskInterface()140 virtual ~DomainMaskInterface() {} 141 }; 142 143 /** \brief Framework for combining a signed distance function (sdf) 144 with a domain mask to produce MADNESS functions. 145 146 This interface provides functor functionality to produce MADNESS 147 functions for 148 - the domain mask (given the sdf) 149 - the derivative of the domain mask 150 - the surface (given the sdf) 151 - the normal derivative of the surface layer 152 153 The functor defaults to the domain mask; however, member functions 154 can toggle between the other options. 155 156 \ingroup mrabcint */ 157 template <std::size_t NDIM> 158 class DomainMaskSDFFunctor : public FunctionFunctorInterface<double,NDIM> { 159 160 private: 161 /// \brief Bury the default constructor DomainMaskSDFFunctor()162 DomainMaskSDFFunctor() {} 163 164 private: // protected may be better if this becomes highly inherited 165 /// The domain mask to use 166 std::shared_ptr<DomainMaskInterface> mask; 167 168 /// The signed distance function 169 std::shared_ptr<SignedDFInterface<NDIM> > sdf; 170 171 int mswitch; ///< Which masking function to use (mask, surface, etc.) 172 173 public: 174 // switch values 175 static const int MASK; ///< Use the \c mask() function in \c mask 176 static const int MASK_COMPLEMENT; ///< Get the complement of \c mask() 177 static const int DMASK; ///< Use the \c dmask() function in \c mask 178 static const int SURFACE; ///< Use the \c surface() function in \c mask 179 static const int DSURFACE; ///< Use the \c dsurface() function in \c mask 180 181 /** \brief Constructor for mask/sdf functor 182 183 \param mask Pointer to the domain mask 184 \param sdf Pointer to the signed distance function */ DomainMaskSDFFunctor(std::shared_ptr<DomainMaskInterface> mask,std::shared_ptr<SignedDFInterface<NDIM>> sdf)185 DomainMaskSDFFunctor(std::shared_ptr<DomainMaskInterface> mask, 186 std::shared_ptr<SignedDFInterface<NDIM> > sdf) 187 : mask(mask), sdf(sdf), mswitch(MASK) 188 {} 189 190 /** \brief Constructor for mask/sdf function specifying the desired 191 function (mask, surface, etc.) 192 193 \param mask Pointer to the domain mask 194 \param sdf Pointer to the signed distance function 195 \param[in] _mswitch Which function to use (MASK, DMASK, SURFACE, 196 DSURFACE, or MASK_COMPLEMENT) */ DomainMaskSDFFunctor(std::shared_ptr<DomainMaskInterface> mask,std::shared_ptr<SignedDFInterface<NDIM>> sdf,int _mswitch)197 DomainMaskSDFFunctor(std::shared_ptr<DomainMaskInterface> mask, 198 std::shared_ptr<SignedDFInterface<NDIM> > sdf, int _mswitch) 199 : mask(mask), sdf(sdf), mswitch(MASK) { 200 if(_mswitch == MASK || _mswitch == DMASK || _mswitch == SURFACE || 201 _mswitch == DSURFACE || _mswitch == MASK_COMPLEMENT) { 202 mswitch = _mswitch; 203 } 204 else { 205 error("Unrecognized function option in DomainMaskSDFFunctor" \ 206 "::DomainMaskSDFFunctor()"); 207 } 208 } 209 210 /** \brief Uses the functor interface to make a MADNESS function 211 212 \param x Point to compute value 213 \return Value of the desired function */ operator()214 double operator()(const Vector<double,NDIM>& x) const { 215 if(mswitch == DomainMaskSDFFunctor<NDIM>::MASK) 216 return mask->mask(sdf->sdf(x)); 217 else if(mswitch == DomainMaskSDFFunctor<NDIM>::MASK_COMPLEMENT) 218 return 1.0 - mask->mask(sdf->sdf(x)); 219 else if(mswitch == DomainMaskSDFFunctor<NDIM>::DMASK) 220 return mask->dmask(sdf->sdf(x)); 221 else if(mswitch == DomainMaskSDFFunctor<NDIM>::SURFACE) 222 return mask->surface(sdf->sdf(x)); 223 else if(mswitch == DomainMaskSDFFunctor<NDIM>::DSURFACE) 224 return mask->dsurface(sdf->sdf(x)); 225 else { 226 error("Unknown function from DomainMaskInterface in " \ 227 "DomainMaskSDFFunctor::operator()"); 228 return 0.0; 229 } 230 } 231 232 /** \brief Toggles which function from DomainMaskInterface to use when 233 making the MADNESS function. 234 235 \param _mswitch The function to use (should be MASK, DMASK, 236 SURFACE, DSURFACE, or MASK_COMPLEMENT) */ setMaskFunction(int _mswitch)237 void setMaskFunction(int _mswitch) { 238 if(_mswitch == MASK || _mswitch == DMASK || _mswitch == SURFACE || 239 _mswitch == DSURFACE || _mswitch == MASK_COMPLEMENT) { 240 mswitch = _mswitch; 241 } 242 else { 243 error("Unrecognized function option in DomainMaskSDFFunctor" \ 244 "::setMaskFunction()"); 245 } 246 } 247 ~DomainMaskSDFFunctor()248 virtual ~DomainMaskSDFFunctor() {} 249 }; 250 251 template<std::size_t NDIM> 252 const int DomainMaskSDFFunctor<NDIM>::MASK = 1; 253 254 template<std::size_t NDIM> 255 const int DomainMaskSDFFunctor<NDIM>::MASK_COMPLEMENT = 2; 256 257 template<std::size_t NDIM> 258 const int DomainMaskSDFFunctor<NDIM>::DMASK = 3; 259 260 template<std::size_t NDIM> 261 const int DomainMaskSDFFunctor<NDIM>::SURFACE = 4; 262 263 template<std::size_t NDIM> 264 const int DomainMaskSDFFunctor<NDIM>::DSURFACE = 5; 265 266 /** \brief Provides the Li-Lowengrub-Ratz-Voight (LLRV) domain mask 267 characteristic functions. 268 269 \ingroup mrabcint 270 271 See X. Li, J. Lowengrub, A. Rätz, and A. Voight, ``Solving PDEs in 272 Complex Geometries: A Diffuse Domain Approach,'' Commun. Math. Sci., 7, 273 p81-107, 2009. 274 275 Given a signed distance, this class implements in the domain mask 276 and surface functions from the above reference. For the domain mask, 277 278 \f[ \varphi(d) = \frac{1}{2}\left( 1 - \tanh\left( 279 \frac{3d}{\varepsilon} \right) \right) \f] 280 281 where \f$d\f$ is the signed distance. The normalized surface function 282 is 283 284 \f[ B(\varphi) = \frac{36}{\varepsilon} \varphi^2 (1-\varphi)^2. \f] 285 286 The constant \f$36/\varepsilon\f$ is chosen to fulfill 287 288 \f[ \int_{-\infty}^\infty B(s) \, ds = 1 \f] 289 290 This class assumes the domain mask is uniformly 0 or 1 outside 291 signed distances \f$ |8 \epsilon| \f$ since the switching function 292 becomes 0/1 to machine precision at these levels. Specifically, 293 for this function the parameter \f$ \epsilon \f$ is an effective 294 measure of the full width of the surface layer since 295 296 \f[ \int_{-\epsilon/2}^{\epsilon/2} B(s) \, ds \doteq 0.987 \f] 297 298 and 299 300 \f[ \int_{-\epsilon}^{\epsilon} B(s) \, ds \doteq 0.999963 \f] */ 301 class LLRVDomainMask : public DomainMaskInterface { 302 private: LLRVDomainMask()303 LLRVDomainMask() : epsilon(0.0) {} ///< Forbidden 304 305 protected: 306 const double epsilon; ///< The width of the transition region 307 308 public: 309 /** \brief Constructor for the domain mask 310 311 \param[in] epsilon The effective width of the surface */ LLRVDomainMask(double epsilon)312 LLRVDomainMask(double epsilon) 313 : epsilon(epsilon) 314 {} 315 316 /** \brief Value of characteristic function at normal distance d from 317 the surface 318 319 \param[in] d The signed distance. Negative is ``inside,'' 320 positive is ``outside.'' 321 \return The domain mask */ mask(double d)322 double mask(double d) const { 323 if (d > 8.0*epsilon) { 324 return 0.0; // we're safely outside 325 } 326 else if (d < -8.0*epsilon) { 327 return 1.0; // inside 328 } 329 else { 330 return 0.5 * (1.0 - tanh(3.0 * d / epsilon)); 331 } 332 } 333 334 /** \brief Derivative of characteristic function with respect to the 335 normal distance 336 337 \param[in] d The signed distance 338 \return The derivative */ dmask(double d)339 double dmask(double d) const { 340 if (fabs(d) > 8.0*epsilon) { 341 return 0.0; // we're safely outside or inside 342 } 343 else { 344 double tanh3d = tanh(3.0*d/epsilon); 345 return 1.5*(tanh3d*tanh3d - 1.0) / epsilon; 346 } 347 } 348 349 /** \brief Value of surface function at distance d normal to surface 350 351 \param[in] d The signed distance 352 \return The value of the surface function */ surface(double d)353 double surface(double d) const { 354 double phi = mask(d); 355 double phic = 1.0 - phi; 356 return 36.0*phi*phi*phic*phic/epsilon; 357 } 358 359 /** \brief Value of d(surface)/ddistance 360 361 \param[in] d The signed distance 362 \return The derivative of the surface function */ dsurface(double d)363 double dsurface(double d) const { 364 double phi = mask(d); 365 double dphi = dmask(d); 366 return 72.0*phi*(1.0-phi)*dphi*(1.0 - 2.0*phi)/epsilon; 367 } 368 ~LLRVDomainMask()369 virtual ~LLRVDomainMask() {} 370 }; 371 372 /** \brief Use a Gaussian for the surface function and the corresponding erf 373 for the domain mask. */ 374 class GaussianDomainMask : public DomainMaskInterface { 375 private: GaussianDomainMask()376 GaussianDomainMask() : epsilon(0.0) {} ///< Forbidden 377 378 protected: 379 const double epsilon; ///< The width of the transition region 380 381 public: 382 /** \brief Constructor for the domain mask 383 384 \param[in] epsilon The effective width of the surface */ GaussianDomainMask(double epsilon)385 GaussianDomainMask(double epsilon) 386 : epsilon(epsilon) 387 {} 388 389 /** \brief Value of characteristic function at normal distance d from 390 the surface 391 392 \param[in] d The signed distance. Negative is ``inside,'' 393 positive is ``outside.'' 394 \return The domain mask */ mask(double d)395 double mask(double d) const { 396 if (d > 8.0*epsilon) { 397 return 0.0; // we're safely outside 398 } 399 else if (d < -8.0*epsilon) { 400 return 1.0; // inside 401 } 402 else { 403 return (1.0 - erf(d / (sqrt(2.0) * epsilon))) * 0.5; 404 } 405 } 406 407 /** \brief Derivative of characteristic function with respect to the 408 normal distance 409 410 \param[in] d The signed distance 411 \return The derivative */ dmask(double d)412 double dmask(double d) const { 413 if (fabs(d) > 8.0*epsilon) { 414 return 0.0; // we're safely outside or inside 415 } 416 else { 417 return -exp(-d*d*0.5/(epsilon*epsilon)) / (sqrt(2.0*constants::pi) 418 * epsilon); 419 } 420 } 421 422 /** \brief Value of surface function at distance d normal to surface 423 424 \param[in] d The signed distance 425 \return The value of the surface function */ surface(double d)426 double surface(double d) const { 427 return exp(-d*d*0.5/(epsilon*epsilon)) / (sqrt(2.0*constants::pi) 428 * epsilon); 429 } 430 431 /** \brief Value of d(surface)/ddistance 432 433 \param[in] d The signed distance 434 \return The derivative of the surface function */ dsurface(double d)435 double dsurface(double d) const { 436 return -exp(-d*d*0.5/(epsilon*epsilon)) * d / (sqrt(2.0*constants::pi) 437 * epsilon*epsilon*epsilon); 438 } 439 ~GaussianDomainMask()440 virtual ~GaussianDomainMask() {} 441 }; 442 443 } // end of madness namespace 444 445 #endif // MADNESS_MRA_SDF_DOMAINMASK_H__INCLUDED 446