Example #1
0
    def grad( self, field, radial_units ):
        r"""
        Returns the gradient of the scalar field in input.

        The gradient is computed by projecting the radial derivative of the field onto the discontinuous
        function space specified in :class:`solver.fem`.
        
        *Arguments*
            field
                the field of which you want to compute the gradient
            radial_units
                'physical' or 'rescaled': the former returns the field's gradient with respect to physical distances
                (units :math:`{M_p}^{-1}`), the latter returns the gradient with respect to the dimensionless rescaled 
                distances used within the code
        
        """
    
        if radial_units=='physical':
            grad_ = Constant(self.Mn) * field.dx(0)
        elif radial_units=='rescaled':
            grad_ = field.dx(0)
        else:
            message = "Invalid choice of radial units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)
    
        grad_ = project( grad_, self.fem.dS, self.fem.func_degree )

        return grad_
Example #2
0
    def scalar_force( self, field ):     
        r"""
        Returns the magnitude of the scalar force associated to the input field, per unit mass (units :math:`M_p`):
        
        .. math :: F_{\varphi} = \frac{\nabla\varphi}{M_P}
        
        if :math:`\varphi` is the input field.
        
        *Arguments*
        field
            the field associated to the scalar force
        
        """

        grad = self.grad( field, 'physical' )
        force = - grad / Constant(self.fields.Mp)
        force = project( force, self.fem.dS, self.fem.func_degree )
        
        return force
Example #3
0
    def strong_residual_form( self, sol, units ):
        r"""
        Computes the residual :math:`F` with respect to the strong form of the Poisson equation Eq. :eq:`Eq_Poisson`.

        In dimensionless in-code units (`units='rescaled'`) it is:

        .. math:: F = \hat{\nabla}^2 \Phi_N - \frac{\hat{\rho} M_n}{2 {M_P}^2}

        and in physical units (`units='physical'`):

        .. math:: F = \nabla^2 \Phi_N - \frac{\rho}{2 {M_P}^2}

        .. note:: In this function, the Laplacian :math:`\hat{\nabla}^2` is obtained by projecting
                  :math:`\frac{\partial^2}{\partial\hat{r}^2} + 2\frac{\partial}{\partial\hat{r}}`.
                  As such, it should not be used with interpolating polynomials of degree less than 2.

        *Parameters*
            sol
                the solution with respect to which the weak residual is computed.
            units
                `'rescaled'` (for the rescaled units used inside the code) or `'physical'`, for physical units

        """
        
        Mn, Mp = Constant( self.Mn ), Constant( self.Mp )
        
        if units=='rescaled':
            resc = 1.
        elif units=='physical':
            resc = Mn**2
        else:
            message = "Invalid choice of units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)

        # define r for use in the computation of the Laplacian
        r = Expression( 'x[0]', degree=self.fem.func_degree )
        
        f = ( sol.dx(0).dx(0) + Constant(2.) / r * sol.dx(0) ) - 1. / (2 * Mp**2) * self.source.rho * Mn
        f *= resc

        F = project( f, self.fem.dS, self.fem.func_degree )

        return F
Example #4
0
    def Qn( self, n, method='derivative', rescale=1., output_rescaled_op=False ):
        r"""
        Computes the operators:
        
        .. math:: Q_n = \frac{\alpha^n}{M^{3n-1}} \nabla^2( ( \nabla^2\varphi )^n )
        
        associated to a field :math:`\varphi`.

        In the examples provided within :math:`\varphi\mathrm{enics}`,
        :math:`\varphi` is the the UV field :math:`\phi` or the IR field :math:`\pi`.
 
        The key to computing :math:`Q_n` is obtaining the rescaled operator 
        :math:`\hat{Q}_n \equiv \hat{\nabla}^2 \left((\hat{\nabla}^2 \hat{\varphi})^{n}\right)`.
        The Laplacian :math:`y\equiv\hat{\nabla}^2\hat{\varphi}` is obtained from the solution
        to the equation of motion; :math:`\hat{Q}_n` can then be computed in three different ways,
        specified by the `method` option:

        * `derivative` (default):
           the rescaled operator is decomposed as:

           .. math:: \hat{\nabla}^2( y^{n} ) = n y^{n-1} \hat{\nabla}^2 y 
                  + n (n-1) y^{n-2} \hat{\nabla} y \cdot \hat{\nabla} y

           before being projected on the discontinuous function space specified in the `fem` instance;

        * `auxiliary`:
           :math:`w \equiv y^n` is projected onto the discontinuous function space specified in the `fem`
           instance, then the code solves for the linear system :math:`\hat{Q}_n = \nabla^2(w)`;

        * `gradient`:
           for the UV and IR theories supplied as :math:`\varphi\mathrm{enics}` examples,
           the weak formulation of the equation :math:`\hat{Q}_n = \hat{\nabla}^2( y^n )` is

           .. math:: \int \hat{Q}_n v \hat{r}^2 d\hat{r} = \int \hat{\nabla}^2 ( y^n ) v \hat{r}^2 d\hat{r} = 
                     - \int \hat{\nabla}( y^n ) \hat{\nabla} v \hat{r}^2 d\hat{r} =
                     - n \int y^{n-1} \hat{\nabla}y \hat{\nabla}v \hat{r}^2 d\hat{r}

           where :math:`v` is a test function, so that the :math:`\hat{Q}_n` operator can be obtained by solving

           .. math:: \int \hat{Q}_n v \hat{r}^2 d\hat{r} =
                     - n \int y^{n-1} \hat{\nabla}y \hat{\nabla}v \hat{r}^2 d\hat{r}

           which is the case for the `gradient` option.

        Further details are given in the `paper <https://arxiv.org/abs/2011.07037>`_ .

        .. note:: The :math:`Q_n` operators are computed starting from the field's rescaled Laplacian
                  :math:`y\equiv\hat{\nabla}^2\hat{\varphi}`, so the method :func:`solve()` (solving the
                  field's equation of motion) must have been called before calling this method.

        *Arguments*
            n
                order of the operator :math:`Q_n`
            method
                `'derivative'`, `'auxiliary'` or `'gradient'`
            rescale
                (optional) temporary auxiliary rescaling for :math:`Q_n`, used in tests or to prevent hitting the 
                maximum/minimum representable number
            output_rescaled_op
                `True` to obtain the rescaled operator 
                :math:`\hat{Q}_n \equiv \hat{\nabla}^2 \left((\hat{\nabla}^2 \hat{\varphi})^{2n+1}\right)`
                alongside the physical operator :math:`Q_n`, in a tuple :math:`(\hat{O}_n, O_n)`;
                `False` otherwise. Default: `False`.

        """

        # copy the Laplacian
        if self.y is None:
            message = "The Laplacian doesn't seem to have been computed. Please run solve() first."
            raise ValueError(message)
        y = d.Function( self.y.function_space() )
        y.assign( self.y )
        y.vector()[:] *= rescale # rescale for better precision (undone later)
        
        # we can obtain Qn in three ways
        if method=='derivative':
            # expand Del( (y^(2n+1) ) and project
            r = Expression( 'x[0]', degree=self.fem.func_degree )
            Qn_1 = n * y**(n-1) * ( y.dx(0).dx(0) + d.Constant(2.) / r * y.dx(0) )
            Qn_2 = n * (n-1.) * y**(n-2) * y.dx(0)**2
            Qn = Qn_1 + Qn_2
            Qn = project( Qn, self.fem.dS, self.fem.func_degree )
            
            
        elif method=='auxiliary':
            # project w=y^n and solve Qn = Del(w)
            w = y**n
            w = project( w, self.fem.dS, self.fem.func_degree )
            
            Qn_ = d.TrialFunction( self.fem.dS )
            v_ = d.TestFunction( self.fem.dS )
            
            r2 = Expression( 'pow(x[0],2)', degree=self.fem.func_degree )
            Qn_a = Qn_ * v_ * r2 * dx
            Qn_L = - inner( grad(w), grad(v_) ) * r2 * dx
            
            Qn = d.Function( self.fem.dS )
            
            Qn_pde = d.LinearVariationalProblem( Qn_a, Qn_L, Qn )
            Qn_solver = d.LinearVariationalSolver( Qn_pde )
            Qn_solver.solve()

            
        elif method=='gradient':
            # expand Del( y^n ) within the weak formulation, then solve the system
            Qn_ = d.TrialFunction( self.fem.dS )
            v_ = d.TestFunction( self.fem.dS )
            
            
            r2 = Expression( 'pow(x[0],2)', degree=self.fem.func_degree )
            Qn_a = Qn_ * v_ * r2 * dx
            Qn_L = - n * y**(n-1) * inner( grad(y), grad(v_) ) * r2 * dx
            
            Qn = d.Function( self.fem.dS )
            
            Qn_pde = d.LinearVariationalProblem( Qn_a, Qn_L, Qn )
            Qn_solver = d.LinearVariationalSolver( Qn_pde )
            Qn_solver.solve()
        
            
        # for the physical operator, unrescale and multiply by the theory-specific remaining terms  
        log10_Qn_coeff, Qn_oth_coeff = self.fields.log10_Qn_coeff(n), self.fields.Qn_oth_coeff(n)
        # compute log and then exp to avoid hitting the maximum/minimum representable number
        log10_R = log10_Qn_coeff + (2*n+2) * log10(self.Mn) + n * ( log10(self.Mf1) - log10( rescale ) )

        phys_Qn = d.Function( self.fem.dS )
        phys_Qn.vector()[:] = Qn_oth_coeff * 10.**log10_R * Qn.vector()[:]
        
        if output_rescaled_op:
            return Qn, phys_Qn
        else:
            return phys_Qn
Example #5
0
    def On( self, n, method='derivative', rescale=1., output_rescaled_op=False ):
        r"""
        Computes the operators:
        
        .. math:: O_n = (-1)^{n+1} \frac{2n+2}{2n+1} \binom{3n}{n} \alpha^{2n+1} \lambda^n
                  \nabla^2 \left((\nabla^2 \varphi)^{2n+1}\right) M^{-6n-2}
        
        associated to a field :math:`\varphi`.

        In the examples provided within :math:`\varphi\mathrm{enics}`, :math:`\varphi` is the the UV field :math:`\phi`
        or the IR field :math:`\pi`.

        The operators are computed starting from the field's rescaled Laplacian
        :math:`y\equiv\hat{\nabla}^2\hat{\varphi}`, so the field's equation of motion must have been solved 
        prior to invoking this method.

        For the method used to compute :math:`\nabla^2 \left((\nabla^2 \varphi)^{2n+1}\right)`, see the documentation
        of :func:`Qn`.

        *Arguments*
            n   
                order of the operator :math:`O_n`
            method
                `'derivative'`, `'auxiliary'` or `'gradient'` - see func:`Qn`
            rescale
                (optional) temporary auxiliary variable used to rescale the Laplacian during the computation of
                :math:`\nabla^2 \left((\nabla^2 \varphi)^{2n+1}\right)`, to prevent hitting the maximum/minimum 
                representable number
            output_rescaled_op
                `True` to obtain the rescaled operator 
                :math:`\hat{O}_n \equiv \hat{\nabla}^2 \left((\hat{\nabla}^2 \hat{\varphi})^{2n+1}\right)`
                alongside the physical operator :math:`O_n`, in a tuple :math:`(\hat{O}_n, O_n)`;
                `False` otherwise. Default: `False`.
        
        """

        # copy the Laplacian
        if self.y is None:
            message = "The Laplacian doesn't seem to have been computed. Please run solve() first."
            raise ValueError(message)
        y = d.Function( self.fem.S )
        y.assign( self.y )
        y.vector()[:] *= rescale # rescale for better precision (undone later)
        
        
        # we can obtain On in three ways
        if method=='derivative':
            # expand Del( (y^(2n+1) ) and project
            r = Expression( 'x[0]', degree=self.fem.func_degree )
            On_1 = (2.*n+1.) * y**(2*n) * ( y.dx(0).dx(0) + d.Constant(2.) / r * y.dx(0) )
            On_2 = (2.*n+1.) * (2.*n) * y**(2*n-1) * y.dx(0)**2
            On = On_1 + On_2
            On = project( On, self.fem.dS, self.fem.func_degree )
            
            
        elif method=='auxiliary':
            # project W=y^(2n+1) and solve On = Del(W)
            W = y**(2*n+1)
            W = project( W, self.fem.dS, self.fem.func_degree )
            
            On_ = d.TrialFunction( self.fem.dS )
            v_ = d.TestFunction( self.fem.dS )
            
            r2 = Expression( 'pow(x[0],2)', degree=self.fem.func_degree )
            On_a = On_ * v_ * r2 * dx
            On_L = - inner( grad(W), grad(v_) ) * r2 * dx
            
            On = d.Function( self.fem.dS )
            
            On_pde = d.LinearVariationalProblem( On_a, On_L, On )
            On_solver = d.LinearVariationalSolver( On_pde )
            On_solver.solve()
            
            
        elif method=='gradient':
            # expand Del( y^(2n+1) ) within the weak formulation, then solve the system
            On_ = d.TrialFunction( self.fem.dS )
            v_ = d.TestFunction( self.fem.dS )
        
            
            r2 = Expression( 'pow(x[0],2)', degree=self.fem.func_degree )
            On_a = On_ * v_ * r2 * dx
            On_L = - (2*n+1) * y**(2*n) * inner( grad(y), grad(v_) ) * r2 * dx
            
            On = d.Function( self.fem.dS )
            
            On_pde = d.LinearVariationalProblem( On_a, On_L, On )
            On_solver = d.LinearVariationalSolver( On_pde )
            On_solver.solve()
        

        # for the physical operator, unrescale and multiply by the theory-specific remaining terms  
        Mn, Mf1 = self.Mn, self.Mf1
        log10_On_coeff, On_oth_coeff = self.fields.log10_On_coeff(n), self.fields.On_oth_coeff(n)
        log10_R = log10_On_coeff + (4*n+4) * log10(Mn) + (2*n+1) * ( log10(Mf1) - log10( rescale ) )
        C_On = (-1)**(n+1) * binom(3*n,n) / (2.*n+1.) * On_oth_coeff * 10.**log10_R
        
        phys_On = d.Function( self.fem.dS )
        phys_On.vector()[:] = C_On * On.vector()[:]
        
        if output_rescaled_op:
            return On, phys_On
        else:
            return phys_On
Example #6
0
    def output_term( self, term='LHS', norm='none', units='rescaled', output_label=False ):
        r"""
        Outputs the left- and right-hand side of the Poisson equation Eq. :eq:`Eq_Poisson`.

        The terms can be output in physical or dimensionless in-code units, by choosing either
        `units='physical'` or `units='rescaled'`:

        ==============   ==============================   ===============================================
         `units=`         `term='LHS'`                    `term='RHS'`
        ==============   ==============================   ===============================================
         `'rescaled'`     :math:`\hat{\nabla}^2\Phi_N`    :math:`\frac{1}{2} \frac{\hat{\rho}}{M_P} M_n`
         `'physical'`     :math:`\nabla^2 \Phi_N`         :math:`\frac{\rho}{2 {M_P}^2}`
        ==============   ==============================   ===============================================

        where :math:`\Phi_N` is the Newtonian potential, :math:`\rho` the source and :math:`M_P` the 
        Planck mass.

        .. note:: In this method, the Laplacian :math:`\hat{\nabla}^2` is obtained by projecting
                  :math:`\frac{\partial^2}{\partial\hat{r}^2} + 2\frac{\partial}{\partial\hat{r}}`.
                  As such, it should not be used with interpolating polynomials of degree less than 2.
            
        *Parameters*
            term
                choice of `'LHS'` and `'RHS'` for the left- and right-hand side of the Poisson equation
            norm 
                `'L2'`, `'linf'` or `'none'`. If `'L2'` or `'linf'`: compute the :math:`L_2` or
                :math:`\ell_{\infty}` norm of the residual; if `'none'`, return the full
                term over the box - as opposed to its norm.
            units
                `rescaled` (default) or `physical`; choice of units for the output
            output_label
                if `True`, output a string with a label for the term (which can be used, e.g. in plot legends)
            
        """

        Mp, Mn =  Constant( self.Mp ), Constant( self.Mn )
        
        if units=='rescaled':
            resc = 1.
            str_nabla2 = '\\hat{\\nabla}^2'
            str_rho = '\\frac{1}{2} \\frac{\\hat{\\rho}}{{M_p}^2} M_n'
            
        elif units=='physical':
            resc = self.Mn**2
            str_nabla2 = '\\nabla^2'
            str_rho = '\\frac{\\rho}{2 {M_p}^2}'
            
        else:
            message = "Invalid choice of units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)
            
        str_PhiN = '\\Phi_N'
        
        # define r for use in the computation of the Laplacian
        r = Expression( 'x[0]', degree=self.fem.func_degree )

        if term=='LHS':
            term = self.PhiN.dx(0).dx(0) + Constant(2.) / r * self.PhiN.dx(0)
            label = r"$%s %s$" % ( str_nabla2, str_PhiN )
        elif term=='RHS':
            term = 1. / (2 * Mp**2) * self.source.rho * Mn
            label = r"$%s$" % str_rho

        # rescale if needed to get physical units
        term *= resc

        term = project( term, self.fem.dS, self.fem.func_degree )

        # 'none' = return function, not norm
        if norm=='none':
            result = term
            # from here on return a norm. This nested if is to preserve the structure of the original
            # built-in FEniCS norm function
        elif norm=='linf':
            # infinity norm, i.e. max abs value at vertices    
            result = r2_norm( term.vector(), self.fem.func_degree, norm_type=norm )
        else:
            result = r2_norm( term, self.fem.func_degree, norm_type=norm )

        if output_label:
            return result, label
        else:
            return result
Example #7
0
    def output_term(self,
                    eqn=1,
                    term='LHS',
                    norm='none',
                    units='rescaled',
                    output_label=False):
        r"""
        Outputs the different terms in the UV system of equations.

        The terms can be output in physical or dimensionless in-code units, by choosing either
        `units='physical'` or `units='rescaled'`.

        Possible choices of terms are listed in the tables below; recall that :math:`Y\equiv\nabla^2\phi`
        and :math:`Z\equiv\nabla^2 H`.

        - `eqn=1`:

        ==============   ===============================================================================================   ==============
         `units=`         `term='LHS'`                                                                                      `term='RHS'`
        ==============   ===============================================================================================   ==============
         `'rescaled'`     :math:`\hat{Y} - \left(\frac{m}{M_n}\right)^2\hat{\phi} - \frac{M_{f2}}{M_{f1}}\alpha\hat{Z}`             :math:`\frac{\hat{\rho}}{M_P}\frac{Mn}{M_{f1}}`
         `'physical'`     :math:`Y -m^2 \phi -\alpha Z`                                                                             :math:`\frac{\rho}{M_P}`
        ==============   ===============================================================================================   ==============



        ==============   =================   =================================================   =============================================   =================
         `units=`         `term=1`            `term=2`                                            `term=3`                                        `term=4`
        ==============   =================   =================================================   =============================================   =================
         `'rescaled'`     :math:`\hat{Y}`     :math:`-\left(\frac{m}{M_n}\right)^2\hat{\phi}`     :math:`-\frac{M_{f2}}{M_{f1}}\alpha\hat{Z}`     same as `'RHS'`
         `'physical'`     :math:`Y`           :math:`-m^2\phi`                                    :math:`-\alpha Z`                               same as `'RHS'`
        ==============   =================   =================================================   =============================================   =================


        
        - `eqn=2`:


        ==============   ===========================================================================================   ==========================================================================
         `units=`         `term='LHS'`                                                                                  `term='RHS'`
        ==============   ===========================================================================================   ==========================================================================
         `'rescaled'`    :math:`\hat{Z} -\left(\frac{M}{M_n}\right)^2\hat{H} - \alpha\frac{M_{f1}}{M_{f2}}\hat{Y}`      :math:`-\frac{\lambda}{6} \left( \frac{M_{f2}}{M_n} \right)^2 \hat{H}^3`
         `'physical'`     :math:`Z -M^2 H -\alpha Y`                                                                    :math:`-\frac{\lambda}{6} H^3`
        ==============   ===========================================================================================   ==========================================================================



        ==============   =================   =============================================   =============================================   =================
         `units=`         `term=1`            `term=2`                                            `term=3`                                        `term=4`
        ==============   =================   =============================================   =============================================   =================
         `'rescaled'`     :math:`\hat{Z}`     :math:`-\left(\frac{M}{Mn}\right)^2\hat{H}`     :math:`-\alpha\frac{M_{f1}}{M_{f2}}\hat{Y}`     same as `'RHS'`
         `'physical'`     :math:`Z`           :math:`-M^2 H`                                  :math:`-\alpha Y`                               same as `'RHS'`
        ==============   =================   =============================================   =============================================   =================


            Although the right-hand-side (RHS) of the equation of motion for :math:`H` is 0,
            this choice allows better comparison of the scale of the equation against the residuals.



        - `eqn=3`:

        ==============   ==========================================   ==================================   =================
         `units=`         `term='LHS'`                                 `term=1`                             `term=2`
        ==============   ==========================================   ==================================   =================
         `'rescaled'`     :math:`\hat{\nabla}^2\hat{\phi}-\hat{Y}`     :math:`\hat{\nabla}^2\hat{\phi}`     :math:`\hat{Y}`
         `'physical'`     :math:`\nabla^2\phi - Y`                     :math:`\nabla^2\phi`                 :math:`Y`
        ==============   ==========================================   ==================================   =================
     


        - `eqn=4`:

        ==============   =======================================   ==================================   =================
         `units=`         `term='LHS'`                              `term=1`                             `term=2`
        ==============   =======================================   ==================================   =================
         `'rescaled'`     :math:`\hat{\nabla}^2\hat{H}-\hat{Z}`     :math:`\hat{\nabla}^2\hat{H}`        :math:`\hat{Z}`
         `'physical'`     :math:`\nabla^2 H - Z`                    :math:`\nabla^2 H`                   :math:`Z`
        ==============   =======================================   ==================================   =================
     


        Equations (1) and (2) allow to look at the terms in the equations of motion,
        Eq. (3) and (4) enforce consistency relations :math:`Y = '\nabla^2\phi` and :math:`Z = \nabla^2 H`. 

        .. note:: In this method, the Laplacian :math:`\hat{\nabla}^2` is obtained by projecting
                  :math:`\frac{\partial^2}{\partial\hat{r}^2} + 2\frac{\partial}{\partial\hat{r}}`.
                  As such, it should not be used with interpolating polynomials of degree less than 2.
            
        *Parameters*
            eqn
                (`integer`) choice of equation
            term
                choice of `LHS`, `RHS` or a number; for `eqn=1,2`, valid terms range from 1 to 4, for
                `eqn=3,4`, valid terms range from 1 to 2
            norm 
                `'L2'`, `'linf'` or `'none'`. If `'L2'` or `'linf'`: compute the :math:`L_2` or
                :math:`\ell_{\infty}` norm of the residual; if `'none'`, return the full
                term over the box - as opposed to its norm.
            units
                `rescaled` (default) or `physical`; choice of units for the output
            output_label
                if `True`, output a string with a label for the term (which can be used, e.g. in plot legends)
            
        """

        # cast params as constant functions so that, if they are set to 0, FEniCS still understand
        # what is being integrated
        m, M, Mp = Constant(self.fields.m), Constant(self.fields.M), Constant(
            self.fields.Mp)
        alpha, lam = Constant(self.fields.alpha), Constant(self.fields.lam)
        Mn, Mf1, Mf2 = Constant(self.Mn), Constant(self.Mf1), Constant(
            self.Mf2)

        if units == 'rescaled':
            resc_13 = 1.
            resc_24 = 1.
            str_phi, str_h, str_y, str_z = '\\hat{\\phi}', '\\hat{H}', '\\hat{Y}', '\\hat{Z}'
            str_nabla2 = '\\hat{\\nabla}^2'
            str_coup_z = '\\alpha \\frac{M_{f2}}{M_{f1}} \\hat{Z}'
            str_coup_y = '\\alpha \\frac{M_{f1}}{M_{f2}} \\hat{Y}'
            str_m2 = '\\left( \\frac{m}{M_n} \\right)^2'
            str_M2 = '\\left( \\frac{M}{M_n} \\right)^2'
            str_nl = '\\frac{\\lambda}{6} \\frac{M_{f2}}{M_n} \\hat{H}^3'
            str_rho = '\\frac{\hat{\\rho}}{M_p}\\frac{M_n}{M_{f1}}'

        elif units == 'physical':
            resc_13 = self.Mn**2 * self.Mf1
            resc_24 = self.Mn**2 * self.Mf2
            str_phi, str_h, str_y, str_z = '\\phi', 'H', 'Y', 'Z'
            str_nabla2 = '\\nabla^2'
            str_coup_z = '\\alpha Z'
            str_coup_y = '\\alpha Y'
            str_m2 = 'm^2'
            str_M2 = 'M^2'
            str_nl = '\\frac{\\lambda}{6} \\hat{H}^3'
            str_rho = '\\frac{\\rho}{M_p}'

        else:
            message = "Invalid choice of units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)

        # split solution in phi, h, y, z
        phi, h, y, z = self.phi, self.h, self.y, self.z

        # define r for use in the computation of the Laplacian
        r = Expression('x[0]', degree=self.fem.func_degree)

        if eqn == 1:
            if term == 'LHS':
                # I expand manually the Laplacian into 2/r df/dr + d2f/dr2
                Term = y - (m / Mn)**2 * phi - alpha * (Mf2 / Mf1) * z
                label = r"$%s - %s%s - %s$" % (str_y, str_m2, str_phi,
                                               str_coup_z)
            elif term == 'RHS':
                Term = self.source.rho / Mp * Mn / Mf1
                label = r"$%s$" % str_rho
            elif term == 1:
                Term = y
                label = r"$%s$" % str_y
            elif term == 2:
                Term = -(m / Mn)**2 * phi
                label = r"$-%s%s$" % (str_m2, str_phi)
            elif term == 3:
                Term = -alpha * (Mf2 / Mf1) * z
                label = r"$-%s$" % str_coup_z
            elif term == 4:
                Term = self.source.rho / Mp * Mn / Mf1
                label = r"$%s$" % str_rho
            # rescale if needed to get physical units
            Term *= resc_13

        elif eqn == 2:
            if term == 'LHS':
                Term = z - (M / Mn)**2 * h - alpha * (Mf1 / Mf2) * y
                label = r"$%s - %s%s -%s$" % (str_z, str_M2, str_h, str_coup_y)
            elif term == 'RHS':
                Term = lam / 6. * (Mf2 / Mn)**2 * h**3
                label = r"$%s$" % str_nl
            elif term == 1:
                Term = z
                label = r"$%s$" % str_z
            elif term == 2:
                Term = -(M / Mn)**2 * h
                label = r"$%s%s$" % (str_M2, str_h)
            elif term == 3:
                Term = -alpha * (Mf1 / Mf2) * y
                label = r"$-%s$" % str_coup_y
            elif term == 4:
                Term = lam / 6. * (Mf2 / Mn)**2 * h**3
                label = r"$%s$" % str_nl
            # rescale if needed to get physical units
            Term *= resc_24

        elif eqn == 3:
            # consistency of y = Del phi
            if term == 'LHS':
                Term = Constant(2.) / r * phi.dx(0) + phi.dx(0).dx(0) - y
                label = r"$%s%s - %s$" % (str_nabla2, str_phi, str_y)
            elif term == 1:
                Term = Constant(2.) / r * phi.dx(0) + phi.dx(0).dx(0)
                label = r"$%s%s$" % (str_nabla2, str_phi)
            elif term == 2:
                Term = y
                label = r"$%s$" % str_y
            # rescale if needed to get physical units
            Term *= resc_13

        elif eqn == 4:
            # consistency of z = Del H
            if term == 'LHS':
                Term = Constant(2.) / r * h.dx(0) + h.dx(0).dx(0) - z
                label = r"$%s%s - %s$" % (str_nabla2, str_h, str_z)
            elif term == 1:
                Term = Constant(2.) / r * h.dx(0) + h.dx(0).dx(0)
                label = r"$%s%s$" % (str_nabla2, str_h)
            elif term == 2:
                Term = z
                label = r"$%s$" % str_z
            # rescale if needed to get physical units
            Term *= resc_24

        Term_func = project(Term, self.fem.dS, self.fem.func_degree)

        # 'none' = return function, not norm
        if norm == 'none':
            result = Term_func
            # from here on return a norm. This nested if is to preserve the structure of the original
            # built-in FEniCS norm function
        elif norm == 'linf':
            # infinity norm, i.e. max abs value at vertices
            result = r2_norm(Term_func.vector(),
                             self.fem.func_degree,
                             norm_type=norm)
        else:
            result = r2_norm(Term_func, self.fem.func_degree, norm_type=norm)

        if output_label:
            return result, label
        else:
            return result
Example #8
0
    def strong_residual_form(self, sol, units):
        r"""
        Computes the residual with respect to the strong form of the equations.

        The total residual is obtained by summing the residuals of all equations:


        .. math:: F = F_1 + F_2 + F_3 + F_4

        where, in dimensionless in-code units (`units='rescaled'`):
        
        .. math:: & F_1(\hat{\phi},\hat{H},\hat{Y},\hat{Z}) = \hat{Y}
                  - \left( \frac{m}{M_n} \right)^2\hat{\phi}
                  - \alpha \frac{M_{f2}}{M_{f1}} \hat{Z} - \frac{\hat{\rho}}{M_p}\frac{M_n}{M_{f1}}

                  & F_2(\hat{\phi},\hat{H},\hat{Y},\hat{Z}) = \hat{Z} 
                  - \left( \frac{M}{M_n} \right)^2 \hat{H}  
                  - \alpha \frac{M_{f1}}{M_{f2}} \hat{Y} 
                  - \frac{\lambda}{6} \left( \frac{M_{f2}}{M_n} \right)^2 \hat{H}^3

                  & F_3(\hat{\phi},\hat{H},\hat{Y},\hat{Z}) = \hat{\nabla}^2\hat{\phi} - \hat{Y}

                  & F_4(\hat{\phi},\hat{H},\hat{Y},\hat{Z}) = \hat{\nabla}^2\hat{H} - \hat{Z}


        and in physical units (`units='physical'`):

        .. math:: & F_1(\phi,H,Y,Z) = Y - m^2 \phi -\alpha H - \frac{\rho}{M_P}

                  & F_2(\phi,H,Y,Z) = Z - M^2 H - \alpha\phi - \frac{\lambda}{6} H^3

                  & F_3(\phi,H,Y,Z) = \nabla^2\phi - Y
        
                  & F_4(\phi,H,Y,Z) = \nabla^2 H - Z



        .. note:: In this function, the Laplacian :math:`\hat{\nabla}^2` is obtained by projecting
                  :math:`\frac{\partial^2}{\partial\hat{r}^2} + 2\frac{\partial}{\partial\hat{r}}`.
                  As such, it should not be used with interpolating polynomials of degree less than 2.

        Note that the weak residual in :func:`weak_residual_form` is just the scalar product
        of the strong residuals by test functions.

        *Parameters*
            sol
                the solution with respect to which the weak residual is computed.
            units
                `'rescaled'` (for the rescaled units used inside the code) or `'physical'`, for physical units

        """

        if units == 'rescaled':
            resc_13 = 1.
            resc_24 = 1.
        elif units == 'physical':
            resc_13 = self.Mn**2 * self.Mf1
            resc_24 = self.Mn**2 * self.Mf2
        else:
            message = "Invalid choice of units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)

        # cast params as constant functions so that, if they are set to 0,
        # fenics still understand what it is integrating
        m, M, Mp = Constant(self.fields.m), Constant(self.fields.M), Constant(
            self.fields.Mp)
        alpha, lam = Constant(self.fields.alpha), Constant(self.fields.lam)
        Mn, Mf1, Mf2 = Constant(self.Mn), Constant(self.Mf1), Constant(
            self.Mf2)

        # split solution in phi, h, y, z
        phi, h, y, z = d.split(sol)

        # initialise residual function
        F = d.Function(self.dV)

        # define r for use in the computation of the Laplacian
        r = Expression('x[0]', degree=self.fem.func_degree)

        # equation 1
        f1 = y - (m / Mn)**2 * phi - alpha * (
            Mf2 / Mf1) * z - self.source.rho / Mp * Mn / Mf1
        f1 *= resc_13
        F1 = project(f1, self.fem.dS, self.fem.func_degree)
        # equation 2
        f2 = z - (M / Mn)**2 * h - alpha * (Mf1 / Mf2) * y - lam / 6. * (
            Mf2 / Mn)**2 * h**3
        f2 *= resc_24
        F2 = project(f2, self.fem.dS, self.fem.func_degree)
        # equation 3  - I expand manually the Laplacian into 2/r df/dr + d2f/dr2
        f3 = Constant(2.) / r * phi.dx(0) + phi.dx(0).dx(0) - y
        f3 *= resc_13
        F3 = project(f3, self.fem.dS, self.fem.func_degree)
        # equation 4
        f4 = Constant(2.) / r * h.dx(0) + h.dx(0).dx(0) - z
        f4 *= resc_24
        F4 = project(f4, self.fem.dS, self.fem.func_degree)

        # combine equations
        fa = d.FunctionAssigner(
            self.dV, [self.fem.dS, self.fem.dS, self.fem.dS, self.fem.dS])
        fa.assign(F, [F1, F2, F3, F4])

        return F
Example #9
0
    def output_term(self,
                    eqn=1,
                    term='LHS',
                    norm='none',
                    units='rescaled',
                    output_label=False):
        r"""
        Outputs the different terms in the IR system of equations.

        The terms can be output in physical or dimensionless in-code units, by choosing either
        `units='physical'` or `units='rescaled'`.

        Possible choices of terms are listed in the tables below; recall that :math:`Y\equiv\nabla^2\phi`
        and :math:`W \equiv Y^n`.

        - `eqn=1`:

        ==============   ===========================================   =================================   =================
         `units=`         `term='LHS'`                                  `term=1`                            `term=2`
        ==============   ===========================================   =================================   =================
         `'rescaled'`     :math:`\hat{Y} - \hat{\nabla}^2\hat{\pi}`     :math:`\hat{\nabla}^2\hat{\pi}`     :math:`\hat{Y}`
         `'physical'`     :math:`Y - \nabla^2\pi`                       :math:`\nabla^2\pi`                 :math:`Y`
        ==============   ===========================================   =================================   =================


        - `eqn=2`:

        ==============   =============================   ===================   =================
         `units=`         `term='LHS'`                    `term=1`              `term=2`
        ==============   =============================   ===================   =================
         `'rescaled'`     :math:`\hat{W} - \hat{Y}^n`     :math:`\hat{Y}^n`     :math:`\hat{W}`
         `'physical'`     :math:`W - Y^n`                 :math:`Y^n`           :math:`W`
        ==============   =============================   ===================   =================



        - `eqn=3`:


        ==============   ============================================================================================================================================================================   =================================================
         `units=`         `term='LHS'`                                                                                                                                                                   `term='RHS'`
        ==============   ============================================================================================================================================================================   =================================================
         `'rescaled'`     :math:`\hat{Y} -\left(\frac{m}{M_n}\right)^2\hat{\pi} - \epsilon \left( \frac{M_n}{\Lambda} \right)^{3n-1} \left( \frac{M_{f1}}{M_n} \right)^{n-1} \hat{\nabla}^2 \hat{W}`     :math:`\frac{\hat{\rho}}{M_P}\frac{Mn}{M_{f1}}`
         `'physical'`     :math:`Y - m^2 \pi - \frac{\epsilon}{\Lambda^{3n-1}} \nabla^2 W`                                                                                                               :math:`\frac{\rho}{M_P}`
        ==============   ============================================================================================================================================================================   =================================================


        ==============   =================   ================================================   =============================================================================================================================   =================
         `units=`         `term=1`            `term=2`                                           `term=3`                                                                                                                        `term=4`
        ==============   =================   ================================================   =============================================================================================================================   =================
         `'rescaled'`     :math:`\hat{Y}`     :math:`-\left(\frac{m}{M_n}\right)^2\hat{\pi}`     :math:`- \epsilon \left( \frac{M_n}{\Lambda} \right)^{3n-1} \left( \frac{M_{f1}}{M_n} \right)^{n-1} \hat{\nabla}^2 \hat{W}`     same as `'RHS'`
         `'physical'`     :math:`Y`           :math:`-m^2 \pi`                                   :math:`- \frac{\epsilon}{\Lambda^{3n-1}} \nabla^2 W`                                                                            same as `'RHS'`
        ==============   =================   ================================================   =============================================================================================================================   =================




        Equation (3) allows to look at the terms in the equation of motion,
        Eq. (1) and (2) enforce consistency relations :math:`Y = '\nabla^2\pi` and :math:`W=Y^n`. 

        .. note:: In this method, the Laplacian :math:`\hat{\nabla}^2` is obtained by projecting
                  :math:`\frac{\partial^2}{\partial\hat{r}^2} + 2\frac{\partial}{\partial\hat{r}}`.
                  As such, it should not be used with interpolating polynomials of degree less than 2.
            
        *Parameters*
            eqn
                (`integer`) choice of equation
            term
                choice of term: for `eqn=1,2`, valid terms range are `'LHS', 1, 2`; for
                `eqn=3`, valid terms are `'LHS', 'RHS'` and integers from 1 to 4
            norm 
                `'L2'`, `'linf'` or `'none'`. If `'L2'` or `'linf'`: compute the :math:`L_2` or
                :math:`\ell_{\infty}` norm of the residual; if `'none'`, return the full
                term over the box - as opposed to its norm.
            units
                `rescaled` (default) or `physical`; choice of units for the output
            output_label
                if `True`, output a string with a label for the term (which can be used, e.g. in plot legends)
            
        """

        # cast params as constant functions so that, if they are set to 0, FEniCS still understand
        # what is being integrated
        m, Lambda, Mp = Constant(self.fields.m), Constant(
            self.fields.Lambda), Constant(self.fields.Mp)
        epsilon = Constant(self.fields.epsilon)
        Mn, Mf1 = Constant(self.Mn), Constant(self.Mf1)
        n = self.fields.n

        if units == 'rescaled':
            resc_1, resc_2, resc_3 = 1., 1., 1.
            str_pi, str_w, str_y = '\\hat{\pi}', '\\hat{W}', '\\hat{Y}'
            str_nabla2 = '\\hat{\\nabla}^2'
            str_m2 = '\\left( \\frac{m}{M_n} \\right)^2'
            str_nl = '\\epsilon \\left( \\frac{M_n}{\\Lambda} \\right)^{%d} \\left( \\frac{M_{f1}}{M_n} \\right)^{%d} \\hat{\\nabla}^2 \\hat{W}' % (
                3 * n - 1, n - 1)
            str_rho = '\\frac{\hat{\\rho}}{M_p}\\frac{M_n}{M_{f1}}'

        elif units == 'physical':
            resc_1 = self.Mn**2 * self.Mf1
            resc_2 = (self.Mn**2 * self.Mf1)**self.fields.n
            resc_3 = self.Mn**2 * self.Mf1
            #print('********************************************************************************************************************')
            #print('   WARNING: numbers in equation 2 may hit the minimum representable number, consider using rescaled units instead')
            #print('********************************************************************************************************************')
            str_pi, str_w, str_y = '\\pi', 'W', 'Y'
            str_nabla2 = '\\nabla^2'
            str_m2 = 'm^2'
            str_nl = '\\frac{\\epsilon}{\\Lambda^{%d}} \\nabla^2 W' % (3 * n -
                                                                       1)
            str_rho = '\\frac{\\rho}{M_p}'

        else:
            message = "Invalid choice of units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)

        pi, w, y = self.pi, self.w, self.y

        # define r for use in the computation of the Laplacian
        r = Expression('x[0]', degree=self.fem.func_degree)

        if eqn == 1:
            if term == 'LHS':
                Term = y - (pi.dx(0).dx(0) + Constant(2.) / r * pi.dx(0))
                label = r"$%s - %s%s$" % (str_y, str_nabla2, str_pi)
            elif term == 1:
                Term = (pi.dx(0).dx(0) + Constant(2.) / r * pi.dx(0))
                label = r"$%s%s$" % (str_nabla2, str_pi)
            elif term == 2:
                Term = y
                label = r"$%s$" % str_y
            # rescale if needed to get physical units
            Term *= resc_1

        elif eqn == 2:
            if term == 'LHS':
                Term = w - y**n
                label = r"$%s - %s^{%d}$" % (str_w, str_y, n)
            elif term == 1:
                Term = y**n
                label = r"$%s^{%d}$" % (str_y, n)
            elif term == 2:
                Term = w
                label = r"$%s$" % str_w
            # rescale if needed to get physical units
            Term *= resc_2

        elif eqn == 3:
            if term == 'LHS':
                Term = y - ( m/Mn )**2 * pi - \
                       epsilon * ( Mn / Lambda )**(3*n-1) * ( Mf1 / Mn )**(n-1) * ( w.dx(0).dx(0) + Constant(2.)/r * w.dx(0) )
                label = r"$%s - %s%s - %s$" % (str_y, str_m2, str_pi, str_nl)
            elif term == 'RHS':
                Term = self.source.rho / Mp * Mn / Mf1
                label = r"$%s$" % str_rho
            elif term == 1:
                Term = y
                label = r"$%s$" % str_y
            elif term == 2:
                Term = -(m / Mn)**2 * pi
                label = r"$-%s %s$" % (str_m2, str_pi)
            elif term == 3:
                Term = -epsilon * (Mn / Lambda)**(3 * n - 1) * (Mf1 / Mn)**(
                    n - 1) * (w.dx(0).dx(0) + Constant(2.) / r * w.dx(0))
                label = r"$-%s$" % str_nl
            elif term == 4:
                Term = self.source.rho / Mp * Mn / Mf1
                label = r"$%s$" % str_rho
            # rescale if needed to get physical units
            Term *= resc_3

        Term = project(Term, self.fem.dS, self.fem.func_degree)

        # 'none' = return function, not norm
        if norm == 'none':
            result = Term
            # from here on return a norm. This nested if is to preserve the structure of the original
            # built-in FEniCS norm function
        elif norm == 'linf':
            # infinity norm, i.e. max abs value at vertices
            result = r2_norm(Term.vector(),
                             self.fem.func_degree,
                             norm_type=norm)
        else:
            result = r2_norm(Term, self.fem.func_degree, norm_type=norm)

        if output_label:
            return result, label
        else:
            return result
Example #10
0
    def strong_residual_form(self, sol, units):
        r"""
        Computes the residual with respect to the strong form of the equations.

        The total residual is obtained by summing the residuals of all equations:

        .. math:: F = F_1 + F_2 + F_3

        where, in dimensionless in-code units (`units='rescaled'`):

        .. math:: & F_1(\hat{\pi},\hat{W},\hat{Y}) = \hat{\nabla}^2 \hat{\pi} - \hat{Y}

                  & F_2(\hat{\pi},\hat{W},\hat{Y}) = \hat{W} - \hat{Y}^n

                  & F_3(\hat{\pi},\hat{W},\hat{Y}) = \hat{Y} - \left( \frac{m}{M_n} \right)^2 \hat{\pi}
                  - \epsilon \left( \frac{M_n}{\Lambda} \right)^{3n-1}
                  \left(\frac{M_{f1}}{M_n}\right)^{n-1} \hat{\nabla}^2 \hat{W}
                  - \frac{\hat{\rho}}{M_P} \frac{M_n}{M_{f1}}


        and in physical units (`units='physical'`):

        .. math:: & F_1(\pi,W,Y) = \nabla^2\pi - Y

                  & F_2(\pi,W,Y) = W - Y^n

                  & F_3(\pi,W,Y) = Y - m^2 \pi - \frac{\epsilon}{\Lambda^{3n-1}} \nabla^2 W - \frac{\rho}{M_P}



        .. note:: In this function, the Laplacian :math:`\hat{\nabla}^2` is obtained by projecting
                  :math:`\frac{\partial^2}{\partial\hat{r}^2} + 2\frac{\partial}{\partial\hat{r}}`.
                  As such, it should not be used with interpolating polynomials of degree less than 2.

        Note that the weak residual in :func:`weak_residual_form` is just the scalar product
        of the strong residuals by test functions.

        *Parameters*
            sol
                the solution with respect to which the weak residual is computed.
            units
                `'rescaled'` (for the rescaled units used inside the code) or `'physical'`, for physical units

        """

        if units == 'rescaled':
            resc_1, resc_2, resc_3 = 1., 1., 1.
        elif units == 'physical':
            resc_1 = self.Mn**2 * self.Mf1
            resc_2 = (self.Mn**2 * self.Mf1)**self.fields.n
            resc_3 = self.Mn**2 * self.Mf1
        else:
            message = "Invalid choice of units: valid choices are 'physical' or 'rescaled'."
            raise ValueError(message)

        # cast params as constant functions so that, if they are set to 0, FEniCS still understand
        # what is being integrated
        m, Lambda, Mp = Constant(self.fields.m), Constant(
            self.fields.Lambda), Constant(self.fields.Mp)
        epsilon = Constant(self.fields.epsilon)
        Mn, Mf1 = Constant(self.Mn), Constant(self.Mf1)
        n = self.fields.n

        # split solution into pi, w, y
        pi, w, y = d.split(sol)

        # initialise residual function
        F = d.Function(self.dV)

        # define r for use in the computation of the Laplacian
        r = Expression('x[0]', degree=self.fem.func_degree)

        # equation 1
        f1 = pi.dx(0).dx(0) + Constant(2.) / r * pi.dx(0) - y
        f1 *= Constant(resc_1)
        F1 = project(f1, self.fem.dS, self.fem.func_degree)
        # equation 2
        f2 = w - y**n
        f2 *= Constant(resc_2)
        F2 = project(f2, self.fem.dS, self.fem.func_degree)
        # equation 3
        f3 = y - ( m/Mn )**2 * pi \
             - epsilon * ( Mn / Lambda )**(3*n-1) * ( Mf1 / Mn )**(n-1) * (  w.dx(0).dx(0) + Constant(2.)/r * w.dx(0) ) \
             - self.source.rho / Mp * Mn / Mf1
        f3 *= Constant(resc_3)
        F3 = project(f3, self.fem.dS, self.fem.func_degree)

        # combine equations
        fa = d.FunctionAssigner(self.dV,
                                [self.fem.dS, self.fem.dS, self.fem.dS])
        fa.assign(F, [F1, F2, F3])

        return F
Example #11
0
    def NL_initial_guess(self, y_method='vector'):
        r"""
        Obtains an initial guess for the Galileon equation of motion by assuming the nonlinear term is
        dominant, i.e.:

        .. math:: -\frac{\epsilon}{\Lambda^{3n-1}}\nabla^2(\nabla^2\pi^n) \approx \frac{\rho}{M_P}

        The initial guess is computed by first solving the Poisson equation:

        .. math:: -\hat{\nabla}\hat{W} =\left( \frac{\Lambda}{M_n} \right)^{3n-1}
                  \left(\frac{M_n}{M_{f1}}\right)^n \frac{\hat{\rho}}{\epsilon M_P}

        and then obtaining :math:`\hat{Y}=\sqrt[n]{\hat{W}}` through one of three methods explained below.
        Finally, :math:`\hat{\pi}` is computed by solving the Poisson equation

        .. math:: \hat{\nabla}\hat{\pi} = \hat{Y}.

        The main methods to obtain :math:`\hat{Y}` from :math:`\hat{Z}` are interpolation and projection:
        the standard FEniCS implementation for both can be chosen by setting `y\_method='interpolate'` 
        and `y\_method='project'`.

        A third method (`y\_method='vector'`, default), formally identical to interpolation,
        consists in assigning :math:`\hat{Y}`'s value at all nodes through e.g.:

        .. code-block:: python
          
            y.vector().set_local( np.sqrt( np.abs( w.vector().get_local() ) ) )

        However, because of differences in the implementations of :math:`\sqrt[n]{\cdot}` called by
        the two methods, the latter generally gives better results compared to `'interpolate'`.

        *Arguments*
            y_method
                `'vector'` (default), `'interpolate'` or `'project'`

        """

        # cast params as constant functions so that, if they are set to 0, FEniCS still understand
        # what is being integrated
        m, Lambda, Mp = Constant(self.fields.m), Constant(
            self.fields.Lambda), Constant(self.fields.Mp)
        epsilon = Constant(self.fields.epsilon)
        Mn, Mf1 = Constant(self.Mn), Constant(self.Mf1)
        n = self.fields.n

        # get the boundary conditions for w only
        def boundary(x):
            return self.fem.mesh.r_max - x[0] < d.DOLFIN_EPS

        wD = Constant(0.)
        w_Dirichlet_bc = d.DirichletBC(self.fem.S,
                                       wD,
                                       boundary,
                                       method='pointwise')

        # define trial and test function
        w_ = d.TrialFunction(self.fem.S)
        v_ = d.TestFunction(self.fem.S)

        # for the measure
        r2 = Expression('pow(x[0],2)', degree=self.fem.func_degree)

        # bilinear and linear forms
        w_a = inner(grad(w_), grad(v_)) * r2 * dx
        w_L = (Lambda / Mn)**(3 * n - 1) * (
            Mn / Mf1)**n / epsilon * self.source.rho / Mp * v_ * r2 * dx

        # define a function for the solution
        w = d.Function(self.fem.S)

        # solve
        w_pde = d.LinearVariationalProblem(w_a, w_L, w, w_Dirichlet_bc)
        w_solver = d.LinearVariationalSolver(w_pde)
        print('Getting NL initial guess...')
        w_solver.solve()

        # now we have w. we can obtain y by projection or interpolation
        if y_method == 'interpolate':
            # I use the functions sqrt and cbrt because they're more precise than pow(w,1/n)
            if n == 2:
                code = "sqrt(fabs(w))"
            elif n == 3:
                code = "cbrt(w)"
            else:
                if n % 2 == 0:  # even power
                    code = "pow(fabs(w),1./n)"
                else:  # odd power
                    code = "pow(w,1./n)"
            y_expression = Expression(code,
                                      w=w,
                                      n=n,
                                      degree=self.fem.func_degree)
            y = d.interpolate(y_expression, self.fem.S)

        elif y_method == 'vector':
            # this should formally be identical to 'interpolate', but it's a workaround to this
            # potential FEniCS bug which occurs in the previous code block:
            # https://bitbucket.org/fenics-project/dolfin/issues/1079/interpolated-expression-gives-wrong-result
            y = d.Function(self.fem.S)
            if n == 2:
                y.vector().set_local(np.sqrt(np.abs(w.vector().get_local())))
            elif n == 3:
                y.vector().set_local(np.cbrt(np.abs(w.vector().get_local())))
            else:
                if n % 2 == 0:  # even power
                    y.vector().set_local(
                        np.abs(w.vector().get_local())**(1. / self.fields.n))
                else:  # odd power
                    y.vector().set_local(
                        w.vector().get_local()**(1. / self.fields.n))

        elif y_method == 'project':
            y = w**(1. / n)
            y = project(y, self.fem.S, self.fem.func_degree)

        # we obtain pi by solving Del pi = y
        piD = Constant(0.)
        pi_Dirichlet_bc = d.DirichletBC(self.fem.S,
                                        piD,
                                        boundary,
                                        method='pointwise')
        pi_ = d.TrialFunction(self.fem.S)
        v_ = d.TestFunction(self.fem.S)
        pi_a = -inner(grad(pi_), grad(v_)) * r2 * dx
        pi_L = y * v_ * r2 * dx
        pi = d.Function(self.fem.S)
        pi_pde = d.LinearVariationalProblem(pi_a, pi_L, pi, pi_Dirichlet_bc)
        pi_solver = d.LinearVariationalSolver(pi_pde)
        pi_solver.solve()

        # now let's pack pi, w, y into a single function
        guess = d.Function(self.V)
        fa = d.FunctionAssigner(self.V, [self.fem.S, self.fem.S, self.fem.S])
        fa.assign(guess, [pi, w, y])

        return guess
Example #12
0
    def KG_initial_guess(self):
        r"""
        Obtains an initial guess for the Galileon equation of motion by assuming the nonlinear term is
        subdominant, i.e.:

        .. math:: \Box\pi - m^2\pi \approx \frac{\rho}{Mp}

        The initial guess is computed by first solving the system of equations:

        .. math:: & \hat{\nabla}^2\hat{\pi} = \hat{Y} \\
                  & \hat{Y} - \left( \frac{m}{M_n} \right)^2\pi = \frac{\hat{\rho}}{M_p}

        and then obtaining :math:`\hat{W}=\hat{Y}^n` by projection.

        """

        # define a function space for (pi, y) only
        piy_E = d.MixedElement([self.fem.Pn, self.fem.Pn])
        piy_V = d.FunctionSpace(self.fem.mesh.mesh, piy_E)

        # get the boundary conditions for pi and y only
        piD, yD = Constant(0.), Constant(0.)

        def boundary(x):
            return self.fem.mesh.r_max - x[0] < d.DOLFIN_EPS

        bc_pi = d.DirichletBC(piy_V.sub(0), piD, boundary, method='pointwise')
        bc_y = d.DirichletBC(piy_V.sub(1), yD, boundary, method='pointwise')
        Dirichlet_bc = [bc_pi, bc_y]

        # Trial functions for pi and y
        u = d.TrialFunction(piy_V)
        pi, y = d.split(u)

        # test functions for the two equations
        v1, v3 = d.TestFunctions(piy_V)

        # cast params as constant functions so that, if they are set to 0, FEniCS still understand
        # what is being integrated
        m, Mp, Mn, Mf1 = Constant(self.fields.m), Constant(
            self.fields.Mp), Constant(self.Mn), Constant(self.Mf1)
        n = self.fields.n

        # r^2
        r2 = Expression('pow(x[0],2)', degree=self.fem.func_degree)

        # bilinear form
        a1 = -inner(grad(pi), grad(v1)) * r2 * dx - y * v1 * r2 * dx
        a3 = y * v3 * r2 * dx - (m / Mn)**2 * pi * v3 * r2 * dx
        a = a1 + a3

        # linear form (L1=0)
        L3 = self.source.rho / Mp * Mn / Mf1 * v3 * r2 * dx
        L = L3

        # solve system
        sol = d.Function(piy_V)
        pde = d.LinearVariationalProblem(a, L, sol, Dirichlet_bc)
        solver = d.LinearVariationalSolver(pde)
        print('Getting KG initial guess...')
        solver.solve()

        # split solution into pi and y - cast as independent functions, not components of a vector function
        pi, y = sol.split(deepcopy=True)

        # obtain w by projecting y**n
        w = y**n
        w = project(w, self.fem.S, self.fem.func_degree)

        # and now pack pi, w, y into one function...
        guess = d.Function(self.V)
        # this syntax is because, even though pi and y are effectively defined on fem.S, from fenics point
        # of view, they are obtained as splits of a function
        fa = d.FunctionAssigner(
            self.V, [pi.function_space(), self.fem.S,
                     y.function_space()])
        fa.assign(guess, [pi, w, y])

        return guess