diff --git a/src/gas_state.F90 b/src/gas_state.F90 index 5d501500..79c32a8d 100644 --- a/src/gas_state.F90 +++ b/src/gas_state.F90 @@ -93,4 +93,56 @@ subroutine f_gas_state_set_size(ptr_c, gas_data_ptr_c) bind(C) call gas_state_set_size(ptr_f, gas_data_n_spec(gas_data_ptr_f)) end subroutine + + subroutine f_gas_state_molar_conc_to_ppb(ptr_c, env_state_ptr_c) bind(C) + type(c_ptr), intent(inout) :: ptr_c + type(c_ptr), intent(in) :: env_state_ptr_c + type(gas_state_t), pointer :: ptr_f => null() + type(env_state_t), pointer :: env_state_ptr_f => null() + + call c_f_pointer(ptr_c, ptr_f) + call c_f_pointer(env_state_ptr_c, env_state_ptr_f) + + call gas_state_molar_conc_to_ppb(ptr_f, env_state_ptr_f) + + end subroutine + + subroutine f_gas_state_scale(ptr_c, alpha) bind(C) + type(c_ptr), intent(inout) :: ptr_c + type(gas_state_t), pointer :: ptr_f => null() + real(kind=c_double), intent(in) :: alpha + + call c_f_pointer(ptr_c, ptr_f) + + call gas_state_scale(ptr_f, alpha) + + end subroutine + + subroutine f_gas_state_add(ptr_c, delta_gas_state_ptr_c) bind(C) + type(c_ptr), intent(inout) :: ptr_c + type(c_ptr), intent(in) :: delta_gas_state_ptr_c + type(gas_state_t), pointer :: ptr_f => null() + type(gas_state_t), pointer :: delta_gas_state_ptr_f => null() + + call c_f_pointer(ptr_c, ptr_f) + call c_f_pointer(delta_gas_state_ptr_c, delta_gas_state_ptr_f) + + call gas_state_add(ptr_f, delta_gas_state_ptr_f) + + end subroutine + + subroutine f_gas_state_add_scaled(ptr_c, delta_gas_state_ptr_c, alpha) bind(C) + type(c_ptr), intent(inout) :: ptr_c + type(c_ptr), intent(in) :: delta_gas_state_ptr_c + type(gas_state_t), pointer :: ptr_f => null() + type(gas_state_t), pointer :: delta_gas_state_ptr_f => null() + real(kind=c_double), intent(in) :: alpha + + call c_f_pointer(ptr_c, ptr_f) + call c_f_pointer(delta_gas_state_ptr_c, delta_gas_state_ptr_f) + + call gas_state_add_scaled(ptr_f, delta_gas_state_ptr_f, alpha) + + end subroutine + end module diff --git a/src/gas_state.hpp b/src/gas_state.hpp index 6b1ad688..68f5eb2c 100644 --- a/src/gas_state.hpp +++ b/src/gas_state.hpp @@ -10,6 +10,7 @@ #include "json_resource.hpp" #include "pmc_resource.hpp" #include "gas_data.hpp" +#include "env_state.hpp" extern "C" void f_gas_state_ctor(void *ptr) noexcept; extern "C" void f_gas_state_dtor(void *ptr) noexcept; @@ -19,7 +20,15 @@ extern "C" void f_gas_state_len(const void *ptr, int *len) noexcept; extern "C" void f_gas_state_to_json(const void *ptr) noexcept; extern "C" void f_gas_state_from_json(const void *ptr, const void *gasdata_ptr) noexcept; extern "C" void f_gas_state_set_size(const void *ptr, const void *gasdata_ptr) noexcept; -extern "C" void f_gas_state_mix_rats(const void *ptr, const double *data, const int *len); +extern "C" void f_gas_state_mix_rats(const void *ptr, const double *data, const int *len) noexcept; +extern "C" void f_gas_state_molar_conc_to_ppb(const void *ptr, const void *envstate_ptr) noexcept; +extern "C" void f_gas_state_scale(const void *ptr_c, const double *alpha) noexcept; +extern "C" void f_gas_state_add(const void *ptr_c, const void *delta_ptr) noexcept; +extern "C" void f_gas_state_add_scaled( + const void *ptr_c, + const void *delta_ptr, + const double *alpha +) noexcept; struct GasState { PMCResource ptr; @@ -126,4 +135,26 @@ struct GasState { ); guard.check_parameters(); } + + static void molar_conc_to_ppb(const GasState &self, const EnvState &env_state) { + + f_gas_state_molar_conc_to_ppb(self.ptr.f_arg(), env_state.ptr.f_arg()); + } + + static void scale(const GasState &self, const double &alpha) { + + f_gas_state_scale(self.ptr.f_arg(), &alpha); + } + + static void add(const GasState &self, const GasState &delta_gas_state) { + + f_gas_state_add(self.ptr.f_arg(), delta_gas_state.ptr.f_arg()); + } + + static void add_scaled(const GasState &self, const GasState &delta_gas_state, + const double &alpha) { + + f_gas_state_add_scaled(self.ptr.f_arg(), delta_gas_state.ptr.f_arg(), &alpha); + } + }; diff --git a/src/pypartmc.cpp b/src/pypartmc.cpp index 621f8772..4aa26309 100644 --- a/src/pypartmc.cpp +++ b/src/pypartmc.cpp @@ -524,6 +524,12 @@ NB_MODULE(_PyPartMC, m) { "Return the mixing ratio of a gas species.") .def_prop_rw("mix_rats", &GasState::mix_rats, &GasState::set_mix_rats, "Provide access (read or write) to the array of mixing ratios.") + .def("molar_conc_to_ppb", &GasState::molar_conc_to_ppb, + "Convert (mol m^{-3}) to (ppb).") + .def("scale", &GasState::scale, "Scale a gas state.") + .def("add", &GasState::add, "Adds the given gas_state_delta.") + .def("add_scaled", &GasState::add_scaled, + "Adds the given gas_state_delta scaled by alpha to an existing gas_state.") ; nb::class_(m, diff --git a/tests/test_gas_state.py b/tests/test_gas_state.py index 3ab4b495..63e6fede 100644 --- a/tests/test_gas_state.py +++ b/tests/test_gas_state.py @@ -7,11 +7,15 @@ import gc import platform +import numpy as np import pytest import PyPartMC as ppmc +from .common import ENV_STATE_CTOR_ARG_MINIMAL +from .test_aero_data import AERO_DATA_CTOR_ARG_MINIMAL from .test_gas_data import GAS_DATA_CTOR_ARG_MINIMAL +from .test_scenario import SCENARIO_CTOR_ARG_MINIMAL GAS_DATA_MINIMAL = ppmc.GasData(GAS_DATA_CTOR_ARG_MINIMAL) @@ -103,6 +107,77 @@ def test_get_mix_rats(): # assert assert len(sut.mix_rats) == len(sut) + @staticmethod + def test_scale(): + # arrange + gas_data = ppmc.GasData( + ( + "SO2", + "CO", + ) + ) + sut = ppmc.GasState(gas_data) + gas_state_init_values = ({"SO2": [0.1]}, {"CO": [0.5]}) + sut.mix_rats = gas_state_init_values + + # act + alpha = 5.0 + sut.scale(alpha) + + # assert + idx_set = [] + for item in gas_state_init_values: + keys = item.keys() + assert len(keys) == 1 + key = tuple(keys)[0] + val = tuple(item.values())[0][0] + idx_set.append(gas_data.spec_by_name(key)) + assert sut[gas_data.spec_by_name(key)] == val * alpha + + @staticmethod + def test_molar_conc_to_ppb(): + # arrange + gas_data = GAS_DATA_MINIMAL + aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL) + scenario = ppmc.Scenario(gas_data, aero_data, SCENARIO_CTOR_ARG_MINIMAL) + env_state = ppmc.EnvState(ENV_STATE_CTOR_ARG_MINIMAL) + scenario.init_env_state(env_state, 0.0) + sut = ppmc.GasState(GAS_DATA_MINIMAL) + sut.mix_rats = GAS_STATE_MINIMAL + + # act + sut.molar_conc_to_ppb(env_state) + + # assert + assert sut.mix_rats[0] > GAS_STATE_MINIMAL[0][list(gas_data.species)[0]][0] + + @staticmethod + def test_add(): + sut = ppmc.GasState(GAS_DATA_MINIMAL) + sut.mix_rats = GAS_STATE_MINIMAL + gas_state_delta = ppmc.GasState(GAS_DATA_MINIMAL) + gas_state_delta.mix_rats = GAS_STATE_MINIMAL + tot_mix_rats = np.add(sut.mix_rats, gas_state_delta.mix_rats) + + # act + sut.add(gas_state_delta) + + assert sut.mix_rats == tot_mix_rats + + @staticmethod + def test_add_scaled(): + sut = ppmc.GasState(GAS_DATA_MINIMAL) + sut.mix_rats = GAS_STATE_MINIMAL + gas_state_delta = ppmc.GasState(GAS_DATA_MINIMAL) + gas_state_delta.mix_rats = GAS_STATE_MINIMAL + alpha = 2.5 + tot_mix_rats = np.add(sut.mix_rats, np.array(gas_state_delta.mix_rats) * alpha) + + # act + sut.add_scaled(gas_state_delta, alpha) + + assert sut.mix_rats == tot_mix_rats + @staticmethod def test_set_mix_rats_from_json(): # arrange