diff --git a/tests/dev_tests/README.md b/tests/dev_tests/README.md new file mode 100644 index 0000000..5682b67 --- /dev/null +++ b/tests/dev_tests/README.md @@ -0,0 +1,28 @@ +# Development test suite + +## Overview + +This directory contains the necessary code and data to enable the generation and execution of development tests for the atoMEC project. These tests are dsigned to evaluate the _performance_ of the code, with a focus on the `CalcEnergy` function, its related components, and behavior under extreme edge cases. They are distinct from the CI tests, which are designed to check the _correctness_ of the code across the full codebase. They are not mandatory but are recommended for developers making significant changes to performance-critical parts of the code, especially when modifications impact the execution time observed in CI tests. + +## Development testing tools + +The development tests themselves are not directly included. Instead, the repository provides the necessary tools to generate and run these tests: + +- `benchmarking.py`: The core module containing functions to set up the benchmarking environment +- `pressure_benchmarks.csv`: The dataset containing parameters for generating test cases +- `test.py`: The template for creating individual test scripts +- `submit.slurm`: A sample SLURM submission script for use on HPC systems +- `run_benchmark_tests.py`: A script that demonstrates how to run the entire testing workflow using the provided tools +- `comp_benchmark_tests.py`: A script that compares the results from two csv files generated from `run_benchmark_tests.py` + +## Environment assumption + +The testing workflow currently assumes that atoMEC is operated within a Conda virtual environment. + +## Execution Instructions + +The full testing workflow can be run on a slurm-based HPC system with the `run_benchmark_tests.py` script. The script needs to be first run in "setup_and_run" mode, which sets up the calculations and submits them to the slurm system (these steps can also be run separately if preferred). Then it should be run in "evaluate" mode, to collect and summarize the results. + +## Evaluation and benchmarking protocol + +Benchmarking should be conducted against the results from the most recent iteration of the development branch. This means that *two* testing workflows should be set-up, one for the branch being submitted as a PR, and one for atoMEC's development branch. After generating the results, performance can be compared by running the `comp_benchmark_tests.py` script. The most important benchmark is considered to be the "Average time % difference", an average of the row-by-row percentage difference between the times taken. diff --git a/tests/dev_tests/benchmarking.py b/tests/dev_tests/benchmarking.py new file mode 100644 index 0000000..f110a5e --- /dev/null +++ b/tests/dev_tests/benchmarking.py @@ -0,0 +1,411 @@ +""" +Sets up and runs advanced benchmarking tests for developers. + +It is designed to submit many jobs simultaneously on an HPC system. +Please see the README.md file for more information. +""" + +import os +import pandas as pd +import shutil +import json +import subprocess +import numpy as np + + +def set_up_calcs(basedir, env, testfile="test.py"): + """ + Set up calculations by creating directories and preparing files. + + Parameters + ---------- + basedir : str + The base directory where calculations will be set up. + env : str + The environment variable to be used in the calculations. + testfile : str, optional + The name of the test file to use for calculations. + + Returns + ------- + None + """ + if os.path.exists(basedir): + user_input = ( + input( + f"The directory {basedir} already exists." + + "Are you sure you want to continue? (yes/no): " + ) + .strip() + .lower() + ) + if user_input != "yes": + print("Operation cancelled.") + return + + # Read the CSV file + df = pd.read_csv("pressure_benchmarks.csv") + + # Loop through the rows of the dataframe + for index, row in df.iterrows(): + species = row["species"] + rho = row["rho"] + rho_round = round(rho, 3) + temp = row["temp"] + temp_round = round(temp, 3) + + # Create the sub-folder + subdir = os.path.join(basedir, f"{species}/rho_{rho_round}/T_{temp_round}") + os.makedirs(subdir, exist_ok=True) + + # Read the original submit.slurm file + with open("submit.slurm", "r") as file: + filedata = file.read() + + # Replace the target string + filedata = filedata.replace("env={ENV}", f"env={env}") + + # Write the modified submit.slurm file to the new location + with open(subdir + "/submit.slurm", "w") as file: + file.write(filedata) + + # same for python script + shutil.copy(testfile, subdir + "/test.py") + + # Make a JSON file containing all the information from the row + row_dict = row.to_dict() + with open(os.path.join(subdir, "input.json"), "w") as f: + json.dump(row_dict, f, indent=4) + + print("Setup complete.") + + +def run_calcs(basedir): + """ + Run calculations by submitting jobs to an HPC system. + + Parameters + ---------- + basedir : str + The base directory where calculations are set up. + + Returns + ------- + None + """ + # Read the CSV file + df = pd.read_csv("pressure_benchmarks.csv") + + # Loop through the rows of the dataframe + for index, row in df.iterrows(): + species = row["species"] + rho = row["rho"] + rho_round = round(rho, 3) + temp = row["temp"] + temp_round = round(temp, 3) + + # Define the sub-folder + subdir = os.path.join(basedir, f"{species}/rho_{rho_round}/T_{temp_round}") + + # Check if the directory exists + if not os.path.exists(subdir): + print(f"Directory does not exist: {subdir}") + continue + + # Check if submit.slurm exists in the directory + if not os.path.exists(os.path.join(subdir, "submit.slurm")): + print(f"submit.slurm does not exist in: {subdir}") + continue + + # Execute sbatch submit.slurm + try: + subprocess.run(["sbatch", "submit.slurm"], check=True, cwd=subdir) + print(f"Job submitted for {species}, rho_{rho}, T_{temp}") + except subprocess.CalledProcessError as e: + print(f"Failed to submit job for {species}, rho_{rho}, T_{temp}: {e}") + + +# Function to check if the values are close +def check_close(input_val, output_val, atol=1e-9, rtol=1e-2): + """ + Check if two values are close to each other within a tolerance. + + By default it checks for a relative tolerance of 1%. + Absolute tolerance is not considered. + + Parameters + ---------- + input_val : float + The first value to compare. + output_val : float + The second value to compare. + atol : float, optional + The absolute tolerance parameter. + rtol : float, optional + The relative tolerance parameter. + + Returns + ------- + bool + True if the values are close within the specified tolerances, False otherwise. + """ + return np.isclose(input_val, output_val, atol=atol, rtol=rtol) + + +# Function to calculate the percentage error +def calculate_percentage_error(value1, value2): + """ + Calculate the percentage error between two values. + + Parameters + ---------- + value1 : float + The first value (reference value). + value2 : float + The second value (value to compare). + + Returns + ------- + float + The percentage error between the two values. + """ + if value1 == 0 and value2 == 0: + return 0 # Avoid division by zero if both values are zero + try: + return round(abs((value1 - value2) / value1) * 100, 3) + except ZeroDivisionError: + return np.inf # Return infinity if value1 is zero + + +# Function to gather benchmark results and save to a new file +def gather_benchmark_results(basedir, new_filename): + """ + Gather benchmark results from JSON files and save them to a new CSV file. + + Parameters + ---------- + basedir : str + The base directory where the benchmark results are stored. + new_filename : str + The name of the new CSV file to save the results. + + Returns + ------- + None + """ + # Read the existing dataframe + df = pd.read_csv("pressure_benchmarks.csv") + + # Prepare a list to hold the new data + new_data = [] + + # Loop through the rows of the dataframe + for index, row in df.iterrows(): + species = row["species"] + rho = row["rho"] + temp = row["temp"] + + # Define the sub-folder + subdir = os.path.join( + basedir, f"{species}/rho_{round(rho, 3)}/T_{round(temp, 3)}" + ) + + # Initialize the outcome and percentage errors + outcome = "fail" + pc_err_st = pc_err_vir = pc_err_id = time_s = None + + # Read input.json and output.json files + try: + with open(os.path.join(subdir, "input.json"), "r") as f: + input_data = json.load(f) + + with open(os.path.join(subdir, "output.json"), "r") as f: + output_data = json.load(f) + + # Extract the relevant values + P_st_rr_input = input_data["P_st_rr"] + P_vir_nocorr_input = input_data["P_vir_nocorr"] + P_id_input = input_data["P_id"] + + P_st_rr_output = output_data["P_st_rr"] + P_vir_nocorr_output = output_data["P_vir_nocorr"] + P_id_output = output_data["P_id"] + time_s = round(output_data["time"], 2) + + # Calculate percentage errors + pc_err_st = calculate_percentage_error(P_st_rr_input, P_st_rr_output) + pc_err_vir = calculate_percentage_error( + P_vir_nocorr_input, P_vir_nocorr_output + ) + pc_err_id = calculate_percentage_error(P_id_input, P_id_output) + + # Check if the values are close within the specified relative tolerance + # The original calculations were converged to 1% + rtol = 1e-2 # 1% + if ( + np.isclose(P_st_rr_input, P_st_rr_output, rtol=rtol) + and np.isclose(P_vir_nocorr_input, P_vir_nocorr_output, rtol=rtol) + and np.isclose(P_id_input, P_id_output, rtol=rtol) + ): + outcome = "pass" + + except Exception as e: + print(f"Error reading files for {species}, rho_{rho}, T_{temp}: {e}") + outcome = "fail" + + # Append the new row to the data list + new_data.append( + [species, rho, temp, outcome, pc_err_st, pc_err_vir, pc_err_id, time_s] + ) + + # Create the new dataframe with additional columns for percentage errors + new_df_columns = [ + "species", + "rho", + "temp", + "outcome", + "pc_err_st", + "pc_err_vir", + "pc_err_id", + "time_s", + ] + new_df = pd.DataFrame(new_data, columns=new_df_columns) + + # Save the new dataframe to the specified CSV file + new_df.to_csv(new_filename, index=False) + print(f"Results saved to {new_filename}") + + +def analyze_benchmark_results(csv_file): + """ + Read benchmark results from a CSV file and analyzes the data. + + Parameters + ---------- + csv_file : str + The path to the CSV file containing benchmark results + + Returns + ------- + None + """ + # Read the CSV file into a DataFrame + df = pd.read_csv(csv_file) + + # Calculate the number of tests passed + total_tests = len(df) + passed_tests = len(df[df["outcome"] == "pass"]) + pass_fraction = passed_tests / total_tests + + # Calculate median and max for error columns + err_st_stats = {"median": df["pc_err_st"].median(), "max": df["pc_err_st"].max()} + err_vir_stats = { + "median": df["pc_err_vir"].median(), + "max": df["pc_err_vir"].max(), + } + err_id_stats = {"median": df["pc_err_id"].median(), "max": df["pc_err_id"].max()} + + # Calculate mean, median, quartiles, and max for time column + time_stats = { + "mean": df["time_s"].mean(), + "median": df["time_s"].median(), + "q1": df["time_s"].quantile(0.25), + "q3": df["time_s"].quantile(0.75), + "max": df["time_s"].max(), + } + + # Format the table + table = f""" + Benchmarking Results Analysis + ----------------------------- + Tests passed: {passed_tests} / {total_tests} ({pass_fraction:.2%}) + + Error Statistics + ----------------------------------------- + | Error Type | Median | Max | + |-------------|------------|------------| + | pc_err_st | {err_st_stats['median']:10.2f} | {err_st_stats['max']:10.2f} | + | pc_err_vir | {err_vir_stats['median']:10.2f} | {err_vir_stats['max']:10.2f} | + | pc_err_id | {err_id_stats['median']:10.2f} | {err_id_stats['max']:10.2f} | + + Time Statistics (s) + ------------------------ + | Statistic | Value | + |-----------|----------| + | Mean | {time_stats['mean']:8.2f} | + | Median | {time_stats['median']:8.2f} | + | Q1 | {time_stats['q1']:8.2f} | + | Q3 | {time_stats['q3']:8.2f} | + | Max | {time_stats['max']:8.2f} | + """ + print(table) + + +def calc_time_diff(csv_ref, csv_new): + """ + Calculate the average percentage difference in timings between two atoMEC versions. + + Parameters + ---------- + csv_ref : str + Filepath to the reference CSV file. + csv_new : str + Filepath to the new CSV file to compare against the reference. + + Returns + ------- + float + The average absolute percentage difference of the 'time_s' column + between the reference and new datasets. + + Notes + ----- + The percentage difference is calculated using the formula: + 100 * abs(time_new - time_ref) / time_ref + This formula gives the average of the absolute relative differences + from the reference to the new values. + """ + # Read the time_s column from each CSV into a DataFrame + df_ref = pd.read_csv(csv_ref)["time_s"] + df_new = pd.read_csv(csv_new)["time_s"] + + # Calculate the absolute percentage difference + time_diff_pc = 100 * (df_ref - df_new) / df_ref + + # Return the mean of these percentage differences, rounded to 2 decimal places + return round(time_diff_pc.mean(), 2) + + +def comp_benchmark_results(csv_ref, csv_new): + """ + Compare benchmark results between a reference CSV file and a new CSV file. + + Prints individual test and timings results, and finally does a row-by-row + percentage difference of timings. + + Parameters + ---------- + csv_ref : str + Filepath to the reference CSV file containing benchmark results. + csv_new : str + Filepath to the new CSV file containing benchmark results to be compared. + + Returns + ------- + None + """ + # Print results from the reference CSV + print("\nResults from reference csv") + print("--------------------------") + analyze_benchmark_results(csv_ref) + + # Print results from the new CSV + print("\nResults from new csv") + print("--------------------------") + analyze_benchmark_results(csv_new) + + # Calculate and print the average time percentage difference + avg_time_diff = calc_time_diff(csv_ref, csv_new) + print("\n-------------------------------------") + print(f" Average time % difference = {avg_time_diff}% ") + print("-------------------------------------") diff --git a/tests/dev_tests/comp_benchmark_tests.py b/tests/dev_tests/comp_benchmark_tests.py new file mode 100644 index 0000000..5864eac --- /dev/null +++ b/tests/dev_tests/comp_benchmark_tests.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +"""Compare results from two versions of atoMEC.""" + +import benchmarking + +# user inputs - MUST BE EDITED + +# reference csv file (generated from latest atoMEC dev branch) +csv_ref_file = "03-11-23_atoMEC38_benchmark_results.csv" + +# new csv file (generated from branch submitted as PR) +csv_new_file = "08-11-23_atoMEC312_benchmark_results.csv" + +# end of user inputs + +benchmarking.comp_benchmark_results(csv_ref_file, csv_new_file) diff --git a/tests/dev_tests/pressure_benchmarks.csv b/tests/dev_tests/pressure_benchmarks.csv new file mode 100644 index 0000000..036cda6 --- /dev/null +++ b/tests/dev_tests/pressure_benchmarks.csv @@ -0,0 +1,101 @@ +species,rho,temp,nconv,nmax,lmax,ngrid,nkpts,P_id,P_st_rr,P_vir_nocorr +C,15.499297,2786.4411157095856,1e-05,24,64,500,20,2072044.7230353267,2017313.5879749784,2039377.1269299167 +C,19.374129,0.861732831109502,1e-05,13,13,500,20,16103.546265615396,21595.25568343568,16021.731183437241 +Mg,17.222007,1393.2206009414344,1e-05,24,60,500,20,1095119.300216119,1068912.6205698776,1075873.3758255616 +H,0.578952,15.66845720164852,1e-05,13,17,500,20,742.5591618533304,742.3922037993696,585.6867926766153 +C,19.374129,696.6102573840757,1e-05,20,36,500,20,622269.3306860247,598344.1075766252,606683.5332387362 +Si,18.631996,696.6102573840757,1e-05,22,44,500,20,531446.1352081429,518812.6632073754,519672.61316564155 +Ne,7.895891,0.430866415554751,1.0000000000000002e-07,19,19,750,225,1620.4179778840714,240.08536105528836,-140.74839213597792 +O,3.630449,348.3055164718118,1e-05,22,44,500,20,54565.730665016374,53604.23695665938,53304.06291678641 +He,5.353887,0.430866415554751,1e-05,13,13,500,100,1517.2580597614203,1730.0200558186996,1522.4201583723757 +Mg,64.582573,116.10169520179876,1e-05,16,20,500,20,266905.8253467549,248319.91539609712,234440.24739279025 +O,14.863006,8574.040885789896,1e-05,36,120,1000,20,6156025.076233147,6035788.265357383,6071568.360390914 +Na,9.666274,21.76900860620513,1e-05,13,17,500,20,3946.783439708051,3955.4136520148368,3034.0137871641264 +He,0.502807,0.430866415554751,1.0000000000000002e-06,16,16,1125,337,6.133380717138889,-7.725225617444218,-11.096506480945479 +Al,9.445146,4.30866415554751,1.0000000000000002e-06,16,16,500,20,2628.5089477983142,3194.662500983706,1900.2567801261862 +Mg,17.222007,1.723465662219004,1.0000000000000002e-06,16,16,500,20,11961.893152232244,7819.237715107365,4269.42263784376 +Al,1.349307,1393.2206009414344,1e-05,40,128,1000,20,86013.76469098694,84806.08443695665,84763.93445120311 +Al,13.493065,0.861732831109502,1.0000000000000002e-06,16,16,500,20,5851.968973077679,6128.275963275882,3547.59116977093 +He,5.353887,0.0861732831109502,1e-05,13,13,500,67,1674.0879962460224,1734.475940857292,1523.9729286474012 +Ne,7.895891,21.54332077773755,1e-05,13,17,500,20,3229.1298428058712,3126.162104865358,2339.265358263317 +B,24.651668,2786.4411157095856,1e-05,22,56,500,20,3057303.207049976,2961155.2552372273,3006617.147725778 +Mg,8.611005,64.62996233321265,1e-05,18,24,500,20,12243.014832745544,12507.159891425832,11353.825157241165 +O,14.863006,174.1527582359059,1e-05,18,28,500,20,88504.34430701211,85850.39449506745,84007.74072717172 +H,12.483579,1378.772529775203,1e-05,18,28,500,20,1694684.8823776576,1570453.6428157066,1646972.544658832 +Na,4.833143,174.1525858893397,1e-05,20,36,500,20,26075.469177663486,25899.663020546384,25195.219285855874 +Mg,19.374763,2.585198493328506,1.0000000000000002e-06,16,16,500,20,15201.58028765471,9466.931256192258,5263.265766508805 +He,0.502807,0.0430866415554751,1.0000000000000002e-06,16,16,1125,337,18.061188707619593,-7.725199018052239,-11.098148387291118 +C,12.635119,13932.185241653116,1e-05,40,140,1000,20,8516607.379886763,8369004.500871842,8401826.80450733 +N,2.52736,0.1723465662219004,1.0000000000000002e-07,19,19,500,30,2.075276973001873,69.5351896626629,-124.94136850303559 +H,0.099869,344.6931324438008,1e-05,20,52,500,20,3301.9030178867306,3219.086812714775,3242.609496543868 +B,36.977477,87.07629294466985,1e-05,13,17,500,20,129929.55355816505,121284.47865108424,117064.5434231308 +H,5.266511,344.6931324438008,1e-05,16,24,500,20,181204.16552210183,164418.37171375376,172667.09473191603 +N,2.52736,64.62996233321265,1.0000000000000002e-06,21,31,500,20,4483.745861729148,4540.798453493657,4226.014189057272 +C,19.374129,253.31282870087145,1e-05,18,28,500,20,201073.28043859446,192306.01322537693,192143.49567784954 +Mg,51.666042,2786.4411157095856,1e-05,24,60,500,20,6697284.531336954,6506423.612025182,6584255.1290801 +H,0.337057,1378.772529775203,1e-05,22,64,500,20,44736.807116474716,43603.10389107079,44053.15686159396 +Mg,1.076376,116.10169520179876,1e-05,22,44,500,20,3766.365140142541,3800.697648459035,3650.0034362991623 +B,24.651668,116.10169520179876,1e-05,16,20,500,20,103203.29138933303,97557.21120025766,95285.38254171523 +Mg,11.624855,43.0866415554751,1e-05,18,24,500,67,10057.94088660828,10702.47710831082,9249.96432616323 +H,0.153417,10.771660388868774,1e-05,13,17,500,20,114.29669542755276,122.32074721750365,91.50819109751524 +H,0.012484,86.1732831109502,1e-05,20,52,500,20,102.1212968405153,101.17078321965263,99.94848586909782 +B,12.325826,0.861732831109502,1.0000000000000002e-06,16,16,500,20,7331.516977271224,9906.389563880288,6389.985486625057 +Ne,15.025693,0.2154332077773755,1.0000000000000002e-06,16,16,500,45,6847.908318250356,2822.3253804670408,1447.9742275631058 +H,0.41904,2.6929150972171936,1e-05,13,13,500,45,55.774132075681464,123.84122617979442,15.392067534998734 +H,0.195057,21.54332077773755,1e-05,16,20,500,20,352.80701165497095,357.5125169917259,315.3183416031003 +Mg,43.055026,11145.764549011628,1e-05,36,112,500,20,22843759.670317728,22538815.6110869,22519877.799240515 +H,0.021572,10.771660388868774,1e-05,16,24,500,20,17.724820762671545,19.49486101825701,16.300197454979777 +C,8.500494,0.430866415554751,1.0000000000000002e-06,16,16,500,20,2100.346957570165,4348.434970767885,2275.8219841213017 +Al,8.095839,8.61732831109502,1.0000000000000002e-06,16,20,500,20,1844.917209555024,2434.0457559681968,1600.6565215364083 +Si,6.987,87.07629294466985,1e-05,18,28,500,20,13594.509951774591,13749.20748217314,12779.112003476848 +Ne,3.728316,64.62996233321265,1e-05,18,28,500,20,5810.035083449752,5942.947612569971,5459.732274366159 +N,2.0,3.446931324438008,1.0000000000000002e-07,19,19,500,100,22.957932598350343,46.6371236967057,-41.14470694851638 +Na,3.86651,696.6102573840757,1e-05,26,68,500,20,117184.52237752012,115256.15571256407,115098.31066130062 +C,0.100006,0.861732831109502,1.0000000000000002e-08,19,19,7593,30,0.0017044666999578198,-0.03543056474796335,-0.04573489324137927 +H,0.153417,172.3465662219004,1e-05,18,36,500,20,2524.5598518494394,2449.7039213881426,2462.601397838032 +He,2.74119,21.54332077773755,1e-05,13,17,500,20,2072.7990362862133,1925.2027888894665,1553.9319743658525 +B,17.256161,0.861732831109502,1.0000000000000002e-06,16,16,500,20,14885.885189890676,17776.144693373008,12627.508200846942 +Al,9.445146,69.66101712107925,1e-05,18,24,500,20,13800.187996344275,14060.228071850977,12813.518491120296 +C,25.832156,5572.882317592455,1e-05,26,76,500,20,6940555.130916195,6771865.5271821935,6837936.951340649 +N,1.75,3.446931324438008,1.0000000000000002e-07,19,19,500,100,18.106748136788806,27.7768530034472,-36.09982786914078 +C,4.174268,435.3814647233492,1e-05,22,44,500,20,82877.85420219765,80903.44557553809,81026.51254810768 +H,0.982675,8.208005216318007,1e-05,13,13,500,20,749.8024726092902,856.1409450479246,566.339027717984 +B,9.860662,21.76900860620513,1.0000000000000002e-06,16,20,500,20,7399.216392922991,8737.71839262863,7059.388342108884 +Mg,51.666042,696.6102573840757,1e-05,20,36,500,20,1462507.6174028104,1407932.5945653832,1420750.3592689722 +Na,7.733027,87.07629294466985,1e-05,18,28,500,20,16770.689241152682,16896.262015579578,15781.789069926801 +Mg,25.833021,2786.4411157095856,1e-05,26,72,500,20,3365633.013756881,3287110.163200919,3312641.872531811 +N,1.75,0.1723465662219004,1.0000000000000002e-06,16,16,750,67,0.00015626913177607007,3.5537687896254395,-83.86969868379147 +Al,16.191678,87.07629294466985,1e-05,18,24,500,20,33575.624850874716,33704.12636102551,31233.941059454155 +Mg,43.055026,3.446931324438008,1.0000000000000002e-06,16,16,500,20,70520.96381561867,40024.958972449334,27155.583353105714 +O,7.261642,86.1732831109502,1e-05,18,24,500,20,18159.014850425745,18034.475615439045,16934.09581004218 +Al,0.674653,696.6102573840757,1e-05,36,116,1000,20,21100.567885432876,20849.884504597376,20775.189380687298 +N,2.75,3.446931324438008,1.0000000000000002e-07,19,19,750,67,45.28898213823985,142.29804259068027,-36.8036903679005 +Si,2.329,174.1525858893397,1e-05,22,44,500,20,12901.774162791582,12907.999910164795,12532.701517759473 +O,2.486325,8574.040885789896,1e-05,56,200,1000,20,1031651.4565232176,1023374.9291797916,1017488.4658179588 +He,0.669236,2757.545059550406,1e-05,34,112,1000,20,89348.6872097471,87635.9315089785,88104.20108905066 +He,2.74119,1.723465662219004,1.0000000000000002e-06,16,16,500,100,257.0643706330844,297.20574127470303,168.50438931780909 +N,3.705859,0.861732831109502,1.0000000000000002e-07,19,19,500,67,37.05440774029069,310.1767632827016,-88.95095787518439 +Al,16.191678,18576.27424835271,1e-05,54,192,1000,20,14017816.344198935,13846420.025505768,13829834.983796185 +N,2.0,0.5170396986657012,1.0000000000000002e-07,19,19,500,30,0.24752000903241236,21.201548398848367,-93.27351974150228 +Al,9.445146,5572.882317592455,1e-05,40,132,1000,20,2438477.186149089,2396723.5566470046,2404261.118093777 +B,24.651668,58.050890687540935,1e-05,13,17,500,20,54822.25071717421,53514.59970763821,49802.817804532126 +B,0.246517,87.07629294466985,1e-05,22,48,500,20,791.91747017267,797.563696885084,770.2670206289662 +N,2.52736,0.6462996233321265,1.0000000000000002e-07,19,19,500,30,3.251339158002116,74.01425602715815,-114.41743304446022 +Ne,7.895891,4.30866415554751,1.0000000000000002e-07,19,19,750,150,916.4214368677691,276.9479038440781,-120.56117740932196 +B,11.093243,116.10169520179876,1e-05,18,24,500,20,43657.7500610936,42168.84351212618,40940.173207310414 +B,12.325826,10.884461216461007,1.0000000000000002e-06,16,16,500,20,8455.229603605061,10660.352836897882,7821.516574773115 +N,2.424,1.723465662219004,1.0000000000000002e-07,19,19,500,45,8.871859592182247,77.29472711009203,-75.55783075600385 +B,19.721324,21.76900860620513,1.0000000000000002e-06,16,20,500,20,24356.011107891016,26116.684084952467,21808.51105041802 +C,8.500494,13932.185241653116,1e-05,44,156,1000,20,5730660.080595019,5643676.005689563,5653126.29938754 +C,12.635119,21.54332077773755,1.0000000000000002e-06,16,20,500,20,10273.347515179765,12612.286732659137,10085.38828684373 +C,25.832156,253.31282870087145,1e-05,18,24,500,20,268613.3784107917,255196.37863070218,255580.8245776044 +Mg,12.916506,1393.2206009414344,1e-05,24,64,500,20,824963.0171843449,806800.1357665508,810924.7730660931 +H,0.982675,86.1732831109502,1e-05,16,24,500,20,8144.758006680799,7606.654133757894,7614.509904700588 +Si,11.645,21.54332077773755,1.0000000000000002e-06,16,20,500,20,4891.991070187644,6021.293349262431,4705.302480247869 +Al,21.588905,2.585198493328506,1.0000000000000002e-06,16,16,500,20,15902.235796147248,13607.953442528125,8313.764690889153 +N,7.829576,4.30866415554751,1.0000000000000002e-06,16,16,500,20,986.8234534579067,2461.583979828196,1219.042096490022 +H,0.00156,21.54332077773755,1e-05,22,52,500,20,3.1169687051281896,3.1936813649872935,3.0279557694947297 +Na,7.733027,17.415189650307482,1e-05,13,17,500,20,2276.3231648714886,2330.1155539733827,1725.48946617373 +O,7.261642,696.6118946764548,1e-05,22,52,500,20,232103.35655636596,226485.58313443142,227473.4649333156 +Al,18.890291,0.861732831109502,1.0000000000000002e-06,16,16,500,20,11850.441039502504,10927.095624507278,6543.079675754301 +B,2.465165,2786.4411157095856,1e-05,34,108,1000,20,306607.69808648265,300637.8484254644,302270.2302787752 diff --git a/tests/dev_tests/run_benchmark_tests.py b/tests/dev_tests/run_benchmark_tests.py new file mode 100644 index 0000000..487d56e --- /dev/null +++ b/tests/dev_tests/run_benchmark_tests.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +"""Example of how to run the benchmark tests.""" + +import sys +from datetime import datetime +import benchmarking + + +# user inputs - MUST BE EDITED + +env = "atoMEC312" # name of conda environment + +# location for calculations - we set up a new folder based on the date and environment +savedir = datetime.now().strftime("%d-%m-%y") + "_" + env + +# type of calculation: either "setup", "run", "setup_and_run", or "evaluate" +calc_type = "evaluate" + +# end of user inputs + +if "setup" in calc_type: + benchmarking.set_up_calcs(savedir, env, testfile="test.py") + +if "run" in calc_type: + benchmarking.run_calcs(savedir) + +if calc_type == "evaluate": + benchmarking.gather_benchmark_results(savedir, savedir + "_benchmark_results.csv") + benchmarking.analyze_benchmark_results(savedir + "_benchmark_results.csv") + +if calc_type not in ["setup", "setup_and_run", "run", "evaluate"]: + sys.exit( + "Calculation type not recognised. Must be one of " + + "'setup', 'setup_and_run', 'run', or 'evaluate'" + ) diff --git a/tests/dev_tests/submit.slurm b/tests/dev_tests/submit.slurm new file mode 100644 index 0000000..29a9857 --- /dev/null +++ b/tests/dev_tests/submit.slurm @@ -0,0 +1,12 @@ +#!/bin/bash -l +#SBATCH --partition=rome +#SBATCH --ntasks=10 +#SBATCH --nodes=1 +#SBATCH --mem=80G + +env={ENV} +module purge +conda activate $env +export OMP_NUM_THREADS=1 + +python -u test.py > test.log diff --git a/tests/dev_tests/test.py b/tests/dev_tests/test.py new file mode 100644 index 0000000..fd352d0 --- /dev/null +++ b/tests/dev_tests/test.py @@ -0,0 +1,66 @@ +"""Test template file for atoMEC.""" + +from atoMEC import Atom, models, config, mathtools, unitconv +from atoMEC.postprocess import pressure +import json +import time + +st_time = time.time() + +config.numcores = -1 + +# load input params and set up the atom and model objects +with open("input.json", "r") as f: + input = json.load(f) + +atom = Atom(input["species"], input["temp"], density=input["rho"], units_temp="eV") +model = models.ISModel(atom, bc="bands", unbound="quantum") + +# the scf calculation +out = model.CalcEnergy( + input["nmax"], + input["lmax"], + grid_params={"ngrid": input["ngrid"], "s0": 1e-4}, + conv_params={"nconv": input["nconv"], "vconv": 1e-1, "econv": 1e-2}, + band_params={"nkpts": input["nkpts"]}, + scf_params={"maxscf": 30}, + write_info=True, + grid_type="sqrt", +) + +# stress tensor pressure +P_st_rr = pressure.stress_tensor( + atom, model, out["orbitals"], out["potential"], only_rr=True +) + +# virial pressure +P_vir_nocorr = pressure.virial( + atom, + model, + out["energy"], + out["density"], + out["orbitals"], + out["potential"], + use_correction=False, + method="B", +) + +# compute ideal pressure +chem_pot = mathtools.chem_pot(out["orbitals"]) +P_elec_id = pressure.ideal_electron(atom, chem_pot) + +end_time = time.time() +tdiff = end_time - st_time + +output_dict = { + "species": input["species"], + "temp": input["temp"], + "rho": input["rho"], + "P_st_rr": unitconv.ha_to_gpa * P_st_rr, + "P_vir_nocorr": unitconv.ha_to_gpa * P_vir_nocorr, + "P_id": unitconv.ha_to_gpa * P_elec_id, + "time": tdiff, +} + +with open("output.json", "w") as f: + json.dump(output_dict, f)