Skip to content

Commit d2d8852

Browse files
jcurtis2slayoo
andauthored
Enable output/input of netCDF files (#232)
Co-authored-by: Sylwester Arabas <[email protected]> Co-authored-by: Sylwester Arabas <[email protected]>
1 parent 7c4fe99 commit d2d8852

13 files changed

+1211
-3
lines changed

.github/workflows/pylint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ jobs:
2424
pip install -e .[tests]
2525
pip install -r .binder/requirements.txt
2626
- run: pylint --unsafe-load-any-extension=y --disable=fixme,no-member,trailing-newlines,missing-module-docstring,missing-class-docstring,missing-function-docstring,unnecessary-pass $(git ls-files '*.py')
27-
- run: nbqa pylint --unsafe-load-any-extension=y --disable=fixme,no-member,wrong-import-position,ungrouped-imports,trailing-whitespace,missing-function-docstring,missing-module-docstring $(git ls-files '*.ipynb')
27+
- run: nbqa pylint --unsafe-load-any-extension=y --disable=fixme,no-member,duplicate-code,wrong-import-position,ungrouped-imports,trailing-whitespace,missing-function-docstring,missing-module-docstring $(git ls-files '*.ipynb')

CMakeLists.txt

+13-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ set(PyPartMC_sources
4949
run_part.F90 run_part_opt.F90 util.F90 aero_data.F90 aero_state.F90 env_state.F90 gas_data.F90
5050
gas_state.F90 scenario.F90 condense.F90 aero_particle.F90 bin_grid.F90
5151
camp_core.F90 photolysis.F90 aero_mode.F90 aero_dist.F90 bin_grid.cpp condense.cpp run_part.cpp
52-
scenario.cpp util.cpp rand.cpp rand.F90
52+
scenario.cpp util.cpp output.cpp output.F90 rand.cpp rand.F90
5353
)
5454
add_prefix(src/ PyPartMC_sources)
5555

@@ -389,11 +389,23 @@ target_include_directories(netcdf_flib PRIVATE
389389
${CMAKE_SOURCE_DIR}/gitmodules/netcdf-fortran/fortran
390390
${CMAKE_SOURCE_DIR}/gitmodules/netcdf-c/include
391391
)
392+
392393
include(${CMAKE_SOURCE_DIR}/gitmodules/netcdf-fortran/CMakeExtras/MatchNetCDFFortranTypes.cmake)
394+
395+
foreach(ftype DOUBLEPRECISION;INT1;INT2;INT8;INT;REAL)
396+
foreach(ctype DOUBLE;FLOAT;INT;LONG;SHORT;SIGNED_CHAR;LONG_LONG)
397+
set(def "NF_${ftype}_IS_C_${ctype}")
398+
if(${def})
399+
target_compile_definitions(netcdf_flib PRIVATE ${def})
400+
endif()
401+
endforeach()
402+
endforeach()
403+
393404
check_fortran_compiler_flag("-fallow-argument-mismatch" COMPILER_HAS_ALLOW_ARGUMENT_MISMATCH)
394405
if(COMPILER_HAS_ALLOW_ARGUMENT_MISMATCH)
395406
target_compile_options(netcdf_flib PRIVATE $<$<COMPILE_LANGUAGE:Fortran>:-fallow-argument-mismatch -w>)
396407
endif()
408+
397409
check_fortran_compiler_flag("-mismatch_all" COMPILER_HAS_MISMATCH_ALL)
398410
if(COMPILER_HAS_MISMATCH_ALL)
399411
target_compile_options(netcdf_flib PRIVATE $<$<COMPILE_LANGUAGE:Fortran>:-mismatch_all>)

examples/process_simulation_output.ipynb

+802
Large diffs are not rendered by default.

src/output.F90

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
!###################################################################################################
2+
! This file is a part of PyPartMC licensed under the GNU General Public License v3 (LICENSE file) #
3+
! Copyright (C) 2022 University of Illinois Urbana-Champaign #
4+
! Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
5+
!###################################################################################################
6+
7+
module PyPartMC_output
8+
9+
use iso_c_binding
10+
use pmc_output
11+
use pmc_util
12+
13+
implicit none
14+
15+
contains
16+
17+
subroutine f_output_state(prefix_data, prefix_size, aero_data_ptr_c, &
18+
aero_state_ptr_c, gas_data_ptr_c, gas_state_ptr_c, env_state_ptr_c, &
19+
index, time, del_t, i_repeat, record_removals, record_optical) bind(C)
20+
21+
character(kind=c_char), dimension(*), intent(in) :: prefix_data
22+
integer(c_int), intent(in) :: prefix_size
23+
type(aero_state_t), pointer :: aero_state_ptr_f => null()
24+
type(aero_data_t), pointer :: aero_data_ptr_f => null()
25+
type(env_state_t), pointer :: env_state_ptr_f => null()
26+
type(gas_state_t), pointer :: gas_state_ptr_f => null()
27+
type(gas_data_t), pointer :: gas_data_ptr_f => null()
28+
29+
type(c_ptr) :: aero_data_ptr_c, aero_state_ptr_c, gas_data_ptr_c, &
30+
gas_state_ptr_c, env_state_ptr_c
31+
integer(c_int), intent(in) :: index, i_repeat
32+
real(c_double), intent(in) :: time, del_t
33+
logical(c_bool), intent(in) :: record_removals, record_optical
34+
35+
character(len=PMC_UUID_LEN) :: uuid
36+
integer :: output_type
37+
character(len=prefix_size) :: prefix
38+
integer :: i
39+
40+
do i=1, prefix_size
41+
prefix(i:i) = prefix_data(i)
42+
end do
43+
44+
output_type = OUTPUT_TYPE_SINGLE
45+
call uuid4_str(uuid)
46+
47+
call c_f_pointer(aero_data_ptr_c, aero_data_ptr_f)
48+
call c_f_pointer(aero_state_ptr_c, aero_state_ptr_f)
49+
call c_f_pointer(gas_data_ptr_c, gas_data_ptr_f)
50+
call c_f_pointer(gas_state_ptr_c, gas_state_ptr_f)
51+
call c_f_pointer(env_state_ptr_c, env_state_ptr_f)
52+
53+
call output_state(prefix, output_type, aero_data_ptr_f, aero_state_ptr_f, &
54+
gas_data_ptr_f, gas_state_ptr_f, env_state_ptr_f, index, time, del_t, &
55+
i_repeat, logical(record_removals), logical(record_optical), uuid)
56+
57+
end subroutine
58+
59+
subroutine f_input_state(filename_data, filename_size, index, time, del_t, &
60+
i_repeat, aero_data_ptr_c, aero_state_ptr_c, gas_data_ptr_c, &
61+
gas_state_ptr_c, env_state_ptr_c) bind(C)
62+
63+
type(aero_state_t), pointer :: aero_state_ptr_f => null()
64+
type(aero_data_t), pointer :: aero_data_ptr_f => null()
65+
type(env_state_t), pointer :: env_state_ptr_f => null()
66+
type(gas_state_t), pointer :: gas_state_ptr_f => null()
67+
type(gas_data_t), pointer :: gas_data_ptr_f => null()
68+
character(kind=c_char), dimension(*), intent(in) :: filename_data
69+
integer(c_int), intent(in) :: filename_size
70+
type(c_ptr) :: aero_data_ptr_c, aero_state_ptr_c, gas_data_ptr_c, &
71+
gas_state_ptr_c, env_state_ptr_c
72+
integer(c_int), intent(out) :: index, i_repeat
73+
real(c_double), intent(out) :: time, del_t
74+
character(len=PMC_UUID_LEN) :: uuid
75+
76+
character(len=filename_size) :: filename
77+
integer :: i
78+
79+
do i=1, filename_size
80+
filename(i:i) = filename_data(i)
81+
end do
82+
83+
call c_f_pointer(aero_data_ptr_c, aero_data_ptr_f)
84+
call c_f_pointer(aero_state_ptr_c, aero_state_ptr_f)
85+
call c_f_pointer(gas_data_ptr_c, gas_data_ptr_f)
86+
call c_f_pointer(gas_state_ptr_c, gas_state_ptr_f)
87+
call c_f_pointer(env_state_ptr_c, env_state_ptr_f)
88+
89+
call input_state(filename, index, time, del_t, i_repeat, uuid, &
90+
aero_data_ptr_f, aero_state_ptr_f, gas_data_ptr_f, gas_state_ptr_f, &
91+
env_state_ptr_f)
92+
93+
end subroutine
94+
95+
end module

src/output.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*##################################################################################################
2+
# This file is a part of PyPartMC licensed under the GNU General Public License v3 (LICENSE file) #
3+
# Copyright (C) 2022 University of Illinois Urbana-Champaign #
4+
# Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
5+
##################################################################################################*/
6+
7+
#include "output.hpp"
8+
9+
void output_state(
10+
const std::string &prefix,
11+
const AeroData &aero_data,
12+
const AeroState &aero_state,
13+
const GasData &gas_data,
14+
const GasState &gas_state,
15+
const EnvState &env_state
16+
){
17+
int index;
18+
double time;
19+
double del_t;
20+
int i_repeat;
21+
bool record_removals;
22+
bool record_optical;
23+
const int prefix_size = prefix.size();
24+
25+
index = 1;
26+
time = 1.0;
27+
del_t = 60.0;
28+
i_repeat = 1;
29+
record_removals = false;
30+
record_optical = false;
31+
32+
f_output_state(prefix.c_str(), &prefix_size, aero_data.ptr.f_arg(),
33+
aero_state.ptr.f_arg(), gas_state.gas_data->ptr.f_arg(),
34+
gas_state.ptr.f_arg(), env_state.ptr.f_arg(), &index, &time, &del_t,
35+
&i_repeat, &record_removals, &record_optical);
36+
}
37+
38+
void input_state(
39+
const std::string &name,
40+
const AeroData &aero_data,
41+
const AeroState &aero_state,
42+
const GasData &gas_data,
43+
const GasState &gas_state,
44+
const EnvState &env_state
45+
){
46+
int index;
47+
double time;
48+
double del_t;
49+
int i_repeat;
50+
const int name_size = name.size();
51+
52+
f_input_state(name.c_str(), &name_size, &index, &time, &del_t, &i_repeat,
53+
aero_data.ptr.f_arg(), aero_state.ptr.f_arg(),
54+
gas_state.gas_data->ptr.f_arg(), gas_state.ptr.f_arg(), env_state.ptr.f_arg());
55+
}

src/output.hpp

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*##################################################################################################
2+
# This file is a part of PyPartMC licensed under the GNU General Public License v3 (LICENSE file) #
3+
# Copyright (C) 2022 University of Illinois Urbana-Champaign #
4+
# Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
5+
##################################################################################################*/
6+
7+
#pragma once
8+
9+
#include "aero_state.hpp"
10+
#include "aero_data.hpp"
11+
#include "aero_dist.hpp"
12+
#include "env_state.hpp"
13+
#include "gas_data.hpp"
14+
#include "gas_state.hpp"
15+
16+
extern "C" void f_output_state(
17+
const char *prefix,
18+
const int *prefix_size,
19+
const void *aero_data,
20+
const void *aero_state,
21+
const void *gas_data,
22+
const void *gas_state,
23+
const void *env_state,
24+
const int *index,
25+
const double *time,
26+
const double *del_t,
27+
const int *i_repeat,
28+
const bool *record_removals,
29+
const bool *record_optical
30+
) noexcept;
31+
32+
extern "C" void f_input_state(
33+
const char *filename,
34+
const int *filename_size,
35+
int *index,
36+
double *time,
37+
double *del_t,
38+
int *i_repeat,
39+
const void *aero_data,
40+
const void *aero_state,
41+
const void *gas_data,
42+
const void *gas_state,
43+
const void *env_state
44+
) noexcept;
45+
46+
void output_state(
47+
const std::string &prefix,
48+
const AeroData &aero_data,
49+
const AeroState &aero_state,
50+
const GasData &gas_data,
51+
const GasState &gas_state,
52+
const EnvState &env_state
53+
);
54+
55+
void input_state(
56+
const std::string &name,
57+
const AeroData &aero_data,
58+
const AeroState &aero_state,
59+
const GasData &gas_data,
60+
const GasState &gas_state,
61+
const EnvState &env_state
62+
);

src/pypartmc.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "bin_grid.hpp"
2424
#include "camp_core.hpp"
2525
#include "photolysis.hpp"
26+
#include "output.hpp"
2627

2728
#define STRINGIFY(x) #x
2829
#define MACRO_STRINGIFY(x) STRINGIFY(x)
@@ -417,6 +418,14 @@ PYBIND11_MODULE(_PyPartMC, m) {
417418
"Evaluate a loss rate function."
418419
);
419420

421+
m.def(
422+
"output_state", &output_state, "Output current state to netCDF file."
423+
);
424+
425+
m.def(
426+
"input_state", &input_state, "Read current state from netCDF output file."
427+
);
428+
420429
m.def(
421430
"rand_init", &rand_init, "Initializes the random number generator to the state defined by the given seed plus offset. If the seed is 0 then a seed is auto-generated from the current time plus offset"
422431
);
@@ -456,6 +465,8 @@ PYBIND11_MODULE(_PyPartMC, m) {
456465
"diam2rad",
457466
"loss_rate_dry_dep",
458467
"loss_rate",
468+
"output_state",
469+
"input_state",
459470
"rand_init",
460471
"rand_normal"
461472
);

src/run_part_opt.F90

+9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ subroutine f_run_part_opt_from_json(ptr_c) bind(C)
3737

3838
call c_f_pointer(ptr_c, run_part_opt)
3939

40+
call spec_file_read_string(file, 'output_prefix', &
41+
run_part_opt%output_prefix)
42+
4043
!!! TODO #55
4144
call spec_file_read_logical(file, 'do_coagulation', run_part_opt%do_coagulation)
4245
if (run_part_opt%do_coagulation) then
@@ -121,6 +124,12 @@ subroutine f_run_part_opt_from_json(ptr_c) bind(C)
121124

122125
run_part_opt%output_type = OUTPUT_TYPE_SINGLE
123126

127+
run_part_opt%i_repeat = 1
128+
run_part_opt%n_repeat = 1
129+
130+
call pmc_srand(0, 0)
131+
call uuid4_str(run_part_opt%uuid)
132+
124133
call pmc_srand(rand_init, 0)
125134

126135
end subroutine

tests/test_aero_dist.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
import PyPartMC as ppmc
1414

1515
from .test_aero_data import AERO_DATA_CTOR_ARG_MINIMAL
16-
from .test_aero_mode import AERO_MODE_CTOR_LOG_NORMAL, AERO_MODE_CTOR_LOG_NORMAL_FULL
16+
from .test_aero_mode import (
17+
AERO_MODE_CTOR_LOG_NORMAL,
18+
AERO_MODE_CTOR_LOG_NORMAL_COAGULATION,
19+
AERO_MODE_CTOR_LOG_NORMAL_FULL,
20+
)
1721

1822
AERO_DIST_CTOR_ARG_MINIMAL = [
1923
AERO_MODE_CTOR_LOG_NORMAL,
@@ -23,6 +27,10 @@
2327
AERO_MODE_CTOR_LOG_NORMAL_FULL,
2428
]
2529

30+
AERO_DIST_CTOR_ARG_COAGULATION = [
31+
AERO_MODE_CTOR_LOG_NORMAL_COAGULATION,
32+
]
33+
2634

2735
@pytest.fixture
2836
def sut_minimal():

tests/test_aero_mode.py

+11
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@
3434
}
3535
}
3636

37+
AERO_MODE_CTOR_LOG_NORMAL_COAGULATION = {
38+
"test_mode": {
39+
"mass_frac": [{"SO4": [1]}],
40+
"diam_type": "geometric",
41+
"mode_type": "log_normal",
42+
"num_conc": 1e12 / si.m**3,
43+
"geom_mean_diam": 2 * si.um,
44+
"log10_geom_std_dev": np.log10(1.6),
45+
}
46+
}
47+
3748

3849
class TestAeroMode:
3950
@staticmethod

0 commit comments

Comments
 (0)