"""Post-processes a set of runs.1. Extracts result data,2. Plots the computed average magnetization against temperature,3. Interpolates the computed points using a PCHIP interpolator,4. Determines the Curie temperature as the value below which the magnetization drops under 0.1."""frompathlibimportPathimportnumpyasnpfromscipy.interpolateimportPchipInterpolatorfromscipy.optimizeimportbrentqfromllg3d.ioimportload_results
[docs]classMagTempData:""" Handle magnetization data using the npz format. - Extracts result data, - Interpolates the computed points using a PCHIP interpolator, - Determines the Curie temperature as the value below which the magnetization drops under 0.1. Args: *files: Paths to the result .npz files """n_interp=200#: number of interpolation pointsdef__init__(self,*files:Path|str)->None:#: list of result filesself.files:list[Path]=[Path(file)forfileinfiles]self.params:dict={}#: common parameters of the runs# Extract data from the runsdata=self._process_jobs()self.temperature=data[:,0]#: temperatures from the runsself.m1_mean=data[:,1]#: mean magnetization# Use PCHIP interpolator which preserves positivity and monotonicity#: interpolated magnetization functionself.interp=PchipInterpolator(self.temperature,self.m1_mean)self.temperature_interp=np.linspace(self.temperature.min(),self.temperature.max(),self.n_interp)#: finer temperature grid for interpolation@propertydefT_Curie(self)->float:""" Return the Curie temperature. It is defined as the temperature at which the magnetization equals 0.1, found using a root-finding algorithm for precision. Returns: float: Curie temperature Raises: ValueError: If the magnetization never crosses 0.1 in the dataset """# Check if magnetization ever crosses 0.1T_min=self.temperature.min()T_max=self.temperature.max()m1_min=self.interp(T_min)m1_max=self.interp(T_max)# If 0.1 is never reachedif(m1_min<0.1andm1_max<0.1)or(m1_min>0.1andm1_max>0.1):raiseValueError(f"Magnetization never crosses 0.1 in the dataset. "f"Range: [{m1_min:.4f}, {m1_max:.4f}]")# Find the exact temperature where m = 0.1 using Brent's methodT_curie=brentq(lambdaT:float(self.interp(T))-0.1,T_min,T_max)returnfloat(T_curie)
[docs]def_process_jobs(self)->np.ndarray:""" Iterates through calculation directories to assemble data. Returns: data a numpy array (T, <m>) Raises: ValueError: If any file does not end with .npz """forfileinself.files:ifnotfile.name.endswith(".npz"):raiseValueError(f"File {file} should end with .npz")# Get parameters from the first run filefirst_results=load_results(self.files[0])self.params=first_results.params.as_dict()# Store common parametersdata=[]# Iterating through run directoriesforfileinself.files:print(f"Processing file: {file}")run_results=load_results(file)params=run_results.params.as_dict()m1_mean=np.nanif"observables"inrun_results.results:m1_mean=run_results.results["observables"].get("m1_mean",np.nan)data.append([params["T"],m1_mean])data.sort()# Sorting by increasing temperaturesreturnnp.array(data)