openjournals / joss-reviews

Reviews for the Journal of Open Source Software
Creative Commons Zero v1.0 Universal
720 stars 38 forks source link

[REVIEW]: GNS: A generalizable Graph Neural Network-based simulator for particulate and fluid modeling #5025

Closed editorialbot closed 1 year ago

editorialbot commented 1 year ago

Submitting author: !--author-handle-->@kks32<!--end-author-handle-- (Krishna Kumar) Repository: https://github.com/geoelements/gns Branch with paper.md (empty if default branch): Version: v1.1.1 Editor: !--editor-->@osorensen<!--end-editor-- Reviewers: @WPettersson, @archermarx Archive: 10.5281/zenodo.8249813

Status

status

Status badge code:

HTML: <a href="https://joss.theoj.org/papers/d5d6ee3247e52ec932107d87ab7a9549"><img src="https://joss.theoj.org/papers/d5d6ee3247e52ec932107d87ab7a9549/status.svg"></a>
Markdown: [![status](https://joss.theoj.org/papers/d5d6ee3247e52ec932107d87ab7a9549/status.svg)](https://joss.theoj.org/papers/d5d6ee3247e52ec932107d87ab7a9549)

Reviewers and authors:

Please avoid lengthy details of difficulties in the review thread. Instead, please create a new issue in the target repository and link to those issues (especially acceptance-blockers) by leaving comments in the review thread below. (For completists: if the target issue tracker is also on GitHub, linking the review thread in the issue or vice versa will create corresponding breadcrumb trails in the link target.)

Reviewer instructions & questions

@WPettersson & @archermarx, your review will be checklist based. Each of you will have a separate checklist that you should update when carrying out your review. First of all you need to run this command in a separate comment to create the checklist:

@editorialbot generate my checklist

The reviewer guidelines are available here: https://joss.readthedocs.io/en/latest/reviewer_guidelines.html. Any questions/concerns please let @osorensen know.

Please start on your review when you are able, and be sure to complete your review in the next six weeks, at the very latest

Checklists

📝 Checklist for @WPettersson

📝 Checklist for @archermarx

osorensen commented 1 year ago

@editorialbot add @archermarx as reviewer

editorialbot commented 1 year ago

@archermarx added to the reviewers list!

osorensen commented 1 year ago

@archermarx, I've added you to the list of reviewers. You can see the review instructions in the post at the top of this thread.

archermarx commented 1 year ago

Review checklist for @archermarx

Conflict of interest

Code of Conduct

General checks

Functionality

Documentation

Software paper

osorensen commented 1 year ago

I've now removed @TranzWu and @olgadoronina as reviewers, since I've not been able to get in touch with either by e-mail or GitHub.

@karanprime, I keep you in the reviewer list until further, given that you've responded that you will complete your review.

archermarx commented 1 year ago

I've gone through the paper and have no major edits. Tomorrow or monday, I will install and test the software to verify it works as intended.

osorensen commented 1 year ago

I've also removed @karanprime from the list of reviewers, as I'm not able to get in touch with them.

osorensen commented 1 year ago

@archermarx, thanks a lot for going through the paper so quickly. Have you had a chance to look at the software yet? It's absolutely fine if you haven't, but if you are able to do so within the next two weeks, it would be very much appreciated.

archermarx commented 1 year ago

Yes! I'll try to take a look today

On Thu, Aug 3, 2023, 9:01 AM Øystein Sørensen @.***> wrote:

@archermarx https://github.com/archermarx, thanks a lot for going through the paper so quickly. Have you had a chance to look at the software yet? It's absolutely fine if you haven't, but if you are able to do so within the next two weeks, it would be very much appreciated.

— Reply to this email directly, view it on GitHub https://github.com/openjournals/joss-reviews/issues/5025#issuecomment-1663944115, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOP3QGV25WSL3OO23BA4UA3XTOOJNANCNFSM6AAAAAATHOIYW4 . You are receiving this because you were mentioned.Message ID: @.***>

archermarx commented 1 year ago

I am encountering an error installing the code. After following the instructions to install pytorch_geometric

pip install pytorch_geometric

I ran

pip install -R requirements.txt

in the gns directory, and after some time I got the following error:

PS C:\Users\thoma\gns> pip install -r requirements.txt
Collecting absl-py (from -r requirements.txt (line 1))
  Using cached absl_py-1.4.0-py3-none-any.whl (126 kB)
Collecting autopep8 (from -r requirements.txt (line 2))
  Using cached autopep8-2.0.2-py2.py3-none-any.whl (45 kB)
Collecting numpy==1.23.1 (from -r requirements.txt (line 3))
  Using cached numpy-1.23.1-cp310-cp310-win_amd64.whl (14.6 MB)
Collecting dm-tree (from -r requirements.txt (line 4))
  Using cached dm_tree-0.1.8-cp310-cp310-win_amd64.whl (101 kB)
Requirement already satisfied: matplotlib in c:\users\thoma\appdata\local\packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\localcache\local-packages\python310\site-packages (from -r requirements.txt (line 5)) (3.6.1)
Collecting pyevtk (from -r requirements.txt (line 6))
  Using cached pyevtk-1.6.0-py3-none-any.whl (20 kB)
Requirement already satisfied: pytest in c:\users\thoma\appdata\local\packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\localcache\local-packages\python310\site-packages (from -r requirements.txt (line 7)) (7.1.3)
Requirement already satisfied: torch in c:\users\thoma\appdata\local\packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\localcache\local-packages\python310\site-packages (from -r requirements.txt (line 8)) (2.0.1)
Requirement already satisfied: torch_geometric in c:\users\thoma\appdata\local\packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\localcache\local-packages\python310\site-packages (from -r requirements.txt (line 9)) (2.3.1)
Collecting torch_sparse (from -r requirements.txt (line 10))
  Using cached torch_sparse-0.6.17.tar.gz (209 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error

  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [17 lines of output]
      Traceback (most recent call last):
        File "C:\Users\thoma\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 353, in <module>
          main()
        File "C:\Users\thoma\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 335, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
        File "C:\Users\thoma\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 118, in get_requires_for_build_wheel
          return hook(config_settings)
        File "C:\Users\thoma\AppData\Local\Temp\pip-build-env-2f8616_v\overlay\Lib\site-packages\setuptools\build_meta.py", line 341, in get_requires_for_build_wheel
          return self._get_build_requires(config_settings, requirements=['wheel'])
        File "C:\Users\thoma\AppData\Local\Temp\pip-build-env-2f8616_v\overlay\Lib\site-packages\setuptools\build_meta.py", line 323, in _get_build_requires
          self.run_setup()
        File "C:\Users\thoma\AppData\Local\Temp\pip-build-env-2f8616_v\overlay\Lib\site-packages\setuptools\build_meta.py", line 487, in run_setup
          super(_BuildMetaLegacyBackend,
        File "C:\Users\thoma\AppData\Local\Temp\pip-build-env-2f8616_v\overlay\Lib\site-packages\setuptools\build_meta.py", line 338, in run_setup
          exec(code, locals())
        File "<string>", line 8, in <module>
      **ModuleNotFoundError: No module named 'torch'**
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.

As can be seen, I have both pytorch and pytorch_geometric installed, so I'm not sure what's causing this.

kks32 commented 1 year ago

@archermarx Sorry about that. If you are on Linux, could you try the following for installation. PyTorch Geometric always creates some issues:

conda install pytorch torchvision torchaudio cpuonly -c pytorch
conda install pyg -c pyg
conda install -c anaconda absl-py 
conda install -c conda-forge numpy dm-tree matplotlib-base pyevtk

Please feel free to open an issue on GNS if you are having issues so we can resolve them. Thank you for reviewing the code!

I have updated instructions on the repo too.

archermarx commented 1 year ago

I'm on windows, but I can take a look using WSL

On Mon, Aug 7, 2023, 2:40 PM Krishna Kumar @.***> wrote:

@archermarx https://github.com/archermarx Sorry about that. If you are on Linux, could you try the following for installation. PyTorch Geometric always creates some issues:

conda install pytorch torchvision torchaudio cpuonly -c pytorch conda install pyg -c pyg conda install -c conda-forge numpy dm-tree matplotlib-base pyevtk

Please feel free to open an issue on GNS https://github.com/geoelements/gns/issues/new if you are having issues so we can resolve them. Thank you for reviewing the code!

I have updated instructions on the repo too.

— Reply to this email directly, view it on GitHub https://github.com/openjournals/joss-reviews/issues/5025#issuecomment-1668403524, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOP3QGSAD44G6KWQ2PQNYIDXUEZCXANCNFSM6AAAAAATHOIYW4 . You are receiving this because you were mentioned.Message ID: @.***>

archermarx commented 1 year ago

So i followed the new conda installation instructions and didn't get any errors. I then attempted to follow your instructions for training. For context, I have the gns repository and the gns-sample repository in the same directory. From the gns repository, I run:

python3 -m gns.train --data_path="../gns-sample/WaterDropSample/dataset/test.npz" --output_path="output" --model_path="models" -ntraining_steps=1000

The terminal outputs world_size=0 and then exits. There does not seem to be any output produced. There is no output file. and the models directory is empty.

Also, how do I run the automated tests?

kks32 commented 1 year ago

Thanks @archermarx for testing it. Apologies, for the issue. This is because we were set-up to run primarily on distributed GPU systems. We have updated the code so you can run it on cpus without running into issues. Assuming you will clone gns-sample to test run in the gns folder itself. Here are the steps:

git clone https://github.com/geoelements/gns-sample

TMP_DIR="./gns-sample"
DATASET_NAME="WaterDropSample"

mkdir -p ${TMP_DIR}/${DATASET_NAME}/models/
mkdir -p ${TMP_DIR}/${DATASET_NAME}/rollout/

DATA_PATH="${TMP_DIR}/${DATASET_NAME}/dataset/"
MODEL_PATH="${TMP_DIR}/${DATASET_NAME}/models/"
ROLLOUT_PATH="${TMP_DIR}/${DATASET_NAME}/rollout/"

python -m gns.train --data_path=${DATA_PATH} --model_path=${MODEL_PATH} --ntraining_steps=10

This should train the gns code for 10 steps. You can see this particular CI testing done in circleci. Since we use random seeding, the loss maybe different at each time.

We also have unit tests which you can run by doing: pytest test/ in the gns folder. This is also done in CircleCI.

I appreciate your time and feedback! Thank you. Apologies for issues.

archermarx commented 1 year ago

@kks32 thanks for your responsiveness on this. I think we're almost there. I am now encountering this error:

rank = cpu, cuda = False
Traceback (most recent call last):
  File "/home/marksta/miniconda3/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/marksta/miniconda3/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/home/marksta/gns/gns/train.py", line 425, in <module>
    app.run(main)
  File "/home/marksta/miniconda3/lib/python3.10/site-packages/absl/app.py", line 308, in run
    _run_main(main, args)
  File "/home/marksta/miniconda3/lib/python3.10/site-packages/absl/app.py", line 254, in _run_main
    sys.exit(main(argv))
  File "/home/marksta/gns/gns/train.py", line 414, in main
    train(device, myflags, world_size)
  File "/home/marksta/gns/gns/train.py", line 274, in train
    pred_acc, target_acc = simulator.predict_accelerations(
  File "/home/marksta/gns/gns/learned_simulator.py", line 280, in predict_accelerations
    node_features, edge_index, edge_features = self._encoder_preprocessor(
  File "/home/marksta/gns/gns/learned_simulator.py", line 127, in _encoder_preprocessor
    senders, receivers = self._compute_graph_connectivity(
  File "/home/marksta/gns/gns/learned_simulator.py", line 96, in _compute_graph_connectivity
    edge_index = radius_graph(
  File "/home/marksta/miniconda3/lib/python3.10/site-packages/torch_geometric/nn/pool/__init__.py", line 210, in radius_graph
    return torch_cluster.radius_graph(x, r, batch, loop, max_num_neighbors,
AttributeError: 'NoneType' object has no attribute 'radius_graph'

I get the same error when running tests. I've attached the full test log to this post.

testlog.txt

kks32 commented 1 year ago

Hi @archermarx Thank you for your patience. The issue is related to Pytorch geometric package and its different packagings. Could you please install torch cluster using conda install pytorch-cluster -c pyg and then try the steps here: https://github.com/openjournals/joss-reviews/issues/5025#issuecomment-1671457463

Appreciate your patience.

archermarx commented 1 year ago

Great. I'll try and take a look today. The improvements you've made to the documentation and install process have addressed my other concerns, so if I can get the code running and test it, I think I'd be ready to approve.

archermarx commented 1 year ago

Ok, I was able to successfully train for 1000 steps, and the loss decreased as expected. One final thing. Running tests still gives me an error, which looks like it has something to do with numpy. Here's the error text:

test/test_data_loader.py EE                                                 [ 33%]
test/test_graph_network.py .                                                [ 50%]
test/test_learned_simulator.py .                                            [ 66%]
test/test_noise_utils.py ..                                                 [100%]

===================================== ERRORS ======================================
_____________________ ERROR at setup of test_samples_dataset ______________________

temp_dir = '/tmp/tmpkm93oz5k'

    @pytest.fixture
    def dummy_data(temp_dir):
        # Create a dummy dataset
        dummy_data = [(np.random.rand(10, 3, 2), i % 3) for i in range(5)]
        data_path = os.path.join(temp_dir, 'data.npz')
>       np.savez(data_path, *dummy_data)

test/test_data_loader.py:25:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../miniconda3/lib/python3.10/site-packages/numpy/lib/npyio.py:639: in savez
    _savez(file, args, kwds, False)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

file = '/tmp/tmpkm93oz5k/data.npz'
args = ((array([[[2.83082370e-01, 2.42428869e-01],
        [8.70220391e-01, 8.13641733e-01],
        [1.34136507e-01, 5.43269...803545]],

       [[0.9102955 , 0.96535921],
        [0.87278961, 0.91629958],
        [0.72771614, 0.51367583]]]), 1))
kwds = {'arr_0': (array([[[2.83082370e-01, 2.42428869e-01],
        [8.70220391e-01, 8.13641733e-01],
        [1.34136507e-01...3]],

       [[0.72921478, 0.46569833],
        [0.89067798, 0.91844627],
        [0.43296379, 0.66740189]]]), 0), ...}
compress = False, allow_pickle = True, pickle_kwargs = None

    def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None):
        # Import is postponed to here since zipfile depends on gzip, an optional
        # component of the so-called standard library.
        import zipfile

        if not hasattr(file, 'write'):
            file = os_fspath(file)
            if not file.endswith('.npz'):
                file = file + '.npz'

        namedict = kwds
        for i, val in enumerate(args):
            key = 'arr_%d' % i
            if key in namedict.keys():
                raise ValueError(
                    "Cannot use un-named variables and keyword %s" % key)
            namedict[key] = val

        if compress:
            compression = zipfile.ZIP_DEFLATED
        else:
            compression = zipfile.ZIP_STORED

        zipf = zipfile_factory(file, mode="w", compression=compression)

        for key, val in namedict.items():
            fname = key + '.npy'
>           val = np.asanyarray(val)
E           ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.

../miniconda3/lib/python3.10/site-packages/numpy/lib/npyio.py:740: ValueError
___________________ ERROR at setup of test_trajectories_dataset ___________________

temp_dir = '/tmp/tmp30m_3nsk'

    @pytest.fixture
    def dummy_data(temp_dir):
        # Create a dummy dataset
        dummy_data = [(np.random.rand(10, 3, 2), i % 3) for i in range(5)]
        data_path = os.path.join(temp_dir, 'data.npz')
>       np.savez(data_path, *dummy_data)

test/test_data_loader.py:25:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../miniconda3/lib/python3.10/site-packages/numpy/lib/npyio.py:639: in savez
    _savez(file, args, kwds, False)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

file = '/tmp/tmp30m_3nsk/data.npz'
args = ((array([[[0.69097819, 0.91616646],
        [0.33162476, 0.03605634],
        [0.56994853, 0.38930877]],

       [[0.4...775784]],

       [[0.96813509, 0.4836435 ],
        [0.75056261, 0.18810344],
        [0.93277804, 0.8280973 ]]]), 1))
kwds = {'arr_0': (array([[[0.69097819, 0.91616646],
        [0.33162476, 0.03605634],
        [0.56994853, 0.38930877]],

   ...7e-01, 1.44940277e-01],
        [8.74275614e-01, 9.13514123e-01],
        [9.54224020e-01, 6.93742218e-01]]]), 0), ...}
compress = False, allow_pickle = True, pickle_kwargs = None

    def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None):
        # Import is postponed to here since zipfile depends on gzip, an optional
        # component of the so-called standard library.
        import zipfile

        if not hasattr(file, 'write'):
            file = os_fspath(file)
            if not file.endswith('.npz'):
                file = file + '.npz'

        namedict = kwds
        for i, val in enumerate(args):
            key = 'arr_%d' % i
            if key in namedict.keys():
                raise ValueError(
                    "Cannot use un-named variables and keyword %s" % key)
            namedict[key] = val

        if compress:
            compression = zipfile.ZIP_DEFLATED
        else:
            compression = zipfile.ZIP_STORED

        zipf = zipfile_factory(file, mode="w", compression=compression)

        for key, val in namedict.items():
            fname = key + '.npy'
>           val = np.asanyarray(val)
E           ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.

../miniconda3/lib/python3.10/site-packages/numpy/lib/npyio.py:740: ValueError
============================= short test summary info =============================
ERROR test/test_data_loader.py::test_samples_dataset - ValueError: setting an array element with a sequence. The requested array has ...
ERROR test/test_data_loader.py::test_trajectories_dataset - ValueError: setting an array element with a sequence. The requested array has ...
=========================== 4 passed, 2 errors in 2.36s ===========================
kks32 commented 1 year ago

Thanks @archermarx ! Ah...seems like the way npz is saved by numpy changed between Python versions of 3.9 and 3.10. I have now added the code to support both versions: https://github.com/geoelements/gns/pull/53

Could you please repull the gns repo and the test should now work. Thanks again for your patience.

archermarx commented 1 year ago

Great! The tests now pass. @osorensen I have completed my review checklist and I think this package is good to go.

osorensen commented 1 year ago

Thanks you very much @archermarx!!!

osorensen commented 1 year ago

@editorialbot generate pdf

osorensen commented 1 year ago

@editorialbot check references

editorialbot commented 1 year ago
Reference check summary (note 'MISSING' DOIs are suggestions that need verification):

OK DOIs

- 10.5281/zenodo.6658322 is OK
- 10.17603/ds2-0phb-dg64 is OK
- 10.1680/jgeot.15.LM.005 is OK

MISSING DOIs

- 10.1016/j.cma.2022.115704 may be a valid DOI for title: Model-Free Data-Driven Inference in Computational Mechanics
- 10.1145/3197517.3201293 may be a valid DOI for title: A Moving Least Squares Material Point Method with Displacement Discontinuity and Two-Way Rigid Body Coupling

INVALID DOIs

- 10.5555/3524938 is INVALID
editorialbot commented 1 year ago

:point_right::page_facing_up: Download article proof :page_facing_up: View article proof on GitHub :page_facing_up: :point_left:

osorensen commented 1 year ago

@kks32, I'm reading through the paper now and will post any editorial comments as issues in the source repository.

kks32 commented 1 year ago

@osorensen Thank you! I have addressed all the suggested editorial comments. Many thanks for your time and reading through the paper.

osorensen commented 1 year ago

Post-Review Checklist for Editor and Authors

Additional Author Tasks After Review is Complete

Editor Tasks Prior to Acceptance

osorensen commented 1 year ago

@kks, could you now complete the "Additional Author Tasks After Review is Complete" listed in the post above?

kks32 commented 1 year ago

@editorialbot generate pdf

editorialbot commented 1 year ago

:point_right::page_facing_up: Download article proof :page_facing_up: View article proof on GitHub :page_facing_up: :point_left:

kks32 commented 1 year ago

GitHub GNS version v1.1.0 Zenodo DOI: 10.5281/zenodo.8249671

kks32 commented 1 year ago

Additional Author Tasks After Review is Complete

osorensen commented 1 year ago

@editorialbot set v1.1.0 as version

editorialbot commented 1 year ago

Done! version is now v1.1.0

osorensen commented 1 year ago

@editorialbot set 10.5281/zenodo.8249671 as archive

editorialbot commented 1 year ago

Done! archive is now 10.5281/zenodo.8249671

osorensen commented 1 year ago

@kks32, regarding this point:

  • [ ] Make sure that the title and author list (including ORCIDs) in the archive match those in the JOSS paper.

Could you please change the title of the archive so it exactly matches the JOSS paper. That is, change it to "GNS: A generalizable Graph Neural Network-based simulator for particulate and fluid modeling".

osorensen commented 1 year ago

I think this means you will have to create a new archive. If so, please list the new DOI here.

kks32 commented 1 year ago

@osorensen Sorry about that, here's the updated DOI: 10.5281/zenodo.8249813 Updated version to v1.1.1

osorensen commented 1 year ago

@editorialbot set v1.1.1 as version

editorialbot commented 1 year ago

Done! version is now v1.1.1

osorensen commented 1 year ago

@editorialbot set 10.5281/zenodo.8249813 as archive

editorialbot commented 1 year ago

Done! archive is now 10.5281/zenodo.8249813

osorensen commented 1 year ago

@editorialbot check references

editorialbot commented 1 year ago
Reference check summary (note 'MISSING' DOIs are suggestions that need verification):

OK DOIs

- 10.5281/zenodo.6658322 is OK
- 10.1016/j.cma.2022.115704 is OK
- 10.17603/ds2-0phb-dg64 is OK
- 10.1680/jgeot.15.LM.005 is OK
- 10.1145/3197517.3201293 is OK

MISSING DOIs

- None

INVALID DOIs

- None
osorensen commented 1 year ago

@editorialbot generate pdf

editorialbot commented 1 year ago

:point_right::page_facing_up: Download article proof :page_facing_up: View article proof on GitHub :page_facing_up: :point_left:

osorensen commented 1 year ago

@editorialbot recommend-accept

editorialbot commented 1 year ago
Attempting dry run of processing paper acceptance...