From 2bf51ba168045dd56ad015b5cc3ed4f7339b21c0 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 11 Aug 2025 16:45:39 +0200 Subject: [PATCH 01/16] blk_1m functions --- parcel.py | 263 ++++++++++++++++++++++++++------------ unit_test/test_moments.py | 6 +- 2 files changed, 187 insertions(+), 82 deletions(-) diff --git a/parcel.py b/parcel.py index 835068d..e863e0b 100755 --- a/parcel.py +++ b/parcel.py @@ -12,7 +12,7 @@ import pdb import subprocess -from libcloudphxx import common, lgrngn +from libcloudphxx import common, lgrngn, blk_1m from libcloudphxx import git_revision as libcloud_version parcel_version = subprocess.check_output(["git", "rev-parse", "HEAD"]).rstrip() @@ -106,6 +106,23 @@ def _micro_init(aerosol, opts, state, info): return micro +def _micro_init_blk_1m(opts, state, info): + """Initialize the bulk scheme with basic options""" + # create options + opts_init = blk_1m.opts_t() + + # switch off unnecessary processes + opts_init.accr = False # no rain accretion + opts_init.conv = False # no autoconversion + opts_init.sedi = False # no sedimentation + + # sanity check + _stats(state, info) + if (state["RH"] > 1): + raise Exception("Please supply initial T,p,r_v below supersaturation") + + return opts_init + def _micro_step(micro, state, info, opts, it, fout): libopts = lgrngn.opts_t() libopts.cond = True @@ -140,6 +157,46 @@ def _micro_step(micro, state, info, opts, it, fout): micro.diag_chem(id_int) state[id_str.replace('_g', '_a')] = np.frombuffer(micro.outbuf())[0] +def _micro_step_blk_1m(micro_opts, state, info, opts, it): + """Execute one timestep of bulk microphysics + + Args: + micro_opts: options for bulk scheme (blk_1m.opts_t) + state: dictionary with model state variables + info: dictionary with model metadata + opts: dictionary with model options + it: current timestep number + """ + # get state variables as numpy arrays + rhod = np.asarray([state["rhod"][0]], dtype=np.float64) + th_d = np.asarray([state["th_d"][0]], dtype=np.float64) + rv = np.asarray([state["r_v"][0]], dtype=np.float64) + + # initialize mixing ratios as numpy arrays + rc = np.zeros(1, dtype=np.float64) # cloud water mixing ratio + rr = np.zeros(1, dtype=np.float64) # rain water mixing ratio + + # Apply saturation adjustment with correct signature: + # adj_cellwise(opts_t, ndarray, ndarray, ndarray, ndarray, ndarray, double) + blk_1m.adj_cellwise( + micro_opts, # options struct + rhod, # dry air density array + th_d, # dry potential temperature array + rv, # water vapor mixing ratio array + rc, # cloud water mixing ratio array + rr, # rain water mixing ratio array + opts["dt"] # timestep as double + ) + + # Update state dictionary with modified values + state["r_v"][0] = rv[0] + state["th_d"][0] = th_d[0] + state["rc"] = rc[0] # store cloud water + state["rr"] = rr[0] # store rain water + + # Update thermodynamic state + _stats(state, info) + def _stats(state, info): state["T"] = np.array([common.T(state["th_d"][0], state["rhod"][0])]) state["RH"] = state["p"] * state["r_v"] / (state["r_v"] + common.eps) / common.p_vs(state["T"][0]) @@ -227,6 +284,35 @@ def _output_init(micro, opts, spectra): return fout +def _output_init_blk_1m(opts): + """Initialize output file for bulk microphysics scheme + + Args: + opts: dictionary with model options + """ + fout = netcdf.netcdf_file(opts["outfile"], 'w') + fout.createDimension('t', None) + + # Basic variables with their units + vars_units = { + "z": ("m",), + "t": ("s",), + "r_v": ("kg/kg",), + "rc": ("kg/kg",), + "rr": ("kg/kg",), + "th_d": ("K",), + "rhod": ("kg/m3",), + "p": ("Pa",), + "T": ("K",), + "RH": ("1",) + } + + for var, (unit,) in vars_units.items(): + fout.createVariable(var, 'd', ('t',)) + fout.variables[var].unit = unit + + return fout + def _output_save(fout, state, rec): for var, val in state.items(): fout.variables[var][int(rec)] = val @@ -248,10 +334,12 @@ def _p_hydro_const_th_rv(z_lev, p_0, th_std, r_v, z_0=0.): return common.p_hydro(z_lev, th_std, r_v, z_0, p_0) def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., - r_0=-1., RH_0=-1., #if none specified, the default will be r_0=.022, + r_0=-1., RH_0=-1., outfile="test.nc", - pprof="pprof_piecewise_const_rhod", - outfreq=100, sd_conc=64, + pprof="pprof_piecewise_const_rhod", + outfreq=100, + scheme="lgrngn", # parameter to select scheme + sd_conc=64, aerosol = '{"ammonium_sulfate": {"kappa": 0.61, "mean_r": [0.02e-6], "gstdev": [1.4], "n_tot": [60.0e6]}}', out_bin = '{"radii": {"rght": 0.0001, "moms": [0], "drwt": "wet", "nbin": 1, "lnli": "log", "left": 1e-09}}', SO2_g = 0., O3_g = 0., H2O2_g = 0., CO2_g = 0., HNO3_g = 0., NH3_g = 0., @@ -366,85 +454,102 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., info = { "RH_max" : 0, "libcloud_Git_revision" : libcloud_version, "parcel_Git_revision" : parcel_version } - micro = _micro_init(aerosol, opts, state, info) - - with _output_init(micro, opts, spectra) as fout: - # adding chem state vars - if micro.opts_init.chem_switch: - state.update({ "SO2_a" : 0.,"O3_a" : 0.,"H2O2_a" : 0.,}) - state.update({ "CO2_a" : 0.,"HNO3_a" : 0.}) - - micro.diag_all() # selecting all particles - micro.diag_chem(_Chem_a_id["NH3_a"]) - state.update({"NH3_a": np.frombuffer(micro.outbuf())[0]}) - - # t=0 : init & save - _output(fout, opts, micro, state, 0, spectra) - - # timestepping - for it in range(1,nt+1): - # diagnostics - # the reasons to use analytic solution: - # - independent of dt - # - same as in 2D kinematic model - state["z"] += w * dt - state["t"] = it * dt - - # pressure - if pprof == "pprof_const_th_rv": - # as in icicle model - p_hydro = _p_hydro_const_th_rv(state["z"], p_0, th_0, r_0) - elif pprof == "pprof_const_rhod": - # as in Grabowski and Wang 2009 - rho = 1.13 # kg/m3 1.13 - state["p"] = _p_hydro_const_rho(state["z"], p_0, rho) - - elif pprof == "pprof_piecewise_const_rhod": - # as in Grabowski and Wang 2009 but calculating pressure - # for rho piecewise constant per each time step - state["p"] = _p_hydro_const_rho(w*dt, state["p"], state["rhod"][0]) - - else: raise Exception("pprof should be pprof_const_th_rv, pprof_const_rhod, or pprof_piecewise_const_rhod") - - # dry air density - if pprof == "pprof_const_th_rv": - state["rhod"][0] = common.rhod(p_hydro, th_0, r_0) - state["p"] = common.p( - state["rhod"][0], - state["r_v"][0], - common.T(state["th_d"][0], state["rhod"][0]) - ) - - else: - state["rhod"][0] = common.rhod( - state["p"], - common.th_dry2std(state["th_d"][0], state["r_v"][0]), - state["r_v"][0] - ) - - # microphysics - _micro_step(micro, state, info, opts, it, fout) - - # TODO: only if user wants to stop @ RH_max - #if (state["RH"] < info["RH_max"]): break - - # output - if (it % outfreq == 0): - print(str(round(it / (nt * 1.) * 100, 2)) + " %") - rec = it/outfreq - _output(fout, opts, micro, state, rec, spectra) - - _save_attrs(fout, info) - _save_attrs(fout, opts) - - if wait != 0: - for it in range (nt+1, nt+wait): + # Initialize the selected scheme + if scheme == "lgrngn": + micro = _micro_init(aerosol, opts, state, info) + fout = _output_init(micro, opts, spectra) + elif scheme == "blk_1m": + micro = _micro_init_blk_1m(opts, state, info) + fout = _output_init_blk_1m(opts) + else: + raise ValueError("Unknown scheme type. Use 'lgrngn' or 'blk_1m'") + + with fout: + # adding chem state vars - only for lgrngn scheme + if scheme == "lgrngn" and micro.opts_init.chem_switch: + state.update({ "SO2_a" : 0.,"O3_a" : 0.,"H2O2_a" : 0.,}) + state.update({ "CO2_a" : 0.,"HNO3_a" : 0.}) + + micro.diag_all() # selecting all particles + micro.diag_chem(_Chem_a_id["NH3_a"]) + state.update({"NH3_a": np.frombuffer(micro.outbuf())[0]}) + + # t=0 : init & save + if scheme == "lgrngn": + _output(fout, opts, micro, state, 0, spectra) + elif scheme == "blk_1m": + _output_save(fout, state, 0) # simpler output for blk_1m + + # timestepping + for it in range(1,nt+1): + # diagnostics + # the reasons to use analytic solution: + # - independent of dt + # - same as in 2D kinematic model + state["z"] += w * dt state["t"] = it * dt - _micro_step(micro, state, info, opts, it, fout) + # pressure + if pprof == "pprof_const_th_rv": + # as in icicle model + p_hydro = _p_hydro_const_th_rv(state["z"], p_0, th_0, r_0) + elif pprof == "pprof_const_rhod": + # as in Grabowski and Wang 2009 + rho = 1.13 # kg/m3 1.13 + state["p"] = _p_hydro_const_rho(state["z"], p_0, rho) + + elif pprof == "pprof_piecewise_const_rhod": + # as in Grabowski and Wang 2009 but calculating pressure + # for rho piecewise constant per each time step + state["p"] = _p_hydro_const_rho(w*dt, state["p"], state["rhod"][0]) + + else: raise Exception("pprof should be pprof_const_th_rv, pprof_const_rhod, or pprof_piecewise_const_rhod") + + # dry air density + if pprof == "pprof_const_th_rv": + state["rhod"][0] = common.rhod(p_hydro, th_0, r_0) + state["p"] = common.p( + state["rhod"][0], + state["r_v"][0], + common.T(state["th_d"][0], state["rhod"][0]) + ) + + else: + state["rhod"][0] = common.rhod( + state["p"], + common.th_dry2std(state["th_d"][0], state["r_v"][0]), + state["r_v"][0] + ) + + # microphysics + if scheme == "lgrngn": + _micro_step(micro, state, info, opts, it, fout) + elif scheme == "blk_1m": + _micro_step_blk_1m(micro, state, info, opts, it) + + # TODO: only if user wants to stop @ RH_max + #if (state["RH"] < info["RH_max"]): break + + # output if (it % outfreq == 0): + print(str(round(it / (nt * 1.) * 100, 2)) + " %") rec = it/outfreq - _output(fout, opts, micro, state, rec, spectra) + if scheme == "lgrngn": + _output(fout, opts, micro, state, rec, spectra) + elif scheme == "blk_1m": + _output_save(fout, state, rec) + + _save_attrs(fout, info) + _save_attrs(fout, opts) + + if wait != 0: + for it in range (nt+1, nt+wait): + state["t"] = it * dt + _micro_step(micro, state, info, opts, it, fout) + + if (it % outfreq == 0): + rec = it/outfreq + _output(fout, opts, micro, state, rec, spectra) def _arguments_checking(opts, spectra, aerosol): if opts["T_0"] < 273.15: diff --git a/unit_test/test_moments.py b/unit_test/test_moments.py index eb986c4..bd64b37 100644 --- a/unit_test/test_moments.py +++ b/unit_test/test_moments.py @@ -4,7 +4,7 @@ sys.path.insert(0, "plots/one_simulat/") from scipy.io import netcdf -from scipy.integrate import trapz +from scipy.integrate import trapezoid import numpy as np import math @@ -66,9 +66,9 @@ def analytic_moms_for_lognormal(mom, n_tot, mean_r, gstdev): def trapez_moms(x_arg, y_arg, mom): """ Returns numerically integrated moments """ if mom == 0: - ret = trapz(y_arg, x_arg) + ret = trapezoid(y_arg, x_arg) else: - ret = trapz(y_arg * x_arg**mom, x_arg) + ret = trapezoid(y_arg * x_arg**mom, x_arg) return ret def test_dry_moments(data): From f4379b9a407b1c22314fd91a4721f31e12854f48 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 12 Aug 2025 16:32:33 +0200 Subject: [PATCH 02/16] ice --- parcel.py | 207 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 182 insertions(+), 25 deletions(-) diff --git a/parcel.py b/parcel.py index e863e0b..17c05c9 100755 --- a/parcel.py +++ b/parcel.py @@ -111,10 +111,35 @@ def _micro_init_blk_1m(opts, state, info): # create options opts_init = blk_1m.opts_t() - # switch off unnecessary processes - opts_init.accr = False # no rain accretion - opts_init.conv = False # no autoconversion - opts_init.sedi = False # no sedimentation + # opts_init.accr = False # no rain accretion + # opts_init.conv = False # no autoconversion + # opts_init.sedi = False # no sedimentation + opts_init.homA1 = False # no ice proceesses + opts_init.homA2 = False + opts_init.hetA = False + opts_init.hetB = False + opts_init.depA = False + opts_init.depB = False + opts_init.rimA = False + opts_init.rimB = False + opts_init.melA = False + opts_init.melB = False + + # sanity check + _stats(state, info) + if (state["RH"] > 1): + raise Exception("Please supply initial T,p,r_v below supersaturation") + + return opts_init + +def _micro_init_blk_1m_ice(opts, state, info): + """Initialize the bulk ice scheme with basic options""" + # create options + opts_init = blk_1m.opts_t() + + # opts_init.accr = False # no rain accretion + # opts_init.conv = False # no autoconversion + # opts_init.sedi = False # no sedimentation # sanity check _stats(state, info) @@ -170,29 +195,114 @@ def _micro_step_blk_1m(micro_opts, state, info, opts, it): # get state variables as numpy arrays rhod = np.asarray([state["rhod"][0]], dtype=np.float64) th_d = np.asarray([state["th_d"][0]], dtype=np.float64) + p = np.asarray([state["p"]], dtype=np.float64) rv = np.asarray([state["r_v"][0]], dtype=np.float64) - - # initialize mixing ratios as numpy arrays - rc = np.zeros(1, dtype=np.float64) # cloud water mixing ratio - rr = np.zeros(1, dtype=np.float64) # rain water mixing ratio - - # Apply saturation adjustment with correct signature: - # adj_cellwise(opts_t, ndarray, ndarray, ndarray, ndarray, ndarray, double) - blk_1m.adj_cellwise( - micro_opts, # options struct - rhod, # dry air density array - th_d, # dry potential temperature array - rv, # water vapor mixing ratio array - rc, # cloud water mixing ratio array - rr, # rain water mixing ratio array - opts["dt"] # timestep as double + rc = np.asarray([state["rc"][0]], dtype=np.float64) + rr = np.asarray([state["rr"][0]], dtype=np.float64) + dot_th_d = np.zeros_like(th_d) + dot_rv = np.zeros_like(rv) + dot_rc = np.zeros_like(rc) + dot_rr = np.zeros_like(rr) + + + blk_1m.adj_cellwise_nwtrph( + micro_opts, # options struct + p, # pressure array + th_d, # dry potential temperature array + rv, # water vapor mixing ratio array + rc, # cloud water mixing ratio array + opts["dt"] # timestep as double + ) + blk_1m.rhs_cellwise_nwtrph( + micro_opts, + dot_th_d, + dot_rv, + dot_rc, + dot_rr, + rhod, + p, + th_d, + rv, + rc, + rr, + opts["dt"] ) + + th_d += dot_th_d * opts["dt"] + rv += dot_rv * opts["dt"] + rc += dot_rc * opts["dt"] + rr += dot_rr * opts["dt"] + + # Update state dictionary with modified values + state["r_v"][0] = rv[0] + state["th_d"][0] = th_d[0] + state["rc"] = np.array([rc[0]]) + state["rr"] = np.array([rr[0]]) + + # Update thermodynamic state + _stats(state, info) + + +def _micro_step_blk_1m_ice(micro_opts, state, info, opts, it): + # get state variables as numpy arrays + rhod = np.asarray([state["rhod"][0]], dtype=np.float64) + th_d = np.asarray([state["th_d"][0]], dtype=np.float64) + p = np.asarray([state["p"]], dtype=np.float64) + rv = np.asarray([state["r_v"][0]], dtype=np.float64) + rc = np.asarray([state["rc"][0]], dtype=np.float64) + rr = np.asarray([state["rr"][0]], dtype=np.float64) + ria = np.asarray([state["ria"][0]], dtype=np.float64) + rib = np.asarray([state["rib"][0]], dtype=np.float64) + dot_th_d = np.zeros_like(th_d) + dot_rv = np.zeros_like(rv) + dot_rc = np.zeros_like(rc) + dot_rr = np.zeros_like(rr) + dot_ria = np.zeros_like(ria) + dot_rib = np.zeros_like(rib) + + + blk_1m.adj_cellwise_nwtrph( + micro_opts, # options struct + p, # pressure array + th_d, # dry potential temperature array + rv, # water vapor mixing ratio array + rc, # cloud water mixing ratio array + opts["dt"] # timestep as double + ) + blk_1m.rhs_cellwise_nwtrph_ice( + micro_opts, + dot_th_d, + dot_rv, + dot_rc, + dot_rr, + dot_ria, + dot_rib, + rhod, + p, + th_d, + rv, + rc, + rr, + ria, + rib, + opts["dt"] + ) + + th_d += dot_th_d * opts["dt"] + rv += dot_rv * opts["dt"] + rc += dot_rc * opts["dt"] + rr += dot_rr * opts["dt"] + ria += dot_ria * opts["dt"] + rib += dot_rib * opts["dt"] + # Update state dictionary with modified values state["r_v"][0] = rv[0] state["th_d"][0] = th_d[0] - state["rc"] = rc[0] # store cloud water - state["rr"] = rr[0] # store rain water + state["rc"] = np.array([rc[0]]) + state["rr"] = np.array([rr[0]]) + state["ria"] = np.array([ria[0]]) + state["rib"] = np.array([rib[0]]) # Update thermodynamic state _stats(state, info) @@ -313,6 +423,37 @@ def _output_init_blk_1m(opts): return fout +def _output_init_blk_1m_ice(opts): + """Initialize output file for bulk ice microphysics scheme + + Args: + opts: dictionary with model options + """ + fout = netcdf.netcdf_file(opts["outfile"], 'w') + fout.createDimension('t', None) + + # Basic variables with their units + vars_units = { + "z": ("m",), + "t": ("s",), + "r_v": ("kg/kg",), + "rc": ("kg/kg",), + "rr": ("kg/kg",), + "ria": ("kg/kg",), + "rib": ("kg/kg",), + "th_d": ("K",), + "rhod": ("kg/m3",), + "p": ("Pa",), + "T": ("K",), + "RH": ("1",) + } + + for var, (unit,) in vars_units.items(): + fout.createVariable(var, 'd', ('t',)) + fout.variables[var].unit = unit + + return fout + def _output_save(fout, state, rec): for var, val in state.items(): fout.variables[var][int(rec)] = val @@ -447,6 +588,16 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., "T" : None, "RH" : None } + if scheme == "blk_1m": + state["rc"] = np.array([0.0]) # initial cloud water + state["rr"] = np.array([0.0]) # initial rain water + + if scheme == "blk_1m_ice": + state["rc"] = np.array([0.0]) # initial cloud water + state["rr"] = np.array([0.0]) # initial rain water + state["ria"] = np.array([0.0]) # initial ice A + state["rib"] = np.array([0.0]) # initial ice B + if opts["chem_dsl"] or opts["chem_dsc"] or opts["chem_rct"]: for key in _Chem_g_id.keys(): state.update({ key : np.array([opts[key]])}) @@ -461,8 +612,11 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., elif scheme == "blk_1m": micro = _micro_init_blk_1m(opts, state, info) fout = _output_init_blk_1m(opts) + elif scheme == "blk_1m_ice": + micro = _micro_init_blk_1m_ice(opts, state, info) + fout = _output_init_blk_1m_ice(opts) else: - raise ValueError("Unknown scheme type. Use 'lgrngn' or 'blk_1m'") + raise ValueError("Unknown scheme type. Use 'lgrngn', 'blk_1m' or 'blk_1m_ice'.") with fout: # adding chem state vars - only for lgrngn scheme @@ -477,11 +631,12 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., # t=0 : init & save if scheme == "lgrngn": _output(fout, opts, micro, state, 0, spectra) - elif scheme == "blk_1m": + elif scheme == "blk_1m" or scheme == "blk_1m_ice": _output_save(fout, state, 0) # simpler output for blk_1m # timestepping - for it in range(1,nt+1): + for it in range(1, nt+1): + print("Timestep", it, "/", nt) # diagnostics # the reasons to use analytic solution: # - independent of dt @@ -526,6 +681,8 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., _micro_step(micro, state, info, opts, it, fout) elif scheme == "blk_1m": _micro_step_blk_1m(micro, state, info, opts, it) + elif scheme == "blk_1m_ice": + _micro_step_blk_1m_ice(micro, state, info, opts, it) # TODO: only if user wants to stop @ RH_max #if (state["RH"] < info["RH_max"]): break @@ -536,7 +693,7 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., rec = it/outfreq if scheme == "lgrngn": _output(fout, opts, micro, state, rec, spectra) - elif scheme == "blk_1m": + elif scheme == "blk_1m" or scheme == "blk_1m_ice": _output_save(fout, state, rec) _save_attrs(fout, info) From 55d294e9d260e1c174f0824a8c0fad40842af36e Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 13 Aug 2025 11:00:27 +0200 Subject: [PATCH 03/16] no collisions --- parcel.py | 67 +++++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/parcel.py b/parcel.py index 17c05c9..c459ed5 100755 --- a/parcel.py +++ b/parcel.py @@ -107,14 +107,15 @@ def _micro_init(aerosol, opts, state, info): return micro def _micro_init_blk_1m(opts, state, info): - """Initialize the bulk scheme with basic options""" - # create options + """Initialize options for bulk microphysics scheme""" opts_init = blk_1m.opts_t() - # opts_init.accr = False # no rain accretion - # opts_init.conv = False # no autoconversion - # opts_init.sedi = False # no sedimentation - opts_init.homA1 = False # no ice proceesses + opts_init.accr = False # no rain accretion + opts_init.conv = False # no autoconversion + opts_init.sedi = False # no sedimentation + + # no ice proceesses: + opts_init.homA1 = False opts_init.homA2 = False opts_init.hetA = False opts_init.hetB = False @@ -133,13 +134,17 @@ def _micro_init_blk_1m(opts, state, info): return opts_init def _micro_init_blk_1m_ice(opts, state, info): - """Initialize the bulk ice scheme with basic options""" - # create options + """Initialize options for bulk microphysics scheme with ice processes""" opts_init = blk_1m.opts_t() - # opts_init.accr = False # no rain accretion - # opts_init.conv = False # no autoconversion - # opts_init.sedi = False # no sedimentation + opts_init.sedi = False # no sedimentation + opts_init.accr = False # no rain accretion + opts_init.conv = False # no autoconversion + + # no collisions for ice: + opts_init.hetB = False + opts_init.rimA = False + opts_init.rimB = False # sanity check _stats(state, info) @@ -183,22 +188,14 @@ def _micro_step(micro, state, info, opts, it, fout): state[id_str.replace('_g', '_a')] = np.frombuffer(micro.outbuf())[0] def _micro_step_blk_1m(micro_opts, state, info, opts, it): - """Execute one timestep of bulk microphysics - - Args: - micro_opts: options for bulk scheme (blk_1m.opts_t) - state: dictionary with model state variables - info: dictionary with model metadata - opts: dictionary with model options - it: current timestep number - """ + """Microphysics step for bulk microphysics scheme""" # get state variables as numpy arrays rhod = np.asarray([state["rhod"][0]], dtype=np.float64) th_d = np.asarray([state["th_d"][0]], dtype=np.float64) p = np.asarray([state["p"]], dtype=np.float64) rv = np.asarray([state["r_v"][0]], dtype=np.float64) - rc = np.asarray([state["rc"][0]], dtype=np.float64) - rr = np.asarray([state["rr"][0]], dtype=np.float64) + rc = np.asarray([state["rc"][0]]) + rr = np.asarray([state["rr"][0]]) dot_th_d = np.zeros_like(th_d) dot_rv = np.zeros_like(rv) dot_rc = np.zeros_like(rc) @@ -206,13 +203,14 @@ def _micro_step_blk_1m(micro_opts, state, info, opts, it): blk_1m.adj_cellwise_nwtrph( - micro_opts, # options struct - p, # pressure array - th_d, # dry potential temperature array - rv, # water vapor mixing ratio array - rc, # cloud water mixing ratio array - opts["dt"] # timestep as double + micro_opts, + p, + th_d, + rv, + rc, + opts["dt"] ) + blk_1m.rhs_cellwise_nwtrph( micro_opts, dot_th_d, @@ -395,11 +393,7 @@ def _output_init(micro, opts, spectra): return fout def _output_init_blk_1m(opts): - """Initialize output file for bulk microphysics scheme - - Args: - opts: dictionary with model options - """ + """Initialize output file for bulk microphysics scheme""" fout = netcdf.netcdf_file(opts["outfile"], 'w') fout.createDimension('t', None) @@ -424,11 +418,7 @@ def _output_init_blk_1m(opts): return fout def _output_init_blk_1m_ice(opts): - """Initialize output file for bulk ice microphysics scheme - - Args: - opts: dictionary with model options - """ + """Initialize output file for bulk ice microphysics scheme""" fout = netcdf.netcdf_file(opts["outfile"], 'w') fout.createDimension('t', None) @@ -636,7 +626,6 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., # timestepping for it in range(1, nt+1): - print("Timestep", it, "/", nt) # diagnostics # the reasons to use analytic solution: # - independent of dt From 4e5700983c8a6980680153edcd064a3fa819e674 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 13 Aug 2025 12:35:31 +0200 Subject: [PATCH 04/16] tests --- long_test/test_compare_schemes.py | 66 +++++++++++++++++++++++++++++++ long_test/test_ice.py | 25 ++++++++++++ parcel.py | 10 ++--- 3 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 long_test/test_compare_schemes.py create mode 100644 long_test/test_ice.py diff --git a/long_test/test_compare_schemes.py b/long_test/test_compare_schemes.py new file mode 100644 index 0000000..4b33280 --- /dev/null +++ b/long_test/test_compare_schemes.py @@ -0,0 +1,66 @@ +import sys +sys.path.insert(0, "../") +sys.path.insert(0, "./") + +import numpy as np +from parcel import parcel +from scipy.io import netcdf +import matplotlib.pyplot as plt + +def run_scheme(scheme, outfile): + args = dict( + dt=0.1, + z_max=800, + w=1.0, + T_0=300, + p_0=101300, + r_0=0.022, + outfile=outfile, + outfreq=50, + scheme=scheme, + aerosol='{"ammonium_sulfate": {"kappa": 0.61, "mean_r": [0.02e-6], "gstdev": [1.4], "n_tot": [60.0e6]}}', + out_bin='{"radius": {"rght": 1, "moms": [3], "drwt": "wet", "nbin": 1, "lnli": "lin", "left": 1e-15}}' + ) + parcel(**args) + with netcdf.netcdf_file(outfile, 'r') as f: + rv = np.array(f.variables['r_v'][:]) + th_d = np.array(f.variables['th_d'][:]) + z = np.array(f.variables['z'][:]) + if scheme.startswith("blk"): + r_tot = np.array(f.variables['rc'][:]) + np.array(f.variables['rr'][:]) + else: + moment_3 = np.array(f.variables['radius_m3'][:]) + r_tot = moment_3 *4/3 * np.pi * 997 #multiply by density of water + return rv, th_d, z, r_tot + +def test_compare_schemes(): + schemes = ["lgrngn", "blk_1m", "blk_1m_ice"] + results = {} + for scheme in schemes: + rv, th_d, z, r_tot = run_scheme(scheme, f"test_{scheme}.nc") + results[scheme] = (rv, th_d, z, r_tot) + # Compare final values + rv_vals = [results[s][0][-1] for s in schemes] + th_d_vals = [results[s][1][-1] for s in schemes] + r_tot_vals = [results[s][2][-1] for s in schemes] + + fig, ax = plt.subplots(1,3, figsize=(12, 6)) + for scheme in schemes: + rv, th_d, z, r_tot = results[scheme] + ax[0].plot(rv, z, label=f"{scheme}") + ax[1].plot(th_d, z, label=f"{scheme}") + ax[2].plot(r_tot, z, label=f"{scheme}") + ax[0].set_ylabel("Height [m]") + ax[0].set_xlabel("Water vapor mixing ratio") + ax[1].set_xlabel("Dry potential temperature") + ax[2].set_xlabel("Total condensed water mixing ratio") + ax[0].legend() + ax[1].legend() + ax[2].legend() + plt.tight_layout() + plt.savefig("plots/outputs/scheme_comparison.svg") + + # Check closeness + for i in range(1, len(schemes)): + assert np.isclose(rv_vals[0], rv_vals[i], rtol=5e-2), f"r_v differs: {rv_vals[0]} vs {rv_vals[i]}" + assert np.isclose(th_d_vals[0], th_d_vals[i], rtol=5e-2), f"th_d differs: {th_d_vals[0]} vs {th_d_vals[i]}" \ No newline at end of file diff --git a/long_test/test_ice.py b/long_test/test_ice.py new file mode 100644 index 0000000..d885f3b --- /dev/null +++ b/long_test/test_ice.py @@ -0,0 +1,25 @@ +import sys +sys.path.insert(0, "../") +sys.path.insert(0, "./") +from parcel import parcel +from scipy.io import netcdf +import numpy as np + +def test_bulk_ice(): + args = dict( + dt=0.1, + z_max=500, + w=1.0, + T_0=273, + p_0=101300, + RH_0 = 1, + outfile="test_bulk_ice.nc", + outfreq=100, + scheme="blk_1m_ice" + ) + parcel(**args) + with netcdf.netcdf_file("test_bulk_ice.nc", 'r') as f: + ria = np.array(f.variables['ria'][:]) + rc = np.array(f.variables['rc'][:]) + assert np.isclose(ria[-1], 7.843e-11, rtol=1e-3) + assert np.isclose(rc[-1], 5.522e-4, rtol=1e-3) diff --git a/parcel.py b/parcel.py index c459ed5..7ed1922 100755 --- a/parcel.py +++ b/parcel.py @@ -472,7 +472,7 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., scheme="lgrngn", # parameter to select scheme sd_conc=64, aerosol = '{"ammonium_sulfate": {"kappa": 0.61, "mean_r": [0.02e-6], "gstdev": [1.4], "n_tot": [60.0e6]}}', - out_bin = '{"radii": {"rght": 0.0001, "moms": [0], "drwt": "wet", "nbin": 1, "lnli": "log", "left": 1e-09}}', + out_bin = '{"radii": {"rght": 0.01, "moms": [0], "drwt": "wet", "nbin": 1, "lnli": "log", "left": 1e-15}}', SO2_g = 0., O3_g = 0., H2O2_g = 0., CO2_g = 0., HNO3_g = 0., NH3_g = 0., chem_dsl = False, chem_dsc = False, chem_rct = False, chem_rho = 1.8e3, @@ -566,7 +566,7 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., r_0 = common.eps * opts["RH_0"] * common.p_vs(T_0) / (p_0 - opts["RH_0"] * common.p_vs(T_0)) # sanity checks for arguments - _arguments_checking(opts, spectra, aerosol) + _arguments_checking(opts, spectra, aerosol, scheme) th_0 = T_0 * (common.p_1000 / p_0)**(common.R_d / common.c_pd) nt = int(z_max / (w * dt)) @@ -697,9 +697,9 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., rec = it/outfreq _output(fout, opts, micro, state, rec, spectra) -def _arguments_checking(opts, spectra, aerosol): - if opts["T_0"] < 273.15: - raise Exception("temperature should be larger than 0C - microphysics works only for warm clouds") +def _arguments_checking(opts, spectra, aerosol, scheme): + if opts["T_0"] < 273.15 and scheme != "blk_1m_ice": + raise Exception("temperature should be larger than 0C for schemes other than blk_1m_ice") elif ((opts["r_0"] >= 0) and (opts["RH_0"] >= 0)): raise Exception("both r_0 and RH_0 specified, please use only one") if opts["w"] < 0: From 6e00d5ac9cae590d1a04abf38ccbf657ab835de2 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 25 Aug 2025 16:54:09 +0200 Subject: [PATCH 05/16] tests description --- long_test/test_compare_schemes.py | 9 +++++++++ long_test/test_ice.py | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/long_test/test_compare_schemes.py b/long_test/test_compare_schemes.py index 4b33280..db0a927 100644 --- a/long_test/test_compare_schemes.py +++ b/long_test/test_compare_schemes.py @@ -7,6 +7,15 @@ from scipy.io import netcdf import matplotlib.pyplot as plt +""" +This test runs the parcel model using three different microphysics schemes: +- lgrngn (Lagrangian particle-based) +- blk_1m (bulk warm) +- blk_1m_ice (bulk ice) + +It compares the evolution of rv, th_d, and total condensed water in different schemes. +""" + def run_scheme(scheme, outfile): args = dict( dt=0.1, diff --git a/long_test/test_ice.py b/long_test/test_ice.py index d885f3b..4b13000 100644 --- a/long_test/test_ice.py +++ b/long_test/test_ice.py @@ -1,3 +1,8 @@ +""" +This test runs the parcel model using blk_1m_ice microphysics scheme. +It checks that the final values of ria and rc match their reference values (are consistent in each run). +""" + import sys sys.path.insert(0, "../") sys.path.insert(0, "./") From c1fb4b2810f68073041c497bc7fe2a20fa70d5b8 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 25 Aug 2025 17:00:20 +0200 Subject: [PATCH 06/16] test compare schemes --- long_test/test_compare_schemes.py | 20 +++++++++----------- long_test/test_ice.py | 1 - 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/long_test/test_compare_schemes.py b/long_test/test_compare_schemes.py index db0a927..2241570 100644 --- a/long_test/test_compare_schemes.py +++ b/long_test/test_compare_schemes.py @@ -1,12 +1,3 @@ -import sys -sys.path.insert(0, "../") -sys.path.insert(0, "./") - -import numpy as np -from parcel import parcel -from scipy.io import netcdf -import matplotlib.pyplot as plt - """ This test runs the parcel model using three different microphysics schemes: - lgrngn (Lagrangian particle-based) @@ -16,18 +7,25 @@ It compares the evolution of rv, th_d, and total condensed water in different schemes. """ +import sys +sys.path.insert(0, "../") +sys.path.insert(0, "./") + +import numpy as np +from parcel import parcel +from scipy.io import netcdf +import matplotlib.pyplot as plt + def run_scheme(scheme, outfile): args = dict( dt=0.1, z_max=800, w=1.0, T_0=300, - p_0=101300, r_0=0.022, outfile=outfile, outfreq=50, scheme=scheme, - aerosol='{"ammonium_sulfate": {"kappa": 0.61, "mean_r": [0.02e-6], "gstdev": [1.4], "n_tot": [60.0e6]}}', out_bin='{"radius": {"rght": 1, "moms": [3], "drwt": "wet", "nbin": 1, "lnli": "lin", "left": 1e-15}}' ) parcel(**args) diff --git a/long_test/test_ice.py b/long_test/test_ice.py index 4b13000..9bc0a38 100644 --- a/long_test/test_ice.py +++ b/long_test/test_ice.py @@ -16,7 +16,6 @@ def test_bulk_ice(): z_max=500, w=1.0, T_0=273, - p_0=101300, RH_0 = 1, outfile="test_bulk_ice.nc", outfreq=100, From a908bed4524d185aae5a32e4460098391a62ac28 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 5 Sep 2025 14:33:39 +0200 Subject: [PATCH 07/16] separate test for plotting --- long_test/test_compare_schemes.py | 28 ++++----------- long_test/test_plot_schemes.py | 60 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 long_test/test_plot_schemes.py diff --git a/long_test/test_compare_schemes.py b/long_test/test_compare_schemes.py index 2241570..4ba093b 100644 --- a/long_test/test_compare_schemes.py +++ b/long_test/test_compare_schemes.py @@ -4,7 +4,7 @@ - blk_1m (bulk warm) - blk_1m_ice (bulk ice) -It compares the evolution of rv, th_d, and total condensed water in different schemes. +It compares the final values of rv, th_d, and total condensed water in different schemes. """ import sys @@ -14,7 +14,6 @@ import numpy as np from parcel import parcel from scipy.io import netcdf -import matplotlib.pyplot as plt def run_scheme(scheme, outfile): args = dict( @@ -38,36 +37,21 @@ def run_scheme(scheme, outfile): else: moment_3 = np.array(f.variables['radius_m3'][:]) r_tot = moment_3 *4/3 * np.pi * 997 #multiply by density of water - return rv, th_d, z, r_tot + return rv, th_d, r_tot, z def test_compare_schemes(): schemes = ["lgrngn", "blk_1m", "blk_1m_ice"] results = {} for scheme in schemes: - rv, th_d, z, r_tot = run_scheme(scheme, f"test_{scheme}.nc") - results[scheme] = (rv, th_d, z, r_tot) + rv, th_d, r_tot, z = run_scheme(scheme, f"test_{scheme}.nc") + results[scheme] = (rv, th_d, r_tot, z) # Compare final values rv_vals = [results[s][0][-1] for s in schemes] th_d_vals = [results[s][1][-1] for s in schemes] r_tot_vals = [results[s][2][-1] for s in schemes] - - fig, ax = plt.subplots(1,3, figsize=(12, 6)) - for scheme in schemes: - rv, th_d, z, r_tot = results[scheme] - ax[0].plot(rv, z, label=f"{scheme}") - ax[1].plot(th_d, z, label=f"{scheme}") - ax[2].plot(r_tot, z, label=f"{scheme}") - ax[0].set_ylabel("Height [m]") - ax[0].set_xlabel("Water vapor mixing ratio") - ax[1].set_xlabel("Dry potential temperature") - ax[2].set_xlabel("Total condensed water mixing ratio") - ax[0].legend() - ax[1].legend() - ax[2].legend() - plt.tight_layout() - plt.savefig("plots/outputs/scheme_comparison.svg") # Check closeness for i in range(1, len(schemes)): assert np.isclose(rv_vals[0], rv_vals[i], rtol=5e-2), f"r_v differs: {rv_vals[0]} vs {rv_vals[i]}" - assert np.isclose(th_d_vals[0], th_d_vals[i], rtol=5e-2), f"th_d differs: {th_d_vals[0]} vs {th_d_vals[i]}" \ No newline at end of file + assert np.isclose(th_d_vals[0], th_d_vals[i], rtol=5e-2), f"th_d differs: {th_d_vals[0]} vs {th_d_vals[i]}" + #assert np.isclose(r_tot_vals[0], r_tot_vals[i], rtol=5e-1), f"r_tot differs: {r_tot_vals[0]} vs {r_tot_vals[i]}" \ No newline at end of file diff --git a/long_test/test_plot_schemes.py b/long_test/test_plot_schemes.py new file mode 100644 index 0000000..73d6bb8 --- /dev/null +++ b/long_test/test_plot_schemes.py @@ -0,0 +1,60 @@ +""" +This test runs the parcel model using three different microphysics schemes: +- lgrngn (Lagrangian particle-based) +- blk_1m (bulk warm) +- blk_1m_ice (bulk ice) + +It plots the evolution of rv, th_d, and total condensed water in different schemes. +""" + +import sys +sys.path.insert(0, "../") +sys.path.insert(0, "./") + +import numpy as np +from parcel import parcel +from scipy.io import netcdf +import matplotlib.pyplot as plt + +def run_scheme(scheme, outfile): + args = dict( + dt=0.1, + z_max=800, + w=1.0, + T_0=300, + r_0=0.022, + outfile=outfile, + outfreq=50, + scheme=scheme, + out_bin='{"radius": {"rght": 1, "moms": [3], "drwt": "wet", "nbin": 1, "lnli": "lin", "left": 1e-15}}' + ) + parcel(**args) + with netcdf.netcdf_file(outfile, 'r') as f: + rv = np.array(f.variables['r_v'][:]) + th_d = np.array(f.variables['th_d'][:]) + z = np.array(f.variables['z'][:]) + if scheme.startswith("blk"): + r_tot = np.array(f.variables['rc'][:]) + np.array(f.variables['rr'][:]) + else: + moment_3 = np.array(f.variables['radius_m3'][:]) + r_tot = moment_3 *4/3 * np.pi * 997 #multiply by density of water + return rv, th_d, r_tot, z + +def test_plot_schemes(): + schemes = ["lgrngn", "blk_1m", "blk_1m_ice"] + fig, ax = plt.subplots(1,3, figsize=(12, 6)) + for scheme in schemes: + rv, th_d, r_tot, z = run_scheme(scheme, f"test_{scheme}.nc") + ax[0].plot(rv, z, label=f"{scheme}", linestyle ='--' if scheme=='blk_1m_ice' else '-') + ax[1].plot(th_d, z, label=f"{scheme}", linestyle ='--' if scheme=='blk_1m_ice' else '-') + ax[2].plot(r_tot, z, label=f"{scheme}", linestyle ='--' if scheme=='blk_1m_ice' else '-') + ax[0].set_ylabel("Height [m]") + ax[0].set_xlabel("Water vapor mixing ratio") + ax[1].set_xlabel("Dry potential temperature") + ax[2].set_xlabel("Total condensed water mixing ratio") + ax[0].legend() + ax[1].legend() + ax[2].legend() + plt.tight_layout() + plt.savefig("plots/outputs/plot_schemes.svg") + \ No newline at end of file From 7cb42449dd9a5ade46d0aba136707689ae8dafe1 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Sep 2025 13:12:51 +0200 Subject: [PATCH 08/16] renaming to opts_init for blk --- parcel.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/parcel.py b/parcel.py index 7ed1922..01361e2 100755 --- a/parcel.py +++ b/parcel.py @@ -62,6 +62,7 @@ def __call__(self, lnr): return res def _micro_init(aerosol, opts, state, info): + """Initialize the lagrangian microphysics scheme""" # lagrangian scheme options opts_init = lgrngn.opts_init_t() @@ -106,7 +107,7 @@ def _micro_init(aerosol, opts, state, info): return micro -def _micro_init_blk_1m(opts, state, info): +def _opts_init_blk_1m(opts, state, info): """Initialize options for bulk microphysics scheme""" opts_init = blk_1m.opts_t() @@ -133,7 +134,7 @@ def _micro_init_blk_1m(opts, state, info): return opts_init -def _micro_init_blk_1m_ice(opts, state, info): +def _opts_init_blk_1m_ice(opts, state, info): """Initialize options for bulk microphysics scheme with ice processes""" opts_init = blk_1m.opts_t() @@ -600,10 +601,10 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., micro = _micro_init(aerosol, opts, state, info) fout = _output_init(micro, opts, spectra) elif scheme == "blk_1m": - micro = _micro_init_blk_1m(opts, state, info) + micro = _opts_init_blk_1m(opts, state, info) fout = _output_init_blk_1m(opts) elif scheme == "blk_1m_ice": - micro = _micro_init_blk_1m_ice(opts, state, info) + micro = _opts_init_blk_1m_ice(opts, state, info) fout = _output_init_blk_1m_ice(opts) else: raise ValueError("Unknown scheme type. Use 'lgrngn', 'blk_1m' or 'blk_1m_ice'.") From ab156e015ce0e418a25af60b9642d0f028a8080e Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Sep 2025 13:41:10 +0200 Subject: [PATCH 09/16] renaming micro_opts and code cleanup --- parcel.py | 52 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/parcel.py b/parcel.py index 01361e2..ce08a41 100755 --- a/parcel.py +++ b/parcel.py @@ -155,6 +155,7 @@ def _opts_init_blk_1m_ice(opts, state, info): return opts_init def _micro_step(micro, state, info, opts, it, fout): + '''Microphysics step for lagrangian scheme''' libopts = lgrngn.opts_t() libopts.cond = True libopts.coal = False @@ -189,12 +190,12 @@ def _micro_step(micro, state, info, opts, it, fout): state[id_str.replace('_g', '_a')] = np.frombuffer(micro.outbuf())[0] def _micro_step_blk_1m(micro_opts, state, info, opts, it): - """Microphysics step for bulk microphysics scheme""" + """Microphysics step for bulk scheme without ice processes""" # get state variables as numpy arrays - rhod = np.asarray([state["rhod"][0]], dtype=np.float64) - th_d = np.asarray([state["th_d"][0]], dtype=np.float64) - p = np.asarray([state["p"]], dtype=np.float64) - rv = np.asarray([state["r_v"][0]], dtype=np.float64) + rhod = np.asarray([state["rhod"][0]]) + th_d = np.asarray([state["th_d"][0]]) + p = np.asarray([state["p"]]) + rv = np.asarray([state["r_v"][0]]) rc = np.asarray([state["rc"][0]]) rr = np.asarray([state["rr"][0]]) dot_th_d = np.zeros_like(th_d) @@ -243,16 +244,16 @@ def _micro_step_blk_1m(micro_opts, state, info, opts, it): def _micro_step_blk_1m_ice(micro_opts, state, info, opts, it): - + """Microphysics step for bulk scheme with ice processes""" # get state variables as numpy arrays - rhod = np.asarray([state["rhod"][0]], dtype=np.float64) - th_d = np.asarray([state["th_d"][0]], dtype=np.float64) - p = np.asarray([state["p"]], dtype=np.float64) - rv = np.asarray([state["r_v"][0]], dtype=np.float64) - rc = np.asarray([state["rc"][0]], dtype=np.float64) - rr = np.asarray([state["rr"][0]], dtype=np.float64) - ria = np.asarray([state["ria"][0]], dtype=np.float64) - rib = np.asarray([state["rib"][0]], dtype=np.float64) + rhod = np.asarray([state["rhod"][0]]) + th_d = np.asarray([state["th_d"][0]]) + p = np.asarray([state["p"]]) + rv = np.asarray([state["r_v"][0]]) + rc = np.asarray([state["rc"][0]]) + rr = np.asarray([state["rr"][0]]) + ria = np.asarray([state["ria"][0]]) + rib = np.asarray([state["rib"][0]]) dot_th_d = np.zeros_like(th_d) dot_rv = np.zeros_like(rv) dot_rc = np.zeros_like(rc) @@ -341,6 +342,7 @@ def _output_bins(fout, t, micro, opts, spectra): fout.variables[dim+'_'+vm][int(t), int(bin)] = np.frombuffer(micro.outbuf()) def _output_init(micro, opts, spectra): + """Initialize output file for lagrangian scheme""" # file & dimensions fout = netcdf.netcdf_file(opts["outfile"], 'w') fout.createDimension('t', None) @@ -394,7 +396,7 @@ def _output_init(micro, opts, spectra): return fout def _output_init_blk_1m(opts): - """Initialize output file for bulk microphysics scheme""" + """Initialize output file for bulk microphysics scheme without ice processes""" fout = netcdf.netcdf_file(opts["outfile"], 'w') fout.createDimension('t', None) @@ -601,10 +603,10 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., micro = _micro_init(aerosol, opts, state, info) fout = _output_init(micro, opts, spectra) elif scheme == "blk_1m": - micro = _opts_init_blk_1m(opts, state, info) + micro_opts = _opts_init_blk_1m(opts, state, info) fout = _output_init_blk_1m(opts) elif scheme == "blk_1m_ice": - micro = _opts_init_blk_1m_ice(opts, state, info) + micro_opts = _opts_init_blk_1m_ice(opts, state, info) fout = _output_init_blk_1m_ice(opts) else: raise ValueError("Unknown scheme type. Use 'lgrngn', 'blk_1m' or 'blk_1m_ice'.") @@ -670,9 +672,9 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., if scheme == "lgrngn": _micro_step(micro, state, info, opts, it, fout) elif scheme == "blk_1m": - _micro_step_blk_1m(micro, state, info, opts, it) + _micro_step_blk_1m(micro_opts, state, info, opts, it) elif scheme == "blk_1m_ice": - _micro_step_blk_1m_ice(micro, state, info, opts, it) + _micro_step_blk_1m_ice(micro_opts, state, info, opts, it) # TODO: only if user wants to stop @ RH_max #if (state["RH"] < info["RH_max"]): break @@ -692,11 +694,19 @@ def parcel(dt=.1, z_max=200., w=1., T_0=300., p_0=101300., if wait != 0: for it in range (nt+1, nt+wait): state["t"] = it * dt - _micro_step(micro, state, info, opts, it, fout) + if scheme == "lgrngn": + _micro_step(micro, state, info, opts, it, fout) + elif scheme == "blk_1m": + _micro_step_blk_1m(micro_opts, state, info, opts, it) + elif scheme == "blk_1m_ice": + _micro_step_blk_1m_ice(micro_opts, state, info, opts, it) if (it % outfreq == 0): rec = it/outfreq - _output(fout, opts, micro, state, rec, spectra) + if scheme == "lgrngn": + _output(fout, opts, micro, state, rec, spectra) + elif scheme == "blk_1m" or scheme == "blk_1m_ice": + _output_save(fout, state, rec) def _arguments_checking(opts, spectra, aerosol, scheme): if opts["T_0"] < 273.15 and scheme != "blk_1m_ice": From 61dbdd98b34dea946760de79d96132049dc5a1f2 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Wed, 17 Sep 2025 16:54:36 +0200 Subject: [PATCH 10/16] use new libcloudph++ API: opts.th_dry and opts_const_p --- long_test/test_compare_schemes.py | 4 +- parcel.py | 152 ++++++++++++++---------------- 2 files changed, 72 insertions(+), 84 deletions(-) diff --git a/long_test/test_compare_schemes.py b/long_test/test_compare_schemes.py index 2241570..fd484f5 100644 --- a/long_test/test_compare_schemes.py +++ b/long_test/test_compare_schemes.py @@ -69,5 +69,5 @@ def test_compare_schemes(): # Check closeness for i in range(1, len(schemes)): - assert np.isclose(rv_vals[0], rv_vals[i], rtol=5e-2), f"r_v differs: {rv_vals[0]} vs {rv_vals[i]}" - assert np.isclose(th_d_vals[0], th_d_vals[i], rtol=5e-2), f"th_d differs: {th_d_vals[0]} vs {th_d_vals[i]}" \ No newline at end of file + assert np.isclose(rv_vals[0], rv_vals[i], rtol=5e-4), f"r_v differs: {rv_vals[0]} vs {rv_vals[i]}" + assert np.isclose(th_d_vals[0], th_d_vals[i], rtol=5e-4), f"th_d differs: {th_d_vals[0]} vs {th_d_vals[i]}" diff --git a/parcel.py b/parcel.py index 7ed1922..c83d86a 100755 --- a/parcel.py +++ b/parcel.py @@ -125,6 +125,10 @@ def _micro_init_blk_1m(opts, state, info): opts_init.rimB = False opts_init.melA = False opts_init.melB = False + + opts_init.adj_nwtrph = True + opts_init.th_dry = True + opts_init.const_p = False # sanity check _stats(state, info) @@ -189,53 +193,43 @@ def _micro_step(micro, state, info, opts, it, fout): def _micro_step_blk_1m(micro_opts, state, info, opts, it): """Microphysics step for bulk microphysics scheme""" - # get state variables as numpy arrays - rhod = np.asarray([state["rhod"][0]], dtype=np.float64) - th_d = np.asarray([state["th_d"][0]], dtype=np.float64) - p = np.asarray([state["p"]], dtype=np.float64) - rv = np.asarray([state["r_v"][0]], dtype=np.float64) - rc = np.asarray([state["rc"][0]]) - rr = np.asarray([state["rr"][0]]) - dot_th_d = np.zeros_like(th_d) - dot_rv = np.zeros_like(rv) - dot_rc = np.zeros_like(rc) - dot_rr = np.zeros_like(rr) - - - blk_1m.adj_cellwise_nwtrph( + # # get state variables as numpy arrays + p = np.asarray(state["p"]) + dot_th_d = np.zeros_like(state["th_d"]) + dot_rv = np.zeros_like(state["r_v"]) + dot_rc = np.zeros_like(state["rc"]) + dot_rr = np.zeros_like(state["rr"]) + + blk_1m.adj_cellwise( micro_opts, + state["rhod"], p, - th_d, - rv, - rc, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], opts["dt"] ) - blk_1m.rhs_cellwise_nwtrph( + blk_1m.rhs_cellwise_revap( micro_opts, dot_th_d, dot_rv, dot_rc, dot_rr, - rhod, + state["rhod"], p, - th_d, - rv, - rc, - rr, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], opts["dt"] ) - th_d += dot_th_d * opts["dt"] - rv += dot_rv * opts["dt"] - rc += dot_rc * opts["dt"] - rr += dot_rr * opts["dt"] - - # Update state dictionary with modified values - state["r_v"][0] = rv[0] - state["th_d"][0] = th_d[0] - state["rc"] = np.array([rc[0]]) - state["rr"] = np.array([rr[0]]) + state["th_d"] += dot_th_d * opts["dt"] + state["r_v"] += dot_rv * opts["dt"] + state["rc"] += dot_rc * opts["dt"] + state["rr"] += dot_rr * opts["dt"] # Update thermodynamic state _stats(state, info) @@ -244,31 +238,26 @@ def _micro_step_blk_1m(micro_opts, state, info, opts, it): def _micro_step_blk_1m_ice(micro_opts, state, info, opts, it): # get state variables as numpy arrays - rhod = np.asarray([state["rhod"][0]], dtype=np.float64) - th_d = np.asarray([state["th_d"][0]], dtype=np.float64) - p = np.asarray([state["p"]], dtype=np.float64) - rv = np.asarray([state["r_v"][0]], dtype=np.float64) - rc = np.asarray([state["rc"][0]], dtype=np.float64) - rr = np.asarray([state["rr"][0]], dtype=np.float64) - ria = np.asarray([state["ria"][0]], dtype=np.float64) - rib = np.asarray([state["rib"][0]], dtype=np.float64) - dot_th_d = np.zeros_like(th_d) - dot_rv = np.zeros_like(rv) - dot_rc = np.zeros_like(rc) - dot_rr = np.zeros_like(rr) - dot_ria = np.zeros_like(ria) - dot_rib = np.zeros_like(rib) - - - blk_1m.adj_cellwise_nwtrph( - micro_opts, # options struct - p, # pressure array - th_d, # dry potential temperature array - rv, # water vapor mixing ratio array - rc, # cloud water mixing ratio array - opts["dt"] # timestep as double + p = np.asarray(state["p"]) + dot_th_d = np.zeros_like(state["th_d"]) + dot_rv = np.zeros_like(state["r_v"]) + dot_rc = np.zeros_like(state["rc"]) + dot_rr = np.zeros_like(state["rr"]) + dot_ria = np.zeros_like(state["ria"]) + dot_rib = np.zeros_like(state["rib"]) + + blk_1m.adj_cellwise( + micro_opts, + state["rhod"], + p, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], + opts["dt"] ) - blk_1m.rhs_cellwise_nwtrph_ice( + + blk_1m.rhs_cellwise_ice( micro_opts, dot_th_d, dot_rv, @@ -276,38 +265,33 @@ def _micro_step_blk_1m_ice(micro_opts, state, info, opts, it): dot_rr, dot_ria, dot_rib, - rhod, + state["rhod"], p, - th_d, - rv, - rc, - rr, - ria, - rib, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], + state["ria"], + state["rib"], opts["dt"] ) - th_d += dot_th_d * opts["dt"] - rv += dot_rv * opts["dt"] - rc += dot_rc * opts["dt"] - rr += dot_rr * opts["dt"] - ria += dot_ria * opts["dt"] - rib += dot_rib * opts["dt"] - - # Update state dictionary with modified values - state["r_v"][0] = rv[0] - state["th_d"][0] = th_d[0] - state["rc"] = np.array([rc[0]]) - state["rr"] = np.array([rr[0]]) - state["ria"] = np.array([ria[0]]) - state["rib"] = np.array([rib[0]]) - + state["th_d"] += dot_th_d * opts["dt"] + state["r_v"] += dot_rv * opts["dt"] + state["rc"] += dot_rc * opts["dt"] + state["rr"] += dot_rr * opts["dt"] + state["ria"] += dot_ria * opts["dt"] + state["rib"] += dot_rib * opts["dt"] + # Update thermodynamic state _stats(state, info) + def _stats(state, info): state["T"] = np.array([common.T(state["th_d"][0], state["rhod"][0])]) state["RH"] = state["p"] * state["r_v"] / (state["r_v"] + common.eps) / common.p_vs(state["T"][0]) + state["T_blk"] = np.array([state["th_d"][0] * common.exner(state["p"])]) + state["RH_blk"] = state["r_v"] / common.r_vs(state["T_blk"][0], state["p"]) info["RH_max"] = max(info["RH_max"], state["RH"]) def _output_bins(fout, t, micro, opts, spectra): @@ -378,7 +362,7 @@ def _output_init(micro, opts, spectra): fout.variables[name+'_m'+str(vm)].unit = 'm^'+str(vm)+' (kg of dry air)^-1' units = {"z" : "m", "t" : "s", "r_v" : "kg/kg", "th_d" : "K", "rhod" : "kg/m3", - "p" : "Pa", "T" : "K", "RH" : "1" + "p" : "Pa", "T" : "K", "RH" : "1", "T_blk" : "K", "RH_blk" : "1" } if micro.opts_init.chem_switch: @@ -408,7 +392,9 @@ def _output_init_blk_1m(opts): "rhod": ("kg/m3",), "p": ("Pa",), "T": ("K",), - "RH": ("1",) + "RH": ("1",), + "T_blk": ("K",), + "RH_blk": ("1",) } for var, (unit,) in vars_units.items(): @@ -435,7 +421,9 @@ def _output_init_blk_1m_ice(opts): "rhod": ("kg/m3",), "p": ("Pa",), "T": ("K",), - "RH": ("1",) + "RH": ("1",), + "T_blk": ("K",), + "RH_blk": ("1",) } for var, (unit,) in vars_units.items(): From 639557ebb33911d96dc9a61536e9c7c76e8382cd Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 3 Oct 2025 16:22:22 +0900 Subject: [PATCH 11/16] updating ice test values --- long_test/test_ice.py | 4 +- parcel.py | 168 +++++++++++++++++++++--------------------- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/long_test/test_ice.py b/long_test/test_ice.py index 9bc0a38..bab957a 100644 --- a/long_test/test_ice.py +++ b/long_test/test_ice.py @@ -25,5 +25,5 @@ def test_bulk_ice(): with netcdf.netcdf_file("test_bulk_ice.nc", 'r') as f: ria = np.array(f.variables['ria'][:]) rc = np.array(f.variables['rc'][:]) - assert np.isclose(ria[-1], 7.843e-11, rtol=1e-3) - assert np.isclose(rc[-1], 5.522e-4, rtol=1e-3) + assert np.isclose(ria[-1], 8.9947e-11, rtol=1e-3) + assert np.isclose(rc[-1], 6.0941e-4, rtol=1e-3) \ No newline at end of file diff --git a/parcel.py b/parcel.py index ed3ec19..9d9b667 100755 --- a/parcel.py +++ b/parcel.py @@ -194,99 +194,99 @@ def _micro_step(micro, state, info, opts, it, fout): state[id_str.replace('_g', '_a')] = np.frombuffer(micro.outbuf())[0] def _micro_step_blk_1m(micro_opts, state, info, opts, it): - """Microphysics step for bulk scheme without ice processes""" - # # get state variables as numpy arrays - p = np.asarray(state["p"]) - dot_th_d = np.zeros_like(state["th_d"]) - dot_rv = np.zeros_like(state["r_v"]) - dot_rc = np.zeros_like(state["rc"]) - dot_rr = np.zeros_like(state["rr"]) - - blk_1m.adj_cellwise( - micro_opts, - state["rhod"], - p, - state["th_d"], - state["r_v"], - state["rc"], - state["rr"], - opts["dt"] + """Microphysics step for bulk scheme without ice processes""" + # get state variables as numpy arrays + p = np.asarray(state["p"]) + dot_th_d = np.zeros_like(state["th_d"]) + dot_rv = np.zeros_like(state["r_v"]) + dot_rc = np.zeros_like(state["rc"]) + dot_rr = np.zeros_like(state["rr"]) + + blk_1m.adj_cellwise( + micro_opts, + state["rhod"], + p, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], + opts["dt"] ) - blk_1m.rhs_cellwise_revap( - micro_opts, - dot_th_d, - dot_rv, - dot_rc, - dot_rr, - state["rhod"], - p, - state["th_d"], - state["r_v"], - state["rc"], - state["rr"], - opts["dt"] - ) + blk_1m.rhs_cellwise_revap( + micro_opts, + dot_th_d, + dot_rv, + dot_rc, + dot_rr, + state["rhod"], + p, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], + opts["dt"] + ) - state["th_d"] += dot_th_d * opts["dt"] - state["r_v"] += dot_rv * opts["dt"] - state["rc"] += dot_rc * opts["dt"] - state["rr"] += dot_rr * opts["dt"] - - # Update thermodynamic state - _stats(state, info) + state["th_d"] += dot_th_d * opts["dt"] + state["r_v"] += dot_rv * opts["dt"] + state["rc"] += dot_rc * opts["dt"] + state["rr"] += dot_rr * opts["dt"] + + # Update thermodynamic state + _stats(state, info) def _micro_step_blk_1m_ice(micro_opts, state, info, opts, it): - """Microphysics step for bulk scheme with ice processes""" - # get state variables as numpy arrays - p = np.asarray(state["p"]) - dot_th_d = np.zeros_like(state["th_d"]) - dot_rv = np.zeros_like(state["r_v"]) - dot_rc = np.zeros_like(state["rc"]) - dot_rr = np.zeros_like(state["rr"]) - dot_ria = np.zeros_like(state["ria"]) - dot_rib = np.zeros_like(state["rib"]) - - blk_1m.adj_cellwise( - micro_opts, - state["rhod"], - p, - state["th_d"], - state["r_v"], - state["rc"], - state["rr"], - opts["dt"] - ) - - blk_1m.rhs_cellwise_ice( - micro_opts, - dot_th_d, - dot_rv, - dot_rc, - dot_rr, - dot_ria, - dot_rib, - state["rhod"], - p, - state["th_d"], - state["r_v"], - state["rc"], - state["rr"], - state["ria"], - state["rib"], - opts["dt"] + """Microphysics step for bulk scheme with ice processes""" + # get state variables as numpy arrays + p = np.asarray(state["p"]) + dot_th_d = np.zeros_like(state["th_d"]) + dot_rv = np.zeros_like(state["r_v"]) + dot_rc = np.zeros_like(state["rc"]) + dot_rr = np.zeros_like(state["rr"]) + dot_ria = np.zeros_like(state["ria"]) + dot_rib = np.zeros_like(state["rib"]) + + blk_1m.adj_cellwise( + micro_opts, + state["rhod"], + p, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], + opts["dt"] ) + + blk_1m.rhs_cellwise_ice( + micro_opts, + dot_th_d, + dot_rv, + dot_rc, + dot_rr, + dot_ria, + dot_rib, + state["rhod"], + p, + state["th_d"], + state["r_v"], + state["rc"], + state["rr"], + state["ria"], + state["rib"], + opts["dt"] + ) - state["th_d"] += dot_th_d * opts["dt"] - state["r_v"] += dot_rv * opts["dt"] - state["rc"] += dot_rc * opts["dt"] - state["rr"] += dot_rr * opts["dt"] - state["ria"] += dot_ria * opts["dt"] - state["rib"] += dot_rib * opts["dt"] + state["th_d"] += dot_th_d * opts["dt"] + state["r_v"] += dot_rv * opts["dt"] + state["rc"] += dot_rc * opts["dt"] + state["rr"] += dot_rr * opts["dt"] + state["ria"] += dot_ria * opts["dt"] + state["rib"] += dot_rib * opts["dt"] - # Update thermodynamic state - _stats(state, info) + # Update thermodynamic state + _stats(state, info) def _stats(state, info): From 804c330216ea30915c79079e9a22fcd4d4faa721 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 7 Oct 2025 15:16:06 +0900 Subject: [PATCH 12/16] test --- .github/workflows/test_parcel.yml | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/test_parcel.yml diff --git a/.github/workflows/test_parcel.yml b/.github/workflows/test_parcel.yml new file mode 100644 index 0000000..9325049 --- /dev/null +++ b/.github/workflows/test_parcel.yml @@ -0,0 +1,69 @@ +name: Test parcel with libcloudph++ + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build_and_test: + runs-on: ubuntu-24.04 + + strategy: + matrix: + build_type: ["RelWithDebInfoPortable", "Debug"] + + steps: + - name: Checkout parcel repo + uses: actions/checkout@v5 + + # === Build libcloudph++ first === + - name: Checkout libcloudph++ repo + uses: actions/checkout@v5 + with: + repository: igfuw/libcloudphxx + path: libcloudphxx + + - name: Build and install libcloudph++ + uses: igfuw/libcloudphxx_build@master + with: + disable_cuda: true + build_type: ${{ matrix.build_type }} + threads: 4 + path: ${{ github.workspace }}/libcloudphxx + install_prefix: ${{ github.workspace }}/installed + + - name: Install libcloudph++ + run: sudo cmake --install libcloudphxx/build + + # === Prepare environment === + - name: Set PYTHONPATH + run: echo "PYTHONPATH=${{ github.workspace }}/installed$(python3 -c "import sysconfig; print(sysconfig.get_path('platlib'))")" >> $GITHUB_ENV + + - name: Check PYTHONPATH + run: echo ${PYTHONPATH} + + # === Load the Apptainer image === + - name: Load UWLCM Apptainer image + uses: igfuw/load_UWLCM_singularity_image@main + with: + path: ${{ github.workspace }}/apptainer_images + + # === Run parcel tests === + - name: Create output folder + run: mkdir -p parcel/plots/outputs + + - name: Run parcel unit tests + working-directory: parcel + run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test + + - name: Run parcel long tests + if: ${{ matrix.build_type == 'RelWithDebInfoPortable' }} + working-directory: parcel + run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v long_test + + - name: Run parcel debug tests + if: ${{ matrix.build_type == 'Debug' }} + working-directory: parcel + run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test_debug From cc8fd26696084c7782b75adc617c0bc60569548a Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 7 Oct 2025 15:52:08 +0900 Subject: [PATCH 13/16] fixing tests --- .github/workflows/test_parcel.yml | 33 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test_parcel.yml b/.github/workflows/test_parcel.yml index 9325049..e04f2b9 100644 --- a/.github/workflows/test_parcel.yml +++ b/.github/workflows/test_parcel.yml @@ -15,8 +15,6 @@ jobs: build_type: ["RelWithDebInfoPortable", "Debug"] steps: - - name: Checkout parcel repo - uses: actions/checkout@v5 # === Build libcloudph++ first === - name: Checkout libcloudph++ repo @@ -44,26 +42,27 @@ jobs: - name: Check PYTHONPATH run: echo ${PYTHONPATH} - # === Load the Apptainer image === - - name: Load UWLCM Apptainer image - uses: igfuw/load_UWLCM_singularity_image@main - with: - path: ${{ github.workspace }}/apptainer_images - # === Run parcel tests === + - name: checkout parcel repo + uses: actions/checkout@v5 + with: + repository: AgnieszkaMakulska/parcel + path: parcel - name: Create output folder - run: mkdir -p parcel/plots/outputs + run: mkdir parcel/plots/outputs - - name: Run parcel unit tests - working-directory: parcel + - name: run parcel unit_test + working-directory: ${{github.workspace}}/parcel + if: ${{matrix.long_tests}} run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test - - name: Run parcel long tests - if: ${{ matrix.build_type == 'RelWithDebInfoPortable' }} - working-directory: parcel + - name: run parcel long_test + working-directory: ${{github.workspace}}/parcel + if: ${{matrix.long_tests}} run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v long_test - - name: Run parcel debug tests - if: ${{ matrix.build_type == 'Debug' }} - working-directory: parcel + - name: run parcel unit_test_debug + working-directory: ${{github.workspace}}/parcel + if: ${{matrix.debug_tests}} run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test_debug + From ea178b9db9ac543998478e0ad11c0566abb48727 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 8 Oct 2025 15:29:05 +0900 Subject: [PATCH 14/16] running all tests --- .github/workflows/test_parcel.yml | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test_parcel.yml b/.github/workflows/test_parcel.yml index e04f2b9..b080558 100644 --- a/.github/workflows/test_parcel.yml +++ b/.github/workflows/test_parcel.yml @@ -51,18 +51,25 @@ jobs: - name: Create output folder run: mkdir parcel/plots/outputs - - name: run parcel unit_test + - name: run parcel tests working-directory: ${{github.workspace}}/parcel - if: ${{matrix.long_tests}} - run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test + run: | + python3 -m pytest -s -v parcel/unit_test + python3 -m pytest -s -v parcel/long_test + python3 -m pytest -s -v parcel/unit_test_debug - - name: run parcel long_test - working-directory: ${{github.workspace}}/parcel - if: ${{matrix.long_tests}} - run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v long_test + # - name: run parcel unit_test + # working-directory: ${{github.workspace}}/parcel + # #if: ${{matrix.long_tests}} + # run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v parcel/unit_test - - name: run parcel unit_test_debug - working-directory: ${{github.workspace}}/parcel - if: ${{matrix.debug_tests}} - run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test_debug + # - name: run parcel long_test + # working-directory: ${{github.workspace}}/parcel + # #if: ${{matrix.long_tests}} + # run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v parcel/long_test + + # - name: run parcel unit_test_debug + # working-directory: ${{github.workspace}}/parcel + # #if: ${{matrix.debug_tests}} + # run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v parcel/unit_test_debug From e09cba77d69f88b0769649839a6b30d814bb0a92 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 8 Oct 2025 15:52:33 +0900 Subject: [PATCH 15/16] using apptainer --- .github/workflows/test_parcel.yml | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test_parcel.yml b/.github/workflows/test_parcel.yml index b080558..0654192 100644 --- a/.github/workflows/test_parcel.yml +++ b/.github/workflows/test_parcel.yml @@ -54,22 +54,6 @@ jobs: - name: run parcel tests working-directory: ${{github.workspace}}/parcel run: | - python3 -m pytest -s -v parcel/unit_test - python3 -m pytest -s -v parcel/long_test - python3 -m pytest -s -v parcel/unit_test_debug - - # - name: run parcel unit_test - # working-directory: ${{github.workspace}}/parcel - # #if: ${{matrix.long_tests}} - # run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v parcel/unit_test - - # - name: run parcel long_test - # working-directory: ${{github.workspace}}/parcel - # #if: ${{matrix.long_tests}} - # run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v parcel/long_test - - # - name: run parcel unit_test_debug - # working-directory: ${{github.workspace}}/parcel - # #if: ${{matrix.debug_tests}} - # run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v parcel/unit_test_debug - + apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test + apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v long_test + apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test_debug \ No newline at end of file From a33f51df7be53a27419edaccfb9e37d4d81ffc21 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 9 Oct 2025 16:25:53 +0900 Subject: [PATCH 16/16] separating tests --- .github/workflows/test_parcel.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_parcel.yml b/.github/workflows/test_parcel.yml index 0654192..92bc0a2 100644 --- a/.github/workflows/test_parcel.yml +++ b/.github/workflows/test_parcel.yml @@ -46,14 +46,19 @@ jobs: - name: checkout parcel repo uses: actions/checkout@v5 with: - repository: AgnieszkaMakulska/parcel + repository: igfuw/parcel path: parcel - name: Create output folder run: mkdir parcel/plots/outputs - name: run parcel tests working-directory: ${{github.workspace}}/parcel - run: | - apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test - apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v long_test - apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test_debug \ No newline at end of file + run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test + + - name: run long tests + working-directory: ${{github.workspace}}/parcel + run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v long_test + + - name: run unit tests debug + working-directory: ${{github.workspace}}/parcel + run: apptainer exec -B${{ github.workspace }}/installed $SI python3 -m pytest -s -v unit_test_debug \ No newline at end of file