geff.bgtypes

This module defines base classes used throughout the GEFF module.

The main purpose of these classes is to address the following. Most quantities appearing in the GEF will scale according to an inverse-time scale, $\omega$, and an energy scale, $\mu$. For example, a time derivative scales with inverse time, $\partial_t \sim \omega$. Consequently, also a gauge field, $A_\mu \sim \omega$, as it appears in covariant derivatives. On the other hand, the amplitude of a scalar field scales with energy, $\varphi \sim \mu$. In typical inflationary contexts, $\omega \equiv H_0$, some constant Hubble rate, and $\mu \equiv M_{\rm P}$, the Planck mass.

For numerical applications, it is convenient to work with dimensionless quantities. For example, it is useful to perform numerical computations using the dimensionless scalar amplitude $\bar{\varphi} = \varphi/\mu$, or the dimensionless gauge field, $\bar{A}_\mu = A_\mu / \omega$. Ultimately, the quantities we are interested in are obviously $\varphi$, $A_\mu$, etc. Therefore, we need an easy way to switch between $\bar{X}$ and $X$.

Throughout the code, we refer to the dimensionless variable, $\bar{X}$, as being in numerical units, while $X$ is in physical units.

To facilitate switching between these two unit systems throughout the code, this module provides the classes BGSystem, Variable, Constant, and Func. The latter three are collectively referred to as Quantity objects. Each Quantity object is defined by a scaling with $\omega$ and $\mu$. For example, the Quantity $X$ scales as $X = \omega^a \mu^b \bar{X}$, where $\bar{X}$ is the re-scaled quantity used for numerical computations. A BGSystem is a collection of Quantity objects, which defines a common unit system by setting the value of $\omega$ and $\mu$.

The user may define variables that evolve with cosmic time using the define_var class factory, which creates subclasses of Variable. Examples of a Variable are the Hubble rate, scalar field amplitude etc. In the same manner, the user can define constants of cosmic time using the define_const class factory, which creates subclasses of Constant. Examples of a Constant are, e.g., coupling strengths. Some quantities are functions of variables, for example, a scalar potential. These are defined by the factory define_func, which creates subclasses of Func.

The following examples illustrates the basic use of these classes:

Examples
  1. Defining a BGSystem
from GEFF.bgtypes import BGSystem, define_var

# define two variables corresponding to physical time and Hubble rate.
time = define_var("t", qu_omega=-1, qu_mu=0)
Hubble = define_var("H", qu_omega=1, qu_mu=0)

# Create a BGSystem with 'time' and 'Hubble'
# We set the reference frequency to 1e-5*Mpl
# The reference energy is the Planck mass in Planck units (so 1)
U = BGSystem({time, Hubble}, omega=1e-5, mu=1)

# The BGSystem knows about the new variables time and Hubble
print(U.quantity_names()) # prints ["t", "H"]

# However, neither the constant nor the variable are instantiated 
# (we did not specify their value)
print(U.variable_names()) # prints []  

# Instantiate the quantities t and H using the units defined by U:
U.initialise("t")(1e5)
U.initialise("H")(1e-5)
print(U.variable_names()) # prints ["t", "H"] 

# The BGSystem now recognizes "t" and "H" as keys:
print(f"In Planck units, the Hubble rate at time {U.t.value:.1e} is {U.H.value:.1e}.")
# gives U.t=1e5, U.H=1e-5

# Convert everything to numerical units
U.units = False
print(f"In numerical units, the Hubble rate at time {U.t.value} is {U.H.value:.1e}.") 
# gives U.t=1, U.H=1
  1. Adding a new Variable
#Let us reset the units of U:
U.units = True

# add a new Val: the electric-field expectation value E^2
U.add_variable("E0", qu_omega=4, qu_mu=0) #since A_mu scales like d / dx^mu 

# initialise E0 with some value in Planck units:
U.initialise("E0")( 6e-10 )
  1. Operations between Variable's
# We add a new Variable to U, the magnetic-field expectation value
U.add_variable("B0", 4, 0)
U.initialise("B0")(1e-10)

# We can safely add up E0 and B0 as they are in the same units:
print(U.E0 + U.B0) #gives 7e-10 = 6e-10 + 1e-10

# This behaviour is not exclusive to Variables,
# but also works for Constants:
U.add_constant("BConst", 4, 0)
U.initialise("BConst")(5e-11)

print(U.E0 + U.BConst) #gives 6.5e-10 = 6e-10 + 5e-11
  1. Changing the value of a Variable
# The value of a `Variable` can be passed directly
U.BConst.value = 1e-3
print(str(U.BConst))

# if we change to numerical units,
# the argument which is passed is treated in numerical units
U.units = False
U.BConst.value = 1
print(U.BConst)
  1. Adding a new Func
#first, return to physical units
U.units = True

# define a new Func: rhoE, the electric-field energy density
rhoE = U.add_function("rhoE", args=[U.E0], qu_omega=2, qu_mu=2) # since 3 * M_pl^2 * H^2 = rho
#Note how E0 is passed for creation to indicate the scaling of the argument

# define rhoE as a function of E0:
def func(x): return 0.5*x
U.initialise("rhoE")( func )
# U.rhoE is now a Callable function with a single argument
  1. Calling a Func with a Val
# Calling rhoE in physical units is straight forwards:
print( U.rhoE( U.E0 ) ) # gives 3e-10 (in Planck units)

# compare this to a direct call to func:
print( func(6e-10) )  # gives 3e-10 (the result in Planck units)

# Switching E0 to numerical units, nothing changes since rhoE is in physical units:
U.E0.units = False # E0.value = 6e10
print( U.rhoE(U.E0) ) # gives 3e-10 (still in Planck units)

# Only switching U.rhoE to numerical units changes the result:
U.rhoE.units = False
print( U.rhoE(U.E0) ) # gives 3. = 3e-10 / (U.omega*U.mu)**2 (now in numerical units)

# Again, this outcome does not depend on the units of E0:
U.E0.units = False
print( U.rhoE(U.E0) ) # gives 3. = 3e-10 / (U.omega*U.mu)**2 (in numerical units)
  1. Calling a Func with a float
# instead of calling rhoE by E0, we can call it by a float:
val = 6e-10

# First the behavior if rhoE is in physical units:
U.rhoE.units = True
print( U.rhoE( val ) ) # gives 3e-10 (in Planck units)

# The behavior is different compared to a Val if rhoE is in numerical units:
U.rhoE.units = False
print( U.rhoE(val) ) #gives 3e-20 = 0.5* (6e-10*U.omega**4) / (U.omega*U.mu)**2

# Unlike a Val, the float does not track units
# Therefore, it is always assumed to be in the units of rhoE
# If you want to get the correct result, you would need to convert val by hand:
print( U.rhoE(val/U.omega**4) ) # gives 3., the expected result in numerical units.

# Overall, its safer to just keep everything in the same units:
U.units = True
  1from ._docs import generate_docs, docs_bgtypes
  2import numpy as np
  3from typing import Callable, ClassVar
  4import pandas as pd
  5from copy import deepcopy
  6
  7class BGSystem:
  8    def __init__(self, quantity_set : set, omega : float, mu : float):
  9        """
 10        Create a new BGSystem in physical units.
 11
 12        Parameters
 13        ----------
 14        quantity_set : set of Quantity
 15            used to define `quantities`
 16        omega : float
 17            the characteristic frequency scale
 18        mu : float
 19            the characteristic energy scale
 20        """
 21        self.quantities : dict = {q.name:q for q in quantity_set}
 22        """A dictionary of all `Quantity` objects for this BGSystem"""
 23        self._omega : float = omega
 24        self._mu : float = mu
 25        self._units=True
 26
 27    @property
 28    def omega(self) -> float:
 29        """A frequency scale (typically the Hubble rate at some reference time)"""
 30        return self._omega
 31    
 32    @property 
 33    def mu(self) -> float:
 34        """An energy scale (typically the Planck mass)"""
 35        return self._mu
 36
 37    @property
 38    def units(self) -> bool:
 39        """Indicates the current units of the BGSystem. `True`:physical units, `False`:numerical units"""
 40        return self._units
 41    
 42    @units.setter
 43    def units(self, newunits:bool):
 44        """Change the units of the BGSystem and its `Quantity` instances."""
 45        for var in vars(self):
 46            obj = getattr(self, var)
 47            if isinstance(obj, Quantity):
 48                obj.units = newunits
 49        self._units=bool(newunits)
 50        return
 51
 52    
 53    @classmethod
 54    def from_system(cls, sys : 'BGSystem', copy : bool=False) -> 'BGSystem':
 55        """Initialize a new `BGSystem` from an existing instance.
 56
 57        The new instance is created with the same quantities, reference frequency and amplitude as the original.
 58        If specified, the `Quantity` instances are also copied to the new BGSystem
 59
 60        Parameters
 61        ----------
 62        sys : BGSystem
 63            the original instance used as a template
 64        copy : Boolean
 65            `True` if `Quantity` instances are also copied
 66        Returns
 67        -------
 68        newinstance : BGSystem
 69            the new instance
 70        """
 71
 72        newinstance = cls(sys.quantity_set(), sys.omega, sys.mu)
 73
 74        if copy:
 75            #store units of original sys
 76            units = sys.units
 77            #match units of new system
 78            sys.units = True
 79
 80            #Copy values and functions
 81            values = sys.variable_list()
 82            funcs = sys.function_list()
 83            consts = sys.constant_list()
 84
 85            for const in consts:
 86                obj = const
 87                newinstance.initialise(obj.name)(obj.value)
 88
 89            for value in values:
 90                obj = value
 91                newinstance.initialise(obj.name)(obj.value)
 92
 93            for func in funcs:
 94                obj = func
 95                newinstance.initialise(obj.name)(obj.basefunc)
 96            
 97            #restore old units
 98            sys.units = units
 99        
100        return newinstance
101    
102    def quantity_set(self) -> set[object]:
103        """
104        Get a set of all `Quantity` objects attributed to this BGSystem.
105
106        Returns
107        -------
108        set : set
109            a set of objects.
110        """
111
112        return set(self.quantities.values())
113    
114    def quantity_names(self) -> list[str]:
115        """
116        Get a list of names for all `Quantity` objects attributed to this BGSystem.
117
118        Returns
119        -------
120        names : list of str
121            the list of names.
122        """
123
124        return list(self.quantities.keys())
125    
126
127    def initialise(self, quantity : str) -> Callable:
128        """
129        Instantiate a `Quantity` object from `quantities`.
130
131        The method creates a function `init` which can be called by
132        an arithmetic type / `Callable` to instantiate a `Val` / `Func`.
133        Calling `init` adds and instance of the `Quantity` as a new attribute to the BGSystem,
134        with the attribute name corresponding to the object's `name` attribute.   
135
136        Parameters
137        ----------
138        quantity : str
139            the name of the object which is to be instantiated.
140
141        Returns
142        -------
143        init : Callable
144            a function used to initialize the `Quantity` object
145        """
146
147        def init(obj : np.ndarray | Callable):
148            """
149            Initialize a `Quantity` object with an arithmetic type / Callable.
150
151            This adds an instance of the `Quantity` as a new attribute to the `BGSystem`, with the attribute name
152            corresponding to the `Quantity` object's `name` attribute. 
153
154            Parameters
155            ----------
156            obj : NDArray or Callable
157                the NDArray / Callable with which the `Quantity` is to be instantiated.
158            """
159
160            q = self.quantities[quantity]
161            setattr( self, quantity, q(obj, self) )
162            return
163        
164        return init
165    
166    def variable_list(self) -> list['Variable']:
167        """
168        Get a list of all `Variable` instances attributed to this BGSystem.
169
170        Returns
171        -------
172        vals : list of Variable
173            the list of `Variable` instances.
174        """
175
176        vals = []
177        for var in vars(self):
178            obj = getattr(self, var)
179            if isinstance(obj, Variable):
180                vals.append(obj)    
181        return vals
182    
183    def variable_names(self) -> list[str]:
184        """
185        Get a list of names for all `Variable` instances attributed to this BGSystem.
186
187        Returns
188        -------
189        names : list of str
190            the list of names.
191        """
192
193        names = []
194        for val in self.variable_list():
195            names.append(val.name)
196        return names
197    
198    def constant_list(self) -> list['Constant']:
199        """
200        Get a list of all `Constant` instances attributed to this BGSystem.
201
202        Returns
203        -------
204        vals : list of Val
205            the list of `Constant` instances.
206        """
207
208        vals = []
209        for var in vars(self):
210            obj = getattr(self, var)
211            if isinstance(obj, Constant):
212                vals.append(obj)    
213        return vals
214    
215    def constant_names(self) -> list[str]:
216        """
217        Get a list of names for all `Constant` instances attributed to this BGSystem.
218
219        Returns
220        -------
221        names : list of str
222            the list of names.
223        """
224
225        names = []
226        for val in self.constant_list():
227            names.append(val.name)
228        return names
229
230    def function_list(self) -> list['Func']:
231        """
232        Get a list of all `Func` instances attributed to this BGSystem.
233
234        Returns
235        -------
236        funcs : list of Func
237            the list of `Func` instances.
238        """
239
240        funcs = []
241        for var in vars(self):
242            obj = getattr(self, var)
243            if isinstance(obj, Func):
244                    funcs.append(obj)      
245        return funcs
246    
247    def function_names(self) -> list[str]:
248        """
249        Get a list of names for all `Func` instances attributed to this BGSystem.
250
251        Returns
252        -------
253        names : list of str
254            the list of names.
255        """
256
257        names = []
258        for val in self.function_list():
259            names.append(val.name)
260        return names
261    
262    def remove(self, name : str):
263        """
264        Remove a `Quantity` object and its instance from the BGSystem.
265
266        Parameters
267        ----------
268        name : str
269            the name of the object
270        """
271
272        delattr(self, name)
273        self.quantities.pop(name)
274        return
275    
276    def add_variable(self, name : str, qu_omega : int, qu_mu : int):
277        """
278        Define a new `Variable` object and add it to `quantities`.
279
280        Parameters
281        ----------
282        name : str
283            the name of the new object.
284        qu_omega : int
285            the 'u_omega' parameter of the new object.
286        qu_mu : int
287            the 'u_mu' parameter of the new object.
288        """
289
290        self.quantities[name] = define_var(name, qu_omega, qu_mu)
291        return
292    
293    def add_constant(self, name : str, qu_omega : int, qu_mu : int):
294        """
295        Define a new `Constant` object and add it to `quantities`.
296
297        Parameters
298        ----------
299        name : str
300            the name of the new object.
301        qu_omega : int
302            the 'u_omega' parameter of the new object.
303        qu_mu : int
304            the 'u_mu' parameter of the new object.
305        """
306
307        self.quantities[name] = define_const(name, qu_omega, qu_mu)
308        return
309    
310    def add_function(self, name : str, args : list['Val'], qu_omega : int, qu_mu : int):
311        """
312        Define a new `Func` object and add it to `quantities`.
313
314        Parameters
315        ----------
316        name : str
317            the name of the new object.
318        args : list of BGVal
319            the 'args' parameter of the new object.
320        qu_omega : int
321            the 'u_omega' parameter of the new object.
322        qu_mu : int
323            the 'u_mu' parameter of the new object.
324        """
325
326        self.quantities[name] = define_func(name, args, qu_omega, qu_mu)
327        return
328    
329    def save_variables(self, path : str):
330        """
331        Save the data in the current GEF instance in an output file.
332
333        Note, data is always stored in numerical units.
334        The save will not store constants or functions, only variables.
335
336        Parameters
337        ----------
338        path : str
339            Path to store data in.
340
341        Raises
342        ------
343        ValueError
344            if the GEF object has no data to store.
345        """
346
347        storeables = self.variable_names()    
348        #Check that all dynamic and derived quantities are initialised in this GEF instance
349        if len(storeables) == 0:
350            raise ValueError("No data to store.")
351        
352        #Create a dictionary used to initialise the pandas DataFrame
353        dic = {}
354
355        #remember the original units of the GEF
356        og_units=self.units
357
358        #Data is always stored unitless
359        self.units = False
360
361        for key in storeables:
362            dic[key] = getattr(self, key).value
363        
364        #Create pandas data frame and store the dictionary under the user-specified path
365        output_df = pd.DataFrame(dic)  
366        output_df.to_csv(path)
367
368        #after storing data, restore original units
369        self.units = og_units
370        return
371    
372    def load_variables(self, path : str):
373        """
374        Load data and store its results in the BGSystem.
375
376        Note, data is always loaded assuming numerical units.
377        Data is only loaded for variables, not for functions or constants.
378
379        Parameters
380        ----------
381        path : None or str
382            Path to load data from
383
384        Raises
385        ------
386        FileNotFoundError
387            if no file is found at `path`.
388        AttributeError
389            if the file contains a column labeled by a key which does not match any BGSystem variable.
390        """
391        #Check if file exists
392        try:
393            #Load from file
394            input_df = pd.read_table(path, sep=",")
395        except FileNotFoundError:
396            raise FileNotFoundError(f"No file found under '{path}'")
397        
398        #Dictionary for easy access using keys
399
400        data = dict(zip(input_df.columns[1:],input_df.values[:,1:].T))
401
402        #Before starting to load, check that the file is compatible with the GEF setup.
403        names = self.quantity_names()
404        for key in data.keys():
405            if key not in names:
406                raise AttributeError(f"The data table you tried to load contains an unknown quantity: '{key}'")
407        
408        #Store current units to switch back to later
409        og_units=self.units
410
411        #GEF data is always stored untiless, thus it is assumed to be untiless when loaded.
412        self.units = False
413        #Load data into background-value attributes
414        for key, values in data.items():
415            if key in self.variable_names():
416                getattr(self, key).value = values
417            else: 
418                self.initialise(key)(values)
419
420        self.units = og_units
421        return
422
423    
424class Quantity:
425    name : ClassVar[str]= ""
426    """The objects name"""
427    description : ClassVar[str]= ""
428    """A brief description of the object"""
429    u_omega : ClassVar[int] = 0
430    """Indicates how the object scales with frequency."""
431    u_mu : ClassVar[int] = 0
432    """Indicates how the object scales with energy."""
433
434    def __init__(self, sys : BGSystem):
435        """
436        Create a new Quantity as part of a BGSystem
437
438        The units of the new object matches those of the BGSystem
439
440        Parameters
441        ----------
442        sys : BGSystem
443            the BGSystem to which the object belongs
444        """
445
446        self._units = sys.units
447        self._conversion = (sys.omega**self.u_omega*sys.mu**self.u_mu)
448
449    def __repr__(self):
450        r"""A string representing the class, giving its name and scaling with frequency ($\omega$) and energy ($\mu$)."""
451        return f"{self.name}({self.u_omega},{self.u_mu})"
452
453    def __str__(self) -> str:
454        """The class instance as a string including its name and current units."""
455
456        if not(self._units):
457            return f"{self.name} (numerical)"
458        elif self._units:
459            return f"{self.name} (physical)"
460    
461    
462    @property
463    def units(self) -> bool:
464        """Indicates the current units of the Quantity. `True`:physical units, `False`:numerical units"""
465        return self._units
466    
467    @units.setter
468    def units(self, units : bool):
469        """Convert the object between numerical and physical units."""
470        self._units = bool(units)
471        return
472    
473    @property
474    def conversion(self) -> float:
475        """A conversion factor between numerical and physical units."""
476        return self._conversion
477    
478    @classmethod
479    def get_description(cls) -> str:
480        """Return a string describing the object."""
481        if cls.description=="":
482            return f"{cls.name}"
483        else:
484            return f"{cls.name} - {cls.description}"
485        
486
487    
488class Val(Quantity):
489    def __init__(self, value : np.ndarray|float, sys : BGSystem):
490        """
491        Create a new instance using a BGSystem.
492
493        Parameters
494        ----------
495        value : NDArray
496            the underlying array of the instance
497        sys : BGSystem
498            the BGSystem to which the instance belongs
499        """
500        super().__init__(sys)
501        self._value =  value*self._conversion**(-sys.units)
502
503    def __str__(self) -> str:
504        """
505        The class instance as a string including its name, current units, and its value.
506
507        Returns
508        -------
509        str
510            the string representation.
511        """
512
513        if not(self._units):
514            return f"{self.name} (numerical): {self.value}"
515        elif self._units:
516            return f"{self.name} (physical): {self.value}"
517    
518    @property
519    def value(self) -> float:
520        """The objects value in its respective units."""
521        return self._value*self._conversion**self._units
522    
523    @value.setter
524    def value(self, newval):
525        """Overwrite the `value` attribute (assuming its current units)."""
526        self._value = newval*self._conversion**(-self._units)
527        return
528    
529    #Define all mathematical operations as operations acting on self.value
530    def __abs__(self):
531        return abs(self.value)
532    
533    def __neg__(self):
534        return -self.value
535    
536    def __pos__(self):
537        return +self.value
538    
539    def __add__(self, other):
540        #self.__Compatible(self, other, "+")
541        return self.value + other
542        
543    __radd__ = __add__
544    
545    def __sub__(self, other):
546        #self.__Compatible(self, other, "-")
547        return self.value - other
548        
549    def __rsub__(self, other):
550        return other - self.value
551
552    def __mul__(self, other):
553        return self.value * other
554        
555    __rmul__ = __mul__
556    
557    def __floordiv__(self, other):
558        return self.value // other
559        
560    def __rfloordiv__(self, other):
561        return other // self.value
562    
563    def __truediv__(self, other):
564        return self.value / other
565        
566    def __rtruediv__(self, other):
567        return other / self.value
568    
569    def __mod__(self, other):
570        return self.value % other
571    
572    def __pow__(self, other):
573        #BGVal should never be exponentiated by another BGVal
574        return self.value ** other
575    
576    def __eq__(self, other):
577        #self.__Compatible(self, other, "==")
578        return self.value == other
579            
580    def __ne__(self, other):
581        #self.__Compatible(self, other, "!=")
582        return self.value != other
583    
584    def __lt__(self, other):
585        #self.__Compatible(self, other, "<")
586        return self.value < other
587
588    def __gt__(self, other):
589        #self.__Compatible(self, other, ">")
590        return self.value > other
591
592    def __le__(self, other):
593        #self.__Compatible(self, other, "<=")
594        return  self.value <= other
595
596    def __ge__(self, other):
597        #self.__Compatible(self, other, ">=")
598        return  self.value >= other
599    
600
601class Func(Quantity):
602    args : ClassVar[list[Val]] = []
603    """Indicates the argument signature for the class."""
604    dtype : ClassVar[np.floating] = np.float64
605    """The data type returned by `__call__`."""
606
607    def __init__(self, func : Callable, sys : BGSystem):
608        """
609        Create a new instance using a `Callable` and a BGSystem
610
611        Parameters
612        ----------
613        func : NDArray
614            the underlying function `f(*args)` of the instance
615        sys : BGSystem
616            the BGSystem to which the instance belongs
617
618        Raises
619        ------
620        TypeError
621            if the number of arguments of `func` do not match `args`
622        ValueError
623            if the return of`func` can't be converted to `dtype`
624        """
625        super().__init__(sys)
626        func = np.vectorize(func, otypes=[self.dtype])
627        
628        try:
629            testargs = [1.0 for arg in self.args]
630            assert func(*testargs).dtype
631        except TypeError:
632            raise TypeError("The number of non-default arguments of 'func' needs to match 'len(self.args)'.")
633        except ValueError:
634            raise ValueError(f"'func' must return a single value which can be converted to '{self.dtype}'")
635        
636        self._basefunc = func
637
638        self._arg_conversions = [(sys.omega**arg.u_omega*sys.mu**arg.u_mu)
639                                 for arg in self.args]
640        
641    @property
642    def basefunc(self) -> Callable:
643        """The underlying function which defines the `__call__` method."""
644        return self._basefunc
645    
646    def get_arg_conversions(self) -> list[float]:
647        """
648        Get a list of conversion factors for each argument of `basefunc`.
649
650        Returns
651        -------
652        arg_conversions : list of float
653            a list of conversion factors
654        """
655
656        return self._arg_conversions
657    
658    def __call__(self, *args):
659        """
660        Define the call method of the class as outlined in its documentation.
661        """
662        def float_handler(x, i):
663            return x*self._arg_conversions[i]**(1-self._units)
664        
665        def val_handler(x, i):
666            conv = x.conversion
667            assert self._arg_conversions[i] == conv
668            return x*conv**(1-x.units)
669
670        typedic = {Variable : val_handler, Val : val_handler, Constant: val_handler}
671
672        args = [typedic.get(arg.__class__.__bases__[0], float_handler)(arg, i) for i, arg in enumerate(args)]
673
674        return self._basefunc(*args)/self._conversion**(1-self._units)
675
676    
677class Variable(Val):
678    dtype : ClassVar[np.floating] = np.float64
679    """The data type of `value`."""
680    
681    def __init__(self, value : np.ndarray, sys : BGSystem):
682        """
683        Create a new instance using a numpy array and a BGSystem
684
685        Parameters
686        ----------
687        value : NDArray
688            the underlying array of the instance
689        sys : BGSystem
690            the BGSystem to which the instance belongs
691        """
692        super().__init__( np.asarray(value, dtype=self.dtype), sys)
693
694    #The following methods ensure that a `Val` instance can be used as an array concerning mathematical operations and indexing.
695    
696    def __getitem__(self, idx):
697        return self.value[idx]
698    
699    def __len__(self):
700        return len(self.value)
701    
702    @property
703    def value(self) -> np.ndarray:
704        """The objects value in its respective units."""
705        return self._value*self._conversion**(self._units)
706
707    @value.setter
708    def value(self, newval):
709        """Overwrite the `value` attribute ensuring it is an array"""
710        self._value = np.asarray(newval*self._conversion**(-self._units), dtype=self.dtype)
711        return
712     
713class Constant(Val):
714    def __init__(self, value, sys : BGSystem):
715        """
716        Create a new instance using a float and a BGSystem
717
718        Parameters
719        ----------
720        value : NDArray
721            the underlying array of the instance
722        sys : BGSystem
723            the BGSystem to which the instance belongs
724
725        Raises
726        ------
727        TypeError 
728            if value cannot be converted to a float
729        """
730        super().__init__( float(value), sys)
731
732class GaugeField:
733    name : ClassVar[str] = ""
734    """The name of the class."""
735    zeros : ClassVar[list[Variable]]= []
736    r"""A list of the associated 0$^{\rm th}$ order quantities."""
737    cutoff : Variable = None
738    """The associated UV cutoff scale."""
739        
740    @classmethod
741    def get_description(cls) -> str:
742        """Return a string describing the object."""
743        return f"{cls.name} - associated with: {[a.name for a in cls.zeros]}, UV cutoff: {cls.cutoff.name}"
744    
745def define_var(qname : str, qu_omega : int, qu_mu : int, qdescription:str="", qdtype : np.dtype=np.float64):
746    """
747    Creates a subclass of `Variable` with custom name, and scaling.
748
749    Parameters
750    ----------
751    qname : str
752        the `name` attribute of the subclass
753    qu_omega : int
754        the `u_omega` attribute of the subclass
755    qu_mu : int
756        the `u_mu` attribute of the subclass
757    qdescription : str
758        a brief description of the subclass
759    qdtype : Numpy Data Type
760        the `dtype` attribute of the subclass
761    
762        
763    Returns
764    -------
765    CustomVar : class
766        the custom subclass
767
768    Raises
769    ------
770    TypeError
771        if the data type is not a subtype of `numpy.floating`
772    """
773
774    if not( np.issubdtype(qdtype, np.floating) ):
775        raise TypeError("BGVal's data-type must be a subtype of 'numpy.floating'.")
776
777    class CustomVar(Variable):
778        __doc__ = docs_bgtypes.DOCS["define_var.CustomVar"]
779        name=qname
780        u_omega = qu_omega
781        u_mu = qu_mu
782        dtype = qdtype
783        description = qdescription
784        def __init__(self, value, sys):
785            super().__init__(value, sys)
786    CustomVar.__qualname__ = f"Val_{qname}"
787    CustomVar.__module__ = __name__
788
789    return CustomVar
790
791def define_const(qname : str, qu_omega : int, qu_mu : int, qdescription:str=""):
792    """
793    Creates a subclass of `Constant` with custom name, and scaling.
794
795    Parameters
796    ----------
797    qname : str
798        the `name` attribute of the subclass
799    qu_omega : int
800        the `u_omega` attribute of the subclass
801    qu_mu : int
802        the `u_mu` attribute of the subclass
803    qdescription : str
804        a brief description of the subclass
805        
806    Returns
807    -------
808    CustomConst : class
809        the custom subclass
810    """
811
812    class CustomConst(Constant):
813        __doc__ = docs_bgtypes.DOCS["define_const.CustomConst"]
814        name=qname
815        u_omega = qu_omega
816        u_mu = qu_mu
817        description = qdescription
818        def __init__(self, value, sys):
819            super().__init__(value, sys)
820    CustomConst.__qualname__ = f"Const_{qname}"
821    CustomConst.__module__ = __name__
822
823    return CustomConst
824
825
826
827def define_func(qname : str, func_args : list[Val], qu_omega : int, qu_mu : int, qdescription:str="", qdtype : np.dtype=np.float64):
828    """
829    Creates a subclass of `Func` with custom name, scaling, and argument signature.
830
831    Parameters
832    ----------
833    qname : str
834        the `name` attribute of the subclass
835    qu_omega : int
836        the `u_omega` attribute of the subclass
837    qu_mu : int
838        the `u_mu` attribute of the subclass
839    qdtype : Numpy Data Type
840        the `dtype` attribute of the subclass
841    qdescription : str
842        a brief description of the subclass
843
844    Returns
845    -------
846    CustomFunc : class
847        the custom subclass
848
849    Raises
850    ------
851    TypeError
852        if the data type is not a subtype of `numpy.floating`
853    """
854
855    if not( np.issubdtype(qdtype, np.floating) ):
856        raise TypeError("define_func's data-type must be a subtype of 'np.floating'.")
857    
858    class CustomFunc(Func):
859        __doc__ = docs_bgtypes.DOCS["define_func.CustomFunc"]
860        name=qname
861        u_omega = qu_omega
862        u_mu = qu_mu
863        args = func_args
864        dtype = qdtype
865        description = qdescription
866        def __init__(self, func, sys):
867            super().__init__(func, sys)
868
869    CustomFunc.__qualname__ = f"Func_{qname}"
870    CustomFunc.__module__ = __name__
871
872    return CustomFunc
873
874def define_gauge(qname : str, qzeros : list[Variable], qcutoff : Variable):
875    """
876    A class factory creating custom  `GaugeField` classes with new name, zero variables, and cutoff scale.
877
878    Parameters
879    ----------
880    qname : str
881        the `name` attribute of the subclass
882    qzeros : list of Variable
883        the `zeros` attribute of the new subclass 
884    qcutoff : Variable
885        the `cutoff` attribute of the new subclass
886
887    Returns
888    -------
889    CustomGaugeField : class
890        the custom subclass
891    """
892    class CustomGaugeField(GaugeField):
893        name = qname
894        zeros = qzeros
895        cutoff = qcutoff
896        
897    CustomGaugeField.__qualname__ = f"GaugeField_{qname}"
898    CustomGaugeField.__module__ = __name__
899    return CustomGaugeField
900
901#Add docstrings
902generate_docs(docs_bgtypes.DOCS)
903    
904#Some usful pre-defined quantities
905#Space--time variables:
906t=define_var("t", -1, 0, "cosmic time")
907N=define_var("N", 0, 0, "e-folds")
908a=define_var("a", 0, 0, "scale factor")
909H=define_var("H", 1, 0, "Hubble rate")
910
911#Inflaton  variables:
912phi=define_var("phi", 0, 1, "inflaton expectation value")
913dphi=define_var("dphi", 1, 1, "inflaton velocity")
914ddphi=define_var("ddphi", 2, 1, "inflaton acceleration")
915
916#Inflaton potential
917V=define_func("V", [phi], 2, 2, "scalar potential")
918dV=define_func("dV", [phi], 2, 2, "scalar-potential derivative")
919
920#Gauge-field variables:
921E=define_var("E", 4, 0, "electric-field expectation value, E^2")
922B=define_var("B", 4, 0, "magnetic-field expectation value, B^2")
923G=define_var("G", 4, 0, "Chern-Pontryagin expectation value, -E.B")
924
925#Auxiliary quantities:
926xi=define_var("xi", 0, 0, "instability parameter")
927kh=define_var("kh", 1, 0, "instability scale")
928
929#constants
930beta=define_const("beta", 0, -1, "inflaton--gauge-field coupling beta/Mp")
931
932#basic gauge-field
933GF = define_gauge("GF", [E, B, G], kh)
class BGSystem:
  8class BGSystem:
  9    def __init__(self, quantity_set : set, omega : float, mu : float):
 10        """
 11        Create a new BGSystem in physical units.
 12
 13        Parameters
 14        ----------
 15        quantity_set : set of Quantity
 16            used to define `quantities`
 17        omega : float
 18            the characteristic frequency scale
 19        mu : float
 20            the characteristic energy scale
 21        """
 22        self.quantities : dict = {q.name:q for q in quantity_set}
 23        """A dictionary of all `Quantity` objects for this BGSystem"""
 24        self._omega : float = omega
 25        self._mu : float = mu
 26        self._units=True
 27
 28    @property
 29    def omega(self) -> float:
 30        """A frequency scale (typically the Hubble rate at some reference time)"""
 31        return self._omega
 32    
 33    @property 
 34    def mu(self) -> float:
 35        """An energy scale (typically the Planck mass)"""
 36        return self._mu
 37
 38    @property
 39    def units(self) -> bool:
 40        """Indicates the current units of the BGSystem. `True`:physical units, `False`:numerical units"""
 41        return self._units
 42    
 43    @units.setter
 44    def units(self, newunits:bool):
 45        """Change the units of the BGSystem and its `Quantity` instances."""
 46        for var in vars(self):
 47            obj = getattr(self, var)
 48            if isinstance(obj, Quantity):
 49                obj.units = newunits
 50        self._units=bool(newunits)
 51        return
 52
 53    
 54    @classmethod
 55    def from_system(cls, sys : 'BGSystem', copy : bool=False) -> 'BGSystem':
 56        """Initialize a new `BGSystem` from an existing instance.
 57
 58        The new instance is created with the same quantities, reference frequency and amplitude as the original.
 59        If specified, the `Quantity` instances are also copied to the new BGSystem
 60
 61        Parameters
 62        ----------
 63        sys : BGSystem
 64            the original instance used as a template
 65        copy : Boolean
 66            `True` if `Quantity` instances are also copied
 67        Returns
 68        -------
 69        newinstance : BGSystem
 70            the new instance
 71        """
 72
 73        newinstance = cls(sys.quantity_set(), sys.omega, sys.mu)
 74
 75        if copy:
 76            #store units of original sys
 77            units = sys.units
 78            #match units of new system
 79            sys.units = True
 80
 81            #Copy values and functions
 82            values = sys.variable_list()
 83            funcs = sys.function_list()
 84            consts = sys.constant_list()
 85
 86            for const in consts:
 87                obj = const
 88                newinstance.initialise(obj.name)(obj.value)
 89
 90            for value in values:
 91                obj = value
 92                newinstance.initialise(obj.name)(obj.value)
 93
 94            for func in funcs:
 95                obj = func
 96                newinstance.initialise(obj.name)(obj.basefunc)
 97            
 98            #restore old units
 99            sys.units = units
100        
101        return newinstance
102    
103    def quantity_set(self) -> set[object]:
104        """
105        Get a set of all `Quantity` objects attributed to this BGSystem.
106
107        Returns
108        -------
109        set : set
110            a set of objects.
111        """
112
113        return set(self.quantities.values())
114    
115    def quantity_names(self) -> list[str]:
116        """
117        Get a list of names for all `Quantity` objects attributed to this BGSystem.
118
119        Returns
120        -------
121        names : list of str
122            the list of names.
123        """
124
125        return list(self.quantities.keys())
126    
127
128    def initialise(self, quantity : str) -> Callable:
129        """
130        Instantiate a `Quantity` object from `quantities`.
131
132        The method creates a function `init` which can be called by
133        an arithmetic type / `Callable` to instantiate a `Val` / `Func`.
134        Calling `init` adds and instance of the `Quantity` as a new attribute to the BGSystem,
135        with the attribute name corresponding to the object's `name` attribute.   
136
137        Parameters
138        ----------
139        quantity : str
140            the name of the object which is to be instantiated.
141
142        Returns
143        -------
144        init : Callable
145            a function used to initialize the `Quantity` object
146        """
147
148        def init(obj : np.ndarray | Callable):
149            """
150            Initialize a `Quantity` object with an arithmetic type / Callable.
151
152            This adds an instance of the `Quantity` as a new attribute to the `BGSystem`, with the attribute name
153            corresponding to the `Quantity` object's `name` attribute. 
154
155            Parameters
156            ----------
157            obj : NDArray or Callable
158                the NDArray / Callable with which the `Quantity` is to be instantiated.
159            """
160
161            q = self.quantities[quantity]
162            setattr( self, quantity, q(obj, self) )
163            return
164        
165        return init
166    
167    def variable_list(self) -> list['Variable']:
168        """
169        Get a list of all `Variable` instances attributed to this BGSystem.
170
171        Returns
172        -------
173        vals : list of Variable
174            the list of `Variable` instances.
175        """
176
177        vals = []
178        for var in vars(self):
179            obj = getattr(self, var)
180            if isinstance(obj, Variable):
181                vals.append(obj)    
182        return vals
183    
184    def variable_names(self) -> list[str]:
185        """
186        Get a list of names for all `Variable` instances attributed to this BGSystem.
187
188        Returns
189        -------
190        names : list of str
191            the list of names.
192        """
193
194        names = []
195        for val in self.variable_list():
196            names.append(val.name)
197        return names
198    
199    def constant_list(self) -> list['Constant']:
200        """
201        Get a list of all `Constant` instances attributed to this BGSystem.
202
203        Returns
204        -------
205        vals : list of Val
206            the list of `Constant` instances.
207        """
208
209        vals = []
210        for var in vars(self):
211            obj = getattr(self, var)
212            if isinstance(obj, Constant):
213                vals.append(obj)    
214        return vals
215    
216    def constant_names(self) -> list[str]:
217        """
218        Get a list of names for all `Constant` instances attributed to this BGSystem.
219
220        Returns
221        -------
222        names : list of str
223            the list of names.
224        """
225
226        names = []
227        for val in self.constant_list():
228            names.append(val.name)
229        return names
230
231    def function_list(self) -> list['Func']:
232        """
233        Get a list of all `Func` instances attributed to this BGSystem.
234
235        Returns
236        -------
237        funcs : list of Func
238            the list of `Func` instances.
239        """
240
241        funcs = []
242        for var in vars(self):
243            obj = getattr(self, var)
244            if isinstance(obj, Func):
245                    funcs.append(obj)      
246        return funcs
247    
248    def function_names(self) -> list[str]:
249        """
250        Get a list of names for all `Func` instances attributed to this BGSystem.
251
252        Returns
253        -------
254        names : list of str
255            the list of names.
256        """
257
258        names = []
259        for val in self.function_list():
260            names.append(val.name)
261        return names
262    
263    def remove(self, name : str):
264        """
265        Remove a `Quantity` object and its instance from the BGSystem.
266
267        Parameters
268        ----------
269        name : str
270            the name of the object
271        """
272
273        delattr(self, name)
274        self.quantities.pop(name)
275        return
276    
277    def add_variable(self, name : str, qu_omega : int, qu_mu : int):
278        """
279        Define a new `Variable` object and add it to `quantities`.
280
281        Parameters
282        ----------
283        name : str
284            the name of the new object.
285        qu_omega : int
286            the 'u_omega' parameter of the new object.
287        qu_mu : int
288            the 'u_mu' parameter of the new object.
289        """
290
291        self.quantities[name] = define_var(name, qu_omega, qu_mu)
292        return
293    
294    def add_constant(self, name : str, qu_omega : int, qu_mu : int):
295        """
296        Define a new `Constant` object and add it to `quantities`.
297
298        Parameters
299        ----------
300        name : str
301            the name of the new object.
302        qu_omega : int
303            the 'u_omega' parameter of the new object.
304        qu_mu : int
305            the 'u_mu' parameter of the new object.
306        """
307
308        self.quantities[name] = define_const(name, qu_omega, qu_mu)
309        return
310    
311    def add_function(self, name : str, args : list['Val'], qu_omega : int, qu_mu : int):
312        """
313        Define a new `Func` object and add it to `quantities`.
314
315        Parameters
316        ----------
317        name : str
318            the name of the new object.
319        args : list of BGVal
320            the 'args' parameter of the new object.
321        qu_omega : int
322            the 'u_omega' parameter of the new object.
323        qu_mu : int
324            the 'u_mu' parameter of the new object.
325        """
326
327        self.quantities[name] = define_func(name, args, qu_omega, qu_mu)
328        return
329    
330    def save_variables(self, path : str):
331        """
332        Save the data in the current GEF instance in an output file.
333
334        Note, data is always stored in numerical units.
335        The save will not store constants or functions, only variables.
336
337        Parameters
338        ----------
339        path : str
340            Path to store data in.
341
342        Raises
343        ------
344        ValueError
345            if the GEF object has no data to store.
346        """
347
348        storeables = self.variable_names()    
349        #Check that all dynamic and derived quantities are initialised in this GEF instance
350        if len(storeables) == 0:
351            raise ValueError("No data to store.")
352        
353        #Create a dictionary used to initialise the pandas DataFrame
354        dic = {}
355
356        #remember the original units of the GEF
357        og_units=self.units
358
359        #Data is always stored unitless
360        self.units = False
361
362        for key in storeables:
363            dic[key] = getattr(self, key).value
364        
365        #Create pandas data frame and store the dictionary under the user-specified path
366        output_df = pd.DataFrame(dic)  
367        output_df.to_csv(path)
368
369        #after storing data, restore original units
370        self.units = og_units
371        return
372    
373    def load_variables(self, path : str):
374        """
375        Load data and store its results in the BGSystem.
376
377        Note, data is always loaded assuming numerical units.
378        Data is only loaded for variables, not for functions or constants.
379
380        Parameters
381        ----------
382        path : None or str
383            Path to load data from
384
385        Raises
386        ------
387        FileNotFoundError
388            if no file is found at `path`.
389        AttributeError
390            if the file contains a column labeled by a key which does not match any BGSystem variable.
391        """
392        #Check if file exists
393        try:
394            #Load from file
395            input_df = pd.read_table(path, sep=",")
396        except FileNotFoundError:
397            raise FileNotFoundError(f"No file found under '{path}'")
398        
399        #Dictionary for easy access using keys
400
401        data = dict(zip(input_df.columns[1:],input_df.values[:,1:].T))
402
403        #Before starting to load, check that the file is compatible with the GEF setup.
404        names = self.quantity_names()
405        for key in data.keys():
406            if key not in names:
407                raise AttributeError(f"The data table you tried to load contains an unknown quantity: '{key}'")
408        
409        #Store current units to switch back to later
410        og_units=self.units
411
412        #GEF data is always stored untiless, thus it is assumed to be untiless when loaded.
413        self.units = False
414        #Load data into background-value attributes
415        for key, values in data.items():
416            if key in self.variable_names():
417                getattr(self, key).value = values
418            else: 
419                self.initialise(key)(values)
420
421        self.units = og_units
422        return

A collection of cosmological variables sharing a system of units.

Instances of this class define two base unit systems, physical units and numerical units, by setting an energy scale, mu, and an inverse time scale, omega.

The cosmological variables (time, Hubble rate, etc.) are represented by Quantity objects. These objects are stored in quantities, and can can be initialise using initialise. Instances of these objects can be collectively converted between units by setting units.

This class is the fundamental building block of the GEFF code.

BGSystem(quantity_set: set, omega: float, mu: float)
 9    def __init__(self, quantity_set : set, omega : float, mu : float):
10        """
11        Create a new BGSystem in physical units.
12
13        Parameters
14        ----------
15        quantity_set : set of Quantity
16            used to define `quantities`
17        omega : float
18            the characteristic frequency scale
19        mu : float
20            the characteristic energy scale
21        """
22        self.quantities : dict = {q.name:q for q in quantity_set}
23        """A dictionary of all `Quantity` objects for this BGSystem"""
24        self._omega : float = omega
25        self._mu : float = mu
26        self._units=True

Create a new BGSystem in physical units.

Parameters
  • quantity_set (set of Quantity): used to define quantities
  • omega (float): the characteristic frequency scale
  • mu (float): the characteristic energy scale
quantities: dict

A dictionary of all Quantity objects for this BGSystem

omega: float
28    @property
29    def omega(self) -> float:
30        """A frequency scale (typically the Hubble rate at some reference time)"""
31        return self._omega

A frequency scale (typically the Hubble rate at some reference time)

mu: float
33    @property 
34    def mu(self) -> float:
35        """An energy scale (typically the Planck mass)"""
36        return self._mu

An energy scale (typically the Planck mass)

units: bool
38    @property
39    def units(self) -> bool:
40        """Indicates the current units of the BGSystem. `True`:physical units, `False`:numerical units"""
41        return self._units

Indicates the current units of the BGSystem. True:physical units, False:numerical units

@classmethod
def from_system( cls, sys: BGSystem, copy: bool = False) -> BGSystem:
 54    @classmethod
 55    def from_system(cls, sys : 'BGSystem', copy : bool=False) -> 'BGSystem':
 56        """Initialize a new `BGSystem` from an existing instance.
 57
 58        The new instance is created with the same quantities, reference frequency and amplitude as the original.
 59        If specified, the `Quantity` instances are also copied to the new BGSystem
 60
 61        Parameters
 62        ----------
 63        sys : BGSystem
 64            the original instance used as a template
 65        copy : Boolean
 66            `True` if `Quantity` instances are also copied
 67        Returns
 68        -------
 69        newinstance : BGSystem
 70            the new instance
 71        """
 72
 73        newinstance = cls(sys.quantity_set(), sys.omega, sys.mu)
 74
 75        if copy:
 76            #store units of original sys
 77            units = sys.units
 78            #match units of new system
 79            sys.units = True
 80
 81            #Copy values and functions
 82            values = sys.variable_list()
 83            funcs = sys.function_list()
 84            consts = sys.constant_list()
 85
 86            for const in consts:
 87                obj = const
 88                newinstance.initialise(obj.name)(obj.value)
 89
 90            for value in values:
 91                obj = value
 92                newinstance.initialise(obj.name)(obj.value)
 93
 94            for func in funcs:
 95                obj = func
 96                newinstance.initialise(obj.name)(obj.basefunc)
 97            
 98            #restore old units
 99            sys.units = units
100        
101        return newinstance

Initialize a new BGSystem from an existing instance.

The new instance is created with the same quantities, reference frequency and amplitude as the original. If specified, the Quantity instances are also copied to the new BGSystem

Parameters
  • sys (BGSystem): the original instance used as a template
  • copy (Boolean): True if Quantity instances are also copied
Returns
  • newinstance (BGSystem): the new instance
def quantity_set(self) -> set[object]:
103    def quantity_set(self) -> set[object]:
104        """
105        Get a set of all `Quantity` objects attributed to this BGSystem.
106
107        Returns
108        -------
109        set : set
110            a set of objects.
111        """
112
113        return set(self.quantities.values())

Get a set of all Quantity objects attributed to this BGSystem.

Returns
  • set (set): a set of objects.
def quantity_names(self) -> list[str]:
115    def quantity_names(self) -> list[str]:
116        """
117        Get a list of names for all `Quantity` objects attributed to this BGSystem.
118
119        Returns
120        -------
121        names : list of str
122            the list of names.
123        """
124
125        return list(self.quantities.keys())

Get a list of names for all Quantity objects attributed to this BGSystem.

Returns
  • names (list of str): the list of names.
def initialise(self, quantity: str) -> Callable:
128    def initialise(self, quantity : str) -> Callable:
129        """
130        Instantiate a `Quantity` object from `quantities`.
131
132        The method creates a function `init` which can be called by
133        an arithmetic type / `Callable` to instantiate a `Val` / `Func`.
134        Calling `init` adds and instance of the `Quantity` as a new attribute to the BGSystem,
135        with the attribute name corresponding to the object's `name` attribute.   
136
137        Parameters
138        ----------
139        quantity : str
140            the name of the object which is to be instantiated.
141
142        Returns
143        -------
144        init : Callable
145            a function used to initialize the `Quantity` object
146        """
147
148        def init(obj : np.ndarray | Callable):
149            """
150            Initialize a `Quantity` object with an arithmetic type / Callable.
151
152            This adds an instance of the `Quantity` as a new attribute to the `BGSystem`, with the attribute name
153            corresponding to the `Quantity` object's `name` attribute. 
154
155            Parameters
156            ----------
157            obj : NDArray or Callable
158                the NDArray / Callable with which the `Quantity` is to be instantiated.
159            """
160
161            q = self.quantities[quantity]
162            setattr( self, quantity, q(obj, self) )
163            return
164        
165        return init

Instantiate a Quantity object from quantities.

The method creates a function init which can be called by an arithmetic type / Callable to instantiate a Val / Func. Calling init adds and instance of the Quantity as a new attribute to the BGSystem, with the attribute name corresponding to the object's name attribute.

Parameters
  • quantity (str): the name of the object which is to be instantiated.
Returns
  • init (Callable): a function used to initialize the Quantity object
def variable_list(self) -> list[Variable]:
167    def variable_list(self) -> list['Variable']:
168        """
169        Get a list of all `Variable` instances attributed to this BGSystem.
170
171        Returns
172        -------
173        vals : list of Variable
174            the list of `Variable` instances.
175        """
176
177        vals = []
178        for var in vars(self):
179            obj = getattr(self, var)
180            if isinstance(obj, Variable):
181                vals.append(obj)    
182        return vals

Get a list of all Variable instances attributed to this BGSystem.

Returns
  • vals (list of Variable): the list of Variable instances.
def variable_names(self) -> list[str]:
184    def variable_names(self) -> list[str]:
185        """
186        Get a list of names for all `Variable` instances attributed to this BGSystem.
187
188        Returns
189        -------
190        names : list of str
191            the list of names.
192        """
193
194        names = []
195        for val in self.variable_list():
196            names.append(val.name)
197        return names

Get a list of names for all Variable instances attributed to this BGSystem.

Returns
  • names (list of str): the list of names.
def constant_list(self) -> list[Constant]:
199    def constant_list(self) -> list['Constant']:
200        """
201        Get a list of all `Constant` instances attributed to this BGSystem.
202
203        Returns
204        -------
205        vals : list of Val
206            the list of `Constant` instances.
207        """
208
209        vals = []
210        for var in vars(self):
211            obj = getattr(self, var)
212            if isinstance(obj, Constant):
213                vals.append(obj)    
214        return vals

Get a list of all Constant instances attributed to this BGSystem.

Returns
  • vals (list of Val): the list of Constant instances.
def constant_names(self) -> list[str]:
216    def constant_names(self) -> list[str]:
217        """
218        Get a list of names for all `Constant` instances attributed to this BGSystem.
219
220        Returns
221        -------
222        names : list of str
223            the list of names.
224        """
225
226        names = []
227        for val in self.constant_list():
228            names.append(val.name)
229        return names

Get a list of names for all Constant instances attributed to this BGSystem.

Returns
  • names (list of str): the list of names.
def function_list(self) -> list[Func]:
231    def function_list(self) -> list['Func']:
232        """
233        Get a list of all `Func` instances attributed to this BGSystem.
234
235        Returns
236        -------
237        funcs : list of Func
238            the list of `Func` instances.
239        """
240
241        funcs = []
242        for var in vars(self):
243            obj = getattr(self, var)
244            if isinstance(obj, Func):
245                    funcs.append(obj)      
246        return funcs

Get a list of all Func instances attributed to this BGSystem.

Returns
  • funcs (list of Func): the list of Func instances.
def function_names(self) -> list[str]:
248    def function_names(self) -> list[str]:
249        """
250        Get a list of names for all `Func` instances attributed to this BGSystem.
251
252        Returns
253        -------
254        names : list of str
255            the list of names.
256        """
257
258        names = []
259        for val in self.function_list():
260            names.append(val.name)
261        return names

Get a list of names for all Func instances attributed to this BGSystem.

Returns
  • names (list of str): the list of names.
def remove(self, name: str):
263    def remove(self, name : str):
264        """
265        Remove a `Quantity` object and its instance from the BGSystem.
266
267        Parameters
268        ----------
269        name : str
270            the name of the object
271        """
272
273        delattr(self, name)
274        self.quantities.pop(name)
275        return

Remove a Quantity object and its instance from the BGSystem.

Parameters
  • name (str): the name of the object
def add_variable(self, name: str, qu_omega: int, qu_mu: int):
277    def add_variable(self, name : str, qu_omega : int, qu_mu : int):
278        """
279        Define a new `Variable` object and add it to `quantities`.
280
281        Parameters
282        ----------
283        name : str
284            the name of the new object.
285        qu_omega : int
286            the 'u_omega' parameter of the new object.
287        qu_mu : int
288            the 'u_mu' parameter of the new object.
289        """
290
291        self.quantities[name] = define_var(name, qu_omega, qu_mu)
292        return

Define a new Variable object and add it to quantities.

Parameters
  • name (str): the name of the new object.
  • qu_omega (int): the 'u_omega' parameter of the new object.
  • qu_mu (int): the 'u_mu' parameter of the new object.
def add_constant(self, name: str, qu_omega: int, qu_mu: int):
294    def add_constant(self, name : str, qu_omega : int, qu_mu : int):
295        """
296        Define a new `Constant` object and add it to `quantities`.
297
298        Parameters
299        ----------
300        name : str
301            the name of the new object.
302        qu_omega : int
303            the 'u_omega' parameter of the new object.
304        qu_mu : int
305            the 'u_mu' parameter of the new object.
306        """
307
308        self.quantities[name] = define_const(name, qu_omega, qu_mu)
309        return

Define a new Constant object and add it to quantities.

Parameters
  • name (str): the name of the new object.
  • qu_omega (int): the 'u_omega' parameter of the new object.
  • qu_mu (int): the 'u_mu' parameter of the new object.
def add_function( self, name: str, args: list[Val], qu_omega: int, qu_mu: int):
311    def add_function(self, name : str, args : list['Val'], qu_omega : int, qu_mu : int):
312        """
313        Define a new `Func` object and add it to `quantities`.
314
315        Parameters
316        ----------
317        name : str
318            the name of the new object.
319        args : list of BGVal
320            the 'args' parameter of the new object.
321        qu_omega : int
322            the 'u_omega' parameter of the new object.
323        qu_mu : int
324            the 'u_mu' parameter of the new object.
325        """
326
327        self.quantities[name] = define_func(name, args, qu_omega, qu_mu)
328        return

Define a new Func object and add it to quantities.

Parameters
  • name (str): the name of the new object.
  • args (list of BGVal): the 'args' parameter of the new object.
  • qu_omega (int): the 'u_omega' parameter of the new object.
  • qu_mu (int): the 'u_mu' parameter of the new object.
def save_variables(self, path: str):
330    def save_variables(self, path : str):
331        """
332        Save the data in the current GEF instance in an output file.
333
334        Note, data is always stored in numerical units.
335        The save will not store constants or functions, only variables.
336
337        Parameters
338        ----------
339        path : str
340            Path to store data in.
341
342        Raises
343        ------
344        ValueError
345            if the GEF object has no data to store.
346        """
347
348        storeables = self.variable_names()    
349        #Check that all dynamic and derived quantities are initialised in this GEF instance
350        if len(storeables) == 0:
351            raise ValueError("No data to store.")
352        
353        #Create a dictionary used to initialise the pandas DataFrame
354        dic = {}
355
356        #remember the original units of the GEF
357        og_units=self.units
358
359        #Data is always stored unitless
360        self.units = False
361
362        for key in storeables:
363            dic[key] = getattr(self, key).value
364        
365        #Create pandas data frame and store the dictionary under the user-specified path
366        output_df = pd.DataFrame(dic)  
367        output_df.to_csv(path)
368
369        #after storing data, restore original units
370        self.units = og_units
371        return

Save the data in the current GEF instance in an output file.

Note, data is always stored in numerical units. The save will not store constants or functions, only variables.

Parameters
  • path (str): Path to store data in.
Raises
  • ValueError: if the GEF object has no data to store.
def load_variables(self, path: str):
373    def load_variables(self, path : str):
374        """
375        Load data and store its results in the BGSystem.
376
377        Note, data is always loaded assuming numerical units.
378        Data is only loaded for variables, not for functions or constants.
379
380        Parameters
381        ----------
382        path : None or str
383            Path to load data from
384
385        Raises
386        ------
387        FileNotFoundError
388            if no file is found at `path`.
389        AttributeError
390            if the file contains a column labeled by a key which does not match any BGSystem variable.
391        """
392        #Check if file exists
393        try:
394            #Load from file
395            input_df = pd.read_table(path, sep=",")
396        except FileNotFoundError:
397            raise FileNotFoundError(f"No file found under '{path}'")
398        
399        #Dictionary for easy access using keys
400
401        data = dict(zip(input_df.columns[1:],input_df.values[:,1:].T))
402
403        #Before starting to load, check that the file is compatible with the GEF setup.
404        names = self.quantity_names()
405        for key in data.keys():
406            if key not in names:
407                raise AttributeError(f"The data table you tried to load contains an unknown quantity: '{key}'")
408        
409        #Store current units to switch back to later
410        og_units=self.units
411
412        #GEF data is always stored untiless, thus it is assumed to be untiless when loaded.
413        self.units = False
414        #Load data into background-value attributes
415        for key, values in data.items():
416            if key in self.variable_names():
417                getattr(self, key).value = values
418            else: 
419                self.initialise(key)(values)
420
421        self.units = og_units
422        return

Load data and store its results in the BGSystem.

Note, data is always loaded assuming numerical units. Data is only loaded for variables, not for functions or constants.

Parameters
  • path (None or str): Path to load data from
Raises
  • FileNotFoundError: if no file is found at path.
  • AttributeError: if the file contains a column labeled by a key which does not match any BGSystem variable.
class Quantity:
425class Quantity:
426    name : ClassVar[str]= ""
427    """The objects name"""
428    description : ClassVar[str]= ""
429    """A brief description of the object"""
430    u_omega : ClassVar[int] = 0
431    """Indicates how the object scales with frequency."""
432    u_mu : ClassVar[int] = 0
433    """Indicates how the object scales with energy."""
434
435    def __init__(self, sys : BGSystem):
436        """
437        Create a new Quantity as part of a BGSystem
438
439        The units of the new object matches those of the BGSystem
440
441        Parameters
442        ----------
443        sys : BGSystem
444            the BGSystem to which the object belongs
445        """
446
447        self._units = sys.units
448        self._conversion = (sys.omega**self.u_omega*sys.mu**self.u_mu)
449
450    def __repr__(self):
451        r"""A string representing the class, giving its name and scaling with frequency ($\omega$) and energy ($\mu$)."""
452        return f"{self.name}({self.u_omega},{self.u_mu})"
453
454    def __str__(self) -> str:
455        """The class instance as a string including its name and current units."""
456
457        if not(self._units):
458            return f"{self.name} (numerical)"
459        elif self._units:
460            return f"{self.name} (physical)"
461    
462    
463    @property
464    def units(self) -> bool:
465        """Indicates the current units of the Quantity. `True`:physical units, `False`:numerical units"""
466        return self._units
467    
468    @units.setter
469    def units(self, units : bool):
470        """Convert the object between numerical and physical units."""
471        self._units = bool(units)
472        return
473    
474    @property
475    def conversion(self) -> float:
476        """A conversion factor between numerical and physical units."""
477        return self._conversion
478    
479    @classmethod
480    def get_description(cls) -> str:
481        """Return a string describing the object."""
482        if cls.description=="":
483            return f"{cls.name}"
484        else:
485            return f"{cls.name} - {cls.description}"

An object representing a cosmological quantity.

A cosmological quantity has a characteristic scaling with respect to a frequency scale (e.g., the Hubble rate at some reference time), and energy scale (e.g., the Planck mass).

Typical such objects are:

  • cosmic time $t = \bar{t}/\omega$
  • frequency $f = \omega \bar{f}$
  • scalar-field vev $\varphi = \mu \bar{\varphi}$
  • scalar potential $ V(\varphi) = \omega^2 \mu^2 \bar{V}(\bar{\varphi} \mu) $
  • gauge fields $A_\mu = \omega\bar{A}_\mu $ (i.e., scales like a time derivative)

Quantity objects are initialized as part of a BGSystem, which defines $\omega$ and $\mu$.

This class is a parent of Val and Func.

Quantity(sys: BGSystem)
435    def __init__(self, sys : BGSystem):
436        """
437        Create a new Quantity as part of a BGSystem
438
439        The units of the new object matches those of the BGSystem
440
441        Parameters
442        ----------
443        sys : BGSystem
444            the BGSystem to which the object belongs
445        """
446
447        self._units = sys.units
448        self._conversion = (sys.omega**self.u_omega*sys.mu**self.u_mu)

Create a new Quantity as part of a BGSystem

The units of the new object matches those of the BGSystem

Parameters
  • sys (BGSystem): the BGSystem to which the object belongs
name: ClassVar[str] = ''

The objects name

description: ClassVar[str] = ''

A brief description of the object

u_omega: ClassVar[int] = 0

Indicates how the object scales with frequency.

u_mu: ClassVar[int] = 0

Indicates how the object scales with energy.

units: bool
463    @property
464    def units(self) -> bool:
465        """Indicates the current units of the Quantity. `True`:physical units, `False`:numerical units"""
466        return self._units

Indicates the current units of the Quantity. True:physical units, False:numerical units

conversion: float
474    @property
475    def conversion(self) -> float:
476        """A conversion factor between numerical and physical units."""
477        return self._conversion

A conversion factor between numerical and physical units.

@classmethod
def get_description(cls) -> str:
479    @classmethod
480    def get_description(cls) -> str:
481        """Return a string describing the object."""
482        if cls.description=="":
483            return f"{cls.name}"
484        else:
485            return f"{cls.name} - {cls.description}"

Return a string describing the object.

class Val(Quantity):
489class Val(Quantity):
490    def __init__(self, value : np.ndarray|float, sys : BGSystem):
491        """
492        Create a new instance using a BGSystem.
493
494        Parameters
495        ----------
496        value : NDArray
497            the underlying array of the instance
498        sys : BGSystem
499            the BGSystem to which the instance belongs
500        """
501        super().__init__(sys)
502        self._value =  value*self._conversion**(-sys.units)
503
504    def __str__(self) -> str:
505        """
506        The class instance as a string including its name, current units, and its value.
507
508        Returns
509        -------
510        str
511            the string representation.
512        """
513
514        if not(self._units):
515            return f"{self.name} (numerical): {self.value}"
516        elif self._units:
517            return f"{self.name} (physical): {self.value}"
518    
519    @property
520    def value(self) -> float:
521        """The objects value in its respective units."""
522        return self._value*self._conversion**self._units
523    
524    @value.setter
525    def value(self, newval):
526        """Overwrite the `value` attribute (assuming its current units)."""
527        self._value = newval*self._conversion**(-self._units)
528        return
529    
530    #Define all mathematical operations as operations acting on self.value
531    def __abs__(self):
532        return abs(self.value)
533    
534    def __neg__(self):
535        return -self.value
536    
537    def __pos__(self):
538        return +self.value
539    
540    def __add__(self, other):
541        #self.__Compatible(self, other, "+")
542        return self.value + other
543        
544    __radd__ = __add__
545    
546    def __sub__(self, other):
547        #self.__Compatible(self, other, "-")
548        return self.value - other
549        
550    def __rsub__(self, other):
551        return other - self.value
552
553    def __mul__(self, other):
554        return self.value * other
555        
556    __rmul__ = __mul__
557    
558    def __floordiv__(self, other):
559        return self.value // other
560        
561    def __rfloordiv__(self, other):
562        return other // self.value
563    
564    def __truediv__(self, other):
565        return self.value / other
566        
567    def __rtruediv__(self, other):
568        return other / self.value
569    
570    def __mod__(self, other):
571        return self.value % other
572    
573    def __pow__(self, other):
574        #BGVal should never be exponentiated by another BGVal
575        return self.value ** other
576    
577    def __eq__(self, other):
578        #self.__Compatible(self, other, "==")
579        return self.value == other
580            
581    def __ne__(self, other):
582        #self.__Compatible(self, other, "!=")
583        return self.value != other
584    
585    def __lt__(self, other):
586        #self.__Compatible(self, other, "<")
587        return self.value < other
588
589    def __gt__(self, other):
590        #self.__Compatible(self, other, ">")
591        return self.value > other
592
593    def __le__(self, other):
594        #self.__Compatible(self, other, "<=")
595        return  self.value <= other
596
597    def __ge__(self, other):
598        #self.__Compatible(self, other, ">=")
599        return  self.value >= other

A Quantity subclass used as basis for defining real-valued variables and constants.

In addition to the basic structure defined by Quantity, this class can be used like an arithmetic type. It defines basic arithmetic operations on its instances as operations on their underlying value attribute.

As a subclass of Quantity it inherits all its attributes and methods, ensuring that value is changed according to units by using conversion.

The class serves as a parent for Variable and Constant.

Val(value: numpy.ndarray | float, sys: BGSystem)
490    def __init__(self, value : np.ndarray|float, sys : BGSystem):
491        """
492        Create a new instance using a BGSystem.
493
494        Parameters
495        ----------
496        value : NDArray
497            the underlying array of the instance
498        sys : BGSystem
499            the BGSystem to which the instance belongs
500        """
501        super().__init__(sys)
502        self._value =  value*self._conversion**(-sys.units)

Create a new instance using a BGSystem.

Parameters
  • value (NDArray): the underlying array of the instance
  • sys (BGSystem): the BGSystem to which the instance belongs
value: float
519    @property
520    def value(self) -> float:
521        """The objects value in its respective units."""
522        return self._value*self._conversion**self._units

The objects value in its respective units.

class Func(Quantity):
602class Func(Quantity):
603    args : ClassVar[list[Val]] = []
604    """Indicates the argument signature for the class."""
605    dtype : ClassVar[np.floating] = np.float64
606    """The data type returned by `__call__`."""
607
608    def __init__(self, func : Callable, sys : BGSystem):
609        """
610        Create a new instance using a `Callable` and a BGSystem
611
612        Parameters
613        ----------
614        func : NDArray
615            the underlying function `f(*args)` of the instance
616        sys : BGSystem
617            the BGSystem to which the instance belongs
618
619        Raises
620        ------
621        TypeError
622            if the number of arguments of `func` do not match `args`
623        ValueError
624            if the return of`func` can't be converted to `dtype`
625        """
626        super().__init__(sys)
627        func = np.vectorize(func, otypes=[self.dtype])
628        
629        try:
630            testargs = [1.0 for arg in self.args]
631            assert func(*testargs).dtype
632        except TypeError:
633            raise TypeError("The number of non-default arguments of 'func' needs to match 'len(self.args)'.")
634        except ValueError:
635            raise ValueError(f"'func' must return a single value which can be converted to '{self.dtype}'")
636        
637        self._basefunc = func
638
639        self._arg_conversions = [(sys.omega**arg.u_omega*sys.mu**arg.u_mu)
640                                 for arg in self.args]
641        
642    @property
643    def basefunc(self) -> Callable:
644        """The underlying function which defines the `__call__` method."""
645        return self._basefunc
646    
647    def get_arg_conversions(self) -> list[float]:
648        """
649        Get a list of conversion factors for each argument of `basefunc`.
650
651        Returns
652        -------
653        arg_conversions : list of float
654            a list of conversion factors
655        """
656
657        return self._arg_conversions
658    
659    def __call__(self, *args):
660        """
661        Define the call method of the class as outlined in its documentation.
662        """
663        def float_handler(x, i):
664            return x*self._arg_conversions[i]**(1-self._units)
665        
666        def val_handler(x, i):
667            conv = x.conversion
668            assert self._arg_conversions[i] == conv
669            return x*conv**(1-x.units)
670
671        typedic = {Variable : val_handler, Val : val_handler, Constant: val_handler}
672
673        args = [typedic.get(arg.__class__.__bases__[0], float_handler)(arg, i) for i, arg in enumerate(args)]
674
675        return self._basefunc(*args)/self._conversion**(1-self._units)

A Quantity subclass representing real functions of variables like the inflaton potential.

An instance of this class can be used as a function, evaluating the underlying method, basefunc depending on the state of units.

In physical units, the call returns the result of basefunc. In numerical units, the call instead returns basefunc(*args)/conversion.
If called by a Val object, the argument is also converted according to the units of the Val instance (generically, identical to the ones of the Func instance). If instead called by a regular arithmetic data type (e.g., float), it is assumed that the argument is in the same unit system as the Func instance.

A typical object is the scalar potential, $V(\varphi) = \omega^2 \mu^2 \bar{V}(\bar{\varphi} \mu) $

To define a custom Func object, use the class factory define_func.

Func(func: Callable, sys: BGSystem)
608    def __init__(self, func : Callable, sys : BGSystem):
609        """
610        Create a new instance using a `Callable` and a BGSystem
611
612        Parameters
613        ----------
614        func : NDArray
615            the underlying function `f(*args)` of the instance
616        sys : BGSystem
617            the BGSystem to which the instance belongs
618
619        Raises
620        ------
621        TypeError
622            if the number of arguments of `func` do not match `args`
623        ValueError
624            if the return of`func` can't be converted to `dtype`
625        """
626        super().__init__(sys)
627        func = np.vectorize(func, otypes=[self.dtype])
628        
629        try:
630            testargs = [1.0 for arg in self.args]
631            assert func(*testargs).dtype
632        except TypeError:
633            raise TypeError("The number of non-default arguments of 'func' needs to match 'len(self.args)'.")
634        except ValueError:
635            raise ValueError(f"'func' must return a single value which can be converted to '{self.dtype}'")
636        
637        self._basefunc = func
638
639        self._arg_conversions = [(sys.omega**arg.u_omega*sys.mu**arg.u_mu)
640                                 for arg in self.args]

Create a new instance using a Callable and a BGSystem

Parameters
  • func (NDArray): the underlying function f(*args) of the instance
  • sys (BGSystem): the BGSystem to which the instance belongs
Raises
  • TypeError: if the number of arguments of func do not match args
  • ValueError: if the return offunc can't be converted to dtype
args: ClassVar[list[Val]] = []

Indicates the argument signature for the class.

dtype: ClassVar[numpy.floating] = <class 'numpy.float64'>

The data type returned by __call__.

basefunc: Callable
642    @property
643    def basefunc(self) -> Callable:
644        """The underlying function which defines the `__call__` method."""
645        return self._basefunc

The underlying function which defines the __call__ method.

def get_arg_conversions(self) -> list[float]:
647    def get_arg_conversions(self) -> list[float]:
648        """
649        Get a list of conversion factors for each argument of `basefunc`.
650
651        Returns
652        -------
653        arg_conversions : list of float
654            a list of conversion factors
655        """
656
657        return self._arg_conversions

Get a list of conversion factors for each argument of basefunc.

Returns
  • arg_conversions (list of float): a list of conversion factors
class Variable(Val):
678class Variable(Val):
679    dtype : ClassVar[np.floating] = np.float64
680    """The data type of `value`."""
681    
682    def __init__(self, value : np.ndarray, sys : BGSystem):
683        """
684        Create a new instance using a numpy array and a BGSystem
685
686        Parameters
687        ----------
688        value : NDArray
689            the underlying array of the instance
690        sys : BGSystem
691            the BGSystem to which the instance belongs
692        """
693        super().__init__( np.asarray(value, dtype=self.dtype), sys)
694
695    #The following methods ensure that a `Val` instance can be used as an array concerning mathematical operations and indexing.
696    
697    def __getitem__(self, idx):
698        return self.value[idx]
699    
700    def __len__(self):
701        return len(self.value)
702    
703    @property
704    def value(self) -> np.ndarray:
705        """The objects value in its respective units."""
706        return self._value*self._conversion**(self._units)
707
708    @value.setter
709    def value(self, newval):
710        """Overwrite the `value` attribute ensuring it is an array"""
711        self._value = np.asarray(newval*self._conversion**(-self._units), dtype=self.dtype)
712        return

A Val subclass representing real-valued variables evolving with cosmic time.

Instances of this class can be used like a Val with value as a 1-D Numpy-Array. Indexed returns the associated index of value.

Variable(value: numpy.ndarray, sys: BGSystem)
682    def __init__(self, value : np.ndarray, sys : BGSystem):
683        """
684        Create a new instance using a numpy array and a BGSystem
685
686        Parameters
687        ----------
688        value : NDArray
689            the underlying array of the instance
690        sys : BGSystem
691            the BGSystem to which the instance belongs
692        """
693        super().__init__( np.asarray(value, dtype=self.dtype), sys)

Create a new instance using a numpy array and a BGSystem

Parameters
  • value (NDArray): the underlying array of the instance
  • sys (BGSystem): the BGSystem to which the instance belongs
dtype: ClassVar[numpy.floating] = <class 'numpy.float64'>

The data type of value.

value: numpy.ndarray
703    @property
704    def value(self) -> np.ndarray:
705        """The objects value in its respective units."""
706        return self._value*self._conversion**(self._units)

The objects value in its respective units.

class Constant(Val):
714class Constant(Val):
715    def __init__(self, value, sys : BGSystem):
716        """
717        Create a new instance using a float and a BGSystem
718
719        Parameters
720        ----------
721        value : NDArray
722            the underlying array of the instance
723        sys : BGSystem
724            the BGSystem to which the instance belongs
725
726        Raises
727        ------
728        TypeError 
729            if value cannot be converted to a float
730        """
731        super().__init__( float(value), sys)

A Val subclass representing a constant of cosmic time.

Instances of this class can be used like a float for mathematical operations as defined by Val.

Constant(value, sys: BGSystem)
715    def __init__(self, value, sys : BGSystem):
716        """
717        Create a new instance using a float and a BGSystem
718
719        Parameters
720        ----------
721        value : NDArray
722            the underlying array of the instance
723        sys : BGSystem
724            the BGSystem to which the instance belongs
725
726        Raises
727        ------
728        TypeError 
729            if value cannot be converted to a float
730        """
731        super().__init__( float(value), sys)

Create a new instance using a float and a BGSystem

Parameters
  • value (NDArray): the underlying array of the instance
  • sys (BGSystem): the BGSystem to which the instance belongs
Raises
  • TypeError: if value cannot be converted to a float
class GaugeField:
733class GaugeField:
734    name : ClassVar[str] = ""
735    """The name of the class."""
736    zeros : ClassVar[list[Variable]]= []
737    r"""A list of the associated 0$^{\rm th}$ order quantities."""
738    cutoff : Variable = None
739    """The associated UV cutoff scale."""
740        
741    @classmethod
742    def get_description(cls) -> str:
743        """Return a string describing the object."""
744        return f"{cls.name} - associated with: {[a.name for a in cls.zeros]}, UV cutoff: {cls.cutoff.name}"

$\newcommand{\bm}[1]{\boldsymbol{#1}}$ A low level class defining some basic properties of gauge-field bilinear towers.

A gauge-field bilinear tower is defined as a collection of the following three objects,

$$ \mathcal{F}_\mathcal{E}^{(n)} = \frac{a^4}{k_{{\rm UV}}^{n+4}}\langle \bm{E} \cdot \operatorname{rot}^n \bm{E}\rangle = \int\limits_{0}^{k_{{\rm UV}}(t)}\frac{{\rm d} k}{k} \frac{a^2 k^{n+3}}{2 \pi^2 k_{{\rm UV}}^{n+4}} \sum_{\lambda}\lambda^n |\dot{A}_\lambda(t,k)|^2\, ,$$ $$ \mathcal{F}_\mathcal{G}^{(n)} = -\frac{a^4}{2 k_{{\rm UV}}^{n+4}}\langle \bm{E} \cdot \operatorname{rot}^n \bm{B} + \bm{B} \cdot \operatorname{rot}^n \bm{E}\rangle = \int\limits_{0}^{k_{{\rm UV}}(t)} \frac{{\rm d} k}{k} \frac{a k^{n+4}}{2 \pi^2 k_{{\rm UV}}^{n+4}}\sum_{\lambda}\lambda^{n+1} \operatorname{Re}[\dot{A}_\lambda(t,k)A_\lambda^*(t,k)] \, ,$$ $$ \mathcal{F}_\mathcal{B}^{(n)} = \frac{a^4}{k_{{\rm UV}}^{n+4}}\langle \bm{E} \cdot \operatorname{rot}^n \bm{E}\rangle = \int\limits_{0}^{k_{{\rm UV}}(t)}\frac{{\rm d} k}{k} \frac{k^{n+5}}{2 \pi^{2}k_{{\rm UV}}^{n+4}} \sum_{\lambda}\lambda^n |A_\lambda(t,k)|^2 \, ,$$

here $k_{\rm UV}$ is a UV cutoff scale, $\bm{E}$ and $\bm{B}$ are electric and magnetic field operators, and $A_\lambda(t,k)$ is a gauge-field mode function. The integer $n$ varies between $0$ and a maximum value, $n_{\rm tr}$.

The GaugeField class collects important information about the collection $\mathcal{F}_\mathcal{X}^{(n)}$. The GEFF code needs to know, which Variable sets the UV cutoff scale, and which Variables correspond to the zero-order quantities, $\langle \bm{E}^2\rangle$, $\langle \bm{B}^2\rangle$, and $-\langle \bm{E} \cdot \bm{B}\rangle$.

Note that a GaugeField is never part of a BGSystem, as the number of variables depends on $n_{\rm tr}$, which is not fixed a priori. In fact, terms with $n>1$ are often only auxiliary and not used by the GEFF after the differential equations have been solved. However, the quantities $\mathcal{F}_\mathcal{X}^{(n)}$ are defined to be unitless, so they do not need to be converted between numerical and physical units.

A custom GaugeField can be defined using BGGauge.

name: ClassVar[str] = ''

The name of the class.

zeros: ClassVar[list[Variable]] = []

A list of the associated 0$^{\rm th}$ order quantities.

cutoff: Variable = None

The associated UV cutoff scale.

@classmethod
def get_description(cls) -> str:
741    @classmethod
742    def get_description(cls) -> str:
743        """Return a string describing the object."""
744        return f"{cls.name} - associated with: {[a.name for a in cls.zeros]}, UV cutoff: {cls.cutoff.name}"

Return a string describing the object.

def define_var( qname: str, qu_omega: int, qu_mu: int, qdescription: str = '', qdtype: numpy.dtype = <class 'numpy.float64'>):
746def define_var(qname : str, qu_omega : int, qu_mu : int, qdescription:str="", qdtype : np.dtype=np.float64):
747    """
748    Creates a subclass of `Variable` with custom name, and scaling.
749
750    Parameters
751    ----------
752    qname : str
753        the `name` attribute of the subclass
754    qu_omega : int
755        the `u_omega` attribute of the subclass
756    qu_mu : int
757        the `u_mu` attribute of the subclass
758    qdescription : str
759        a brief description of the subclass
760    qdtype : Numpy Data Type
761        the `dtype` attribute of the subclass
762    
763        
764    Returns
765    -------
766    CustomVar : class
767        the custom subclass
768
769    Raises
770    ------
771    TypeError
772        if the data type is not a subtype of `numpy.floating`
773    """
774
775    if not( np.issubdtype(qdtype, np.floating) ):
776        raise TypeError("BGVal's data-type must be a subtype of 'numpy.floating'.")
777
778    class CustomVar(Variable):
779        __doc__ = docs_bgtypes.DOCS["define_var.CustomVar"]
780        name=qname
781        u_omega = qu_omega
782        u_mu = qu_mu
783        dtype = qdtype
784        description = qdescription
785        def __init__(self, value, sys):
786            super().__init__(value, sys)
787    CustomVar.__qualname__ = f"Val_{qname}"
788    CustomVar.__module__ = __name__
789
790    return CustomVar

Creates a subclass of Variable with custom name, and scaling.

Parameters
  • qname (str): the name attribute of the subclass
  • qu_omega (int): the u_omega attribute of the subclass
  • qu_mu (int): the u_mu attribute of the subclass
  • qdescription (str): a brief description of the subclass
  • qdtype (Numpy Data Type): the dtype attribute of the subclass
Returns
  • CustomVar (class): the custom subclass
Raises
  • TypeError: if the data type is not a subtype of numpy.floating
def define_const(qname: str, qu_omega: int, qu_mu: int, qdescription: str = ''):
792def define_const(qname : str, qu_omega : int, qu_mu : int, qdescription:str=""):
793    """
794    Creates a subclass of `Constant` with custom name, and scaling.
795
796    Parameters
797    ----------
798    qname : str
799        the `name` attribute of the subclass
800    qu_omega : int
801        the `u_omega` attribute of the subclass
802    qu_mu : int
803        the `u_mu` attribute of the subclass
804    qdescription : str
805        a brief description of the subclass
806        
807    Returns
808    -------
809    CustomConst : class
810        the custom subclass
811    """
812
813    class CustomConst(Constant):
814        __doc__ = docs_bgtypes.DOCS["define_const.CustomConst"]
815        name=qname
816        u_omega = qu_omega
817        u_mu = qu_mu
818        description = qdescription
819        def __init__(self, value, sys):
820            super().__init__(value, sys)
821    CustomConst.__qualname__ = f"Const_{qname}"
822    CustomConst.__module__ = __name__
823
824    return CustomConst

Creates a subclass of Constant with custom name, and scaling.

Parameters
  • qname (str): the name attribute of the subclass
  • qu_omega (int): the u_omega attribute of the subclass
  • qu_mu (int): the u_mu attribute of the subclass
  • qdescription (str): a brief description of the subclass
Returns
  • CustomConst (class): the custom subclass
def define_func( qname: str, func_args: list[Val], qu_omega: int, qu_mu: int, qdescription: str = '', qdtype: numpy.dtype = <class 'numpy.float64'>):
828def define_func(qname : str, func_args : list[Val], qu_omega : int, qu_mu : int, qdescription:str="", qdtype : np.dtype=np.float64):
829    """
830    Creates a subclass of `Func` with custom name, scaling, and argument signature.
831
832    Parameters
833    ----------
834    qname : str
835        the `name` attribute of the subclass
836    qu_omega : int
837        the `u_omega` attribute of the subclass
838    qu_mu : int
839        the `u_mu` attribute of the subclass
840    qdtype : Numpy Data Type
841        the `dtype` attribute of the subclass
842    qdescription : str
843        a brief description of the subclass
844
845    Returns
846    -------
847    CustomFunc : class
848        the custom subclass
849
850    Raises
851    ------
852    TypeError
853        if the data type is not a subtype of `numpy.floating`
854    """
855
856    if not( np.issubdtype(qdtype, np.floating) ):
857        raise TypeError("define_func's data-type must be a subtype of 'np.floating'.")
858    
859    class CustomFunc(Func):
860        __doc__ = docs_bgtypes.DOCS["define_func.CustomFunc"]
861        name=qname
862        u_omega = qu_omega
863        u_mu = qu_mu
864        args = func_args
865        dtype = qdtype
866        description = qdescription
867        def __init__(self, func, sys):
868            super().__init__(func, sys)
869
870    CustomFunc.__qualname__ = f"Func_{qname}"
871    CustomFunc.__module__ = __name__
872
873    return CustomFunc

Creates a subclass of Func with custom name, scaling, and argument signature.

Parameters
  • qname (str): the name attribute of the subclass
  • qu_omega (int): the u_omega attribute of the subclass
  • qu_mu (int): the u_mu attribute of the subclass
  • qdtype (Numpy Data Type): the dtype attribute of the subclass
  • qdescription (str): a brief description of the subclass
Returns
  • CustomFunc (class): the custom subclass
Raises
  • TypeError: if the data type is not a subtype of numpy.floating
def define_gauge( qname: str, qzeros: list[Variable], qcutoff: Variable):
875def define_gauge(qname : str, qzeros : list[Variable], qcutoff : Variable):
876    """
877    A class factory creating custom  `GaugeField` classes with new name, zero variables, and cutoff scale.
878
879    Parameters
880    ----------
881    qname : str
882        the `name` attribute of the subclass
883    qzeros : list of Variable
884        the `zeros` attribute of the new subclass 
885    qcutoff : Variable
886        the `cutoff` attribute of the new subclass
887
888    Returns
889    -------
890    CustomGaugeField : class
891        the custom subclass
892    """
893    class CustomGaugeField(GaugeField):
894        name = qname
895        zeros = qzeros
896        cutoff = qcutoff
897        
898    CustomGaugeField.__qualname__ = f"GaugeField_{qname}"
899    CustomGaugeField.__module__ = __name__
900    return CustomGaugeField

A class factory creating custom GaugeField classes with new name, zero variables, and cutoff scale.

Parameters
  • qname (str): the name attribute of the subclass
  • qzeros (list of Variable): the zeros attribute of the new subclass
  • qcutoff (Variable): the cutoff attribute of the new subclass
Returns
  • CustomGaugeField (class): the custom subclass