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&auml;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