From 31feb5b44b3075dafc117f6fdf111a58bfefd896 Mon Sep 17 00:00:00 2001 From: "D. A. Clarke" Date: Wed, 27 Nov 2024 17:46:24 -0700 Subject: [PATCH] fix some stuff in the plotter; suppress warnings in initialize; other small changes --- applications/main_getBeta.py | 3 +- examples/main_plotting.py | 5 +- latqcdtools/base/initialize.py | 3 +- latqcdtools/base/plotting.py | 93 +++++++++++++++++--------- latqcdtools/base/utilities.py | 18 ++++- latqcdtools/interfaces/confReader.py | 24 +++---- latqcdtools/physics/referenceScales.py | 26 ++++--- 7 files changed, 114 insertions(+), 58 deletions(-) diff --git a/applications/main_getBeta.py b/applications/main_getBeta.py index fb4eb6a..3d5ffd2 100755 --- a/applications/main_getBeta.py +++ b/applications/main_getBeta.py @@ -9,14 +9,13 @@ # import argparse -from latqcdtools.physics.referenceScales import r0_div_a, a_times_fk, ignoreBetaRange, CY_phys, CY_param +from latqcdtools.physics.referenceScales import r0_div_a, a_times_fk, CY_phys, CY_param from latqcdtools.physics.constants import fk_phys, r0_phys from latqcdtools.base.utilities import getArgs from latqcdtools.math.optimize import persistentSolve import latqcdtools.base.logger as logger logger.set_log_level('INFO') -ignoreBetaRange() parser = argparse.ArgumentParser(description='Compute a and T.') diff --git a/examples/main_plotting.py b/examples/main_plotting.py index c4d077a..21ce563 100644 --- a/examples/main_plotting.py +++ b/examples/main_plotting.py @@ -10,11 +10,10 @@ import numpy as np from latqcdtools.base.plotting import latexify, set_params, plot_file, clearPlot, preliminary, \ plot_bar, plot_hist, plt -from latqcdtools.base.initialize import initialize, finalize from latqcdtools.statistics.statistics import plot_func from latqcdtools.interfaces.interfaces import readGPL import latqcdtools.base.logger as logger - +from latqcdtools.base.initialize import initialize, finalize initialize() logger.set_log_level('INFO') @@ -43,7 +42,7 @@ legend_title="$\\ev{\\int dx}$", xlabelpos=(0.6,0.05), ylabelpos=(0.05,0.4), - font_size=10, + font_size=14, xmin=1.0, xmax=3.0, ymin=1.0 diff --git a/latqcdtools/base/initialize.py b/latqcdtools/base/initialize.py index 8af2afc..60ffada 100644 --- a/latqcdtools/base/initialize.py +++ b/latqcdtools/base/initialize.py @@ -6,7 +6,7 @@ # Some routines to set up the toolbox, especially for keeping a record of what you did. # -import os, sys +import os, sys, warnings import latqcdtools.base.logger as logger from latqcdtools.base.utilities import shell, createFilePath @@ -53,6 +53,7 @@ def initialize(logFile=None): Some common tasks to do at the start of a run where you want to keep track of things. """ global INITIALIZED + warnings.simplefilter('ignore', UserWarning) introduceYourself() if logFile is None: logFile = 'log/' + os.path.splitext(os.path.basename(sys.argv[0]))[0] + '.log' diff --git a/latqcdtools/base/plotting.py b/latqcdtools/base/plotting.py index 9b7f46e..48594f9 100644 --- a/latqcdtools/base/plotting.py +++ b/latqcdtools/base/plotting.py @@ -65,7 +65,8 @@ 'title': None, 'label': None, # What are the data called? (Will appear in legend.) 'color': None, # Color for your data. (By default each new set automatically gets different color.) - 'font_size': 12, # Default font size for text. + 'facecolor' : 'color', # Sometimes one differentiates between colors of faces and edges. (By default same as color.) + 'font_size': 12, # Controls font size for everything. 'font_weight': 'normal', # Default style of font ('normal', 'bold', 'heavy', 'light') 'alpha': 0.5, # General transparency for data. 'ticksintoplot': True, # Put ticks into plotting area. @@ -93,8 +94,13 @@ # 6 10 7 'center left' 'center' 'center right' # 3 8 4 'lower left' 'lower center' 'lower right' 'legendpos': 'best', - 'bbox_to_anchor': None, # Manual position of the legend. The very bottom-left is (0,0), and the very - # top-right is (1,1). If you set this, legendpos appears to get ignored. + 'bbox_to_anchor': None, # Manual position of the legend. If this is not None, then legendpos + # controls the point on the legend that bbox_to_anchor controls. + # for example legendpos='lower left' along with bbox_to_anchor=(0,0) + # puts the lower left corner of the legend at the bottom left corner + # of the plot. The same legendpos with bbox_to_anchor=(1,1) puts the + # lower left corner of the legend in the upper right corner of the plot. + # This is not my fault--this was a matplotlib design choice. 'legend_ncol': 1, # Number of columns in the legend. 'legend_col_spacing': None, # Spacing between columns in the legend. 'handletextpad': 0.2, # Spacing between symbol and text in legend. @@ -139,6 +145,11 @@ def latexify(bold=False): plt.rcParams['axes.formatter.use_mathtext'] = True +def resetLEGEND(): + global LEGEND + LEGEND = False + + def clearPlot(): """ Clears plot object, legend handles, and zorder. Useful if you want to do multiple plots in the same script. @@ -147,15 +158,11 @@ def clearPlot(): global INITIALIZE, ZOD, LEGEND, legend_labels, legend_handles ZOD = 10 INITIALIZE = True - LEGEND = False + resetLEGEND() legend_handles = { plt : [] } legend_labels = { plt : [] } plt.clf() - - -def resetLEGEND(): - global LEGEND - LEGEND = False + plt.close('all') def getColorGradient(NUM_COLORS,map='viridis') -> list: @@ -316,7 +323,8 @@ def fill_param_dict(params): if not LEGEND: # When filling params, check if we show the legend. This is triggered by one of these keys # being different from the default value. - for key in ('legend_title', 'legendpos', 'legend_ncol', 'legendpos_col_spacing', 'label', 'alpha_legend'): + for key in ('legend_title', 'legendpos', 'legend_ncol', 'legendpos_col_spacing', 'label', + 'alpha_legend', 'bbox_to_anchor'): if key in params: if params[key] != default_params[key]: logger.debug('Found legend trigger',key) @@ -397,6 +405,8 @@ def set_params(**params): ax.set_ylabel(params['ylabel']) ax.yaxis.get_label().set_fontsize(params['font_size']) + ax.tick_params(axis='both', which='major', labelsize=params['font_size']) + if params['title'] is not None: checkType(str,title=params['title']) plt.title(params['title']) @@ -525,8 +535,6 @@ def plot_file(filename, xcol=0, ycol=1, yecol=None, xecol=None, func = None, fun plot_lines(xdata, ydata, yedata=yedata, xedata=xedata, **params) elif style == "fill": plot_fill(xdata, ydata, yedata=yedata, **params) - elif style == "band": - plot_band(xdata, ydata, yedata, xedata, **params) else: logger.TBError("Unknown style",style) @@ -561,8 +569,8 @@ def plot_vspan(minVal,maxVal,**params): if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,handle) + set_params(**params) # Needed to put in labels globals()['ZOD'] += 1 - set_params(**params) # Needed to put in labels def plot_hspan(minVal,maxVal,**params): @@ -583,11 +591,11 @@ def plot_hspan(minVal,maxVal,**params): if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,handle) + set_params(**params) # Needed to put in labels globals()['ZOD'] += 1 - set_params(**params) # Needed to put in labels -def plot_hline(y,**params): +def plot_hline(y,minVal=None,maxVal=None,**params): """ Plot a horizontal line at y. """ @@ -597,15 +605,26 @@ def plot_hline(y,**params): ZOD = params['ZOD'] if ZOD is None: ZOD = globals()['ZOD'] - handle = ax.axhline(y=y,color=params['color'], **optional) + x1, x2 = ax.get_xlim() + if minVal is not None: + x1=minVal + if maxVal is not None: + x2=maxVal + logger.debug('x1, x2 = ',x1,x2) + # The matplotlib people are psychopaths. I couldn't get this to work in any other + # way. Yes, the comma after handle in the else branch is mandatory. + if (minVal is None) and (maxVal is None): + handle = ax.axhline(y=y,color=params['color'], **optional) + else: + handle, = ax.plot([x1, x2], [y, y], color=params['color'], **optional) globals()['ZOD'] += 1 if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,handle) - set_params(**params) + set_params(**params) -def plot_vline(x,**params): +def plot_vline(x,minVal=None,maxVal=None,**params): """ Plot a vertical line at x. """ @@ -615,12 +634,21 @@ def plot_vline(x,**params): ZOD = params['ZOD'] if ZOD is None: ZOD = globals()['ZOD'] - handle = ax.axvline(x=x,color=params['color'], **optional) + y1, y2 = ax.get_ylim() + if minVal is not None: + y1=minVal + if maxVal is not None: + y2=maxVal + logger.debug('y1, y2 = ',y1,y2) + if (minVal is None) and (maxVal is None): + handle = ax.axvline(x=x,color=params['color'], **optional) + else: + handle, = ax.plot([x, x], [y1, y2], color=params['color'], **optional) globals()['ZOD'] += 1 if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,handle) - set_params(**params) + set_params(**params) def plot_dots(xdata, ydata, yedata = None, xedata = None, **params): @@ -686,9 +714,9 @@ def plot_dots(xdata, ydata, yedata = None, xedata = None, **params): if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,handle) + set_params(**params) # Needed to put in labels globals()['ZOD'] += 1 - set_params(**params) # Needed to put in labels def plot_bar(xdata, ydata, width=None, align='edge', edgecolor='#666677',linewidth=0.2, **params): @@ -727,9 +755,9 @@ def plot_bar(xdata, ydata, width=None, align='edge', edgecolor='#666677',linewid if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,bar) + set_params(**params) # Needed to put in labels globals()['ZOD'] += 1 - set_params(**params) # Needed to put in labels def plot_hist(data, bins = None, density=False, label=None, weights=None, **params): @@ -815,10 +843,10 @@ def plot_lines(xdata, ydata, yedata=None, xedata=None, **params): if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,(line,handle)) - set_params(**params) # Needed to put in labels + set_params(**params) # Needed to put in labels -def plot_fill(xdata, ydata, yedata, xedata=None, center = True , **params): +def plot_fill(xdata, ydata, yedata, xedata=None, center=True, **params): """ Plot a filled region within ydata +/- yedata. Can set xedata along with yedata=None for vertical bands. @@ -827,7 +855,7 @@ def plot_fill(xdata, ydata, yedata, xedata=None, center = True , **params): ydata (array-like) yedata (array-like): y error xedata (array-like, optional): x error. Defaults to None. - pattern (_type_, optional): _description_. Defaults to None. + center (bool): Do you show the central line? Defaults to True. **params: Additional parameters that can be set. """ if (yedata is None) and (xedata is None): @@ -849,6 +877,11 @@ def plot_fill(xdata, ydata, yedata, xedata=None, center = True , **params): if ZOD is None: ZOD = globals()['ZOD'] + if params['facecolor'] == 'color': + facecol=params['color'] + else: + facecol=params['facecolor'] + if center : if params['color'] is not None: handle = ax.errorbar(xdata*params['xscale'], ydata*params['yscale'], linewidth=params['linewidth'], @@ -856,8 +889,6 @@ def plot_fill(xdata, ydata, yedata, xedata=None, center = True , **params): else: handle = ax.errorbar(xdata*params['xscale'], ydata*params['yscale'], linewidth=params['linewidth'], zorder=ZOD+1, alpha = params['alpha_lines'], **optional) - - col = handle[0].get_color() else: col = params['color'] @@ -866,23 +897,23 @@ def plot_fill(xdata, ydata, yedata, xedata=None, center = True , **params): pl = ax.fill_between(xdata*params['xscale'], (np.asarray(ydata*params['yscale']) - np.asarray(yedata[0]*params['yscale'])), (np.asarray(ydata*params['yscale']) + np.asarray(yedata[1]*params['yscale'])), - facecolor=col, alpha=params['alpha'], linewidth=params['linewidth'], + facecolor=facecol, alpha=params['alpha'], linewidth=params['linewidth'], zorder=ZOD, edgecolor=col, hatch=params['hatch']) else: pl = ax.fill_between(xdata*params['xscale'], (np.asarray(ydata*params['yscale']) - np.asarray(yedata*params['yscale'])), (np.asarray(ydata*params['yscale']) + np.asarray(yedata*params['yscale'])), - facecolor=col, alpha=params['alpha'], linewidth=params['linewidth'], + facecolor=facecol, alpha=params['alpha'], linewidth=params['linewidth'], zorder=ZOD, edgecolor=col, hatch=params['hatch']) else: pl = ax.fill_betweenx(ydata*params['yscale'], (np.asarray(xdata*params['xscale']) - np.asarray(xedata*params['xscale'])), (np.asarray(xdata*params['xscale']) + np.asarray(xedata*params['xscale'])), - facecolor=col, alpha=params['alpha'],linewidth=params['linewidth'], + facecolor=facecol, alpha=params['alpha'],linewidth=params['linewidth'], zorder=ZOD, edgecolor=col, hatch=params['hatch']) globals()['ZOD'] += 2 if params['label'] is not None: _update_labels(ax,params['label']) _update_handles(ax,(handle,pl)) - set_params(**params) # Needed to put in labels + set_params(**params) # Needed to put in labels diff --git a/latqcdtools/base/utilities.py b/latqcdtools/base/utilities.py index 6a2243e..51fff69 100644 --- a/latqcdtools/base/utilities.py +++ b/latqcdtools/base/utilities.py @@ -8,7 +8,7 @@ from subprocess import run, PIPE import numpy as np -import time, re, datetime, os +import time, re, datetime, os, shutil import latqcdtools.base.logger as logger @@ -316,6 +316,22 @@ def deleteFile(target): logger.warn('Unable to remove file',target) +def deleteFolder(target): + """ + Delete the folder at target, if it exists. + """ + if os.path.isdir(target): + try: + shutil.rmtree(target) + logger.info("Deleted folder",target,"and its subdirectories.") + return + except OSError: + pass + else: + pass + logger.warn('Unable to remove folder',target) + + def createFilePath(fullFileName): """ Create the directory path if it isn't there already. diff --git a/latqcdtools/interfaces/confReader.py b/latqcdtools/interfaces/confReader.py index fdb46d2..259d70a 100644 --- a/latqcdtools/interfaces/confReader.py +++ b/latqcdtools/interfaces/confReader.py @@ -10,7 +10,7 @@ import struct import latqcdtools.base.logger as logger -from latqcdtools.base.speedify import numbaON +from latqcdtools.base.speedify import numbaON,DEFAULTTHREADS from latqcdtools.math.math import rel_check from latqcdtools.base.check import checkType numbaON() @@ -38,7 +38,7 @@ def __init__(self, Ns, Nt, nproc=None): checkType(int,Ns=Ns) checkType(int,Nt=Nt) if nproc==None: - self.nproc=self.Nt + self.nproc=min(self.Nt,DEFAULTTHREADS) else: checkType(int,nproc=nproc) self.nproc=nproc @@ -83,7 +83,7 @@ def getByteSize(self): elif self.precision == 'd': return 8 else: - logger.TBError('Unknown precision',self.precision,'(expected f or d)') + logger.TBRaise('Unknown precision',self.precision,'(expected f or d)') @@ -111,7 +111,7 @@ def readHeader(self,fileName): minOffset = 78 # Minimal number of characters for the entries above, not including what comes on RHS. self.offset = header.find(b'END_HEADER\n')+11 if self.offset < minOffset: - logger.TBError(fileName,'not in NERSC format.') + logger.TBRaise(fileName,'not in NERSC format.') # Convert header to metaData dictionary. entries = header[:self.offset-1].split(b'\n')[1:-1] @@ -127,13 +127,13 @@ def readHeader(self,fileName): Nz = int(metaData[b'DIMENSION_3']) Nt = int(metaData[b'DIMENSION_4']) if Nx != self.Ns: - logger.TBError('Read Nx =',Nx,'. Expected ',self.Ns) + logger.TBRaise('Read Nx =',Nx,'. Expected ',self.Ns) if Ny != self.Ns: - logger.TBError('Read Ny =',Ny,'. Expected ',self.Ns) + logger.TBRaise('Read Ny =',Ny,'. Expected ',self.Ns) if Nz != self.Ns: - logger.TBError('Read Nz =',Nz,'. Expected ',self.Ns) + logger.TBRaise('Read Nz =',Nz,'. Expected ',self.Ns) if Nt != self.Nt: - logger.TBError('Read Nt =',Nt,'. Expected ',self.Nt) + logger.TBRaise('Read Nt =',Nt,'. Expected ',self.Nt) # Extract endianness if metaData[b'FLOATING_POINT'].endswith(b'SMALL'): @@ -141,7 +141,7 @@ def readHeader(self,fileName): elif metaData[b'FLOATING_POINT'].endswith(b'BIG'): self.endianness = '>' else: - logger.TBError('Unrecognized endianness.') + logger.TBRaise('Unrecognized endianness.') # Extract precision if metaData[b'FLOATING_POINT'].startswith(b'IEEE32'): @@ -149,7 +149,7 @@ def readHeader(self,fileName): elif metaData[b'FLOATING_POINT'].startswith(b'IEEE64'): self.precision = 'd' else: - logger.TBError('Unrecognized precision.') + logger.TBRaise('Unrecognized precision.') # Extract number of rows if metaData[b'DATATYPE'].endswith(b'3x3'): @@ -196,14 +196,14 @@ def readConf(self,fileName): if self.linkTrace is not None: linkTrace = self.gauge.getLinkTrace() if not rel_check(linkTrace, self.linkTrace): - logger.TBError(' is wrong. Compare:',linkTrace,'with',self.linkTrace) + logger.TBRaise(' is wrong. Compare:',linkTrace,'with',self.linkTrace) logger.details('Configuration',fileName,'has correct .') # Check if self.plaquette is not None: plaq = self.gauge.getPlaquette() if not rel_check(plaq, self.plaquette): - logger.TBError(' is wrong. Compare:',plaq,'with',self.plaquette) + logger.TBRaise(' is wrong. Compare:',plaq,'with',self.plaquette) logger.details('Configuration', fileName, 'has correct .') return self.gauge diff --git a/latqcdtools/physics/referenceScales.py b/latqcdtools/physics/referenceScales.py index 15cba78..97ca602 100644 --- a/latqcdtools/physics/referenceScales.py +++ b/latqcdtools/physics/referenceScales.py @@ -1,7 +1,7 @@ # # referenceScales.py # -# A collection of scales and related functions for pure SU(3) configurations. +# A collection of scales computed as a function of beta. # import numpy as np @@ -52,7 +52,7 @@ def _betaRangeWarn(beta, beta_range): """ global CHECKBETARANGE if isinstance(beta , (float,np.floating)) : - beta = np.array([ beta , beta ]) + beta = np.array([ beta , beta ]) if CHECKBETARANGE: if np.sort(beta)[0] < beta_range[0] or np.sort(beta)[-1] > beta_range[1]: logger.warn("beta out of fit range [" + str(beta_range[0]) + "," + str(beta_range[1]) + "]",frame=3) @@ -80,13 +80,13 @@ def allton_type_ansatz(beta, c0, c2, d2): # ===================================================== f_K scales -def a_times_fk(beta: float, year): +def a_times_fk(beta, year): """ Get a*f_k(beta). Args: beta (float) - year (int): year that parameterization was determined + year (int/str): year that parameterization was determined Returns: float: a*f_k @@ -130,13 +130,18 @@ def a_div_r1(beta, year): Args: beta (float) - year (int): year that parameterization was determined + year (int/str): year that parameterization was determined Returns: float: a/r_1 """ - # https://arxiv.org/pdf/2107.10011.pdf, 10.1103/PhysRevD.104.074512 + # https://arxiv.org/pdf/2107.10011.pdf. Note that in this study, they say they update + # numbers from https://arxiv.org/pdf/1407.6387.pdf making changes that seem unrelated + # to the beta range, while providing no new beta range. I therefore assume the beta + # range is the same as Table IV of https://arxiv.org/pdf/1407.6387.pdf if str(year) == "2021": + beta_range = [5.9, 7.825] + _betaRangeWarn(beta, beta_range) c0 = 43.16 c2 = 339472 d2 = 5452.0 @@ -149,11 +154,16 @@ def a_div_r1(beta, year): d2 = 5584 # https://arxiv.org/pdf/1407.6387.pdf elif str(year) == "2014": + beta_range = [5.9, 7.825] + _betaRangeWarn(beta, beta_range) c0 = 43.1 c2 = 343236.0 d2 = 5514.0 - # https://arxiv.org/pdf/1111.1710.pdf + # https://arxiv.org/pdf/1111.1710.pdf. It should be noted that this one has its LCP + # tuned at ms/ml=20. elif str(year) == "2012": + beta_range = [5.9, 7.28] + _betaRangeWarn(beta, beta_range) c0 = 44.06 c2 = 272102.0 d2 = 4281.0 @@ -202,7 +212,7 @@ def r0_div_a(beta,year): Args: beta (float) - year (int): year that parameterization was determined + year (int/str): year that parameterization was determined Returns: float: r0/a