## NIFTY (Numerical Information Field Theory) has been developed at the
## Max-Planck-Institute for Astrophysics.
##
## Copyright (C) 2013 Max-Planck-Society
##
## Author: Marco Selig
## Project homepage:
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
## See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see .
from sys import stdout as so
import os
import inspect
import d2o
import keepers
from nifty import __version__
MPI = d2o.config.dependency_injector[
keepers.get_Configuration('D2O')['mpi_module']]
comm = MPI.COMM_WORLD
size = comm.size
rank = comm.rank
class switch(object):
"""
.. __ __ __
.. /__/ / /_ / /
.. _______ __ __ __ / _/ _______ / /___
.. / _____/ | |/\/ / / / / / / ____/ / _ |
.. /_____ / | / / / / /_ / /____ / / / /
.. /_______/ |__/\__/ /__/ \___/ \______/ /__/ /__/ class
NIFTY support class for switches.
Parameters
----------
default : bool
Default status of the switch (default: False).
See Also
--------
notification : A derived class for displaying notifications.
Examples
--------
>>> option = switch()
>>> option.status
False
>>> option
OFF
>>> print(option)
OFF
>>> option.on()
>>> print(option)
ON
Attributes
----------
status : bool
Status of the switch.
"""
def __init__(self,default=False):
"""
Initilizes the switch and sets the `status`
Parameters
----------
default : bool
Default status of the switch (default: False).
"""
self.status = bool(default)
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def on(self):
"""
Switches the `status` to True.
"""
self.status = True
def off(self):
"""
Switches the `status` to False.
"""
self.status = False
def toggle(self):
"""
Switches the `status`.
"""
self.status = not self.status
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def __repr__(self):
if(self.status):
return "ON"
else:
return "OFF"
##-----------------------------------------------------------------------------
##-----------------------------------------------------------------------------
class notification(switch):
"""
.. __ __ ____ __ __ __
.. / /_ /__/ / _/ /__/ / /_ /__/
.. __ ___ ______ / _/ __ / /_ __ _______ ____ __ / _/ __ ______ __ ___
.. / _ | / _ | / / / / / _/ / / / ____/ / _ / / / / / / _ | / _ |
.. / / / / / /_/ / / /_ / / / / / / / /____ / /_/ / / /_ / / / /_/ / / / / /
.. /__/ /__/ \______/ \___/ /__/ /__/ /__/ \______/ \______| \___/ /__/ \______/ /__/ /__/ class
NIFTY support class for notifications.
Parameters
----------
default : bool
Default status of the switch (default: False).
ccode : string
Color code as string (default: "\033[0m"). The surrounding special
characters are added if missing.
Notes
-----
The color code is a special ANSI escape code, for a list of valid codes
see [#]_. Multiple codes can be combined by seperating them with a
semicolon ';'.
References
----------
.. [#] Wikipedia, `ANSI escape code `_.
Examples
--------
>>> note = notification()
>>> note.status
True
>>> note.cprint("This is noteworthy.")
This is noteworthy.
>>> note.cflush("12"); note.cflush('3')
123
>>> note.off()
>>> note.cprint("This is noteworthy.")
>>>
Raises
------
TypeError
If `ccode` is no string.
Attributes
----------
status : bool
Status of the switch.
ccode : string
Color code as string.
"""
_code = "\033[0m" ## "\033[39;49m"
_ccode_red = "\033[31;1m"
_ccode_yellow = "\033[33;1m"
_ccode_green = "\033[32;1m"
def __init__(self,default=True,ccode="\033[0m"):
"""
Initializes the notification and sets `status` and `ccode`
Parameters
----------
default : bool
Default status of the switch (default: False).
ccode : string
Color code as string (default: "\033[0m"). The surrounding
special characters are added if missing.
Raises
------
TypeError
If `ccode` is no string.
"""
self.status = bool(default)
## check colour code
if(not isinstance(ccode,str)):
raise TypeError(about._errors.cstring("ERROR: invalid input."))
if(ccode[0]!="\033"):
ccode = "\033"+ccode
if(ccode[1]!='['):
ccode = ccode[0]+'['+ccode[1:]
if(ccode[-1]!='m'):
ccode = ccode+'m'
self.ccode = ccode
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def set_ccode(self,newccode=None):
"""
Resets the the `ccode` string.
Parameters
----------
newccode : string
Color code as string (default: "\033[0m"). The surrounding
characters "\033", '[', and 'm' are added if missing.
Returns
-------
None
Raises
------
TypeError
If `ccode` is no string.
Examples
--------
>>> note = notification()
>>> note.set_ccode("31;1") ## "31;1" corresponds to red and bright
"""
if(newccode is None):
newccode = self._code
else:
## check colour code
if(not isinstance(newccode,str)):
raise TypeError(about._errors.cstring("ERROR: invalid input."))
if(newccode[0]!="\033"):
newccode = "\033"+newccode
if(newccode[1]!='['):
newccode = newccode[0]+'['+newccode[1:]
if(newccode[-1]!='m'):
newccode = newccode+'m'
self.ccode = newccode
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def _get_caller(self):
result = ''
i = 2
current = inspect.stack()[i][3]
while current != '':
result = '.' + current + result
i += 1
current = inspect.stack()[i][3]
return result[1:]
def cstring(self, subject):
"""
Casts an object to a string and augments that with a colour code.
Parameters
----------
subject : {string, object}
String to be augmented with a color code. A given object is
cast to its string representation by :py:func:`str`.
Returns
-------
cstring : string
String augmented with a color code.
"""
if rank == 0:
return self.ccode + str(self._get_caller()) + ':\n' + \
str(subject) + self._code + '\n'
def cflush(self, subject):
"""
Flushes an object in its colour coded sting representation to the
standard output (*without* line break).
Parameters
----------
subject : {string, object}
String to be flushed. A given object is
cast to a string by :py:func:`str`.
Returns
-------
None
"""
if self.status and rank == 0:
so.write(self.cstring(subject))
so.flush()
def cprint(self, subject):
"""
Flushes an object in its colour coded sting representation to the
standard output (*with* line break).
Parameters
----------
subject : {string, object}
String to be flushed. A given object is
cast to a string by :py:func:`str`.
Returns
-------
None
"""
if self.status and rank == 0:
so.write(self.cstring(subject)+"\n")
so.flush()
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def __repr__(self):
if(self.status):
return self.cstring("ON")
else:
return self.cstring("OFF")
##-----------------------------------------------------------------------------
class _about(object): ## nifty support class for global settings
"""
NIFTY support class for global settings.
.. warning::
Turning off the `_error` notification will suppress all NIFTY error
strings (not recommended).
Examples
--------
>>> from nifty import *
>>> about
nifty version 0.2.0
>>> print(about)
nifty version 0.2.0
- errors = ON (immutable)
- warnings = ON
- infos = OFF
- multiprocessing = ON
- hermitianize = ON
- lm2gl = ON
>>> about.infos.on()
>>> about.about.save_config()
>>> from nifty import *
INFO: nifty version 0.2.0
>>> print(about)
nifty version 0.2.0
- errors = ON (immutable)
- warnings = ON
- infos = ON
- multiprocessing = ON
- hermitianize = ON
- lm2gl = ON
Attributes
----------
warnings : notification
Notification instance controlling whether warings shall be printed.
infos : notification
Notification instance controlling whether information shall be
printed.
multiprocessing : switch
Switch instance controlling whether multiprocessing might be
performed.
hermitianize : switch
Switch instance controlling whether hermitian symmetry for certain
:py:class:`rg_space` instances is inforced.
lm2gl : switch
Switch instance controlling whether default target of a
:py:class:`lm_space` instance is a :py:class:`gl_space` or a
:py:class:`hp_space` instance.
"""
def __init__(self):
"""
Initializes the _about and sets the attributes.
"""
## version
self._version = str(__version__)
## switches and notifications
self._errors = notification(default=True,
ccode=notification._code)
self.warnings = notification(default=True,
ccode=notification._code)
self.infos = notification(default=False,
ccode=notification._code)
self.multiprocessing = switch(default=True)
self.hermitianize = switch(default=True)
self.lm2gl = switch(default=True)
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def load_config(self,force=True):
"""
Reads the configuration file "~/.nifty/nifty_config".
Parameters
----------
force : bool
Whether to cause an error if the file does not exsist or not.
Returns
-------
None
Raises
------
ValueError
If the configuration file is malformed.
OSError
If the configuration file does not exist.
"""
nconfig = os.path.expanduser('~')+"/.nifty/nifty_config"
if(os.path.isfile(nconfig)):
rawconfig = []
with open(nconfig,'r') as configfile:
for ll in configfile:
if(not ll.startswith('#')):
rawconfig += ll.split()
try:
self._errors = notification(default=True,ccode=rawconfig[0])
self.warnings = notification(default=int(rawconfig[1]),ccode=rawconfig[2])
self.infos = notification(default=int(rawconfig[3]),ccode=rawconfig[4])
self.multiprocessing = switch(default=int(rawconfig[5]))
self.hermitianize = switch(default=int(rawconfig[6]))
self.lm2gl = switch(default=int(rawconfig[7]))
except(IndexError):
raise ValueError(about._errors.cstring("ERROR: '"+nconfig+"' damaged."))
elif(force):
raise OSError(about._errors.cstring("ERROR: '"+nconfig+"' nonexisting."))
def save_config(self):
"""
Writes to the configuration file "~/.nifty/nifty_config".
Returns
-------
None
"""
rawconfig = [self._errors.ccode[2:-1],str(int(self.warnings.status)),self.warnings.ccode[2:-1],str(int(self.infos.status)),self.infos.ccode[2:-1],str(int(self.multiprocessing.status)),str(int(self.hermitianize.status)),str(int(self.lm2gl.status))]
nconfig = os.path.expanduser('~')+"/.nifty/nifty_config"
if(os.path.isfile(nconfig)):
rawconfig = [self._errors.ccode[2:-1],str(int(self.warnings.status)),self.warnings.ccode[2:-1],str(int(self.infos.status)),self.infos.ccode[2:-1],str(int(self.multiprocessing.status)),str(int(self.hermitianize.status)),str(int(self.lm2gl.status))]
nconfig = os.path.expanduser('~')+"/.nifty/nifty_config"
with open(nconfig,'r') as sourcefile:
with open(nconfig+"_",'w') as targetfile:
for ll in sourcefile:
if(ll.startswith('#')):
targetfile.write(ll)
else:
ll = ll.replace(ll.split()[0],rawconfig[0]) ## one(!) per line
rawconfig = rawconfig[1:]
targetfile.write(ll)
os.rename(nconfig+"_",nconfig) ## overwrite old congiguration
else:
if(not os.path.exists(os.path.expanduser('~')+"/.nifty")):
os.makedirs(os.path.expanduser('~')+"/.nifty")
with open(nconfig,'w') as targetfile:
for rr in rawconfig:
targetfile.write(rr+"\n") ## one(!) per line
##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def __repr__(self):
return "nifty version "+self._version
def __str__(self):
return "nifty version "+self._version+"\n- errors = "+self._errors.cstring("ON")+" (immutable)\n- warnings = "+str(self.warnings)+"\n- infos = "+str(self.infos)+"\n- multiprocessing = "+str(self.multiprocessing)+"\n- hermitianize = "+str(self.hermitianize)+"\n- lm2gl = "+str(self.lm2gl)
##-----------------------------------------------------------------------------
## set global instance
about = _about()
#about.load_config(force=False)
#about.infos.cprint("INFO: "+about.__repr__())