Source code for pycif.plugins.controlvects.standard.utils.rescale_std

import numpy as np
from logging import debug
from .get_physical import get_physical
from .....utils.check.errclass import CifError


[docs] def rescale_std(controlvect, tracer, comp, trcr, glob_err, **kwargs): """Re-scale errors for a given tracer depending on the specied total budget :param controlvect: reference control vector :param tracer: tracer object :param glob_err: error on global budget :return: """ debug("Computing global error for rescaling") # First try reading from pre-computed simulation file_scaling = f"{controlvect.workdir}/controlvect/globerror_scaling_{comp}_{trcr}.txt" try: with open(file_scaling, "r") as f: errscalar = float(f.read()) controlvect.std[tracer.xpointer: tracer.xpointer + tracer.dim] *= errscalar return except IOError: debug("No file to retrieve, computing from scratch") pass except Exception as e: debug( f"Could not retrieve information from {file_scaling}. " "Check the file and consider removing it to force re-computation of error scaling factor" ) raise e # Fetch tracer std from global control vector # If scalar, re-build physical values if getattr(tracer, "type", "scalar") == "scalar": xb, std = get_physical(controlvect, tracer, comp, trcr) else: std = controlvect.std[tracer.xpointer: tracer.xpointer + tracer.dim] # Scale with areas if tracer.hresol not in ["hpixels", "global"]: areas = tracer.region_areas elif tracer.hresol == "hpixels": areas = tracer.domain.areas else: raise CifError("Check how to deal with global resolution") xerr = (np.reshape(std, (tracer.ndates, tracer.nlev, -1)) * areas.flatten()[np.newaxis]).flatten() # Re-scaling by total areas if getattr(glob_err, "surface_unit", False): xerr /= areas.mean() # Put std in absolute value using "unit_scale" # Also include temporal period unit_scale = getattr(glob_err, "unit_scale", 1) xerr *= unit_scale # Re-create temporal correlation if any to aggregate temporally tcorr = np.eye(tracer.ndates) if hasattr(tracer, "tcorrelations") \ and getattr(glob_err, "account_correlations", True): tcorr = tracer.tcorrelations tsqrt_evalues = tcorr.sqrt_evalues tevectors = tcorr.evectors tcorr = tevectors.dot(np.diag(tsqrt_evalues ** 2)).dot(tevectors.T) xerr = np.reshape(xerr, (tracer.ndates, -1)) xerr = np.sqrt(np.sum(xerr * np.dot(tcorr, xerr), axis=0)) # Rescale to period error if getattr(glob_err, "frequency_unit", False): xerr /= np.sqrt(tracer.ndates) # vertical correlation ? vcorr = np.eye(tracer.nlev) xerr = np.reshape(xerr, (tracer.nlev, -1)) xerr = np.sqrt(np.sum(xerr * np.dot(vcorr, xerr), axis=0)) # Re-create horizontal correlation if any hcorr = np.eye(xerr.size) if hasattr(tracer, "hcorrelations") \ and getattr(glob_err, "account_correlations", True): hcorr = tracer.hcorrelations hsqrt_evalues = hcorr.sqrt_evalues hevectors = hcorr.evectors hcorr = hevectors.dot(np.diag(hsqrt_evalues ** 2)).dot(hevectors.T) # Computing covariances cov = np.outer(xerr, xerr) * hcorr # Scale to the chosen absolute unit, e.g., Tg/y toterr = np.sqrt(cov.sum()) # Re-scale by number of pixels if surface unit if getattr(glob_err, "surface_unit", False): toterr /= np.sqrt(xerr.size) debug(f"Total errors after unit scaling: {toterr}") debug(f"Target total errors: {glob_err.total}") # Compute error errscalar = np.sqrt(glob_err.total / toterr) debug(f"Total error scaled by {errscalar} (sqrt)") controlvect.std[tracer.xpointer: tracer.xpointer + tracer.dim] *= errscalar # Dump scaling factor for later use with open(f"{controlvect.workdir}/controlvect/globerror_scaling_{comp}_{trcr}.txt", "w") as f: f.write(str(errscalar))