drm_tabulate
Implementation details of util/drm_tabulate.py.
drm_tabulate.py: extract named fields from a pile of DRMs
usage
drm_tabulate.py [-1sn] [--json|--pandas] [ -m ATTR ] [-a ATTR | -f FILE] DRM ...
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
DRM
|
file
|
a list of DRM pickles |
required |
Attributes of an observation "obs" in the DRM are printed by naming them in one of the following ways.
If any DRM argument is a directory, we descend from there recursively into scripts, .exp, or .fam directories to find pickles (drm/*.pkl).
Attribute naming
-a ATTR: outputobs[ATTR](repeat -a OK, see below)-S ATTR: output the SPC attribute ATTR forobs[star_ind]-P ATTR: output the SPC attributes ATTR forobs[plan_inds[:]]-p ATTR: output the list of SPC attributes ATTR forobs[plan_inds]-e CODE: compute a value by [e]valuating the Python CODE expression
Pseudo-attributes
-s: output the DRM seed number (__seed__, boolean)-n: output the DRM observation number (__obs_num__, boolean)--plan_num: output the planet number (__plan_num__, boolean)-N,--name: output the scenario name (__scenario__, boolean)-B,--basename: last directory in scenario (__basename__, boolean)-1: supply the CSV header on line 1 (__header__, boolean)
Match only a subset of observations
-m ATTR: produce output only if ATTR is a keyword in obs-M ATTR: produce output if ATTR is NOT a keyword in obs
External file
- -f FILE: take ATTRs from FILE instead of
-a/-S/-P/-poptions (see below)
DRM ATTRIBUTES
The main degree of freedom is attribute specification -- which is done using either of two notations. Below, suppose "obs" is one entry in the DRM.
-
-a=> Directly named attributes- Get a field in obs by giving:
-a arrival_time - Drill into nested attributes with
.. For exampleobs['char_mode']['lam']is extracted with:-a char_mode.lamFor lists, give the index number;obs['plan_inds'][0]is-a plan_inds.0The last phase angle,obs['char_params'][-1], is:-a char_params.phi.-1 - Supply a comma-separated list of such DRM fields at once with
-a arrival_time,slew_time,scMass
- Get a field in obs by giving:
-
-e=> Evaluated expressions- A Python expression can be given, which is evaluated in the context
of variables named for each field in "obs". To output a count of
detections using the
obs['det_status']list, use:-e "np.sum(det_status == 1)" - No comma-separated lists are allowed, due to ambiguity.
- The full SPC is available, if desired, using
spc[...], so-e "spc['Spec'][star_ind]"<==>-S SpecSee below for more on-S. - "Boxing" the result of
-eallows the value to be scalar-expanded across multiple planets (cf.-Pbelow). For star name:-e "[spc['Name'][star_ind]]"<==>-S Name
- A Python expression can be given, which is evaluated in the context
of variables named for each field in "obs". To output a count of
detections using the
For either -a or -e, the resulting column can be custom-named with
a label:attr construct, such as
-a "lambda:char_mode.lam"
-e "det_count:np.sum(det_status == 1)"
otherwise a basic generated name is used. (But: Attributes in comma-separated expressions cannot be custom-named.)
STAR-PLANET ATTRIBUTES
-
-S=> shortcut for Star attributes.-S ATTRmeans: look up the named ATTR forobs['star_ind']in the corresponding SPC file, e.g.-S Spec=>spc['Spec'][obs['star_ind']]which will output the spectral class ofobs['star_ind'] -
-P=> shortcut for Planet attributes.-P ATTRmeans to look up the named ATTR for each planet inobs['plan_inds']in the SPC file, e.g.-P Mp=>spc['Mp'][obs['plan_inds']].Note that
obs['plan_inds']is in general a vector. So, if you give-P, this program "scalar-expands" the vector to write one row of output for each plan_ind in plan_inds. This facilitates row-by-row processing.Note that if
plan_inds = [], no record will be written. To write a record in the zero-planet case anyway, specify--empty. -
-p=> shortcut for alternate Planet attributes.This is the same lookup as
-P, but the vector is output to that one column in the row. This would be more useful for the JSON output; the CSV format looks like:"[0.282, 1.044]"which would not look like a number to downstream consumers.
For all of the above, an optional column-name can be given just as
for -a and -e. If not given, the attribute name, or a generated
string, will be used.
The SPC file is loaded using the filename convention that
.../drm/NAME.pkl goes with .../spc/NAME.spc
If no -S/-P/-p is given, the SPC is not loaded, to allow use of
this program when only the DRM is present. If the SPC file is needed
to support spc[...] within -e constructs above, load of the SPC
can be forced by giving --load_spc (or if using External File,
"load_spc": true).
All attributes in the SPC file are available. In addition, the following attributes are derived from the SPC and made available: _earthlike: is the planet Earthlike (Radius, SMA), via -P _earthlike
EXTERNAL FILE
These expressions can become complex, so the "-a ATTR" and all above
constructs can be placed in a JSON file and specified with -f FILE:
# drm_tabulate parameters: count chars
{
"__match__": "char_info",
"lambda": "char_mode.lam",
"__eval__": {
"char_count": "np.sum(char_info[0]['char_status'] == 1)"
},
"__star__": {
"Spec": "Spec",
"Name": "Name"
}
}
As an extension to JSON, comment lines (beginning with #) are discarded.
Recall JSON uses double-quotes for strings, and Python dictionary keys
can be extracted with d['attr'], so there is no conflict between quote marks.
The column name (e.g., char_count above) is used as the column name for the CSV.
Above, __match__ abbreviates the -m construct, so the JSON file can be
self-contained. The full list is:
"__eval__" -> -e
"__star__" -> -S
"__planet__" -> -P (one row per planet)
"__planets__" -> -p (list placed in one field)
"__match_inv__" -M (inverse match)
Other program flags (like -s) can be given by Booleans in the
JSON file as well. See the top of this usage note for the __attribute_name__
controlling each flag.
As on the command line, the SPC file is loaded if __star__, __planet__,
or __planets__is present. To force the load if an __eval__ construct needs
the SPC file, use --load_spc, or the Boolean directive "__load_spc__": true
within the JSON.
Discarding #-starting lines permits use of the shell's "shebang" convention. You can capture a complex argument structure in a file and re-use it in other circumstances. See: util/drm-tab-demo.json5 for an example.
USAGE
Typical usage:
# each arrival_time for one pickle
util/drm_tabulate.py -a arrival_time sims/HabEx_4m_dmag26/drm/777.pkl
# each char_time, looking only at chars, recursive in drm/
util/drm_tabulate.py -m char_time -a char_time sims/HabEx_4m_dmag26/drm
# view available fields (first observation) (first char observation)
util/drm_tabulate.py -A sims/HabEx_4m_dmag26/drm/777.pkl
util/drm_tabulate.py -A -m char_info sims/HabEx_4m_dmag26/drm/777.pkl
# number of successful chars, only for chars
util/drm_tabulate.py -m char_info -e "np.sum(char_info[0]['char_status'] == 1)" sims/HabEx_4m_dmag26/drm
# planet-by-planet output of: char_status, planet mass, star spectral class, etc.
util/drm_tabulate.py -ns1 -m char_time -e "CS:char_status" -P Mp -e "SpecLetter:[spc['Spec'][star_ind][0]]" -S Spec -a ct:char_time -a char_mode.lam sims/.../drm
EnsembleSummary
Bases: object
Compute, store, and dump summary information for an ensemble of many simulations.
Source code in util/drm_tabulate.py
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 | |
__init__(in_files, args)
Load an ensemble of simulations.
Source code in util/drm_tabulate.py
755 756 757 758 759 760 761 762 | |
dump(args, outfile)
Dump reduced data to output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
namespace
|
program input arguments |
required |
outfile
|
file
|
output file object |
required |
Source code in util/drm_tabulate.py
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 | |
load_and_reduce()
Load sim drm/spc, reduce each sim, accumulate summaries across sims.
Each dict gives one summary statistic over that single sim.
Source code in util/drm_tabulate.py
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 | |
regroup_and_accum(reductions, attr_names)
Accumulate various summaries across the ensemble.
Nothing is returned: result is placed in the object state.
Source code in util/drm_tabulate.py
783 784 785 786 787 788 789 790 791 792 793 794 795 | |
JSONWithCommentsDecoder
Bases: JSONDecoder
Remove JSON lines starting with # to allow -f with shebang
Source code in util/drm_tabulate.py
861 862 863 864 865 866 867 868 | |
NumpyEncoder
Bases: JSONEncoder
Custom JSON encoder for numpy types.
Source code in util/drm_tabulate.py
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | |
SimulationRun
Bases: object
Load and summarize a simulation: one DRM and its corresponding SPC.
Source code in util/drm_tabulate.py
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | |
extract_attrs(match, match_inv, attrs, show_attrs)
Extract attributes from each obs in the DRM.
Returns a dictionary mapping attributes to lists, one list entry per DRM observation.
Source code in util/drm_tabulate.py
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 | |
extract_pseudo(obs, attr, nobs=None)
Extract pseudo-attribute, which can have special naming conventions.
Source code in util/drm_tabulate.py
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | |
extract_value_any(obs, attr, **kwargs)
Selector function that switches on the attribute flavor to extract a value.
Source code in util/drm_tabulate.py
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | |
extract_value_obs(obs, attr, just_peeking=False)
Extract a value, attr, from an observation, obs, allowing recursive lookup.
The just_peeking argument is to detect presence of an attribute. It returns True/False, not the attribute itself.
Source code in util/drm_tabulate.py
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | |
extract_value_planet(obs, attr)
Extract planet-related attribute by evaluating attr in the context of obs.
Source code in util/drm_tabulate.py
524 525 526 527 528 529 530 531 532 533 534 535 536 537 | |
extract_value_star(obs, attr)
Extract star-related attribute by evaluating attr in the context of obs.
Source code in util/drm_tabulate.py
510 511 512 513 514 515 516 517 518 519 520 521 522 | |
get_scenario(f)
Get scenario name from DRM file name.
Attempt to honor sandbox conventions, e.g., sims/aas_2024a.fam/H6H_TSDD_DulzE_omniNUV_20240107/drm/777.pkl --> aas_2024a.fam/H6H_TSDD_DulzE_omniNUV_20240107 If the given filename does not appear to follow this convention, we attempt to do something reasonable.
Source code in util/drm_tabulate.py
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | |
is_earthlike_all()
Is the planet earthlike? (for a vector of every planet)
This follows the reference version in reduce_drms.py.
Source code in util/drm_tabulate.py
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | |
perform_eval(obs, attr)
Extract attribute by evaluating attr in the context of obs.
Source code in util/drm_tabulate.py
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 | |
show_attributes(obs, msg_obs=None, msg_spc=None)
Pretty-print available attributes to stderr.
Source code in util/drm_tabulate.py
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | |
summarize(args, econo=True)
Find the summary of the sim as a dictionary held within the object.
The convention is that the summary is built up by calling a series of analytic routines, each of which returns a dictionary of summary information. The overall summary dictionary is a union of each individual summary. If econo, delete the DRM and keep only the summary.
Source code in util/drm_tabulate.py
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | |
WorkerMap
Bases: object
Abstracts the multiprocessing worker-pool; switches to no workers if jobs <= 1.
This allows you to go back to ordinary single-job processing by setting the number of jobs to 1.
Source code in util/drm_tabulate.py
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | |
argtexts_to_object(args)
Convert lists of argument-texts to a dict-of-dicts indexed by flavor.
Source code in util/drm_tabulate.py
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 | |
expand_drm(progname, drm)
Expand drm input arg such that it descends into directories.
TODO: Filename manipulation using Pathlib not f-strings.
Source code in util/drm_tabulate.py
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 | |
json_to_object(args)
Load a JSON dictionary, extract any program flags, return the remainder.
Source code in util/drm_tabulate.py
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 | |
outer_load_and_reduce(f, verb=0, args=None)
Load a sim and summarize it into a dict.
This must be present at the outer scope of the file so it can be loaded by a separate process that is created by the multiprocessing module.
Source code in util/drm_tabulate.py
741 742 743 744 745 746 747 748 749 750 | |
process_attr_program_inputs(args)
Process given attribute inputs, whether arguments or in a file.
The args.___ namespace is updated by the JSON file contents. The return value is a dict of Attribute objects, indexed by the attribute name ("column name") in the output tablulation.
Source code in util/drm_tabulate.py
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 | |
strip_units(x)
Strip astropy units from x.
Source code in util/drm_tabulate.py
309 310 311 312 313 314 315 | |