Skip to content

Commit a6829d7

Browse files
authored
Merge pull request #77 from cropsinsilico/model_copies
Model copies
2 parents fedaf69 + 7ad0a51 commit a6829d7

82 files changed

Lines changed: 1995 additions & 508 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-install.yml

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ env:
2828
USEVIRTUALENV: 0
2929
BUILDDOCS: 0
3030
YGG_GHA_JOB_ID_SUFFIX: null
31+
TMPDIR: /tmp/
3132
jobs:
3233
test_pip:
3334
name: Test (${{ matrix.python-version }}, ${{ matrix.os }}, ${{ matrix.install-method
@@ -202,8 +203,13 @@ jobs:
202203
if: matrix.os == 'macos-latest' && matrix.install-method == 'conda'
203204
run: ./utils/setup_old_mac_sdk.sh
204205
- name: (CONDA) Install dependencies & package from existing build
206+
id: conda1
207+
continue-on-error: true
205208
if: matrix.install-method == 'conda'
206209
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
210+
- name: (CONDA) Install dependencies & package from existing build (re-attempt)
211+
if: ${{ steps.test2.outcome == 'failure' && matrix.install-method == 'conda'}}
212+
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
207213
- name: (MACOS,CONDA) Set allow_multiple_omp config
208214
if: matrix.install-method == 'conda' && matrix.os == 'macos-latest'
209215
run: yggconfig --allow-multiple-omp
@@ -226,6 +232,7 @@ jobs:
226232
- name: Re-run test failures (1st)
227233
if: ${{ steps.test1.outcome == 'failure' }}
228234
run: yggtest --ci --cov-append ${{ matrix.test-flags1 }} --last-failed --last-failed-no-failures=none
235+
--debug
229236
- name: Run tests (2nd)
230237
if: matrix.test-flags2 != ''
231238
id: test2
@@ -234,16 +241,17 @@ jobs:
234241
- name: Re-run test failures (2nd)
235242
if: ${{ steps.test2.outcome == 'failure' }}
236243
run: yggtest --ci --cov-append ${{ matrix.test-flags2 }} --last-failed --last-failed-no-failures=none
244+
--debug
237245
- name: Combine coverage reports
238246
continue-on-error: true
239-
run: 'ls .*
247+
run: '# ls .*
240248
241249
coverage combine --append
242250
243251
'
244252
- name: (CONDA) Generate XML version of coverage report
245253
if: matrix.install-method == 'conda'
246-
run: 'ls .*
254+
run: '# ls .*
247255
248256
coverage xml
249257
@@ -461,8 +469,13 @@ jobs:
461469
if: matrix.os == 'macos-latest' && matrix.install-method == 'conda'
462470
run: ./utils/setup_old_mac_sdk.sh
463471
- name: (CONDA) Install dependencies & package from existing build
472+
id: conda1
473+
continue-on-error: true
464474
if: matrix.install-method == 'conda'
465475
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
476+
- name: (CONDA) Install dependencies & package from existing build (re-attempt)
477+
if: ${{ steps.test2.outcome == 'failure' && matrix.install-method == 'conda'}}
478+
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
466479
- name: (MACOS,CONDA) Set allow_multiple_omp config
467480
if: matrix.install-method == 'conda' && matrix.os == 'macos-latest'
468481
run: yggconfig --allow-multiple-omp
@@ -485,6 +498,7 @@ jobs:
485498
- name: Re-run test failures (1st)
486499
if: ${{ steps.test1.outcome == 'failure' }}
487500
run: yggtest --ci --cov-append ${{ matrix.test-flags1 }} --last-failed --last-failed-no-failures=none
501+
--debug
488502
- name: Run tests (2nd)
489503
if: matrix.test-flags2 != ''
490504
id: test2
@@ -493,16 +507,17 @@ jobs:
493507
- name: Re-run test failures (2nd)
494508
if: ${{ steps.test2.outcome == 'failure' }}
495509
run: yggtest --ci --cov-append ${{ matrix.test-flags2 }} --last-failed --last-failed-no-failures=none
510+
--debug
496511
- name: Combine coverage reports
497512
continue-on-error: true
498-
run: 'ls .*
513+
run: '# ls .*
499514
500515
coverage combine --append
501516
502517
'
503518
- name: (CONDA) Generate XML version of coverage report
504519
if: matrix.install-method == 'conda'
505-
run: 'ls .*
520+
run: '# ls .*
506521
507522
coverage xml
508523
@@ -700,8 +715,13 @@ jobs:
700715
if: matrix.os == 'macos-latest' && matrix.install-method == 'conda'
701716
run: ./utils/setup_old_mac_sdk.sh
702717
- name: (CONDA) Install dependencies & package from existing build
718+
id: conda1
719+
continue-on-error: true
703720
if: matrix.install-method == 'conda'
704721
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
722+
- name: (CONDA) Install dependencies & package from existing build (re-attempt)
723+
if: ${{ steps.test2.outcome == 'failure' && matrix.install-method == 'conda'}}
724+
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
705725
- name: (MACOS,CONDA) Set allow_multiple_omp config
706726
if: matrix.install-method == 'conda' && matrix.os == 'macos-latest'
707727
run: yggconfig --allow-multiple-omp
@@ -724,6 +744,7 @@ jobs:
724744
- name: Re-run test failures (1st)
725745
if: ${{ steps.test1.outcome == 'failure' }}
726746
run: yggtest --ci --cov-append ${{ matrix.test-flags1 }} --last-failed --last-failed-no-failures=none
747+
--debug
727748
- name: Run tests (2nd)
728749
if: matrix.test-flags2 != ''
729750
id: test2
@@ -732,16 +753,17 @@ jobs:
732753
- name: Re-run test failures (2nd)
733754
if: ${{ steps.test2.outcome == 'failure' }}
734755
run: yggtest --ci --cov-append ${{ matrix.test-flags2 }} --last-failed --last-failed-no-failures=none
756+
--debug
735757
- name: Combine coverage reports
736758
continue-on-error: true
737-
run: 'ls .*
759+
run: '# ls .*
738760
739761
coverage combine --append
740762
741763
'
742764
- name: (CONDA) Generate XML version of coverage report
743765
if: matrix.install-method == 'conda'
744-
run: 'ls .*
766+
run: '# ls .*
745767
746768
coverage xml
747769
@@ -941,8 +963,13 @@ jobs:
941963
if: matrix.os == 'macos-latest' && matrix.install-method == 'conda'
942964
run: ./utils/setup_old_mac_sdk.sh
943965
- name: (CONDA) Install dependencies & package from existing build
966+
id: conda1
967+
continue-on-error: true
944968
if: matrix.install-method == 'conda'
945969
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
970+
- name: (CONDA) Install dependencies & package from existing build (re-attempt)
971+
if: ${{ steps.test2.outcome == 'failure' && matrix.install-method == 'conda'}}
972+
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
946973
- name: (MACOS,CONDA) Set allow_multiple_omp config
947974
if: matrix.install-method == 'conda' && matrix.os == 'macos-latest'
948975
run: yggconfig --allow-multiple-omp
@@ -965,6 +992,7 @@ jobs:
965992
- name: Re-run test failures (1st)
966993
if: ${{ steps.test1.outcome == 'failure' }}
967994
run: yggtest --ci --cov-append ${{ matrix.test-flags1 }} --last-failed --last-failed-no-failures=none
995+
--debug
968996
- name: Run tests (2nd)
969997
if: matrix.test-flags2 != ''
970998
id: test2
@@ -973,16 +1001,17 @@ jobs:
9731001
- name: Re-run test failures (2nd)
9741002
if: ${{ steps.test2.outcome == 'failure' }}
9751003
run: yggtest --ci --cov-append ${{ matrix.test-flags2 }} --last-failed --last-failed-no-failures=none
1004+
--debug
9761005
- name: Combine coverage reports
9771006
continue-on-error: true
978-
run: 'ls .*
1007+
run: '# ls .*
9791008
9801009
coverage combine --append
9811010
9821011
'
9831012
- name: (CONDA) Generate XML version of coverage report
9841013
if: matrix.install-method == 'conda'
985-
run: 'ls .*
1014+
run: '# ls .*
9861015
9871016
coverage xml
9881017
@@ -1180,8 +1209,13 @@ jobs:
11801209
if: matrix.os == 'macos-latest' && matrix.install-method == 'conda'
11811210
run: ./utils/setup_old_mac_sdk.sh
11821211
- name: (CONDA) Install dependencies & package from existing build
1212+
id: conda1
1213+
continue-on-error: true
11831214
if: matrix.install-method == 'conda'
11841215
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
1216+
- name: (CONDA) Install dependencies & package from existing build (re-attempt)
1217+
if: ${{ steps.test2.outcome == 'failure' && matrix.install-method == 'conda'}}
1218+
run: python utils/setup_test_env.py install ${{ matrix.install-method }} --without-build
11851219
- name: (MACOS,CONDA) Set allow_multiple_omp config
11861220
if: matrix.install-method == 'conda' && matrix.os == 'macos-latest'
11871221
run: yggconfig --allow-multiple-omp
@@ -1204,6 +1238,7 @@ jobs:
12041238
- name: Re-run test failures (1st)
12051239
if: ${{ steps.test1.outcome == 'failure' }}
12061240
run: yggtest --ci --cov-append ${{ matrix.test-flags1 }} --last-failed --last-failed-no-failures=none
1241+
--debug
12071242
- name: Run tests (2nd)
12081243
if: matrix.test-flags2 != ''
12091244
id: test2
@@ -1212,16 +1247,17 @@ jobs:
12121247
- name: Re-run test failures (2nd)
12131248
if: ${{ steps.test2.outcome == 'failure' }}
12141249
run: yggtest --ci --cov-append ${{ matrix.test-flags2 }} --last-failed --last-failed-no-failures=none
1250+
--debug
12151251
- name: Combine coverage reports
12161252
continue-on-error: true
1217-
run: 'ls .*
1253+
run: '# ls .*
12181254
12191255
coverage combine --append
12201256
12211257
'
12221258
- name: (CONDA) Generate XML version of coverage report
12231259
if: matrix.install-method == 'conda'
1224-
run: 'ls .*
1260+
run: '# ls .*
12251261
12261262
coverage xml
12271263

HISTORY.rst

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
History
33
=======
44

5-
1.5.0 (XXXX-XX-XX)
5+
1.6.0 (2021-04-14)
66
------------------
77

88
* Made the asynchronous comm class more generic so it can be used to wrap any comm type and is more robust
@@ -24,7 +24,18 @@ History
2424
* Added new tests for transformations as part of comms and fixed bugs that those tests showed in how empty messages are transformed
2525
* Modify comm class such that the type is updated based on the transformed datatype when receiving *and* sending
2626
* Added a dedicated CommMessage class for wrapping messages with information about the message (e.g. header, work comms, status) and update comm & connection methods to expect this type
27-
* Fixed a bug that caused segfault when calling yggdrasil interface from inside a threaded model by introducing an 'allow_threading' parameter for models which sets a new parameter 'allow_multiple_comms' for comms associated with the model and causes the comm to be initialized such that multiple connections to the same address can be made (this is really just important for ZMQ comms and should only be invoked when using a server/client communication pattern).
27+
* Fixed a bug that caused segfault when calling yggdrasil interface from inside a threaded model by introducing an 'allow_threading' parameter for models which sets a new parameter 'allow_multiple_comms' for comms associated with the model and causes the comm to be initialized such that multiple connections to the same address can be made (this is really just important for ZMQ comms and should only be invoked when using a server/client communication pattern)
28+
* Allow multiple models to be run from a single YAML entry via the 'copies' model parameter.
29+
* Added DuplicatedModelDriver to handle model duplication via 'copies'
30+
* Added comm parameter 'dont_copy' to prevent duplication of comms (sharing) when a model is duplicated.
31+
* Updated ZMQProxy class so that server comms 'sign on' to the proxy by responding to a sign-on message that is sent continuously until a server signs on. Requests from clients received before the sign-on exchange are backlogged and sent after sign-on.
32+
* Updated ZMQComm to allow multiple connections during threading or when a model is duplicated.
33+
* Added rpc_lesson2b to demonstrate use of 'copies' parameter.
34+
* Updated the classes in the C interface to use bit flags
35+
* Updated documentation with information on using threads with yggdrasil and more advanced RPC features.
36+
* Refactored CommBase so that there are two components to send and receive calls and use the refactoring to cut down on repeat serialization in async comms and connection drivers.
37+
* Change fmt input parameter to YggAsciiArrayOutput Python interface to optional
38+
* Allow delimiter in YAML to override format_str provided via the interface for output serialization
2839
* Refactor CommBase so that there are two components to send and receive calls and use the refactoring to cut down on repeat serialization in async comms and connection drivers.
2940

3041
When sending...
@@ -37,10 +48,6 @@ History
3748
1) recv_message, which receives the message and deserializes it, and
3849
2) finalize_message, which filters and transforms messages and performs actions associated with specific message types.
3950

40-
TODO
41-
~~~~
42-
43-
* Updated documentation with information on using threads with yggdrasil.
4451

4552
1.5.0 (2021-02-10) Migrate to GHA, refactor CLI, & fix bugs
4653
------------------

docs/source/examples/make_example_rst.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
'all': 'Mixed',
1616
'all_nomatlab': 'Mixed w/o Matlab',
1717
'fortran': 'Fortran'}
18-
_default_lang = 'python'
18+
_default_lang = ['python', 'cpp', 'c', 'R', 'fortran', 'matlab']
1919

2020

2121
def get_file(fname, local=False):
@@ -75,13 +75,25 @@ def get_rel_path(fname, upone=False):
7575

7676

7777
def write_src_ref(fd, k):
78-
write_src(fd, k, _default_lang, upone=True)
78+
default = None
79+
for x in _default_lang:
80+
if x in source[k]:
81+
default = x
82+
break
83+
assert(default)
84+
write_src(fd, k, default, upone=True)
7985
fd.write('\n')
8086
write_ref_link(fd, k)
8187

8288

8389
def write_yml_ref(fd, k):
84-
write_yml(fd, k, _default_lang, upone=True)
90+
default = None
91+
for x in _default_lang:
92+
if x in source[k]:
93+
default = x
94+
break
95+
assert(default)
96+
write_yml(fd, k, default, upone=True)
8597
fd.write('\n')
8698
write_ref_link(fd, k)
8799

@@ -178,6 +190,7 @@ def write_yml(fd, k, lang, upone=False):
178190
rst_examples.append('gs_lesson4b') # Special case
179191
rst_examples += ['formatted_io%d' % x for x in range(1, 10)]
180192
rst_examples += ['rpc_lesson%d' % x for x in range(1, 4)]
193+
rst_examples += ['rpc_lesson%db' % x for x in range(2, 4)]
181194
rst_examples += ['model_function', 'conditional_io', 'transformed_io']
182195
rst_examples += ['timesync%d' % x for x in range(1, 3)]
183196
make_toc_file(rst_examples)

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Welcome to yggdrasil's documentation!
2424
units
2525
c_format_strings
2626
debugging
27+
threading
2728
examples/examples_toc
2829
advanced/index
2930
development/index

docs/source/server_client_io.rst

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@ that the model will act as a client of the ``<server_model_name>`` model.
4141

4242
.. include:: examples/rpc_lesson1_yml.rst
4343

44+
In addition to the RPC API call, the example server also has an input ``params``.
45+
Models acting as servers can have as many inputs/outputs as desired in addition to
46+
the RPC connections. While the example input is not used to modify the output
47+
in this example, such a comm could be used to initialize a model with
48+
parameters and/or initial conditions.
49+
50+
51+
Using Existing Inputs/Outputs
52+
-----------------------------
53+
54+
Models that have already been integrated via |yggdrasil| can also be turned
55+
into servers without modifying the code. Instead of passing a boolean to
56+
the ``is_server`` parameter, such models can provide a mapping with ``input``
57+
and ``output`` parameters that explicitly outline which of a existing model's
58+
inputs/outputs should be used for the RPC call. Receive/send calls to named
59+
input/output channels will then behave as receive/send calls on a server
60+
interface comm.
61+
62+
.. todo:: Example source code and YAML of server replacing an existing input/output
63+
4464

4565
One Server, Two Clients
4666
-----------------------
@@ -62,4 +82,52 @@ source code.
6282
During runtime, request messages from both clients will be routed to the
6383
server model which will process the requests in the order they are received.
6484

65-
.. todo:: Section on having multiple servers.
85+
86+
Two Servers, Two Clients
87+
------------------------
88+
89+
There is also no limit on the number of copies of a server model that can be
90+
used to responsd to RPC requests from the clients. In the example below, the
91+
server and clients are the same as above, but 2 copies of the server model
92+
are run as specified by the model ``copies`` parameter in the server YAML.
93+
94+
.. include:: examples/rpc_lesson2b_yml.rst
95+
96+
This allow client requests to be returned twice as fast, but precludes any
97+
use of an internal state by the server model as there is no way for a client
98+
to be sure that the same server model is responding to its requests and only
99+
its requests.
100+
101+
102+
Wrapped Function Server
103+
-----------------------
104+
105+
Models that are created by letting |yggdrasil| automatically
106+
:ref:`wrap a function <autowrap_rst>` can also act as servers and/or clients.
107+
In the example below, the model acting as a server
108+
is a very simple function that takes a string as an input and returns the
109+
same string and the client is a function that takes a string as an input,
110+
calls the server models with the input string and returns the response.
111+
112+
When a client model is autowrapped from a function, additional care must be
113+
taken so that the client RPC comm can be reused during each call to the
114+
model. In interpreted models (Python, R, MATLAB), this is done by passing the
115+
keyword ``global_scope`` to the RPC client interface initialization function
116+
(``YggRpcClient`` in Python). In compiled models (C, C++, Fortran), this is
117+
done by framing RPC client interface initialization calls with the
118+
``WITH_GLOBAL_SCOPE`` macro (see the language specific versions of this
119+
example for specifics).
120+
121+
.. include:: examples/rpc_lesson3_src.rst
122+
123+
The RPC connection between the server and the client is controlled by the
124+
same ``is_server`` and ``client_of`` YAML parameters as before.
125+
126+
.. include:: examples/rpc_lesson3_yml.rst
127+
128+
By default, all inputs to a wrapped server function will be used in
129+
the RPC call. However if only some of the inputs should be passed in by the
130+
RPC calls, they can be specified explicitly by providing the ``is_server``
131+
parameter with a map that contains ``input`` and ``output`` parameters that
132+
map to the names of function input/output variables (as in the case of
133+
using existing input/output channels above).

0 commit comments

Comments
 (0)