├── .github └── workflows │ ├── pyfalco-publish.yml │ └── pyfalco_test.yml ├── .gitignore ├── .profile ├── LICENSE ├── README.md ├── data ├── WFIRST │ ├── PhaseA │ │ ├── SP_SPCdisk_12BB_C87_65WA200_26LS88_N1000gray_20170130a.fits.zip │ │ └── WFIRST_CGI_apod_SPC_20170714.mat.zip │ ├── PhaseB │ │ ├── FPM_res100_SPC-20190130.fits.zip │ │ ├── LS_symm_CGI180718_Str3.20pct_38D91_N120_pixel.fits.zip │ │ ├── SPM_SPC-20181220_1000_rounded9_gray.fits.zip │ │ ├── SPM_SPC-20190130.fits.zip │ │ ├── readme_SPC-20181220.txt │ │ └── readme_SPC-20190130.txt │ └── PhaseC │ │ └── 2019-10-09 CGI entrance pupil 8k binary.png ├── brief │ └── .placeholder.txt ├── config │ └── .placeholder.txt ├── jac │ └── .placeholder.txt ├── material │ └── .placeholder.txt └── ws │ └── .placeholder.txt ├── docs ├── api.rst ├── conf.py ├── falco_conversion_items.rtf ├── falco_logo.png ├── index.rst └── list_of_tests.txt ├── examples ├── DST_DMLC_probing │ ├── EXAMPLE_config_DST_LC.py │ └── EXAMPLE_main_DST_DMLC_probing.py ├── WFIRST_LC │ ├── EXAMPLE_config_WFIRST_LC.py │ └── EXAMPLE_main_WFIRST_LC.py ├── WFIRST_LC_AD_EFC │ ├── EXAMPLE_config_WFIRST_LC_AD_EFC.py │ └── EXAMPLE_main_WFIRST_LC_AD_EFC.py ├── WFIRST_SPC_Spec_PhaseB_simple │ ├── EXAMPLE_config_WFIRST_SPC_Spec_PhaseB_simple.py │ └── EXAMPLE_main_WFIRST_SPC_Spec_PhaseB_simple.py ├── demo_gen_annular_FPM.py ├── demo_gen_bowtie_FPM.py ├── demo_gen_bowtie_LS.py ├── demo_gen_pupil_LUVOIR_A_final.py ├── demo_gen_pupil_LUVOIR_B.py ├── demo_gen_pupil_Roman_CGI_20200513.py ├── demo_gen_pupil_Simple.py └── try_running_falco │ ├── EXAMPLE_try_running_FALCO.py │ ├── EXAMPLE_try_running_FALCO_AD_EFC.py │ ├── config.yaml │ ├── hd_config.yaml │ └── hd_main.py ├── extra ├── falco_gen_ellipse.py ├── script_test_DM_surface_fitting_with_DM_WFIRST_LC.py ├── script_test_Zernike_map_fitting_with_DM_WFIRST_LC.py ├── script_test_random_map_fitting_with_DM_WFIRST_LC.py ├── test_growing_object.py └── testing_dm_surface_fitting ├── falco ├── __init__.py ├── _globals.py ├── check.py ├── config │ ├── Eval.py │ ├── ModelParameters.py │ ├── ModelVariables.py │ ├── Object.py │ ├── Probe.py │ ├── ProbeSchedule.py │ ├── __init__.py │ ├── init_from_mat.py │ └── yaml_loader.py ├── ctrl.py ├── data │ ├── MgF2_data_from_Rodriguez-deMarcos_wvlUM_n_k.txt │ ├── add_header_to_inf_func_files.py │ ├── brief │ │ └── .placeholder.txt │ ├── chromium_wvlUM_n_k_Johnson_1974.csv │ ├── influence_BMC_2kDM_400micron_res10.fits │ ├── influence_BMC_2kDM_400micron_res20.fits │ ├── influence_BMC_kiloDM_300micron_res10_spline.fits │ ├── influence_dm5v2.fits │ ├── influence_dm_FEA_10milsV2_20160330.fits │ ├── nickel_data_from_Palik_via_Bala_wvlNM_n_k.txt │ └── ws │ │ └── .placeholder.txt ├── dftidefs.py ├── diff_dm.py ├── dm.py ├── est.py ├── fftutils.py ├── hexsegmirror.py ├── hlc.py ├── imaging.py ├── mask.py ├── mkl_fft.py ├── model │ ├── __init__.py │ ├── jacobians.py │ └── models.py ├── plot.py ├── prop.py ├── proper │ ├── .use_ffti │ ├── __init__.py │ ├── cubic_conv_c.c │ ├── cubic_conv_threaded_c.c │ ├── examples │ │ ├── __init__.py │ │ ├── coronagraph.py │ │ ├── coronagraph_demo.py │ │ ├── example_system.py │ │ ├── hubble_simple.py │ │ ├── microscope.py │ │ ├── multi_example.py │ │ ├── occulter_demo.py │ │ ├── psdtest.py │ │ ├── run_coronagraph.py │ │ ├── run_coronagraph_dm.py │ │ ├── run_example.py │ │ ├── run_occulter.py │ │ ├── simple_prescription.py │ │ ├── simple_telescope.py │ │ ├── talbot.py │ │ ├── talbot_correct.py │ │ ├── talbot_correct_demo.py │ │ ├── talbot_demo.py │ │ ├── telescope.py │ │ ├── telescope_dm.py │ │ ├── testmulti1.py │ │ └── testmulti2.py │ ├── influence_dm5v2_1.fits │ ├── libcconv.py │ ├── libcconvthread.py │ ├── libszoom.py │ ├── prop_8th_order_mask.py │ ├── prop_add_phase.py │ ├── prop_add_wavefront.py │ ├── prop_begin.py │ ├── prop_circular_aperture.py │ ├── prop_circular_obscuration.py │ ├── prop_compile_c.py │ ├── prop_cubic_conv.py │ ├── prop_define_entrance.py │ ├── prop_dftidefs.py │ ├── prop_divide.py │ ├── prop_dm.py │ ├── prop_ellipse.py │ ├── prop_elliptical_aperture.py │ ├── prop_elliptical_obscuration.py │ ├── prop_end.py │ ├── prop_end_savestate.py │ ├── prop_errormap.py │ ├── prop_execute_multi.py │ ├── prop_ffti.py │ ├── prop_fftw.py │ ├── prop_fftw_wisdom.py │ ├── prop_fit_dm.py │ ├── prop_fit_zernikes.py │ ├── prop_fits_read.py │ ├── prop_fits_write.py │ ├── prop_get_amplitude.py │ ├── prop_get_beamradius.py │ ├── prop_get_distancetofocus.py │ ├── prop_get_fratio.py │ ├── prop_get_gridsize.py │ ├── prop_get_nyquistsampling.py │ ├── prop_get_phase.py │ ├── prop_get_refradius.py │ ├── prop_get_sampling.py │ ├── prop_get_sampling_arcsec.py │ ├── prop_get_sampling_radians.py │ ├── prop_get_wavefront.py │ ├── prop_get_wavelength.py │ ├── prop_get_z.py │ ├── prop_hex_wavefront.py │ ├── prop_hex_zernikes.py │ ├── prop_init_savestate.py │ ├── prop_irregular_polygon.py │ ├── prop_is_statesaved.py │ ├── prop_lens.py │ ├── prop_load_fftw_wisdom.py │ ├── prop_magnify.py │ ├── prop_multiply.py │ ├── prop_noll_zernikes.py │ ├── prop_pixellate.py │ ├── prop_polygon.py │ ├── prop_print_zernikes.py │ ├── prop_propagate.py │ ├── prop_psd_errormap.py │ ├── prop_ptp.py │ ├── prop_qphase.py │ ├── prop_radius.py │ ├── prop_readmap.py │ ├── prop_rectangle.py │ ├── prop_rectangular_aperture.py │ ├── prop_rectangular_obscuration.py │ ├── prop_resamplemap.py │ ├── prop_rotate.py │ ├── prop_rounded_rectangle.py │ ├── prop_run.py │ ├── prop_run_multi.py │ ├── prop_savestate.py │ ├── prop_select_propagator.py │ ├── prop_set_antialiasing.py │ ├── prop_shift_center.py │ ├── prop_sinc.py │ ├── prop_state.py │ ├── prop_stw.py │ ├── prop_szoom.py │ ├── prop_szoom_c.c │ ├── prop_table.py │ ├── prop_use_ffti.py │ ├── prop_use_fftw.py │ ├── prop_wavefront.py │ ├── prop_writemap.py │ ├── prop_wts.py │ ├── prop_zernikes.py │ └── switch_set.py ├── setup.py ├── thinfilm.py ├── util.py ├── wfsc.py └── zern.py ├── pyproject.toml ├── requirements.txt ├── roman ├── EXAMPLE_config_Roman_CGI_HLC_NFOV_Band1.py ├── EXAMPLE_config_Roman_CGI_HLC_NFOV_Band2.py ├── EXAMPLE_config_Roman_CGI_HLC_NFOV_Band3.py ├── EXAMPLE_config_Roman_CGI_HLC_NFOV_Band4.py ├── EXAMPLE_config_Roman_CGI_SPC_Bowtie_Band2.py ├── EXAMPLE_config_Roman_CGI_SPC_Bowtie_Band3.py ├── EXAMPLE_config_Roman_CGI_SPC_Multistar_Band1.py ├── EXAMPLE_config_Roman_CGI_SPC_Multistar_Band4.py ├── EXAMPLE_config_Roman_CGI_SPC_RotatedBowtie_Band2.py ├── EXAMPLE_config_Roman_CGI_SPC_RotatedBowtie_Band3.py ├── EXAMPLE_config_Roman_CGI_SPC_WFOV_Band1.py ├── EXAMPLE_config_Roman_CGI_SPC_WFOV_Band4.py ├── EXAMPLE_main_Roman_CGI_any.py ├── flatmaps │ ├── dm1_m_design_hlc_band1.fits │ ├── dm1_m_design_hlc_band2.fits │ ├── dm1_m_design_hlc_band3.fits │ ├── dm1_m_design_hlc_band4.fits │ ├── dm1_m_flat_hlc_band1.fits │ ├── dm1_m_flat_hlc_band2.fits │ ├── dm1_m_flat_hlc_band3.fits │ ├── dm1_m_flat_hlc_band4.fits │ ├── dm1_m_spc-spec_band2.fits │ ├── dm1_m_spc-spec_band3.fits │ ├── dm1_m_spc-spec_rotated_band2.fits │ ├── dm1_m_spc-spec_rotated_band3.fits │ ├── dm1_m_spc-wide_band1.fits │ ├── dm1_m_spc-wide_band4.fits │ ├── dm2_m_design_hlc_band1.fits │ ├── dm2_m_design_hlc_band2.fits │ ├── dm2_m_design_hlc_band3.fits │ ├── dm2_m_design_hlc_band4.fits │ ├── dm2_m_flat_hlc_band1.fits │ ├── dm2_m_flat_hlc_band2.fits │ ├── dm2_m_flat_hlc_band3.fits │ ├── dm2_m_flat_hlc_band4.fits │ ├── dm2_m_spc-spec_band2.fits │ ├── dm2_m_spc-spec_band3.fits │ ├── dm2_m_spc-spec_rotated_band2.fits │ ├── dm2_m_spc-spec_rotated_band3.fits │ ├── dm2_m_spc-wide_band1.fits │ ├── dm2_m_spc-wide_band4.fits │ ├── hlc_flattened_dm1.fits │ ├── hlc_flattened_dm2.fits │ ├── hlc_flattened_with_pattern_dm1.fits │ ├── hlc_flattened_with_pattern_dm2.fits │ ├── spc_spec_band3_flattened_dm1.fits │ ├── spc_spec_band3_flattened_dm2.fits │ ├── spc_wide_band4_flattened_dm1.fits │ └── spc_wide_band4_flattened_dm2.fits └── roman_phasec.py ├── setup.cfg ├── setup.py └── tests ├── functional ├── check_LC_all.py ├── config_wfsc_flc.py ├── config_wfsc_lc.py ├── config_wfsc_vc.py ├── test_diff_dm_model.py ├── test_dm_orientation.py ├── test_fit_surf_to_act.py ├── test_jacobian_flc.py ├── test_jacobian_lc.py ├── test_jacobian_vc.py ├── test_pairwise_probing.py ├── test_parallelization.py ├── test_wfsc_flc.py ├── test_wfsc_lc.py └── test_wfsc_vc.py ├── runtests.sh └── unit ├── pupil_CGI-20200513_8k_binary_noF.png ├── test_check.py ├── test_configure_dark_hole_region.py ├── test_dm_surface_fitting.py ├── test_fit_surf_to_dm_lsq.py ├── test_mask_fpm_annular.py ├── test_mask_fpm_bowtie.py ├── test_mask_pupil_LUVOIR_A.py ├── test_mask_pupil_LUVOIR_B.py ├── test_mask_pupil_Roman.py ├── test_mask_pupil_simple.py ├── test_mask_sw.py ├── test_mft2.py ├── test_mp.py ├── test_mp_yaml.py ├── test_noisy_image.py ├── test_set_jacobian_modal_weights.py ├── test_set_spectral_properties.py ├── test_thinfilm.py ├── test_util.py └── testdata ├── pupil_CGI-20200513_8k_binary_noF.png └── ut_influence_dm5v2_inffix.fits /.github/workflows/pyfalco-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: 'Upload pyfalco package to PYPI' 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.7' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/pyfalco_test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions (currently only Python 3.7) 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | #description: 'Generates pyfalco build, runs tests on push or pull requests of master branch.' 4 | name: 'pyfalco' 5 | 6 | on: 7 | push: 8 | branches: [ dev-aj] 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | python-version: ['3.12', ] 20 | 21 | steps: 22 | # This step checks out a copy of your repository. 23 | - uses: actions/checkout@v2 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Install dependencies 30 | # NO PROPER 31 | run: | 32 | pip install astropy 33 | pip install extension-helpers 34 | python -m pip install --upgrade pip 35 | pip install pytest 36 | pip install pytest-cov 37 | pip install pytest-html 38 | pip install numpy 39 | pip install scipy 40 | pip install psutil 41 | pip install matplotlib 42 | pip install coveralls 43 | pip install deepmerge 44 | # WITH PROPER 45 | # run: | 46 | # pip install astropy 47 | # pip install extension-helpers 48 | # mkdir PYPROPER 49 | # cd PYPROPER 50 | # curl https://sourceforge.net/projects/proper-library/files/proper_v3.2.7_python_15jun2022.zip -o myfile.zip -L 51 | # unzip -o myfile.zip 52 | # cd proper* 53 | # python setup.py install 54 | # cd ../../ 55 | # python -m pip install --upgrade pip 56 | # pip install pytest 57 | # pip install pytest-cov 58 | # pip install pytest-html 59 | # pip install numpy 60 | # pip install scipy 61 | # pip install psutil 62 | # pip install matplotlib 63 | # pip install coveralls 64 | - name: Test with pytest 65 | run: | 66 | # python -m pytest tests/ -v --html=./TestReport/TestReport.html 67 | python3 -m pytest --cov-report=html --cov=./falco tests/ -v --html=./TestReport/TestReport.html 68 | # - name: Upload coverage data to coveralls.io 69 | # run: coveralls 70 | # env: 71 | # GITHUB_TOKEN: ${{ secrets.COVERALLS_TOKEN }} 72 | # - uses: actions/upload-artifact@v2 73 | # with: 74 | # name: my-artifact 75 | # path: | 76 | # ./htmlcov/index.html 77 | # #./TestReport/TestReport.html 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij configuration 2 | .idea/ 3 | 4 | # Environment configuration 5 | .envrc 6 | 7 | falco/data/brief/** 8 | falco/data/ws/** 9 | 10 | data/material/*.npy 11 | zOld/** 12 | 13 | #--Sync only example files 14 | roman/*.py 15 | roman/*.fits 16 | roman/*.txt 17 | roman/flatmaps/*.fits 18 | !roman/EXAMPLE_*.py 19 | !roman/flatmaps/dm*.fits 20 | 21 | !examples/EXAMPLE_*.py 22 | 23 | data/WFIRST/PhaseB/*.fits 24 | 25 | 26 | # **.fits 27 | **.pkl 28 | examples/**.pkl 29 | **.DS_Store 30 | 31 | build/ 32 | dist/ 33 | docs/_build 34 | *.pyc 35 | *.o 36 | *.so 37 | *.egg-info 38 | *.swp 39 | *.mat 40 | *~ 41 | 42 | 43 | # Exceptions # 44 | 45 | !.placeholder.txt 46 | !**/.placeholder.txt 47 | !**/*.placeholder.txt 48 | !falco/data/brief/.placeholder.txt 49 | !falco/data/config/.placeholder.txt 50 | !falco/data/ws/.placeholder.txt 51 | !roman/flatmaps/.placeholder.txt 52 | !roman/flatmaps/*.fits 53 | 54 | 55 | -------------------------------------------------------------------------------- /.profile: -------------------------------------------------------------------------------- 1 | export PYTHONPATH="" 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![FALCO](https://github.com/ajeldorado/falco-matlab/blob/master/docs/falco_logo.png?raw=true) 2 | # FALCO: Fast Linearized Coronagraph Optimizer in Python 3 3 | [![Coverage Status](https://coveralls.io/repos/github/ajeldorado/falco-python/badge.svg?branch=master)](https://coveralls.io/github/ajeldorado/falco-python?branch=master) 4 | 5 | The Fast Linearized Coronagraph Optimizer (FALCO) is an open-source package of routines and example scripts for coronagraphic focal plane wavefront correction. The goal of FALCO is to provide a free, modular framework for the simulation or testbed operation of several common types of coronagraphs, and the design of coronagraphs that use wavefront control algorithms to shape deformable mirrors (DMs) and masks. FALCO includes routines for pair-wise probing estimation of the complex electric field and Electric Field Conjugation (EFC) control, and we ask the community to contribute other wavefront correction algorithms and optical layouts. FALCO utilizes and builds upon PROPER, an established optical propagation library. The key innovation in FALCO is the rapid computation of the linearized response matrix for each DM, which facilitates re-linearization after each control step for faster DM-integrated coronagraph design and wavefront correction experiments. FALCO is freely available as source code in MATLAB at github.com/ajeldorado/falco-matlab and in Python 3 at github.com/ajeldorado/falco-python. 6 | 7 | Developed by A.J. Riggs at the Jet Propulsion Laboratory, California Institute of Technology. 8 | Major contributions and testing were provided by Garreth Ruane, Luis Marchen, Santos (Felipe) Fregoso, Erkin Sidick, Carl Coker, Navtej Saini, and Jorge Llop-Sayson. 9 | 10 | ********************************************** 11 | ### DOCUMENTATION 12 | 13 | * The only non-standard library you need is PROPER, available only via download here: https://sourceforge.net/projects/proper-library/ 14 | * To get started, add PROPER and falco-python to your PYTHONPATH. Then try running some scripts in the falco-python/examples/ folder starting with EXAMPLE_main* or demo_*. 15 | * Documentation on specific usage cases is available at the Matlab version's Github Wiki at https://github.com/ajeldorado/falco-matlab/wiki. 16 | * For an overview of FALCO and its uses, refer to the SPIE conference paper "Fast Linearized Coronagraph Optimizer (FALCO) I: A software toolbox for rapid coronagraphic design and wavefront correction". 17 | DOI: 10.1117/12.2313812 18 | ********************************************** 19 | -------------------------------------------------------------------------------- /data/WFIRST/PhaseA/SP_SPCdisk_12BB_C87_65WA200_26LS88_N1000gray_20170130a.fits.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseA/SP_SPCdisk_12BB_C87_65WA200_26LS88_N1000gray_20170130a.fits.zip -------------------------------------------------------------------------------- /data/WFIRST/PhaseA/WFIRST_CGI_apod_SPC_20170714.mat.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseA/WFIRST_CGI_apod_SPC_20170714.mat.zip -------------------------------------------------------------------------------- /data/WFIRST/PhaseB/FPM_res100_SPC-20190130.fits.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseB/FPM_res100_SPC-20190130.fits.zip -------------------------------------------------------------------------------- /data/WFIRST/PhaseB/LS_symm_CGI180718_Str3.20pct_38D91_N120_pixel.fits.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseB/LS_symm_CGI180718_Str3.20pct_38D91_N120_pixel.fits.zip -------------------------------------------------------------------------------- /data/WFIRST/PhaseB/SPM_SPC-20181220_1000_rounded9_gray.fits.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseB/SPM_SPC-20181220_1000_rounded9_gray.fits.zip -------------------------------------------------------------------------------- /data/WFIRST/PhaseB/SPM_SPC-20190130.fits.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseB/SPM_SPC-20190130.fits.zip -------------------------------------------------------------------------------- /data/WFIRST/PhaseC/2019-10-09 CGI entrance pupil 8k binary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/data/WFIRST/PhaseC/2019-10-09 CGI entrance pupil 8k binary.png -------------------------------------------------------------------------------- /data/brief/.placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder file to allow git to sync this entire folder. 2 | -------------------------------------------------------------------------------- /data/config/.placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder file to allow git to sync this entire folder. 2 | -------------------------------------------------------------------------------- /data/jac/.placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder file to allow git to sync this entire folder. 2 | -------------------------------------------------------------------------------- /data/material/.placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder file to allow git to sync this entire folder. 2 | -------------------------------------------------------------------------------- /data/ws/.placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder file to allow git to sync this entire folder. 2 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | 2 | API Documentation 3 | ================== 4 | 5 | .. automodule:: falco 6 | :members: 7 | :undoc-members: 8 | 9 | .. autofunction:: init_from_mat 10 | -------------------------------------------------------------------------------- /docs/falco_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/docs/falco_logo.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. FALCO documentation master file, created by 2 | sphinx-quickstart on Thu Jun 21 20:18:02 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | FALCO: FAst Linearized Coronagraph Optimization 7 | =============================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | api.rst 14 | 15 | 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | -------------------------------------------------------------------------------- /examples/DST_DMLC_probing/EXAMPLE_main_DST_DMLC_probing.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.insert(0,"../") 3 | from copy import deepcopy 4 | import os 5 | # import numpy as np 6 | 7 | import falco 8 | 9 | import EXAMPLE_config_DST_LC as CONFIG 10 | 11 | # %% Load/run config script 12 | mp = deepcopy(CONFIG.mp) 13 | 14 | 15 | # %% Define directories for data output 16 | 17 | mp.path = falco.config.Object() 18 | # mp.path.config = './' # Location of config files and minimal output files. Default is [mainPath filesep 'data' filesep 'brief' filesep] 19 | # mp.path.ws = './' # (Mostly) complete workspace from end of trial. Default is [mainPath filesep 'data' filesep 'ws' filesep]; 20 | 21 | 22 | # %% Overwrite default values as desired 23 | 24 | # Special Computational Settings 25 | mp.flagPlot = True 26 | mp.flagParallel = False # whether to use multiprocessing to parallelize some large computations 27 | mp.Nthreads = 4 # Number of threads to use when using multiprocessing. If undefined, it is set to the max number of cores 28 | 29 | # Record Keeping 30 | mp.TrialNum = 1 31 | mp.SeriesNum = 1 32 | 33 | # Use just 1 wavelength for initial debugging of code 34 | mp.fracBW = 0.01 # fractional bandwidth of the whole bandpass (Delta lambda / lambda0) 35 | mp.Nsbp = 1 # Number of sub-bandpasses to divide the whole bandpass into for estimation and control 36 | mp.Nwpsbp = 1 37 | mp.estimator = 'perfect' 38 | 39 | 40 | # %% Perform the Wavefront Sensing and Control 41 | 42 | mp.runLabel = ('Series%04d_Trial%04d_%s' % 43 | (mp.SeriesNum, mp.TrialNum, mp.coro)) 44 | 45 | out = falco.setup.flesh_out_workspace(mp) 46 | 47 | falco.wfsc.loop(mp, out) 48 | 49 | # %% Plot the output 50 | 51 | falco.plot.plot_trial_output(out) 52 | 53 | fnPickle = os.path.join(mp.path.brief, f'{mp.runLabel}_snippet.pkl') 54 | falco.plot.plot_trial_output_from_pickle(fnPickle) 55 | -------------------------------------------------------------------------------- /examples/WFIRST_LC/EXAMPLE_main_WFIRST_LC.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.insert(0,"../") 3 | from copy import deepcopy 4 | import os 5 | # import numpy as np 6 | 7 | import falco 8 | 9 | import EXAMPLE_config_WFIRST_LC as CONFIG 10 | 11 | # %% Load/run config script 12 | mp = deepcopy(CONFIG.mp) 13 | 14 | 15 | # %% Set Output Data Directories 16 | mp.path = falco.config.Object() 17 | # mp.path.config = './' # Location of config files and minimal output files. Default is [mainPath filesep 'data' filesep 'brief' filesep] 18 | # mp.path.ws = './' # (Mostly) complete workspace from end of trial. Default is [mainPath filesep 'data' filesep 'ws' filesep]; 19 | 20 | 21 | # %% Overwrite default values as desired 22 | 23 | # Special Computational Settings 24 | mp.flagPlot = True 25 | mp.flagParallel = False # whether to use multiprocessing to parallelize some large computations 26 | mp.Nthreads = 4 # Number of threads to use when using multiprocessing. If undefined, it is set to the max number of cores 27 | 28 | # Record Keeping 29 | mp.TrialNum = 1 30 | mp.SeriesNum = 1 31 | 32 | # Use just 1 wavelength for initial debugging of code 33 | mp.fracBW = 0.01 # fractional bandwidth of the whole bandpass (Delta lambda / lambda0) 34 | mp.Nsbp = 1 # Number of sub-bandpasses to divide the whole bandpass into for estimation and control 35 | mp.Nwpsbp = 1 36 | 37 | 38 | # %% Perform the Wavefront Sensing and Control 39 | 40 | mp.runLabel = ('Series%04d_Trial%04d_%s' % 41 | (mp.SeriesNum, mp.TrialNum, mp.coro)) 42 | 43 | out = falco.setup.flesh_out_workspace(mp) 44 | 45 | falco.wfsc.loop(mp, out) 46 | 47 | 48 | # %% Plot the output 49 | 50 | falco.plot.plot_trial_output(out) 51 | 52 | fnPickle = os.path.join(mp.path.brief, f'{mp.runLabel}_snippet.pkl') 53 | falco.plot.plot_trial_output_from_pickle(fnPickle) 54 | -------------------------------------------------------------------------------- /examples/WFIRST_LC_AD_EFC/EXAMPLE_main_WFIRST_LC_AD_EFC.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.insert(0,"../") 3 | from copy import deepcopy 4 | import os 5 | import time 6 | 7 | import numpy as np 8 | 9 | import falco 10 | 11 | import EXAMPLE_config_WFIRST_LC_AD_EFC as CONFIG 12 | 13 | # %% Load/run config script 14 | mp = deepcopy(CONFIG.mp) 15 | 16 | 17 | # %% Set Output Data Directories 18 | mp.path = falco.config.Object() 19 | # mp.path.config = './' # Location of config files and minimal output files. Default is [mainPath filesep 'data' filesep 'brief' filesep] 20 | # mp.path.ws = './' # (Mostly) complete workspace from end of trial. Default is [mainPath filesep 'data' filesep 'ws' filesep]; 21 | 22 | # %% Overwrite default values as desired 23 | 24 | # Special Computational Settings 25 | mp.flagPlot = True 26 | mp.flagParallel = False # whether to use multiprocessing to parallelize some large computations 27 | mp.Nthreads = 4 # Number of threads to use when using multiprocessing. If undefined, it is set to the max number of cores 28 | 29 | # Record Keeping 30 | mp.TrialNum = 1 31 | mp.SeriesNum = 1 32 | 33 | # Use just 1 wavelength for initial debugging of code 34 | mp.fracBW = 0.01 # fractional bandwidth of the whole bandpass (Delta lambda / lambda0) 35 | mp.Nsbp = 1 # Number of sub-bandpasses to divide the whole bandpass into for estimation and control 36 | mp.Nwpsbp = 1 37 | 38 | # # Use least-squares surface fitting instead of back-propagation model. 39 | # mp.dm1.useDifferentiableModel = False 40 | # mp.dm2.useDifferentiableModel = False 41 | # mp.dm1.surfFitMethod = 'lsq' 42 | # mp.dm2.surfFitMethod = 'lsq' 43 | 44 | 45 | # %% Set up the workspace 46 | 47 | mp.runLabel = ('Series%04d_Trial%04d_%s' % 48 | (mp.SeriesNum, mp.TrialNum, mp.coro)) 49 | 50 | out = falco.setup.flesh_out_workspace(mp) 51 | 52 | 53 | # %% Compute the scaling factor for the actuator commands in the AD-EFC cost function 54 | 55 | # Check a subset of actuators to see what the max actuator effect is in the pupil plane 56 | if np.any(mp.dm_ind == 1): 57 | mp.ctrl.ad.dm1_act_mask_for_jac_norm = np.eye(mp.dm1.Nact, dtype=bool).flatten() 58 | if np.any(mp.dm_ind == 2): 59 | mp.ctrl.ad.dm2_act_mask_for_jac_norm = np.eye(mp.dm2.Nact, dtype=bool).flatten() 60 | 61 | falco.ctrl.set_utu_scale_fac(mp) 62 | 63 | 64 | # %% Calculate and use the Jacobian just upfront to weed out weak actuators 65 | 66 | cvar = falco.config.Object() 67 | cvar.Itr = 0 68 | cvar.flagRelin = True 69 | 70 | falco.setup.falco_set_jacobian_modal_weights(mp) 71 | 72 | # Compute the control Jacobians for each DM 73 | jacStruct = falco.model.jacobian(mp) 74 | 75 | falco.ctrl.cull_weak_actuators(mp, cvar, jacStruct) 76 | falco.ctrl.init(mp, cvar) 77 | 78 | 79 | # %% Perform the Wavefront Sensing and Control 80 | tStart = time.time() 81 | falco.wfsc.loop(mp, out) 82 | tStop = time.time() 83 | 84 | tDiff = tStop-tStart 85 | print(tDiff) 86 | 87 | # %% Plot the output 88 | 89 | falco.plot.plot_trial_output(out) 90 | 91 | fnPickle = os.path.join(mp.path.brief, f'{mp.runLabel}_snippet.pkl') 92 | falco.plot.plot_trial_output_from_pickle(fnPickle) 93 | -------------------------------------------------------------------------------- /examples/WFIRST_SPC_Spec_PhaseB_simple/EXAMPLE_main_WFIRST_SPC_Spec_PhaseB_simple.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.insert(0, "../") 3 | from copy import deepcopy 4 | import os 5 | # import numpy as np 6 | 7 | import falco 8 | 9 | import EXAMPLE_config_WFIRST_SPC_Spec_PhaseB_simple as CONFIG 10 | 11 | # %% Load/run config script 12 | mp = deepcopy(CONFIG.mp) 13 | 14 | 15 | # %% Set Output Data Directories 16 | mp.path = falco.config.Object() 17 | # mp.path.config = './' # Location of config files and minimal output files. Default is [mainPath filesep 'data' filesep 'brief' filesep] 18 | # mp.path.ws = './' # (Mostly) complete workspace from end of trial. Default is [mainPath filesep 'data' filesep 'ws' filesep]; 19 | 20 | 21 | # %% Overwrite values from config file if desired 22 | 23 | # ## Special Computational Settings 24 | mp.flagPlot = True 25 | mp.flagParallel = False # whether to use multiprocessing to parallelize some large computations 26 | mp.Nthreads = 4 # Number of threads to use when using multiprocessing. 27 | 28 | # Record Keeping 29 | mp.TrialNum = 1 30 | mp.SeriesNum = 1 31 | 32 | # Use just 1 wavelength for initial testing of code 33 | mp.fracBW = 0.01 # fractional bandwidth of the whole bandpass (Delta lambda / lambda0) 34 | mp.Nsbp = 1 # Number of sub-bandpasses to divide the whole bandpass into for estimation and control 35 | mp.Nwpsbp = 1 # Number of wavelengths to used to approximate an image in each sub-bandpass 36 | mp.estimator = 'perfect' 37 | 38 | 39 | # %% Perform the Wavefront Sensing and Control 40 | 41 | mp.runLabel = ('Series%04d_Trial%04d_%s' % 42 | (mp.SeriesNum, mp.TrialNum, mp.coro)) 43 | 44 | out = falco.setup.flesh_out_workspace(mp) 45 | 46 | falco.wfsc.loop(mp, out) 47 | 48 | 49 | # %% Plot the output 50 | 51 | falco.plot.plot_trial_output(out) 52 | 53 | fnPickle = os.path.join(mp.path.brief, f'{mp.runLabel}_snippet.pkl') 54 | falco.plot.plot_trial_output_from_pickle(fnPickle) 55 | -------------------------------------------------------------------------------- /examples/demo_gen_annular_FPM.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,"../") 3 | import falco 4 | import numpy as np 5 | 6 | import matplotlib.pyplot as plt 7 | 8 | 9 | inputs = {} 10 | inputs["FPMampFac"] = 0. 11 | inputs["pixresFPM"] = 3 12 | inputs["rhoInner"] = 6.5 13 | inputs["centering"] = 'pixel' 14 | 15 | # %% With Outer Ring 16 | 17 | inputs["rhoOuter"] = 20.0 18 | fpm = falco.mask.falco_gen_annular_FPM(inputs) 19 | 20 | plt.imshow(fpm); plt.colorbar(); plt.pause(0.1) 21 | if("centering" in inputs.keys()): # Check symmetry 22 | if inputs["centering"]=='pixel': 23 | plt.imshow(fpm[1::,1::]-np.fliplr(fpm[1::,1::])); plt.colorbar(); plt.pause(0.1) #--Check centering 24 | elif inputs["centering"]=='interpixel': 25 | plt.imshow(fpm-np.fliplr(fpm)); plt.colorbar(); plt.pause(0.1) #--Check centering 26 | 27 | # %% Without Outer Ring 28 | 29 | inputs["rhoOuter"] = np.inf 30 | fpm = falco.mask.falco_gen_annular_FPM(inputs) 31 | 32 | plt.imshow(fpm); plt.colorbar(); plt.pause(0.1) 33 | if("centering" in inputs.keys()): # Check symmetry 34 | if inputs["centering"]=='pixel': 35 | plt.imshow(fpm[1::,1::]-np.fliplr(fpm[1::,1::])); plt.colorbar(); plt.pause(0.1) #--Check centering 36 | elif inputs["centering"]=='interpixel': 37 | plt.imshow(fpm-np.fliplr(fpm)); plt.colorbar(); plt.pause(0.1) #--Check centering 38 | 39 | -------------------------------------------------------------------------------- /examples/demo_gen_bowtie_FPM.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,"../") 3 | import falco 4 | import numpy as np 5 | 6 | import matplotlib.pyplot as plt 7 | 8 | 9 | inputs = {} # initialize 10 | inputs["angDegrees"] = 65 # Opening angle on each side of the bowtie 11 | inputs["pixresFPM"] = 6 12 | inputs["rhoInner"] = 2.6 13 | inputs["rhoOuter"] = 9.0 14 | inputs["centering"] = 'pixel' 15 | 16 | fpm = falco.mask.falco_gen_bowtie_FPM(inputs) 17 | 18 | plt.imshow(fpm); plt.colorbar(); plt.pause(0.1) 19 | 20 | if("centering" in inputs.keys()): 21 | if inputs["centering"]=='pixel': 22 | plt.imshow(fpm[1::,1::]-np.fliplr(fpm[1::,1::])); plt.colorbar(); plt.pause(0.1) #--Check centering 23 | elif inputs["centering"]=='interpixel': 24 | plt.imshow(fpm-np.fliplr(fpm)); plt.colorbar(); plt.pause(0.1) #--Check centering 25 | 26 | -------------------------------------------------------------------------------- /examples/demo_gen_bowtie_LS.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,"../") 3 | import falco 4 | import numpy as np 5 | 6 | import matplotlib.pyplot as plt 7 | 8 | inputs = {} 9 | inputs["Nbeam"] = 100 10 | inputs["ID"] = 0.38 11 | inputs["OD"] = 0.92 12 | inputs["ang"] = 115 13 | 14 | #--Optional Inputs 15 | #inputs['centering'] = 'pixel' 16 | #inputs['xShear'] = 0. #--x-axis shear of mask [pupil diameters] 17 | #inputs['yShear'] = 0.5 #--y-axis shear of mask [pupil diameters] 18 | #inputs['clocking'] = 30 #--Clocking of the mask [degrees] 19 | #inputs['magfac'] = 1.5 #--magnification factor of the pupil diameter 20 | 21 | LS = falco.mask.falco_gen_bowtie_LS(inputs) 22 | 23 | plt.imshow(LS); plt.colorbar(); plt.pause(0.1) 24 | 25 | if("centering" in inputs.keys()): 26 | if inputs["centering"]=='pixel': 27 | plt.imshow(LS[1::,1::]-np.fliplr(LS[1::,1::])); plt.colorbar(); plt.pause(0.1) #--Check centering 28 | elif inputs["centering"]=='interpixel': 29 | plt.imshow(LS-np.fliplr(LS)); plt.colorbar(); plt.pause(0.1) #--Check centering 30 | -------------------------------------------------------------------------------- /examples/demo_gen_pupil_LUVOIR_A_final.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0, "../") 3 | import falco 4 | import numpy as np 5 | 6 | import matplotlib.pyplot as plt 7 | 8 | inputs = {} 9 | inputs["Nbeam"] = 1000 10 | inputs["magfacD"] = 1. 11 | inputs["wStrut"] = 0.01 12 | 13 | pupil = falco.mask.falco_gen_pupil_LUVOIR_A_final(inputs) 14 | 15 | plt.imshow(pupil); plt.colorbar(); plt.pause(0.1) 16 | 17 | plt.imshow(pupil[1::,1::]-np.fliplr(pupil[1::,1::])); plt.colorbar(); plt.pause(0.1) #--Check centering 18 | 19 | #figure(2); imagesc(pupil); axis xy equal tight; title('Input Pupil','Fontsize',20); colorbar; 20 | 21 | #figure(3); imagesc(pupil(2:end,2:end)-fliplr(pupil(2:end,2:end))); axis xy equal tight; title('Symmetry Check: Differencing','Fontsize',20); colorbar 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/demo_gen_pupil_LUVOIR_B.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import falco 4 | 5 | Nbeam = 300 6 | 7 | inputs = {} 8 | inputs["Nbeam"] = Nbeam 9 | pupil = falco.mask.falco_gen_pupil_LUVOIR_B(inputs) 10 | 11 | plt.imshow(falco.util.pad_crop(pupil, 308)) 12 | plt.colorbar() 13 | plt.pause(0.1) 14 | 15 | # Check centering and symmetry of the pupil 16 | plt.imshow(pupil[1::, 1::] - np.fliplr(pupil[1::, 1::])) 17 | plt.colorbar() 18 | plt.pause(0.1) 19 | -------------------------------------------------------------------------------- /examples/demo_gen_pupil_Roman_CGI_20200513.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,"../") 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | # from astropy.io import fits 6 | 7 | import falco 8 | 9 | 10 | # Test magnification, rotation, and shear of pupil 11 | 12 | DeltaY = 2 # pixels 13 | Nbeam = 1000 14 | centering = 'interpixel' 15 | changes = {} 16 | changes['yShear'] = -DeltaY / Nbeam 17 | changes['magFac'] = 0.7 18 | pupilA = falco.mask.falco_gen_pupil_Roman_CGI_20200513(Nbeam, centering, changes) 19 | pupilBprime = np.roll(np.rot90(pupilA, 2), (-2*DeltaY, 0), axis=(0, 1)) 20 | 21 | changes['clock_deg'] = 180 22 | pupilB = falco.mask.falco_gen_pupil_Roman_CGI_20200513(Nbeam, centering, changes) 23 | 24 | plt.figure(1); plt.imshow(pupilB); plt.gray(); plt.colorbar(); plt.pause(1) 25 | plt.figure(2); plt.imshow(pupilBprime); plt.colorbar(); plt.pause(1) 26 | plt.figure(3); plt.imshow(pupilB - pupilBprime); plt.colorbar(); plt.pause(1) 27 | 28 | # hdu = fits.PrimaryHDU(pupilA) 29 | # hdu.writeto('/Users/ajriggs/Downloads/pupilA_python.fits', overwrite=True) 30 | 31 | # hdu = fits.PrimaryHDU(pupilB) 32 | # hdu.writeto('/Users/ajriggs/Downloads/pupilB_python.fits', overwrite=True) 33 | 34 | 35 | # %% Lyot stop mode 36 | 37 | Nbeam = 309 38 | centering = 'pixel' 39 | 40 | del changes 41 | changes = {} 42 | changes['flagLyot'] = True 43 | changes['ID'] = 0.50 44 | changes['OD'] = 0.80 45 | changes['wStrut'] = 0.036 46 | lyot = falco.mask.falco_gen_pupil_Roman_CGI_20200513(Nbeam, centering, changes); 47 | lyotCropped = lyot[1::,1::] 48 | 49 | plt.figure(4); plt.imshow(lyot); plt.gray(); plt.colorbar(); plt.pause(1) 50 | plt.figure(5); plt.imshow(lyotCropped - np.fliplr(lyotCropped)); plt.colorbar(); plt.pause(1) 51 | -------------------------------------------------------------------------------- /examples/demo_gen_pupil_Simple.py: -------------------------------------------------------------------------------- 1 | """Check visually the output of falco_gen_pupil_Simple.""" 2 | import sys 3 | sys.path.insert(0,"../") 4 | import falco 5 | import numpy as np 6 | 7 | import matplotlib.pyplot as plt 8 | from astropy.io import fits 9 | 10 | Nbeam = 250 11 | 12 | inputs = {} # initialize 13 | inputs["Nbeam"] = 500 # Number of samples across the beam 14 | inputs["Npad"] = 700 # Number of pixels across square output array 15 | inputs["OD"] = 1.00 # Inner diameter (fraction of Nbeam) 16 | 17 | # OPTIONAL INPUTS 18 | inputs["ID"] = 0.20 # Outer diameter (fraction of Nbeam) 19 | inputs["angStrut"] = np.array([0, 90, 180, 270]) # Array of strut angles [deg] 20 | inputs["wStrut"] = 0.01 # Strut widths (fraction of Nbeam) 21 | inputs["stretch"] = 1. # Create an elliptical aperture by changing Nbeam along the horizontal direction by a factor of stretch 22 | inputs["centering"] = 'pixel' 23 | inputs["clocking"] = 15 # CCW rotation. Doesn't work with flag HG. [degrees] 24 | inputs["xShear"] = 0.1 # [pupil diameters] 25 | inputs["yShear"] = -0.15 # [pupil diameters] 26 | # inputs["flagHG"] = True # Cannot do lateral shear or clocking 27 | 28 | pupil = falco.mask.falco_gen_pupil_Simple(inputs) 29 | 30 | plt.figure(); plt.imshow(pupil); plt.colorbar(); plt.gca().invert_yaxis(); plt.pause(0.1) 31 | 32 | # if("centering" in inputs.keys()): 33 | # if inputs["centering"]=='pixel': 34 | # plt.imshow(pupil[1::,1::]-np.fliplr(pupil[1::,1::])); plt.colorbar(); plt.pause(0.1) #-Check centering 35 | # elif inputs["centering"]=='interpixel': 36 | # plt.imshow(pupil-np.fliplr(pupil)); plt.colorbar(); plt.pause(0.1) #-Check centering 37 | 38 | # hdu = fits.PrimaryHDU(pupil) 39 | # hdu.writeto('/Users/ajriggs/Downloads/simple_pupil_python.fits', overwrite=True) 40 | 41 | # %% Simplest pupil--no optional inputs 42 | inputs = {} # initialize 43 | inputs["Nbeam"] = 500 # Number of samples across the beam 44 | inputs["Npad"] = 700 # Number of pixels across square output array 45 | inputs["OD"] = 0.80 # Inner diameter (fraction of Nbeam) 46 | 47 | pupil = falco.mask.falco_gen_pupil_Simple(inputs) 48 | 49 | plt.figure(); plt.imshow(pupil); plt.colorbar(); plt.gca().invert_yaxis(); plt.pause(0.1) 50 | -------------------------------------------------------------------------------- /examples/try_running_falco/EXAMPLE_try_running_FALCO.py: -------------------------------------------------------------------------------- 1 | """Simple functional example used to verify that FALCO runs correctly.""" 2 | import os 3 | from pathlib import Path 4 | import falco 5 | 6 | HERE = os.path.dirname(os.path.abspath(__file__)) 7 | mp = falco.config.ModelParameters.from_yaml_file(os.path.join(HERE, 'config.yaml')) 8 | 9 | # %% Define directories for data output 10 | # # Location of config files and minimal output files. 11 | # # Default is mp.path.falco + 'data/brief/' 12 | # mp.path.config = os.path.join(mp.path.falco, 'data', 'brief') 13 | # # (Mostly) complete workspace from end of trial. 14 | # # Default is mp.path.falco + 'data/ws/' 15 | # mp.path.ws = os.path.join(mp.path.falco, 'data', 'ws') 16 | 17 | 18 | # %% Overwrite values from config file if desired 19 | 20 | # ## Special Computational Settings 21 | mp.flagPlot = True 22 | mp.flagParallel = False # whether to use multiprocessing to parallelize some large computations 23 | # mp.Nthreads = 2 # Number of threads to use when using multiprocessing. 24 | 25 | # Record Keeping 26 | mp.TrialNum = 1 27 | mp.SeriesNum = 1 28 | 29 | # Use just 1 wavelength for initial testing of code 30 | mp.fracBW = 0.01 # fractional bandwidth of the whole bandpass (Delta lambda / lambda0) 31 | mp.Nsbp = 1 # Number of sub-bandpasses to divide the whole bandpass into for estimation and control 32 | mp.Nwpsbp = 1 # Number of wavelengths to used to approximate an image in each sub-bandpass 33 | 34 | mp.Nitr = 3 # Number of wavefront control iterations 35 | 36 | 37 | # %% Perform the Wavefront Sensing and Control 38 | 39 | mp.runLabel = ('Series%04d_Trial%04d_%s' % 40 | (mp.SeriesNum, mp.TrialNum, mp.coro)) 41 | 42 | out = falco.setup.flesh_out_workspace(mp) 43 | 44 | falco.wfsc.loop(mp, out) 45 | 46 | 47 | # %% Plot the output 48 | 49 | falco.plot.plot_trial_output(out) 50 | 51 | fnPickle = os.path.join(mp.path.brief, f'{mp.runLabel}_snippet.pkl') 52 | falco.plot.plot_trial_output_from_pickle(fnPickle) 53 | -------------------------------------------------------------------------------- /extra/script_test_DM_surface_fitting_with_DM_WFIRST_LC.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,"../") 3 | sys.path.insert(0,"../tests/") 4 | import falco 5 | import numpy as np 6 | import testing_defaults_WFIRST_LC as DEFAULTS 7 | import matplotlib.pyplot as plt 8 | 9 | mp = DEFAULTS.mp 10 | 11 | flagPlotDebug = False 12 | 13 | mp.path = falco.config.Object() 14 | mp.path.falco = './' #--Location of FALCO 15 | mp.path.proper = './' #--Location of the MATLAB PROPER library 16 | 17 | ##--Output Data Directories (Comment these lines out to use defaults within falco-matlab/data/ directory.) 18 | mp.path.config = './' #--Location of config files and minimal output files. Default is [mainPath filesep 'data' filesep 'brief' filesep] 19 | mp.path.ws = './' # (Mostly) complete workspace from end of trial. Default is [mainPath filesep 'data' filesep 'ws' filesep]; 20 | 21 | 22 | ## Step 3: Overwrite default values as desired 23 | 24 | mp.dm1.xtilt = 45 25 | mp.dm1.ytilt = 20 26 | mp.dm1.zrot = 30 27 | 28 | mp.dm1.xc = mp.dm1.Nact/2 - 1/2 + 1; 29 | mp.dm1.yc = mp.dm1.Nact/2 - 1/2 -1; 30 | 31 | 32 | ## Step 4: Initialize the rest of the workspace 33 | 34 | out = falco.setup.flesh_out_workspace(mp) 35 | 36 | 37 | # %% Generate a DM surface and try to re-create the actuator commands 38 | 39 | normFac = 1; 40 | mp.dm1.V = normFac*np.random.rand(mp.dm1.Nact,mp.dm1.Nact) 41 | DM1Surf = falco.dm.gen_surf_from_act(mp.dm1, mp.dm1.compact.dx, mp.dm1.compact.Ndm) 42 | 43 | if(flagPlotDebug): 44 | plt.figure(1); plt.imshow(mp.dm1.V); plt.colorbar(); plt.pause(0.1); 45 | plt.figure(2); plt.imshow(DM1Surf); plt.colorbar(); plt.pause(0.1); 46 | 47 | #--Fit the surface 48 | # DMSurf = pad_crop(DMSurf,500); 49 | Vout = falco.dm.fit_surf_to_act(mp.dm1,DM1Surf)/mp.dm1.VtoH 50 | Verror = mp.dm1.V - Vout; 51 | rmsVError = np.sqrt(np.mean(Verror.flatten()**2))/normFac; 52 | print('RMS fitting error to voltage map is %.2f%%.\n'%(rmsVError*100)) 53 | 54 | if(flagPlotDebug): 55 | plt.figure(3); plt.imshow(Vout); plt.colorbar(); plt.pause(0.1); 56 | plt.figure(4); plt.imshow(Verror); plt.colorbar(); plt.pause(0.1); 57 | -------------------------------------------------------------------------------- /extra/script_test_random_map_fitting_with_DM_WFIRST_LC.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0,"../") 3 | sys.path.insert(0,"../tests/") 4 | import falco 5 | import falco.proper as proper 6 | import numpy as np 7 | import testing_defaults_WFIRST_LC as DEFAULTS 8 | import matplotlib.pyplot as plt 9 | 10 | mp = DEFAULTS.mp 11 | 12 | flagPlotDebug = True 13 | 14 | mp.path = falco.config.Object() 15 | mp.path.falco = './' #--Location of FALCO 16 | mp.path.proper = './' #--Location of the MATLAB PROPER library 17 | 18 | ##--Output Data Directories (Comment these lines out to use defaults within falco-matlab/data/ directory.) 19 | mp.path.config = './' #--Location of config files and minimal output files. Default is [mainPath filesep 'data' filesep 'brief' filesep] 20 | mp.path.ws = './' # (Mostly) complete workspace from end of trial. Default is [mainPath filesep 'data' filesep 'ws' filesep]; 21 | 22 | mp.flagParallel = False 23 | 24 | ## Step 3: Overwrite default values as desired 25 | 26 | mp.dm1.xtilt = 45 27 | mp.dm1.ytilt = 20 28 | mp.dm1.zrot = 30 29 | 30 | mp.dm1.xc = (mp.dm1.Nact/2 - 1/2) + 1; 31 | mp.dm1.yc = (mp.dm1.Nact/2 - 1/2) -1; 32 | 33 | 34 | ## Step 4: Initialize the rest of the workspace 35 | 36 | out = falco.setup.flesh_out_workspace(mp) 37 | 38 | 39 | ## Step 5 40 | 41 | # Determine the region of the array corresponding to the DM surface for use in the fitting. 42 | mp.dm1.V = np.ones((mp.dm1.Nact,mp.dm1.Nact)) 43 | testSurf = falco.dm.gen_surf_from_act(mp.dm1, mp.dm1.compact.dx, mp.dm1.compact.NdmPad) 44 | testArea = np.zeros(testSurf.shape) 45 | testArea[testSurf >= 0.5*np.max(testSurf)] = 1 46 | 47 | #--PROPER initialization 48 | pupil_ratio = 1 # beam diameter fraction 49 | wl_dummy = 1e-6 # dummy value needed to initialize wavelength in PROPER (meters) 50 | wavefront = proper.prop_begin(mp.dm1.compact.NdmPad*mp.dm1.dx, wl_dummy, mp.dm1.compact.NdmPad, pupil_ratio) 51 | # PSD Error Map Generation using PROPER 52 | amp = 9.6e-19; b = 4.0; 53 | c = 3.0; 54 | errorMap = proper.prop_psd_errormap( wavefront, amp, b, c, TPF=True ) 55 | errorMap = errorMap*testArea; 56 | 57 | if(flagPlotDebug): 58 | plt.figure(1); plt.imshow(testArea); plt.colorbar(); plt.pause(0.1); 59 | plt.figure(2); plt.imshow(errorMap); plt.colorbar(); plt.pause(0.1); 60 | 61 | #--Fit the surface 62 | Vout = falco.dm.fit_surf_to_act(mp.dm1,errorMap)/mp.dm1.VtoH 63 | mp.dm1.V = Vout 64 | DM1Surf = falco.dm.gen_surf_from_act(mp.dm1, mp.dm1.compact.dx, mp.dm1.compact.NdmPad) 65 | surfError = errorMap - DM1Surf; 66 | rmsError = np.sqrt(np.mean((surfError[testArea==1].flatten()**2))) 67 | print('RMS fitting error to voltage map is %.2e meters.\n'%rmsError) 68 | 69 | if(flagPlotDebug): 70 | plt.figure(3); plt.imshow(Vout); plt.colorbar(); plt.pause(0.1); 71 | plt.figure(4); plt.imshow(DM1Surf); plt.colorbar(); plt.pause(0.1); 72 | plt.figure(5); plt.imshow(surfError); plt.colorbar(); plt.title('Difference'); plt.pause(0.1); 73 | 74 | -------------------------------------------------------------------------------- /extra/test_growing_object.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | sys.path.append('/Users/sfregoso/Documents/old_mac/MacPro_2012thru2015/Santos/Work/FelipeStuff/FALCO/git_repos/public/falco-python/') 4 | import falco 5 | 6 | class EmptyObj(object): 7 | pass 8 | 9 | def AddToObject(mp): 10 | mp.felipe = EmptyObj() 11 | mp.felipe.age = 39 12 | 13 | def AddMoreVariables(mp): 14 | mp.felipe.eye = EmptyObj() 15 | mp.felipe.eye.color = 'brown' 16 | mp.felipe.eye.stigmatism = False 17 | mp.felipe.hair = EmptyObj() 18 | mp.felipe.hair.color = 'black' 19 | mp.felipe.hair.length = 'short' 20 | mp.felipe.hair.dyed = False 21 | 22 | mp = falco.config.ModelParameters() 23 | AddToObject(mp) 24 | AddMoreVariables(mp) 25 | 26 | print(mp.felipe.age) 27 | print(mp.felipe.eye.color) 28 | print(mp.felipe.eye.stigmatism) 29 | print(mp.felipe.hair.length) 30 | print(mp.felipe.hair.color) 31 | print(mp.felipe.hair.dyed) 32 | -------------------------------------------------------------------------------- /extra/testing_dm_surface_fitting: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Thu Nov 21 10:11:55 2019 5 | 6 | @author: ajriggs 7 | """ 8 | 9 | import sys 10 | sys.path.insert(0,"../") 11 | import falco 12 | import falco.proper as proper 13 | import numpy as np 14 | from astropy.io import fits 15 | 16 | import matplotlib.pyplot as plt 17 | 18 | #% Steps: 19 | #% 1) Create random DM command map. 20 | #% 2) Generate DM surface 21 | #% 3) Derotate DM surface 22 | #% 4) Downsample DM surface 23 | #% 5) Convert DM surface to DM commands 24 | 25 | #clear all 26 | 27 | dm = falco.config.Object() 28 | 29 | dm.xtilt = 0. 30 | 31 | N = 400; 32 | dm.centering = 'pixel'; 33 | dm.dm_spacing = 1e-3; 34 | dm.dx_inf0 = 1e-4; 35 | orderOfOps = 'XYZ'; 36 | dm.inf_sign = '+'; 37 | dm.inf_fn = falco.INFLUENCE_XINETICS #'influence_dm5v2.fits'; 38 | dm.inf0 = np.squeeze(fits.getdata(dm.inf_fn, ext=0)) 39 | #dm.inf0 = fitsread(dm.inf_fn); 40 | cshift = 0 41 | dx = 2e-4# # [meters] 42 | dm.dx = dx 43 | dm.Nact = 50 44 | dm.xc = dm.Nact/2 - 1/2 45 | dm.yc = dm.Nact/2 - 1/2 46 | #dm.xc = dm.Nact/2 - 1/2 + 1; 47 | #dm.yc = dm.Nact/2 - 1/2 - 1; 48 | 49 | dm.VtoH = 1e-9*np.ones((dm.Nact,dm.Nact)) 50 | 51 | #dm.V = np.eye(dm.Nact) 52 | dm.V = np.zeros((dm.Nact,dm.Nact)) 53 | dm.V[0,0] = 1; dm.V[0,-1]= 1; dm.V[-1,0] = 1; dm.V[-1,-1] = 1; 54 | dm.V[9,9] = 2; 55 | dm.V[29,9] = 2; 56 | 57 | dm.xtilt = 45 58 | dm.ytilt = 20 59 | dm.zrot = 30 60 | 61 | #--PROPER initialization 62 | pupil_ratio = 1; # beam diameter fraction 63 | wl_dummy = 1e-6; #--dummy value needed to initialize wavelength in PROPER (meters) 64 | bm = proper.prop_begin(N*dx, wl_dummy, N, pupil_ratio) 65 | 66 | #--Generate the DM surface 67 | H = dm.VtoH*dm.V; 68 | DMSurf = falco.dm.propcustom_dm(bm, H, dm.xc-cshift, dm.yc-cshift, dm.dm_spacing, 69 | XTILT=dm.xtilt, YTILT=dm.ytilt, ZTILT=dm.zrot,XYZ=True, 70 | inf_sign=dm.inf_sign, inf_fn=dm.inf_fn) 71 | 72 | 73 | plt.figure(1); h = plt.imshow(H); plt.colorbar(); plt.gca().invert_yaxis() 74 | #ax = h.axes 75 | #ax.invert_yaxis(); 76 | plt.figure(2); plt.imshow(DMSurf); plt.colorbar(); plt.gca().invert_yaxis() 77 | 78 | 79 | #--Fit the surface 80 | #DMSurf = falco.util.pad_crop(DMSurf,500) 81 | Vout = falco.dm.fit_surf_to_act(dm,DMSurf) 82 | plt.figure(3); plt.imshow(Vout); plt.colorbar(); plt.gca().invert_yaxis() 83 | 84 | plt.figure(4); plt.imshow(Vout-H); plt.colorbar(); plt.gca().invert_yaxis() 85 | print(np.max(np.abs(Vout-H))) 86 | 87 | hdu = fits.PrimaryHDU(Vout-H) 88 | hdu.writeto('/Users/ajriggs/Downloads/diff_python.fits',overwrite=True) 89 | 90 | -------------------------------------------------------------------------------- /falco/__init__.py: -------------------------------------------------------------------------------- 1 | from falco.config import init_from_mat as ifm 2 | from .setup import * 3 | from .util import * 4 | # from .configs import * 5 | from .imaging import * 6 | from .dm import * 7 | from .diff_dm import * 8 | from .est import * 9 | from .ctrl import * 10 | from .hlc import * 11 | from .wfsc import * 12 | from .mask import * 13 | from .plot import * 14 | from .prop import * 15 | from .hexsegmirror import * 16 | from .thinfilm import * 17 | from .zern import * 18 | from . import model 19 | from ._globals import INFLUENCE_XINETICS 20 | from ._globals import INFLUENCE_BMC_KILO 21 | from ._globals import INFLUENCE_BMC_2K 22 | from ._globals import INFLUENCE_BMC_2K_RES20 23 | -------------------------------------------------------------------------------- /falco/_globals.py: -------------------------------------------------------------------------------- 1 | import os 2 | INFLUENCE_XINETICS = os.path.dirname(os.path.abspath(__file__)) + "/data/influence_dm5v2.fits" 3 | INFLUENCE_BMC_KILO = os.path.dirname(os.path.abspath(__file__)) + "/data/influence_BMC_kiloDM_300micron_res10_spline.fits" 4 | INFLUENCE_BMC_2K = os.path.dirname(os.path.abspath(__file__)) + "/data/influence_BMC_2kDM_400micron_res10.fits" 5 | INFLUENCE_BMC_2K_RES20 = os.path.dirname(os.path.abspath(__file__)) + "/data/influence_BMC_2kDM_400micron_res20.fits" 6 | 7 | -------------------------------------------------------------------------------- /falco/config/Eval.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | import copy 3 | 4 | @dataclass 5 | class Eval: 6 | """ 7 | A lazily evaluated expression. 8 | 9 | When evaluating, it exposes numpy, falco, and math to the injected code under `np`, `falco`, and `math`, 10 | and the model parameters object under `mp`. 11 | """ 12 | 13 | globals: any 14 | """ 15 | The globals exposed to the injected code, in a dictionary. 16 | Use this to provide libraries, i.e. `{"np": numpy}` 17 | """ 18 | 19 | locals: any 20 | """ 21 | The locals exposed to the injected code, in a dictionary. 22 | This can be used to provide recursive access to the parsed config object. 23 | Modifying the locals after instantiating this class is OK. (and often necessary) 24 | """ 25 | 26 | code: str 27 | "The injected code to be run lazily." 28 | 29 | in_progress = False 30 | """ 31 | Whether evaluation is currently in progress. 32 | 33 | If a circular dependency happens, this variable is used to detect it. 34 | """ 35 | 36 | def evaluate(self): 37 | """Runs the code.""" 38 | if self.in_progress: 39 | raise ValueError("Circular parameter evaluation dependency detected.") 40 | 41 | self.in_progress = True 42 | try: 43 | result = eval(self.code, self.globals, self.locals) 44 | finally: 45 | self.in_progress = False 46 | return result 47 | 48 | def __deepcopy__(self, memo): 49 | return Eval(self.globals, copy.deepcopy(self.locals, memo), self.code) 50 | -------------------------------------------------------------------------------- /falco/config/ModelVariables.py: -------------------------------------------------------------------------------- 1 | from falco.config import Object 2 | 3 | 4 | class ModelVariables(Object): 5 | """Model variables for FALCO model inputs.""" 6 | 7 | def __init__(self, **kwargs): 8 | self.sbpIndex = 0 9 | "list index of subband" 10 | 11 | self.wpsbpIndex = 0 12 | "list index of wavelength" 13 | 14 | self.starIndex = 0 15 | "list index of star" 16 | 17 | self.zernIndex = 1 18 | "Noll Zernike" 19 | 20 | self.whichSource = 'star' 21 | 22 | self.x_offset = 0 23 | "lambda_central/D" 24 | 25 | self.y_offset = 0 26 | "lambda_central/D" 27 | 28 | super().__init__(**kwargs) 29 | -------------------------------------------------------------------------------- /falco/config/Object.py: -------------------------------------------------------------------------------- 1 | import deepmerge 2 | from dataclasses import dataclass, field 3 | 4 | from falco.config.Eval import Eval 5 | 6 | 7 | class Object: 8 | """ 9 | An object wrapper around a dictionary. 10 | 11 | Allows use of field syntax (`obj.field`) as well as index syntax (`obj['field']`). 12 | Automatically evaluates `Eval` lazy parameters. 13 | """ 14 | 15 | def __init__(self, **kwargs): 16 | data = kwargs if kwargs is not None else {} 17 | if self.__dict__ is not None: 18 | data = deepmerge.conservative_merger.merge(data, self.__dict__) 19 | self.__dict__ = {"data": data} 20 | 21 | def merge(self, **kwargs): 22 | deepmerge.always_merger.merge(self.data, kwargs) 23 | 24 | def __getattr__(self, item): 25 | # Normally `self.data` exists in `self.__dict__`, so referring to `self.data` 26 | # doesn't result in a `__getattr__` call. 27 | # But if `__init__` has not been called yet, then `data` does not exist 28 | # and any `__getattr__` call for any attribute will recurse later in the function. 29 | # So we just create the dictionary now because it needs to exist anyway. 30 | if item == 'data': 31 | self.data = {} 32 | return self.data 33 | 34 | if item not in self.data: 35 | raise AttributeError 36 | 37 | if isinstance(self.data[item], Eval): 38 | return self.data[item].evaluate() 39 | return self.data[item] 40 | 41 | def __getitem__(self, item): 42 | return self.__getattr__(item) 43 | 44 | def __setitem__(self, key, value): 45 | self.data[key] = value 46 | 47 | def __eq__(self, other): 48 | if not isinstance(other, Object): 49 | return False 50 | return self.data == other.data and self.__dict__ == other.__dict__ 51 | 52 | def show(self): 53 | """Print self as a dictionary""" 54 | print(self.data) 55 | -------------------------------------------------------------------------------- /falco/config/Probe.py: -------------------------------------------------------------------------------- 1 | from falco.config import Object 2 | 3 | 4 | class Probe(Object): 5 | """Define the probe properties.""" 6 | 7 | def __init__(self, **kwargs): 8 | self.Npairs = 3 9 | "Number of pair-wise probe pairs to use." 10 | 11 | self.whichDM = 1 12 | "Which DM to use for probing. 1 or 2." 13 | 14 | self.xOffset = 0 15 | "x-offset of the probe center from the DM grid center [actuators]. Use to avoid obscurations." 16 | 17 | self.yOffset = 0 18 | "y-offset of the probe center from the DM grid center [actuators]. Use to avoid obscurations." 19 | 20 | self.rotation = 0 21 | "rotation angle applied to the probe command [degrees]." 22 | 23 | self.gainFudge = 1 24 | "empirical fudge factor to make average probe amplitude match desired value." 25 | 26 | self.radius = 12 27 | "Half-width of the square probed region in the image plane [lambda/D]. (NOTE: Only used for square probes.)" 28 | 29 | self.axis = 'alternate' 30 | """ 31 | Which axis to have the phase discontinuity along. Values can be 'x', 'y', or 'xy' / 'alt' / 'alternate'. 32 | The 'alternate' option causes the bad axis to switch between x and y for each subsequent probe pair. 33 | (NOTE: Only used for square probes.) 34 | """ 35 | 36 | self.width = 12 37 | """ 38 | Width of rectangular probe in focal plane [lambda/D]. 39 | (NOTE: Only used for rectangular probes. radius is used instead for a square probed region) 40 | """ 41 | 42 | self.xiOffset = 6 43 | """ 44 | Horizontal offset from star of rectangular probe in focal plane [lambda/D]. 45 | (NOTE: Only used for rectangular probes. No offset for square probed region.) 46 | """ 47 | 48 | self.height = 24 49 | """ 50 | Height of rectangular probe in focal plane [lambda/D]. 51 | (NOTE: Only used for rectangular probes. radius is used instead for a square probed region) 52 | """ 53 | 54 | self.etaOffset = 0 55 | """ 56 | Vertical offset from star of rectangular probe in focal plane [lambda/D]. 57 | (NOTE: Only used for rectangular probes. No offset for square probed region.) 58 | """ 59 | 60 | super().__init__(**kwargs) 61 | -------------------------------------------------------------------------------- /falco/config/ProbeSchedule.py: -------------------------------------------------------------------------------- 1 | from falco.config import Object 2 | 3 | 4 | class ProbeSchedule(Object): 5 | """Object containing scheduled probe properties for each WFSC iteration.""" 6 | 7 | def __init__(self, **kwargs): 8 | self.xOffsetVec = None 9 | "Vector of x-offsets (one value per WFSC iteration) of the probe center from the DM grid center [actuators]" 10 | 11 | self.yOffsetVec = None 12 | "Vector of x-offsets (one value per WFSC iteration) of the probe center from the DM grid center [actuators]" 13 | 14 | self.rotationVec = None 15 | "Vector of the rotation angle to add to the probes at each WFSC iteration [degrees]" 16 | 17 | self.InormProbeVec = None 18 | "Vector of the desired normalized intensity of the probes at each WFSC iteration" 19 | 20 | super().__init__(**kwargs) 21 | -------------------------------------------------------------------------------- /falco/config/__init__.py: -------------------------------------------------------------------------------- 1 | from .Object import Object 2 | from .Probe import Probe 3 | from .Eval import Eval 4 | from .ProbeSchedule import ProbeSchedule 5 | from .ModelVariables import ModelVariables 6 | from .ModelParameters import ModelParameters 7 | from .ModelParameters import Object 8 | -------------------------------------------------------------------------------- /falco/config/yaml_loader.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | from falco.config import Eval, Object 3 | 4 | def load_from_str(yaml_str, eval_globals, eval_locals, ctors_dict): 5 | """ 6 | Loads a yaml string into a config Object. 7 | Automatically provides a tag constructor for the Eval class, and 8 | all dictionaries are automatically converted to the config Object class. 9 | 10 | See Eval for more details on the globals and locals arguments. 11 | 12 | :param yaml_str: a yaml string 13 | :param eval_globals: globals to expose to eval code, in a dictionary 14 | :param eval_locals: locals to expose to eval code, in a dictionary 15 | :param ctors_dict: a dictionary of extra constructors, such as `{'!Probe', object_constructor(Probe)}` 16 | :return a python object 17 | """ 18 | result_obj = yaml.load(yaml_str, Loader=_get_loader(eval_globals, eval_locals, ctors_dict)) 19 | return result_obj.data 20 | 21 | def object_constructor(noarg_constructor): 22 | """ 23 | Makes a yaml class loader from a class constructor that takes no arguments. 24 | :param noarg_constructor: a constructor with no arguments 25 | """ 26 | def _result(loader: yaml.SafeLoader, node: yaml.nodes.MappingNode): 27 | obj = noarg_constructor(**loader.construct_mapping(node)) 28 | return obj 29 | return _result 30 | 31 | def _eval_constructor(eval_globals, eval_locals): 32 | def _result(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode): 33 | s = loader.construct_scalar(node) 34 | if not isinstance(s, str): 35 | raise ValueError(f"Cannot eval anything other than a string. Found type {type(s)}: {s}") 36 | return Eval(eval_globals, eval_locals, loader.construct_yaml_str(node)) 37 | return _result 38 | 39 | def _get_loader(eval_globals, eval_locals, ctors_dict): 40 | """Add constructors to PyYAML loader.""" 41 | loader = yaml.SafeLoader 42 | loader.add_constructor(u'tag:yaml.org,2002:map', object_constructor(Object)) 43 | loader.add_constructor("!eval", _eval_constructor(eval_globals, eval_locals)) 44 | 45 | for tag in ctors_dict: 46 | loader.add_constructor(tag, ctors_dict[tag]) 47 | return loader 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /falco/data/add_header_to_inf_func_files.py: -------------------------------------------------------------------------------- 1 | """Add new header values to the influence function FITS files to function 2 | after prop_dm.py in PROPER changed how the header values are used.""" 3 | from astropy.io import fits 4 | import os 5 | 6 | HERE = os.path.abspath(os.path.dirname(__file__)) 7 | 8 | fnList = [ 9 | 'influence_BMC_2kDM_400micron_res10.fits', 10 | 'influence_BMC_2kDM_400micron_res20.fits', 11 | 'influence_BMC_kiloDM_300micron_res10_spline.fits', 12 | 'influence_dm5v2.fits', 13 | 'influence_dm_FEA_10milsV2_20160330.fits', 14 | ] 15 | 16 | for index, fn in enumerate(fnList): 17 | 18 | print(fn) 19 | fnFull = os.path.join(HERE, fn) 20 | inf_func = fits.getdata(fnFull) 21 | hdul = fits.open(fnFull) # open a FITS file 22 | hdr = hdul[0].header # the primary HDU header 23 | P2PD_M = hdr['P2PDX_M'] 24 | C2CD_M = hdr['C2CDX_M'] 25 | 26 | # Output 27 | hdu = fits.PrimaryHDU(inf_func) 28 | hdu.header['P2PD_M'] = (P2PD_M, 'pixel2pix distance in meters') 29 | hdu.header['P2PDX_M'] = P2PD_M 30 | hdu.header['P2PDY_M'] = P2PD_M 31 | hdu.header['C2CD_M'] = (C2CD_M, 'center2cen dist of actuators in meters') 32 | hdu.header['C2CDX_M'] = C2CD_M 33 | hdu.header['C2CDY_M'] = C2CD_M 34 | 35 | # fnOut = os.path.join(HERE, 'test_file.fits') 36 | fnOut = fnFull 37 | hdu.writeto(fnOut, overwrite=True) 38 | -------------------------------------------------------------------------------- /falco/data/brief/.placeholder.txt: -------------------------------------------------------------------------------- 1 | File used to get this subdirectory to sync with git. 2 | -------------------------------------------------------------------------------- /falco/data/chromium_wvlUM_n_k_Johnson_1974.csv: -------------------------------------------------------------------------------- 1 | 0.188,1.28,1.64 2 | 0.192,1.31,1.65 3 | 0.195,1.35,1.68 4 | 0.199,1.39,1.7 5 | 0.203,1.43,1.7 6 | 0.207,1.46,1.71 7 | 0.212,1.46,1.72 8 | 0.216,1.47,1.72 9 | 0.221,1.45,1.73 10 | 0.226,1.43,1.74 11 | 0.231,1.4,1.77 12 | 0.237,1.38,1.8 13 | 0.243,1.36,1.85 14 | 0.249,1.36,1.91 15 | 0.255,1.37,1.97 16 | 0.262,1.38,2.03 17 | 0.269,1.39,2.08 18 | 0.276,1.43,2.15 19 | 0.284,1.45,2.21 20 | 0.292,1.48,2.28 21 | 0.301,1.53,2.34 22 | 0.311,1.58,2.4 23 | 0.32,1.65,2.47 24 | 0.332,1.69,2.53 25 | 0.342,1.76,2.58 26 | 0.354,1.84,2.64 27 | 0.368,1.87,2.69 28 | 0.381,1.92,2.74 29 | 0.397,2,2.83 30 | 0.413,2.08,2.93 31 | 0.431,2.19,3.04 32 | 0.451,2.33,3.14 33 | 0.471,2.51,3.24 34 | 0.496,2.75,3.3 35 | 0.521,2.94,3.33 36 | 0.549,3.18,3.33 37 | 0.582,3.22,3.3 38 | 0.617,3.17,3.3 39 | 0.659,3.09,3.34 40 | 0.704,3.05,3.39 41 | 0.756,3.08,3.42 42 | 0.821,3.2,3.48 43 | 0.892,3.3,3.52 44 | 0.984,3.41,3.57 45 | 1.088,3.58,3.58 46 | 1.216,3.67,3.6 47 | 1.393,3.69,3.84 48 | 1.61,3.66,4.31 49 | 1.937,3.71,5.04 -------------------------------------------------------------------------------- /falco/data/influence_BMC_2kDM_400micron_res10.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/falco/data/influence_BMC_2kDM_400micron_res10.fits -------------------------------------------------------------------------------- /falco/data/influence_BMC_2kDM_400micron_res20.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/falco/data/influence_BMC_2kDM_400micron_res20.fits -------------------------------------------------------------------------------- /falco/data/influence_BMC_kiloDM_300micron_res10_spline.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/falco/data/influence_BMC_kiloDM_300micron_res10_spline.fits -------------------------------------------------------------------------------- /falco/data/influence_dm5v2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/falco/data/influence_dm5v2.fits -------------------------------------------------------------------------------- /falco/data/influence_dm_FEA_10milsV2_20160330.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/falco/data/influence_dm_FEA_10milsV2_20160330.fits -------------------------------------------------------------------------------- /falco/data/nickel_data_from_Palik_via_Bala_wvlNM_n_k.txt: -------------------------------------------------------------------------------- 1 | 302.400 1.74 1.99 2 | 310.000 1.73 1.98 3 | 317.900 1.72 1.98 4 | 326.300 1.69 1.99 5 | 335.100 1.66 2.02 6 | 344.400 1.64 2.07 7 | 354.200 1.63 2.11 8 | 364.700 1.62 2.17 9 | 375.700 1.61 2.23 10 | 387.500 1.61 2.3 11 | 400.000 1.61 2.36 12 | 413.300 1.61 2.44 13 | 427.500 1.62 2.52 14 | 442.800 1.62 2.61 15 | 459.200 1.64 2.71 16 | 476.900 1.66 2.81 17 | 495.900 1.67 2.93 18 | 516.600 1.71 3.06 19 | 539.100 1.75 3.19 20 | 563.600 1.8 3.33 21 | 590.400 1.85 3.48 22 | 619.900 1.93 3.65 23 | 636.000 1.98 3.74 24 | 653.000 2.02 3.82 25 | 670.000 2.08 3.91 26 | 689.000 2.14 4 27 | 709.000 2.21 4.09 28 | 729.000 2.28 4.18 29 | 751.000 2.36 4.25 30 | 775.000 2.43 4.31 31 | 800.000 2.48 4.38 32 | 827.000 2.53 4.47 33 | 855.000 2.59 4.55 34 | 886.000 2.65 4.63 35 | 918.000 2.69 4.73 36 | 954.000 2.74 4.85 37 | 992.000 2.8 4.97 38 | 1033.000 2.85 5.1 39 | 1078.000 2.91 5.24 40 | 1127.000 2.97 5.38 41 | 1181.000 3.01 5.55 42 | 1240.000 3.06 5.74 43 | 1305.000 3.11 5.98 44 | 1378.000 3.18 6.23 45 | 1459.000 3.27 6.51 46 | 1550.000 3.38 6.82 47 | 1653.000 3.49 7.13 48 | 1771.000 3.59 7.48 49 | 1907.000 3.69 7.92 50 | 2066.000 3.84 8.35 51 | 2254.000 3.9 8.92 52 | 2480.000 4.03 9.64 53 | 2755.000 4.2 10.2 54 | 3100.000 3.84 11.4 55 | 3263.000 3.92 12.1 56 | 3444.000 4 12.7 57 | 3647.000 4.07 13.4 58 | 3875.000 4.12 14.2 59 | 4133.000 4.19 15 60 | 4428.000 4.3 16 61 | 4769.000 4.29 17.1 62 | 5166.000 4.16 18.4 63 | 5636.000 4.11 20.2 64 | 6199.000 4.12 22.5 65 | 6526.000 4.3 23.8 66 | 6888.000 4.45 25.2 67 | 7293.000 4.68 26.8 68 | 7749.000 5 28.6 69 | 8266.000 5.45 30.6 70 | 8856.000 5.83 32.8 71 | 9537.000 6.44 35.3 72 | 10330.000 7.11 38.3 73 | 11270.000 8.12 41.8 74 | 12400.000 9.54 45.8 -------------------------------------------------------------------------------- /falco/data/ws/.placeholder.txt: -------------------------------------------------------------------------------- 1 | File used to get this subdirectory to sync with git. 2 | -------------------------------------------------------------------------------- /falco/fftutils.py: -------------------------------------------------------------------------------- 1 | try: 2 | from mkl_fft import (fft2, ifft2, fftshift, ifftshift) 3 | print("Loaded FFT utilities from 'MKL_FFT'") 4 | except: 5 | print("Loaded FFT utilities from 'numpy.fft'") 6 | from numpy.fft import (fft2, ifft2, fftshift, ifftshift) 7 | 8 | 9 | from numpy.fft import fftfreq 10 | -------------------------------------------------------------------------------- /falco/model/__init__.py: -------------------------------------------------------------------------------- 1 | from .models import * -------------------------------------------------------------------------------- /falco/proper/.use_ffti: -------------------------------------------------------------------------------- 1 | Using FFTI routines 2 | -------------------------------------------------------------------------------- /falco/proper/examples/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | -------------------------------------------------------------------------------- /falco/proper/examples/coronagraph_demo.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | import matplotlib.pylab as plt 14 | 15 | 16 | def coronagraph_demo(): 17 | 18 | n = 512 # grid size 19 | lamda = 0.55 # wavelength (microns) 20 | 21 | (no_errors, no_errors_sampl) = proper.prop_run("run_coronagraph_dm", lamda, n, PASSVALUE = {'use_errors': False, 'use_dm': False, 'occulter_type': '8TH_ORDER'}, VERBOSE = False) 22 | 23 | (with_errors, with_errors_sampl) = proper.prop_run("run_coronagraph_dm", lamda, n, PASSVALUE = {'use_errors': True, 'use_dm': False, 'occulter_type': '8TH_ORDER'}, VERBOSE = False) 24 | 25 | (with_dm, with_dm_sampl) = proper.prop_run("run_coronagraph_dm", lamda, n, PASSVALUE = {'use_errors': True, 'use_dm': True, 'occulter_type': '8TH_ORDER'}, VERBOSE = False) 26 | 27 | nd = 256 28 | psfs = np.zeros([3,nd,nd], dtype = np.float64) 29 | psfs[0,:,:] = no_errors[int(n/2-nd/2):int(n/2+nd/2),int(n/2-nd/2):int(n/2+nd/2)] 30 | psfs[1,:,:] = with_errors[int(n/2-nd/2):int(n/2+nd/2),int(n/2-nd/2):int(n/2+nd/2)] 31 | psfs[2,:,:] = with_dm[int(n/2-nd/2):int(n/2+nd/2),int(n/2-nd/2):int(n/2+nd/2)] 32 | 33 | plt.figure(figsize = (14,7)) 34 | plt.suptitle("PSFs", fontsize = 18, fontweight = 'bold') 35 | 36 | plt.subplot(1,3,1) 37 | plt.title('No errors') 38 | plt.imshow(psfs[0,:,:]**0.25, origin = "lower", cmap = plt.cm.gray) 39 | plt.subplot(1,3,2) 40 | plt.title('With errors') 41 | plt.imshow(psfs[1,:,:]**0.25, origin = "lower", cmap = plt.cm.gray) 42 | plt.subplot(1,3,3) 43 | plt.title('DM corrected') 44 | plt.imshow(psfs[2,:,:]**0.25, origin = "lower", cmap = plt.cm.gray) 45 | plt.show() 46 | 47 | print("Maximum speckle flux / stellar flux :") 48 | print(" No wavefront errors = {0:0.3E}".format(np.max(no_errors), np.min(no_errors))) 49 | print(" With wavefront errors = {0:0.3E}".format(np.max(with_errors))) 50 | print(" With DM correction = {0:0.3E}".format(np.max(with_dm))) 51 | 52 | 53 | if __name__ == '__main__': 54 | coronagraph_demo() 55 | -------------------------------------------------------------------------------- /falco/proper/examples/example_system.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | def example_system(wavelength, gridsize): 14 | 15 | diam = 1. 16 | lens_fl = 20. 17 | beam_ratio = 0.5 18 | 19 | # Define the wavefront 20 | wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) 21 | 22 | if not proper.prop_is_statesaved(wfo): 23 | proper.prop_circular_aperture(wfo, diam/2) 24 | proper.prop_define_entrance(wfo) 25 | proper.prop_lens(wfo, lens_fl, '1st lens') 26 | proper.prop_propagate(wfo, lens_fl, 'intermediate focus') 27 | 28 | proper.prop_state(wfo) 29 | 30 | # we are now at the intermediate focus, so pretend that 31 | # we do something to the wavefront here and continue on 32 | proper.prop_propagate(wfo, lens_fl, 'second lens') 33 | proper.prop_lens(wfo, lens_fl, 'second lens') 34 | (wfo, sampling) = proper.prop_end(wfo) 35 | 36 | return (wfo, sampling) 37 | -------------------------------------------------------------------------------- /falco/proper/examples/hubble_simple.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | 14 | def hubble_simple(wavelength, gridsize, PASSVALUE = {'delta_sec': 0.}): 15 | # Define entrance aperture diameter and other quantities 16 | diam = 2.4 # telescope diameter in meters 17 | fl_pri = 5.52085 # HST primary focal length (m) 18 | d_pri_sec = 4.907028205 # primary to secondary separation (m) 19 | fl_sec = -0.6790325 # HST secondary focal length (m) 20 | d_sec_to_focus = 6.3919974 # nominal distance from secondary to focus 21 | beam_ratio = 0.5 # initial beam width/grid width 22 | 23 | # delta_sec = additional primary-to-secondary separation offset (m) 24 | delta_sec = PASSVALUE['delta_sec'] 25 | 26 | # Define the wavefront 27 | wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) 28 | 29 | # Define a circular aperture 30 | proper.prop_circular_aperture(wfo, diam/2) # HST aperture (primary mirror) 31 | proper.prop_circular_obscuration(wfo, 0.396) # secondary mirror obscuration 32 | proper.prop_rectangular_obscuration(wfo, 0.0264, 2.5) # secondary vane (vertical) 33 | proper.prop_rectangular_obscuration(wfo, 2.5, 0.0264) # secondary vane (horizontal) 34 | proper.prop_circular_obscuration(wfo, 0.078, -0.9066, -0.5538) # primary mirror pad 1 35 | proper.prop_circular_obscuration(wfo, 0.078, 0., 1.0705) # primary mirror pad 2 36 | proper.prop_circular_obscuration(wfo, 0.078, 0.9127, -0.5477) # primary mirror pad 3 37 | 38 | # Define entrance 39 | proper.prop_define_entrance(wfo) 40 | 41 | # Define a lens 42 | proper.prop_lens(wfo, fl_pri, "primary") # primary mirror 43 | 44 | # Propagate the wavefront 45 | proper.prop_propagate(wfo, d_pri_sec+delta_sec, "secondary") 46 | 47 | proper.prop_lens(wfo, fl_sec, "secondary") 48 | 49 | proper.prop_propagate(wfo, d_sec_to_focus+delta_sec, "HST focus", TO_PLANE = False) 50 | 51 | # End 52 | (wfo, sampling) = proper.prop_end(wfo) 53 | 54 | return (wfo, sampling) 55 | -------------------------------------------------------------------------------- /falco/proper/examples/microscope.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | 14 | def microscope(wavelength, gridsize, PASSVALUE = {'focus_offset': 0.}): 15 | 16 | # Define entrance aperture diameter and other quantities 17 | d_objective = 0.005 # objective diameter in meters 18 | fl_objective = 0.010 # objective focal length in meters 19 | fl_eyepiece = 0.020 # eyepiece focal length 20 | fl_eye = 0.022 # human eye focal length 21 | 22 | beam_ratio = 0.4 23 | 24 | # Define the wavefront 25 | wfo = proper.prop_begin(d_objective, wavelength, gridsize, beam_ratio) 26 | 27 | d1 = 0.160 # standard tube length 28 | d_intermediate_image = fl_objective + d1 29 | 30 | # Compute in-focus distance of object from objective 31 | d_object = 1 /(1/fl_objective - 1/d_intermediate_image) 32 | 33 | # Define a circular aperture 34 | proper.prop_circular_aperture(wfo, d_objective/2.) 35 | 36 | # Define entrance 37 | proper.prop_define_entrance(wfo) 38 | 39 | # simulate the diverging wavefront emitted from a point source placed 40 | # "d_object" in front of the objective by using a negative lens (focal 41 | # length = -d_object) placed at the location of the objective 42 | 43 | focus_offset = PASSVALUE['focus_offset'] 44 | 45 | # Define a lens 46 | proper.prop_lens(wfo, -(d_object + focus_offset)) 47 | proper.prop_lens(wfo, fl_objective, "objective") 48 | 49 | # Propagate the wavefront 50 | proper.prop_propagate(wfo, d_intermediate_image, "intermediate image") 51 | proper.prop_propagate(wfo, fl_eyepiece, "eyepiece") 52 | proper.prop_lens(wfo, fl_eyepiece, "eyepiece") 53 | exit_pupil_distance = fl_eyepiece / (1 - fl_eyepiece/(d_intermediate_image+fl_eyepiece)) 54 | proper.prop_propagate(wfo, exit_pupil_distance, "exit pupil/eye") 55 | 56 | proper.prop_lens(wfo, fl_eye, "eye") 57 | proper.prop_propagate(wfo, fl_eye, "retina") 58 | 59 | # End 60 | (wfo, sampling) = proper.prop_end(wfo) 61 | 62 | return (wfo, sampling) 63 | -------------------------------------------------------------------------------- /falco/proper/examples/multi_example.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | 14 | def multi_example(lambda_m, n, PASSVALUE = {'use_dm': False, 'dm': np.zeros([48,48], dtype = np.float64)}): 15 | 16 | diam = 0.048 17 | pupil_ratio = 0.25 18 | fl_lens = 0.48 19 | n_actuators = 48 # number of DM actuators in each dimension 20 | 21 | wfo = proper.prop_begin(diam, lambda_m, n, pupil_ratio) 22 | proper.prop_circular_aperture(wfo, diam/2) 23 | proper.prop_define_entrance(wfo) 24 | 25 | if PASSVALUE['use_dm']: 26 | dm_xc = n_actuators/2. 27 | dm_yc = n_actuators/2. 28 | dm_spacing = 1.e-3 29 | 30 | proper.prop_dm(wfo, PASSVALUE['dm'], dm_xc, dm_yc, dm_spacing) 31 | 32 | proper.prop_lens(wfo, fl_lens) 33 | 34 | proper.prop_propagate(wfo, fl_lens) 35 | 36 | (wfo, sampling) = proper.prop_end(wfo, NOABS = True) 37 | 38 | return (wfo, sampling) 39 | -------------------------------------------------------------------------------- /falco/proper/examples/occulter_demo.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | def occulter_demo(): 14 | 15 | n = 512 # grid size 16 | lamda = 0.55 # wavelength (microns) 17 | 18 | (solid, sampl_solid) = proper.prop_run('run_occulter', lamda, n, PASSVALUE = {"occulter_type": "SOLID"}) 19 | 20 | (gaussian, sampl_gauss) = proper.prop_run('run_occulter', lamda, n, PASSVALUE = {"occulter_type": "GAUSSIAN"}) 21 | 22 | (eighth_order, sampl_eighth_order) = proper.prop_run('run_occulter', lamda, n, PASSVALUE = {"occulter_type": "8TH_ORDER"}) 23 | 24 | return 25 | 26 | if __name__ == '__main__': 27 | occulter_demo() 28 | -------------------------------------------------------------------------------- /falco/proper/examples/psdtest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | 14 | def psdtest(wavelength, gridsize, PASSVALUE = {'usepsdmap': True}): 15 | 16 | lens_diam = 0.212 # 0.212 meter lean diameter 17 | lens_fl = 24. * lens_diam # focal length (f/24 focal ratio) 18 | beam_width_ratio = 0.5 19 | 20 | wfo = proper.prop_begin(lens_diam, wavelength, gridsize, beam_width_ratio) 21 | 22 | # Create circular entrance aperture 23 | proper.prop_circular_aperture(wfo, lens_diam/2) 24 | proper.prop_define_entrance(wfo) 25 | 26 | #-- If the variable usepsdmap is not defined (via optional passed value), 27 | #-- read in and use the map which represents the wavefront error (in this case, 28 | #-- it's in nanometers, hence we need to multiply it by 1e9 to convert it to meters) 29 | #-- and has a sampling of 0.4 mm/pixel. If usepsdmap is defined, then generate 30 | #-- and use a PSD-defined map. The maps have an RMS of about 1.0 nm. 31 | if PASSVALUE['usepsdmap']: 32 | a = 3.29e-23 # low-freq power in m^4 33 | b = 212.26 # correlation length (cycles/m) 34 | c = 7.8 # high-freq falloff (r^-c) 35 | 36 | proper.prop_psd_errormap(wfo, a, b, c) 37 | else: 38 | proper.prop_errormap(wfo, 'errormap.fits', SAMPLING = 0.0004, 39 | MULTIPLY = 1e-9, WAVEFRONT = True) 40 | 41 | 42 | proper.prop_lens(wfo, lens_fl, 'telescope lens') 43 | proper.prop_propagate(wfo, proper.prop_get_distancetofocus(wfo), 'intermediate focus') 44 | 45 | # multiply field by occulting mask with 4*lam/D HWHM transmission 46 | mask = proper.prop_8th_order_mask(wfo, 4, CIRCULAR = True, MASK = True) 47 | 48 | proper.prop_propagate(wfo, lens_fl, 'pupil imaging lens') 49 | proper.prop_lens(wfo, lens_fl, 'pupil imaging lens') 50 | proper.prop_propagate(wfo, lens_fl, 'lyot stop') 51 | proper.prop_circular_aperture(wfo, 0.53, NORM = True) # Lyot stop 52 | 53 | proper.prop_propagate(wfo, lens_fl, 'reimaging lens') 54 | proper.prop_lens(wfo, lens_fl, 'reimaging lens') 55 | proper.prop_propagate(wfo, proper.prop_get_distancetofocus(wfo), 'final focus') 56 | 57 | (wfo, sampling) = proper.prop_end(wfo) 58 | 59 | return (wfo, sampling) 60 | -------------------------------------------------------------------------------- /falco/proper/examples/run_coronagraph.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | from telescope import telescope 13 | from coronagraph import coronagraph 14 | 15 | 16 | def run_coronagraph(wavelength, grid_size, PASSVALUE = {'use_errors': False, 'occulter_type': 'GAUSSIAN'}): 17 | 18 | diam = 0.1 # telescope diameter in meters 19 | f_lens = 24 * diam 20 | 21 | beam_ratio = 0.3 22 | 23 | # Define the wavefront 24 | wfo = proper.prop_begin(diam, wavelength, grid_size, beam_ratio) 25 | 26 | # Circular aperture 27 | proper.prop_circular_aperture(wfo, diam/2) 28 | proper.prop_define_entrance(wfo) 29 | 30 | # Define telescope optical assembly 31 | telescope(wfo, f_lens, PASSVALUE['use_errors']) 32 | 33 | # Coronagraph 34 | coronagraph(wfo, f_lens, PASSVALUE['occulter_type'], diam) 35 | 36 | # End 37 | (wfo, sampling) = proper.prop_end(wfo) 38 | 39 | return (wfo, sampling) 40 | -------------------------------------------------------------------------------- /falco/proper/examples/run_coronagraph_dm.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | from telescope_dm import telescope_dm 13 | from coronagraph import coronagraph 14 | 15 | 16 | def run_coronagraph_dm(wavelength, grid_size, PASSVALUE = {'use_errors': False, 'use_dm': False, 'occulter_type': 'GAUSSIAN'}): 17 | 18 | diam = 0.1 # telescope diameter in meters 19 | f_lens = 24 * diam 20 | 21 | beam_ratio = 0.3 22 | 23 | # Define the wavefront 24 | wfo = proper.prop_begin(diam, wavelength, grid_size, beam_ratio) 25 | 26 | # Circular aperture 27 | proper.prop_circular_aperture(wfo, diam/2) 28 | proper.prop_define_entrance(wfo) 29 | 30 | # Define telescope optical assembly 31 | telescope_dm(wfo, f_lens, PASSVALUE["use_errors"], PASSVALUE["use_dm"]) 32 | 33 | # Coronagraph 34 | coronagraph(wfo, f_lens, PASSVALUE["occulter_type"], diam) 35 | 36 | # End 37 | (wfo, sampling) = proper.prop_end(wfo) 38 | 39 | return (wfo, sampling) 40 | -------------------------------------------------------------------------------- /falco/proper/examples/run_example.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | def run_example(wavelength, gridsize): 14 | 15 | proper.prop_init_savestate() 16 | 17 | for i in range(11): 18 | (psf, sampling) = proper.prop_run('example_system', wavelength, gridsize) 19 | 20 | #-- let us pretend that we now do something useful with 21 | #-- this iteration's PSF and then compute another 22 | 23 | proper.prop_end_savestate() 24 | 25 | if __name__ == '__main__': 26 | run_example(0.5, 512) 27 | -------------------------------------------------------------------------------- /falco/proper/examples/run_occulter.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | from telescope import telescope 13 | from coronagraph import coronagraph 14 | 15 | 16 | def run_occulter(wavelength, grid_size, PASSVALUE = {'occulter_type': 'GAUSSIAN'}): 17 | 18 | diam = 0.1 # telescope diameter in meters 19 | f_lens = 24 * diam 20 | 21 | beam_ratio = 0.3 22 | 23 | # Define the wavefront 24 | wfo = proper.prop_begin(diam, wavelength, grid_size, beam_ratio) 25 | 26 | # Circular aperture 27 | proper.prop_circular_aperture(wfo, diam/2) 28 | proper.prop_define_entrance(wfo) 29 | 30 | use_errors = False 31 | use_dm = False 32 | 33 | # Coronagraph 34 | coronagraph(wfo, f_lens, PASSVALUE["occulter_type"], diam) 35 | 36 | # End 37 | (wfo, sampling) = proper.prop_end(wfo) 38 | 39 | return (wfo, sampling) 40 | -------------------------------------------------------------------------------- /falco/proper/examples/simple_prescription.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | 14 | def simple_prescription(wavelength, gridsize): 15 | 16 | # Define entrance aperture diameter and other quantities 17 | diam = 1.0 18 | focal_ratio = 15.0 19 | focal_length = diam * focal_ratio 20 | beam_ratio = 0.5 21 | 22 | # Define the wavefront 23 | wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) 24 | 25 | # Define a circular aperture 26 | proper.prop_circular_aperture(wfo, diam/2) 27 | 28 | # Define entrance 29 | proper.prop_define_entrance(wfo) 30 | 31 | # Define a lens 32 | proper.prop_lens(wfo, focal_length) 33 | 34 | # Propagate the wavefront 35 | proper.prop_propagate(wfo, focal_length) 36 | 37 | # End 38 | (wfo, sampling) = proper.prop_end(wfo) 39 | 40 | return (wfo, sampling) 41 | -------------------------------------------------------------------------------- /falco/proper/examples/simple_telescope.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | 14 | def simple_telescope(wavelength, gridsize): 15 | 16 | # Define entrance aperture diameter and other quantities 17 | d_objective = 0.060 # objective diameter in meters 18 | fl_objective = 15.0 * d_objective # objective focal length in meters 19 | fl_eyepiece = 0.021 # eyepiece focal length 20 | fl_eye = 0.022 # human eye focal length 21 | beam_ratio = 0.5 # initial beam width/grid width 22 | 23 | # Define the wavefront 24 | wfo = proper.prop_begin(d_objective, wavelength, gridsize, beam_ratio) 25 | 26 | # Define a circular aperture 27 | proper.prop_circular_aperture(wfo, d_objective/2) 28 | 29 | # Define entrance 30 | proper.prop_define_entrance(wfo) 31 | 32 | # Define a lens 33 | proper.prop_lens(wfo, fl_objective, "objective") 34 | 35 | # Propagate the wavefront 36 | proper.prop_propagate(wfo, fl_objective+fl_eyepiece, "eyepiece") 37 | 38 | # Define another lens 39 | proper.prop_lens(wfo, fl_eyepiece, "eyepiece") 40 | 41 | exit_pupil_distance = fl_eyepiece / (1 - fl_eyepiece/(fl_objective+fl_eyepiece)) 42 | proper.prop_propagate(wfo, exit_pupil_distance, "exit pupil at eye lens") 43 | 44 | proper.prop_lens(wfo, fl_eye, "eye") 45 | proper.prop_propagate(wfo, fl_eye, "retina") 46 | 47 | # End 48 | (wfo, sampling) = proper.prop_end(wfo) 49 | 50 | return (wfo, sampling) 51 | -------------------------------------------------------------------------------- /falco/proper/examples/talbot.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | 14 | 15 | def talbot(wavelength, gridsize, PASSVALUE = {'period': 0., 'diam': 0., 'dist': 0.}): 16 | talbot_length = 2. * PASSVALUE['period']**2 / wavelength 17 | 18 | wfo = proper.prop_begin(PASSVALUE['diam'], wavelength, gridsize) 19 | 20 | # create 1-D grating pattern 21 | m = 0.2 22 | x = (np.arange(gridsize, dtype = np.float64) - gridsize/2) \ 23 | * proper.prop_get_sampling(wfo) 24 | 25 | grating = 0.5 * (1 + m * np.cos(2*np.pi*x/PASSVALUE['period'])) 26 | 27 | # create 2-D amplitude grating pattern 28 | grating = np.dot(grating.reshape(gridsize,1), np.ones([1,gridsize], dtype = np.float64)) 29 | 30 | proper.prop_multiply(wfo, grating) 31 | 32 | proper.prop_define_entrance(wfo) 33 | 34 | proper.prop_propagate(wfo, PASSVALUE['dist'], TO_PLANE = True) 35 | 36 | (wfo, sampling) = proper.prop_end(wfo, NOABS = True) 37 | 38 | return (wfo, sampling) 39 | -------------------------------------------------------------------------------- /falco/proper/examples/talbot_correct.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | 14 | 15 | def talbot_correct(wavelength, gridsize, PASSVALUE = {'period': 0., 'diam': 0., 'dist': 0.}): 16 | talbot_length = 2. * PASSVALUE['period']**2 / wavelength 17 | 18 | wfo = proper.prop_begin(PASSVALUE['diam'], wavelength, gridsize) 19 | 20 | # Create 1-D grating pattern 21 | m = 0.2 22 | x = (np.arange(gridsize, dtype = np.float64) - gridsize/2) \ 23 | * proper.prop_get_sampling(wfo) 24 | 25 | grating = 0.5 * (1 + m * np.cos(2*np.pi*x/PASSVALUE['period'])) 26 | 27 | # create 2-D amplitude grating pattern 28 | grating = np.dot(grating.reshape(gridsize,1), np.ones([1,gridsize], dtype = np.float64)) 29 | 30 | proper.prop_multiply(wfo, grating) 31 | 32 | proper.prop_define_entrance(wfo) 33 | 34 | if PASSVALUE['dist'] <= 0.25*talbot_length: 35 | proper.prop_propagate(wfo, PASSVALUE['dist'], TO_PLANE = True) 36 | else: 37 | proper.prop_propagate(wfo, 0.25*talbot_length, TO_PLANE = True) 38 | phase = proper.prop_get_phase(wfo) # in radians 39 | phase *= wavelength / (2 * np.pi) # in meters 40 | proper.prop_add_phase(wfo, -phase) 41 | proper.prop_propagate(wfo, PASSVALUE['dist'] - 0.25*talbot_length, TO_PLANE = True) 42 | 43 | (wfo, sampling) = proper.prop_end(wfo, NOABS = True) 44 | 45 | return (wfo, sampling) 46 | -------------------------------------------------------------------------------- /falco/proper/examples/talbot_correct_demo.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | import matplotlib.pylab as plt 14 | 15 | def talbot_correct_demo(): 16 | diam = 0.1 # beam diameter in meters 17 | period = 0.04 # period of cosine pattern (meters) 18 | wavelength_microns = 0.5 19 | wavelength_m = wavelength_microns * 1.e-6 20 | n = 128 21 | 22 | nseg = 9 23 | talbot_length = 2 * period**2 / wavelength_m 24 | delta_length = talbot_length / (nseg - 1.) 25 | 26 | z = 0. 27 | 28 | plt.close('all') 29 | f = plt.figure(figsize = (8, 18)) 30 | 31 | for i in range(nseg): 32 | (wavefront, sampling) = proper.prop_run('talbot_correct', 33 | wavelength_microns, n, 34 | PASSVALUE = {'diam': diam, 'period': period, 'dist': z}) 35 | 36 | # Extract central cross-section of array 37 | wavefront = wavefront[:,n//2] 38 | 39 | amp = np.abs(wavefront) 40 | amp -= np.mean(amp) 41 | phase = np.arctan2(wavefront.imag, wavefront.real) 42 | phase -= np.mean(phase) 43 | 44 | ax1 = f.add_subplot(nseg,2,2*i+1) 45 | if i == 0: 46 | ax1.set_title('Amplitude') 47 | ax1.set_ylim(-0.0015, 0.0015) 48 | ax1.plot(amp) 49 | ax2 = f.add_subplot(nseg,2,2*i+2) 50 | if i == 0: 51 | ax2.set_title('Phase') 52 | ax2.set_ylim(-0.25, 0.25) 53 | ax2.plot(phase) 54 | 55 | z += delta_length 56 | 57 | plt.show() 58 | 59 | return 60 | 61 | 62 | if __name__ == '__main__': 63 | talbot_correct_demo() 64 | -------------------------------------------------------------------------------- /falco/proper/examples/talbot_demo.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | 15 | def talbot_demo(): 16 | diam = 0.1 # beam diameter in meters 17 | period = 0.04 # period of cosine pattern (meters) 18 | wavelength_microns = 0.5 19 | wavelength_m = wavelength_microns * 1.e-6 20 | n = 128 21 | 22 | nseg = 9 23 | talbot_length = 2 * period**2 / wavelength_m 24 | delta_length = talbot_length / (nseg - 1.) 25 | 26 | z = 0. 27 | 28 | plt.close('all') 29 | f = plt.figure(figsize = (8, 18)) 30 | 31 | for i in range(nseg): 32 | (wavefront, sampling) = proper.prop_run('talbot', 33 | wavelength_microns, n, 34 | PASSVALUE = {'diam': diam, 'period': period, 'dist': z}) 35 | 36 | # Extract central cross-section of array 37 | wavefront = wavefront[:,n//2] 38 | 39 | amp = np.abs(wavefront) 40 | amp -= np.mean(amp) 41 | phase = np.arctan2(wavefront.imag, wavefront.real) 42 | phase -= np.mean(phase) 43 | 44 | ax1 = f.add_subplot(nseg,2,2*i+1) 45 | if i == 0: 46 | ax1.set_title('Amplitude') 47 | ax1.set_ylim(-0.0015, 0.0015) 48 | ax1.plot(amp) 49 | ax2 = f.add_subplot(nseg,2,2*i+2) 50 | if i == 0: 51 | ax2.set_title('Phase') 52 | ax2.set_ylim(-0.25, 0.25) 53 | ax2.plot(phase) 54 | 55 | z += delta_length 56 | 57 | f.tight_layout() 58 | plt.show() 59 | 60 | return 61 | 62 | 63 | if __name__ == '__main__': 64 | talbot_demo() 65 | -------------------------------------------------------------------------------- /falco/proper/examples/telescope.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | 13 | 14 | def telescope(wfo, f_lens, use_errors, use_dm = False): 15 | 16 | if use_errors: 17 | rms_error = 10.e-9 # RMS wavefront error in meters 18 | c_freq = 15. # correlation frequency (cycles/meter) 19 | high_power = 3. # high frewquency falloff (r^-high_power) 20 | 21 | proper.prop_psd_errormap(wfo, rms_error, c_freq, high_power, RMS = True, MAP = "obj_map", FILE = "telescope_obj.fits") 22 | 23 | proper.prop_lens(wfo, f_lens, "objective") 24 | 25 | # propagate through focus to pupil 26 | proper.prop_propagate(wfo, f_lens*2, "telescope pupil imaging lens") 27 | 28 | proper.prop_lens(wfo, f_lens, "telescope pupil imaging lens") 29 | 30 | # propagate to a deformable mirror (to be inserted later) 31 | proper.prop_propagate(wfo, f_lens, "DM") 32 | 33 | proper.prop_propagate(wfo, f_lens, "coronagraph lens") 34 | 35 | return 36 | -------------------------------------------------------------------------------- /falco/proper/examples/telescope_dm.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | 14 | 15 | def telescope_dm(wfo, f_lens, use_errors, use_dm): 16 | 17 | if use_errors: 18 | rms_error = 10.e-9 # RMS wavefront error in meters 19 | c_freq = 15. # correlation frequency (cycles/meter) 20 | high_power = 3. # high frewquency falloff (r^-high_power) 21 | 22 | obj_map = proper.prop_psd_errormap(wfo, rms_error, c_freq, high_power, RMS = True, MAP = "obj_map", FILE = "telescope_obj.fits") 23 | 24 | proper.prop_lens(wfo, f_lens, "objective") 25 | 26 | # propagate through focus to pupil 27 | proper.prop_propagate(wfo, f_lens*2, "telescope pupil imaging lens") 28 | proper.prop_lens(wfo, f_lens, "telescope pupil imaging lens") 29 | proper.prop_propagate(wfo, f_lens, "DM") 30 | 31 | if use_dm: 32 | nact = 49 # number of DM actuators along one axis 33 | nact_across_pupil = 47 # number of DM actuators across pupil 34 | dm_xc = nact // 2 35 | dm_yc = nact // 2 36 | d_beam = 2 * proper.prop_get_beamradius(wfo) # beam diameter 37 | act_spacing = d_beam / nact_across_pupil # actuator spacing 38 | map_spacing = proper.prop_get_sampling(wfo) # map sampling 39 | 40 | # have passed through focus, so pupil has rotated 180 deg; 41 | # need to rotate error map (also need to shift due to the way 42 | # the rotate() function operates to recenter map) 43 | obj_map = np.roll(np.roll(np.rot90(obj_map, 2), 1, 0), 1, 1) 44 | 45 | # interpolate map to match number of DM actuators 46 | dm_map = proper.prop_magnify(obj_map, map_spacing/act_spacing, nact, QUICK = True) 47 | 48 | # Need to put on opposite pattern; convert wavefront error to surface height 49 | proper.prop_dm(wfo, -dm_map/2, dm_xc, dm_yc, act_spacing, FIT = True) 50 | 51 | proper.prop_propagate(wfo, f_lens, "coronagraph lens") 52 | 53 | return 54 | -------------------------------------------------------------------------------- /falco/proper/examples/testmulti1.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | 14 | 15 | def testmulti1(): 16 | lambda_min = 0.5 17 | lambda_max = 0.7 18 | nlambda = 9 19 | gridsize = 256 20 | npsf = 256 21 | final_sampling = 1.5e-6 22 | 23 | # generate array of wavelengths 24 | wavelength = np.arange(nlambda) / (nlambda - 1.) * (lambda_max - lambda_min) + lambda_min 25 | 26 | # Create DM pattern (a couple of 0.1 micron pokes) 27 | optval = {'use_dm': True, 'dm': np.zeros([48,48], dtype = np.float64)} 28 | optval['dm'][20,20] = 0.2e-6 29 | optval['dm'][15,25] = 0.2e-6 30 | 31 | # generate monchromatic fields in parallel 32 | (fields, sampling) = proper.prop_run_multi('multi_example', wavelength, gridsize, PASSVALUE = optval) 33 | 34 | # resample fields to same scale, convert to PSFs 35 | psfs = np.zeros([nlambda, npsf, npsf], dtype = np.float64) 36 | for i in range(nlambda): 37 | mag = sampling[i] / final_sampling 38 | field = proper.prop_magnify(fields[i,:,:], mag, npsf, CONSERVE = True) 39 | psfs[i,:,:] = np.abs(field)**2 40 | 41 | # add PSFs together 42 | psf = np.sum(psfs, axis = 0) / nlambda 43 | 44 | return 45 | 46 | 47 | if __name__ == '__main__': 48 | testmulti1() 49 | -------------------------------------------------------------------------------- /falco/proper/examples/testmulti2.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import proper 12 | import numpy as np 13 | 14 | 15 | def testmulti2(): 16 | wavelength = 0.6 17 | gridsize = 256 18 | 19 | # create different DM ripple patterns (50 nm amplitude) 20 | npatterns = 3 21 | optval = [] 22 | for i in range(npatterns): 23 | optval.append = {'use_dm': True, 'dm': np.zeros([48,48], dtype=np.float64)} 24 | 25 | x = np.dot((np.arange(48.)/47 * (2*np.pi)).reshape(48,1), np.ones([1,48], dtype = np.float64)) 26 | 27 | for i in range(npatterns): 28 | optval[i]['dm'] = 5.e-8 * np.cos(4*x*(i+1)) 29 | 30 | # generate monochromatic field in parallel 31 | (fields, sampling) = proper.prop_run_multi('multi_example', wavelength, gridsize, PASSVALUE = optval) 32 | 33 | return 34 | 35 | if __name__ == '__main__': 36 | testmulti2() 37 | -------------------------------------------------------------------------------- /falco/proper/influence_dm5v2_1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/falco/proper/influence_dm5v2_1.fits -------------------------------------------------------------------------------- /falco/proper/libcconv.py: -------------------------------------------------------------------------------- 1 | def __bootstrap__(): 2 | global __bootstrap__, __loader__, __file__ 3 | import sys, pkg_resources, imp 4 | __file__ = pkg_resources.resource_filename(__name__, 'libcconv.cpython-36m-x86_64-linux-gnu.so') 5 | __loader__ = None; del __bootstrap__, __loader__ 6 | imp.load_dynamic(__name__,__file__) 7 | __bootstrap__() 8 | -------------------------------------------------------------------------------- /falco/proper/libcconvthread.py: -------------------------------------------------------------------------------- 1 | def __bootstrap__(): 2 | global __bootstrap__, __loader__, __file__ 3 | import sys, pkg_resources, imp 4 | __file__ = pkg_resources.resource_filename(__name__, 'libcconvthread.cpython-36m-x86_64-linux-gnu.so') 5 | __loader__ = None; del __bootstrap__, __loader__ 6 | imp.load_dynamic(__name__,__file__) 7 | __bootstrap__() 8 | -------------------------------------------------------------------------------- /falco/proper/libszoom.py: -------------------------------------------------------------------------------- 1 | def __bootstrap__(): 2 | global __bootstrap__, __loader__, __file__ 3 | import sys, pkg_resources, imp 4 | __file__ = pkg_resources.resource_filename(__name__, 'libszoom.cpython-36m-x86_64-linux-gnu.so') 5 | __loader__ = None; del __bootstrap__, __loader__ 6 | imp.load_dynamic(__name__,__file__) 7 | __bootstrap__() 8 | -------------------------------------------------------------------------------- /falco/proper/prop_add_phase.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified 18 April 2019 by J. Krist (switched to *=) 10 | 11 | 12 | import falco.proper as proper 13 | import numpy as np 14 | 15 | 16 | def prop_add_phase(wf, phase_error): 17 | """Add a phase error map or value to the current wavefront array. 18 | 19 | The phase error array is assumed to be at the same sampling as the current 20 | wavefront. Note that this is wavefront, not surface error. 21 | 22 | Parameters 23 | ---------- 24 | wf : object 25 | WaveFront class object 26 | 27 | phase_error : numpy ndarray 28 | A scalar or 2D image containing the phase error in meters 29 | 30 | Returns 31 | ------- 32 | None 33 | """ 34 | i = complex(0., 1.) 35 | 36 | if type(phase_error) != np.ndarray and type(phase_error) != list: 37 | phase_error = float(phase_error) 38 | wf.wfarr *= np.exp(2*np.pi*i/wf.lamda*phase_error) 39 | else: 40 | phase_error = np.asarray(phase_error) 41 | wf.wfarr *= np.exp(2*np.pi*i/wf.lamda*proper.prop_shift_center(phase_error)) 42 | 43 | return 44 | -------------------------------------------------------------------------------- /falco/proper/prop_add_wavefront.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_add_wavefront(wfo, wf): 16 | """Add a wavefront the current wavefront array. 17 | 18 | The wavefront array is assumed to be at the same sampling as the current 19 | wavefront. 20 | 21 | Parameters 22 | ---------- 23 | wfo : object 24 | WaveFront class object 25 | 26 | wf : numpy ndarray 27 | A scalar or 2D image containing the value or wavefront to add. 28 | NOTE: All responsibility is on the user to ensure that the two fields 29 | have the same sampling and reference phase curvature. NO CHECKING is 30 | done by this routine. 31 | 32 | Returns 33 | ------- 34 | None 35 | """ 36 | if type(wf) != np.ndarray and type(wf) != list: 37 | wfo.wfarr += wf 38 | else: 39 | wfo.wfarr += proper.prop_shift_center(wf) 40 | 41 | return 42 | -------------------------------------------------------------------------------- /falco/proper/prop_begin.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019 - moved search for FFTW wisdom file 10 | # from prop_fftw to here to avoid redundant searches 11 | 12 | import falco.proper as proper 13 | import numpy as np 14 | 15 | def prop_begin(beam_diameter, lamda, grid_n, beam_diam_fraction = 0.5): 16 | """Initialize variables for PROPER routines. 17 | 18 | This routine must be called before any other PROPER routines in order to 19 | initialize required variables. 20 | 21 | Parameters 22 | ---------- 23 | beam_diameter : float 24 | Initial diameter of beam in meters 25 | 26 | lamda : float 27 | Wavelength in meters 28 | 29 | grid_n : int 30 | Wavefront gridsize in pixels (n by n) 31 | 32 | beam_diam_fraction : float 33 | Fraction of the grid width corresponding to the beam diameter. If not 34 | specified, it is assumed to be 0.5. 35 | 36 | Returns 37 | ------- 38 | wf : numpy ndarray 39 | Initialized wavefront array structure created by this routine 40 | """ 41 | grid_n = int(grid_n) 42 | proper.n = grid_n 43 | 44 | ndiam = grid_n * beam_diam_fraction 45 | proper.ndiam = ndiam 46 | 47 | diam = float(beam_diameter) 48 | 49 | w0 = diam / 2. 50 | z_ray = np.pi * w0**2 / lamda 51 | 52 | proper.rayleigh_factor = 1. 53 | proper.old_opd = 0. 54 | 55 | nlist = 1500 56 | 57 | if proper.use_fftw == True and proper.use_ffti == False: 58 | proper.prop_load_fftw_wisdom( grid_n, proper.fft_nthreads ) 59 | 60 | # Create WaveFront object 61 | wf = proper.WaveFront(diam, ndiam, lamda, grid_n, w0, z_ray) 62 | 63 | if proper.do_table: 64 | proper.lens_fl_list = np.zeros(nlist, dtype = np.float64) # list of lens focal lengths 65 | proper.lens_eff_fratio_list = np.zeros(nlist, dtype = np.float64) # list of effective fratios after each lens 66 | proper.beam_diam_list = np.zeros(nlist, dtype = np.float64) # list of beam diameters at each lens 67 | proper.distance_list = np.zeros(nlist, dtype = np.float64) # list of propagation distances 68 | proper.surface_name_list = np.zeros(nlist, dtype = "S25") # list of surface names 69 | proper.sampling_list = np.zeros(nlist, dtype = np.float64) # list of sampling at each surface 70 | proper.action_num = 0 71 | 72 | return wf 73 | -------------------------------------------------------------------------------- /falco/proper/prop_circular_aperture.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_circular_aperture(wf, radius, xc = 0.0, yc = 0.0, **kwargs): 15 | """Multiply the wavefront by a circular clear aperture. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | radius : float 23 | Radius of aperture in meters, unless norm is specified 24 | 25 | xc : float 26 | X-center of aperture relative to center of wavefront. Default is 0.0 27 | 28 | yc : float 29 | Y-center of aperture relative to center of wavefront. Default is 0.0 30 | 31 | 32 | Returns 33 | ------- 34 | numpy ndarray: 35 | Multiplies current wavefront in wf object by a circular aperture. 36 | 37 | 38 | Other Parameters 39 | ----------------- 40 | NORM : bool 41 | If set to True, the specified radius and xc, yc aperure centers are 42 | assumed to be normalized to the current beam radius (e.g. radius is 1.0 43 | means the aperture is the same size as the current beam). xc, yc = 0,0 44 | is the center of the wavefront. Default is False. 45 | """ 46 | 47 | norm = proper.switch_set("NORM", **kwargs) 48 | 49 | wf.wfarr *= proper.prop_shift_center(proper.prop_ellipse(wf, radius, radius, xc, yc, NORM = norm)) 50 | 51 | return 52 | -------------------------------------------------------------------------------- /falco/proper/prop_circular_obscuration.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_circular_obscuration(wf, radius, xc = 0.0, yc = 0.0, **kwargs): 15 | """Multiply the wavefront by a circular obscuration. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | radius : float 23 | Radius of aperture in meters, unless norm is specified 24 | 25 | xc : float 26 | X-center of aperture relative to center of wavefront. Default is 0.0 27 | 28 | yc : float 29 | Y-center of aperture relative to center of wavefront. Default is 0.0 30 | 31 | 32 | Returns 33 | ------- 34 | Multiplies current wavefront in "wf" by a circular obscuration (0 inside, 35 | 1 outside). 36 | 37 | 38 | Other Parameters 39 | ---------------- 40 | NORM : bool 41 | If set to True, the specified radius and xc, yc aperure centers are 42 | assumed to be normalized to the current beam radius (e.g. radius is 1.0 43 | means the aperture is the same size as the current beam). xc, yc = 0,0 44 | is the center of the wavefront. Default is False. 45 | """ 46 | 47 | norm = proper.switch_set("NORM",**kwargs) 48 | wf.wfarr *= proper.prop_shift_center(proper.prop_ellipse(wf, radius, radius, xc, yc, NORM = norm, DARK = True)) 49 | 50 | return 51 | -------------------------------------------------------------------------------- /falco/proper/prop_compile_c.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import os 12 | import falco.proper as proper 13 | import subprocess 14 | 15 | 16 | def prop_compile_c(): 17 | """Compile cubic convoluton interpolation and szoom C modules. 18 | 19 | Parameters 20 | ---------- 21 | None 22 | 23 | Returns 24 | ------- 25 | None 26 | """ 27 | if os.name == 'posix': 28 | # Compile and flags 29 | if proper.system == 'Linux': 30 | CC = 'gcc' 31 | FLAGS = ['-shared', '-fPIC'] 32 | elif proper.system == 'Darwin': 33 | CC = 'gcc' 34 | FLAGS = ['-shared', '-framework Python'] 35 | 36 | # Compile cubic convolution interpolation C code 37 | print('Compiling ' + proper.cubic_conv_c + ' ...') 38 | cmd = CC + ' ' + proper.cubic_conv_c + ' -o ' + proper.cubic_conv_lib 39 | for flag in FLAGS: 40 | cmd += ' ' + flag 41 | subprocess.call(cmd, shell = True) 42 | 43 | # Compile threaded cubic convolution interpolation C code 44 | print('Compiling ' + proper.cubic_conv_threaded_c + ' ...') 45 | cmd = CC + ' ' + proper.cubic_conv_threaded_c + ' -o ' + proper.cubic_conv_threaded_lib 46 | for flag in FLAGS: 47 | cmd += ' ' + flag 48 | subprocess.call(cmd, shell = True) 49 | 50 | # Compile szoom C code 51 | print('Compiling ' + proper.szoom_c + ' ...') 52 | cmd = CC + ' ' + proper.szoom_c + ' -o ' + proper.szoom_c_lib 53 | for flag in FLAGS: 54 | cmd += ' ' + flag 55 | subprocess.call(cmd, shell = True) 56 | elif os.name == 'nt': 57 | print('PROP_COMPILE_C: Cubic convolution interpolation C modules only available for Linux and Mac.') 58 | else: 59 | print('PROP_COMPILE_C: Unknown operating system type = ' + proper.system) 60 | 61 | return 62 | -------------------------------------------------------------------------------- /falco/proper/prop_define_entrance.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_define_entrance(wf): 16 | """Establish entrance aperture function for later use by pyPROPER routines. 17 | 18 | The input image describes the entrance aperture amplitude (0 to 1). This 19 | routine then normalizes the wavefront to have a total intensity of one. 20 | 21 | Parameters 22 | ---------- 23 | wf : obj 24 | WaveFront class object 25 | 26 | Returns 27 | ------- 28 | None 29 | """ 30 | total_original_pupil = np.sum(np.abs(wf.wfarr)**2) 31 | proper.total_original_pupil = total_original_pupil 32 | 33 | wf.wfarr /= np.sqrt(total_original_pupil) 34 | 35 | return 36 | -------------------------------------------------------------------------------- /falco/proper/prop_divide.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist on 19 April 2019: changed to /= 10 | 11 | 12 | 13 | import falco.proper as proper 14 | import numpy as np 15 | 16 | 17 | def prop_divide(wf, value): 18 | """Divide the current wavefront amplitude by a user-provided value or array. 19 | 20 | Parameters 21 | ---------- 22 | wf : obj 23 | The current WaveFront class object 24 | 25 | value : numpy ndarray 26 | Either a scalar or a 2-D array containing the amplitude map by which 27 | the current wavefront will be divided. The map is assumed to be 28 | centered at pixel (n/2,n/2). 29 | 30 | Returns 31 | ------- 32 | None 33 | """ 34 | if type(value) != np.ndarray and type(value) != list: 35 | value = float(value) 36 | wf.wfarr /= complex(value, 0.) 37 | else: 38 | value = np.asarray(value) 39 | wf.wfarr /= proper.prop_shift_center(value) 40 | 41 | return 42 | -------------------------------------------------------------------------------- /falco/proper/prop_elliptical_aperture.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_elliptical_aperture(wf, xradius, yradius, xc = 0.0, yc = 0.0, **kwargs): 15 | """Multiply the wavefront by an elliptical clear aperture. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | xradius : float 23 | X ellipse radius in meters, unless norm is specified 24 | 25 | yradius : float 26 | Y ellipse radius in meters, unless norm is specified 27 | 28 | xc : float 29 | X-center of aperture relative to center of wavefront. Default is 0.0 30 | 31 | yc : float 32 | Y-center of aperture relative to center of wavefront. Default is 0.0 33 | 34 | 35 | Returns 36 | ------- 37 | None 38 | Multiplies current wavefront in wf object by an elliptical aperture 39 | 40 | 41 | Other Parameters 42 | ---------------- 43 | NORM : bool 44 | If set to True, the specified radii and aperure center are assumed to 45 | be normalized to the current beam radius. Default is False. 46 | 47 | ROTATION : float 48 | Angle in degrees to rotate the ellipse about its center. 49 | """ 50 | 51 | norm = proper.switch_set("NORM",**kwargs) 52 | 53 | if "ROTATION" in kwargs: 54 | rotation = kwargs["ROTATION"] 55 | else: 56 | rotation = 0.0 57 | 58 | wf.wfarr *= proper.prop_shift_center(proper.prop_ellipse(wf, xradius, yradius, xc, yc, ROTATION=rotation, NORM=norm)) 59 | 60 | return 61 | -------------------------------------------------------------------------------- /falco/proper/prop_elliptical_obscuration.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_elliptical_obscuration(wf, xradius, yradius, xc = 0.0, yc = 0.0, **kwargs): 15 | """Multiply the wavefront by an elliptical obscuration 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | xradius : float 23 | X ellipse radius in meters, unless norm is specified 24 | 25 | yradius : float 26 | Y ellipse radius in meters, unless norm is specified 27 | 28 | xc : float 29 | X-center of aperture relative to center of wavefront. Default is 0.0 30 | 31 | yc : float 32 | Y-center of aperture relative to center of wavefront. Default is 0.0 33 | 34 | 35 | Returns 36 | ------- 37 | None 38 | Multiplies current wavefront in wf object by an elliptical aperture 39 | 40 | 41 | Other Parameters 42 | ---------------- 43 | NORM : bool 44 | If set to True, the specified radii and obscuration center are assumed to 45 | be normalized to the current beam radius. Default is False. 46 | 47 | ROTATION : float 48 | Angle in degrees to rotate the ellipse about its center. 49 | """ 50 | 51 | norm = proper.switch_set("NORM",**kwargs) 52 | 53 | if "ROTATION" in kwargs: 54 | rotation = kwargs["ROTATION"] 55 | else: 56 | rotation = 0.0 57 | 58 | wf.wfarr *= proper.prop_shift_center(proper.prop_ellipse(wf, xradius, yradius, xc, yc, DARK=True, ROTATION=rotation, NORM=norm)) 59 | 60 | return 61 | -------------------------------------------------------------------------------- /falco/proper/prop_end.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_end(wf, **kwargs): 16 | """Set variables needed to properly conclude a propagation run. 17 | 18 | Parameters 19 | ---------- 20 | wf : obj 21 | The current WaveFront class object 22 | 23 | 24 | Returns 25 | ------- 26 | wf.wfarr : numpy ndarray 27 | Wavefront array 28 | 29 | sampling : float 30 | Sampling in meters 31 | 32 | 33 | Other Parameters 34 | ---------------- 35 | EXTRACT : int 36 | Returns the dx by dx pixel central portion of the wavefront. 37 | 38 | NOABS : bool 39 | If set, the complex-values wavefront field is returned. By default, the 40 | intensity (modulus squared) of the field is returned. 41 | """ 42 | sampling = proper.prop_get_sampling(wf) 43 | 44 | if proper.switch_set("NOABS",**kwargs): 45 | wf.wfarr = proper.prop_shift_center(wf.wfarr) 46 | else: 47 | wf.wfarr = proper.prop_shift_center(np.abs(wf.wfarr)**2) 48 | 49 | if "EXTRACT" in kwargs: 50 | EXTRACT = kwargs["EXTRACT"] 51 | ny, nx = wf.wfarr.shape 52 | wf.wfarr = wf.wfarr[ny/2-EXTRACT/2:ny/2+EXTRACT/2,nx/2-EXTRACT/2:nx/2+EXTRACT/2] 53 | 54 | return (wf.wfarr, sampling) 55 | -------------------------------------------------------------------------------- /falco/proper/prop_end_savestate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import os 12 | import falco.proper as proper 13 | from glob import glob 14 | 15 | 16 | def prop_end_savestate(): 17 | """Terminate the current save state system. 18 | 19 | This deletes the files created by prop_state/prop_savestate. 20 | 21 | Parameters 22 | ---------- 23 | None 24 | 25 | Returns 26 | ------- 27 | None 28 | """ 29 | proper.save_state = 0 30 | proper.save_state_lam = [] 31 | 32 | statefile = proper.statefile 33 | 34 | sfiles = glob('*' + statefile) 35 | if len(sfiles) > 0: 36 | for sfile in sfiles: 37 | os.remove(sfile) 38 | 39 | return 40 | -------------------------------------------------------------------------------- /falco/proper/prop_execute_multi.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019 - added IS_MULTI=1 to prop_run to 10 | # tell it that it is running run prop_run_multi. 11 | 12 | 13 | import falco.proper as proper 14 | 15 | 16 | def prop_execute_multi(params): 17 | """Execute a PROPER prescription from within an prop_run_multi. 18 | 19 | This internal routine is called by prop_run_multi and is not intended for 20 | general users. 21 | 22 | Parameters 23 | ---------- 24 | params : list 25 | List of input parameters 26 | 27 | Returns 28 | ------- 29 | psf : numpy ndarray 30 | Image containing result of the propagation pf the prescription routine. 31 | 32 | sampling : float 33 | Returns sampling of "result" in meters per element. 34 | """ 35 | routine_name, lamda, gridsize, passvalue, quiet, phase_offset = params 36 | 37 | if passvalue != {}: 38 | psf, sampling = proper.prop_run(routine_name, lamda, gridsize, PASSVALUE = passvalue, QUIET = quiet, PHASE_OFFSET = phase_offset, IS_MULTI=True) 39 | else: 40 | psf, sampling = proper.prop_run(routine_name, lamda, gridsize, QUIET = quiet, PHASE_OFFSET = phase_offset, IS_MULTI=True) 41 | 42 | return (psf, sampling) 43 | -------------------------------------------------------------------------------- /falco/proper/prop_fftw.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # modified 18 April 2019 by J. Krist - Removed user-specified NTHREADS option, 10 | # instead set to experimentally-derived optimums. 11 | 12 | import os 13 | import falco.proper as proper 14 | import numpy as np 15 | import _pickle as pickle 16 | import multiprocessing as mp 17 | 18 | 19 | def prop_fftw( a, directionFFTW = 'FFTW_FORWARD' ): 20 | """Compute FFT of wavefront array using FFTW or MKL Intel FFT library routines 21 | 22 | Parameters 23 | ---------- 24 | a : numpy ndarray 25 | Input wavefront 26 | 27 | directionFFTW : str 28 | Direction for the Fourier transform 29 | 30 | Returns 31 | ---------- 32 | out : numpy ndarray 33 | Fourier transform of input complex array 34 | 35 | Raises 36 | ------ 37 | ValueError 38 | Input array is not 2D. 39 | 40 | ValueError 41 | Data type is not double complex. 42 | """ 43 | # Check array size and type 44 | if len(a.shape) != 2: 45 | raise ValueError('PROP_FFTW: Input array is not 2D. Stopping.') 46 | 47 | # check if the data type is double complex 48 | if a.dtype != np.complex128: 49 | raise ValueError('PROP_FFTW: Data type is not double complex. Stopping.') 50 | 51 | if proper.use_ffti: 52 | if directionFFTW == 'FFTW_FORWARD': 53 | proper.prop_ffti.fft2(a) 54 | else: 55 | proper.prop_ffti.ifft2(a) 56 | else: 57 | try: 58 | import pyfftw 59 | except ImportError: 60 | raise ImportError("pyfftw not installed. Stopping.") 61 | 62 | if proper.fftw_use_wisdom: 63 | flags = ['FFTW_UNALIGNED'] 64 | else: 65 | flags = ['FFTW_UNALIGNED','FFTW_ESTIMATE'] 66 | 67 | if directionFFTW == 'FFTW_FORWARD': 68 | fftw_obj = pyfftw.FFTW( a, a, direction='FFTW_FORWARD', axes=(0,1), flags=flags, threads=proper.fft_nthreads ) 69 | else: 70 | fftw_obj = pyfftw.FFTW( a, a, direction='FFTW_BACKWARD', axes=(0,1), flags=flags, threads=proper.fft_nthreads ) 71 | 72 | a = fftw_obj() 73 | 74 | return 75 | -------------------------------------------------------------------------------- /falco/proper/prop_fit_dm.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import numpy as np 12 | try: 13 | from scipy.ndimage import convolve 14 | except: 15 | from scipy.ndimage.filters import convolve 16 | 17 | import falco.proper as proper 18 | 19 | 20 | def prop_fit_dm(dm_z, inf_kernel): 21 | """Determine deformable mirror actuator piston values that generate a desired 22 | DM surface, accounting for the effect of the actuator influence function. 23 | 24 | Parameters 25 | ---------- 26 | dm_z : numpy ndarray 27 | DM surface to match (2D array, with each element representing the 28 | desired surface amplitude for the corresponding actuator) 29 | 30 | inf_kernel : numpy ndarray 31 | Influence function kernel (2D image sampled at actuator spacing) 32 | 33 | Returns 34 | ------- 35 | dm_z_command : numpy ndarray 36 | DM actuator positions that create the desired surface when the influence 37 | function is included (2D image) 38 | 39 | dm_surface : numpy ndarray 40 | dm_z_command array convolved with inf_kernel 41 | 42 | Notes 43 | ----- 44 | Intended for use by the dm function and not for general users 45 | """ 46 | e0 = 1000000. 47 | 48 | dm_z_command = np.copy(dm_z) 49 | last_good_dm = np.copy(dm_z_command) 50 | dm_surface = convolve(dm_z_command, inf_kernel) 51 | diff = dm_z - dm_surface 52 | e = np.sqrt(np.sum(diff**2)) 53 | 54 | # Iterate for a solution 55 | while (e < e0 and (e0-e)/e > 0.01): 56 | last_good_dm = np.copy(dm_z_command) 57 | e0 = e 58 | dm_z_command = dm_z_command + diff 59 | dm_surface = convolve(dm_z_command, inf_kernel) 60 | diff = dm_z - dm_surface 61 | if np.max(np.abs(diff)) < 1.e-15: 62 | break 63 | e = np.sqrt(np.sum(diff**2)) 64 | 65 | # If fit diverged, use the result from the previous iteration 66 | if e > e0: 67 | dm_z_command = last_good_dm 68 | 69 | return (dm_z_command, dm_surface) 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /falco/proper/prop_fits_read.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019 - switched to astropy.io.fits 10 | 11 | import astropy.io.fits as pyfits 12 | import falco.proper as proper 13 | import numpy as np 14 | 15 | def prop_fits_read(fname, header = False): 16 | """Function to read an input FITS image. 17 | 18 | Parameters 19 | ---------- 20 | fname : str 21 | FITS image name 22 | 23 | header : bool 24 | Get FITS image header? Default is False. 25 | 26 | 27 | Returns 28 | ------- 29 | fitsarr : numpy ndarray 30 | 2D array of input image 31 | """ 32 | try: 33 | imgarr, imgheader = pyfits.getdata(fname, header=True, ignore_missing_end=True) 34 | except IOError: 35 | raise IOError("Unable to read FITS image %s. Stopping" %(fname)) 36 | 37 | imgarr = imgarr.astype(np.float64) 38 | 39 | if header: 40 | return (imgarr, imgheader) 41 | else: 42 | return imgarr 43 | -------------------------------------------------------------------------------- /falco/proper/prop_fits_write.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019: switched to astropy.io.fits and 10 | # allow file overwrite 11 | 12 | import os 13 | import astropy.io.fits as pyfits 14 | import falco.proper as proper 15 | 16 | 17 | def prop_fits_write(fname, img, **kwargs): 18 | """Function to write FITS image with optional header keywords. 19 | 20 | Parameters 21 | ---------- 22 | fname : str 23 | FITS image name 24 | 25 | img : numpy ndarray 26 | 2D image array 27 | 28 | Returns 29 | ------- 30 | None 31 | 32 | Other Parameters 33 | ---------------- 34 | HEADER : dict 35 | Dictionary of FITS image header keywords and value 36 | """ 37 | hdu = pyfits.PrimaryHDU(img) 38 | 39 | if "HEADER" in kwargs: 40 | for key,value in kwargs["HEADER"].items(): 41 | hdu.header.set(key, value[0], value[1]) 42 | 43 | if os.path.isfile(fname): 44 | os.remove(fname) 45 | 46 | try: 47 | hdu.writeto(fname, overwrite=True) 48 | except IOError: 49 | raise IOError('Unable to write FITS image %s. Stopping.' %fname) 50 | 51 | return 52 | -------------------------------------------------------------------------------- /falco/proper/prop_get_amplitude.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_get_amplitude(wf): 16 | """Function returns amplitude of current wavefront 17 | 18 | Parameters 19 | ---------- 20 | wf : obj 21 | Wavefront class object 22 | 23 | 24 | Returns 25 | ------- 26 | amplitude : numpy ndarray 27 | A 2D image corresponding to the amplitude of the current wavefront 28 | """ 29 | return proper.prop_shift_center(np.abs(wf.wfarr)) 30 | -------------------------------------------------------------------------------- /falco/proper/prop_get_beamradius.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import numpy as np 12 | import falco.proper as proper 13 | 14 | 15 | def prop_get_beamradius(wf): 16 | """This function returns the half-width-at-half-max of the Gaussian beam 17 | that is used to keep track of the beam size. 18 | 19 | The Gaussian beam starts off with a beam diameter equal to the entrance 20 | pupil diameter. This ignores widening of the beam due to aberrations. 21 | 22 | Parameters 23 | ---------- 24 | wf : obj 25 | Wavefront class object 26 | 27 | Returns 28 | ------- 29 | beam radius : float 30 | Beam radius in meters 31 | """ 32 | return wf.w0 * np.sqrt( 1.0 + (wf.lamda * (wf.z - wf.z_w0)/(np.pi * wf.w0**2))**2 ) 33 | -------------------------------------------------------------------------------- /falco/proper/prop_get_distancetofocus.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import falco.proper as proper 11 | 12 | 13 | def prop_get_distancetofocus(wf): 14 | """Function to determine distance to focus (in meters) from current location. 15 | 16 | Parameters 17 | ---------- 18 | wf : obj 19 | Wavefront class object 20 | 21 | Returns 22 | ------- 23 | distance to focus : float 24 | Distance to focus in meters 25 | """ 26 | return wf.z_w0 - wf.z 27 | -------------------------------------------------------------------------------- /falco/proper/prop_get_fratio.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import falco.proper as proper 11 | 12 | 13 | def prop_get_fratio(wf): 14 | """Function to compute the current beam's focal ratio by dividing the 15 | current distance to focus by the current beam diameter. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | Wavefront class object 21 | 22 | Returns 23 | ------- 24 | float 25 | Current beam's focal ratio 26 | """ 27 | return wf.current_fratio 28 | -------------------------------------------------------------------------------- /falco/proper/prop_get_gridsize.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import falco.proper as proper 11 | 12 | 13 | def prop_get_gridsize(wf): 14 | """Function returns dimension of wavefront grid 15 | 16 | Parameter 17 | --------- 18 | wf : obj 19 | Wavefront class object 20 | 21 | Returns 22 | ------- 23 | float 24 | Wavefront grid size 25 | """ 26 | return wf.ngrid 27 | -------------------------------------------------------------------------------- /falco/proper/prop_get_nyquistsampling.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import falco.proper as proper 11 | 12 | 13 | def prop_get_nyquistsampling(wf, lamx = 0.0): 14 | """Funtion determines the Nyquist sampling interval for the current beam, 15 | which is focal_ratio * wavelength / 2. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | Wavefront class object 21 | 22 | lamx : float 23 | Wavelength to use for computing sampling. By default, the current 24 | wavefront's wavelength is used. This parameter can be used when you 25 | want to know the Nyquist sampling for a wavelength other than for the 26 | current wavefront. 27 | 28 | Returns 29 | ------- 30 | float 31 | Nyquist sampling interval corresponding to the current wavefront 32 | """ 33 | if lamx != 0.: 34 | return wf.current_fratio * lamx / 2. 35 | else: 36 | return wf.current_fratio * wf.lamda / 2. 37 | -------------------------------------------------------------------------------- /falco/proper/prop_get_phase.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_get_phase(wf): 16 | """Function returns the phase of the current wavefront in radians. 17 | 18 | Parameters 19 | ---------- 20 | wf : obj 21 | Wavefront class object 22 | 23 | Returns 24 | ------- 25 | numpy ndarray 26 | A 2D image corresponding to the phase of the current wavefront 27 | """ 28 | return proper.prop_shift_center(np.arctan2(wf.wfarr.imag, wf.wfarr.real)) 29 | -------------------------------------------------------------------------------- /falco/proper/prop_get_refradius.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | import falco.proper as proper 10 | 11 | 12 | def prop_get_refradius(wf): 13 | """Function returns the radius of the reference sphere to which the current 14 | wavefront's phase is reference. 15 | 16 | The reference radius is defined to be the distance from the pilot beam waist 17 | to the current position. If the reference surface is planar (near field), 18 | then the radius will be 0.0. Assuming that forward propagation occurs from 19 | left to right, a negative radius indicates that the center of the reference 20 | surface is to the right of the current position (e.g. the beam is converging). 21 | 22 | Parameters 23 | ---------- 24 | wf : obj 25 | Wavefront class object 26 | 27 | Returns 28 | ------- 29 | float 30 | Radius of curvature of sphere to which the current wavefront's phase 31 | is referenced. 32 | """ 33 | return wf.z - wf.z_w0 34 | -------------------------------------------------------------------------------- /falco/proper/prop_get_sampling.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import falco.proper as proper 11 | 12 | def prop_get_sampling(wf): 13 | """Function returns the sampling interval of the current wavefront in meters. 14 | 15 | Parameters 16 | ---------- 17 | wf : obj 18 | Wavefront class object 19 | 20 | Returns 21 | ------- 22 | float 23 | Current wavefront sampling (meters/pixel) 24 | """ 25 | return wf.dx 26 | -------------------------------------------------------------------------------- /falco/proper/prop_get_sampling_arcsec.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_get_sampling_arcsec(wf): 16 | """Function determines current wavefront sampling in arcseconds/pixel. 17 | 18 | This is only valid when the current wavefront is at focus. 19 | 20 | Parameters 21 | ---------- 22 | wf : obj 23 | Wavefront class object 24 | 25 | Returns 26 | ------- 27 | float 28 | The current wavefront sampling in arcseconds/pixel 29 | """ 30 | fl = proper.prop_get_fratio(wf) * wf.diam 31 | return proper.prop_get_sampling(wf) * 360. * 3600. / (2. * np.pi * fl) 32 | -------------------------------------------------------------------------------- /falco/proper/prop_get_sampling_radians.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_get_sampling_radians(wf): 15 | """Function determines current wavefront sampling in radians/pixel. 16 | 17 | This funtion is only valid when the current wavefront is at focus. 18 | 19 | Parameters 20 | ---------- 21 | wf : obj 22 | Wavefront class object 23 | 24 | Returns 25 | ------- 26 | float 27 | Current wavefront sampling in radians/pixel 28 | """ 29 | fl = proper.prop_get_fratio(wf) * wf.diam 30 | return proper.prop_get_sampling(wf) / fl 31 | -------------------------------------------------------------------------------- /falco/proper/prop_get_wavefront.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_get_wavefront(wf): 15 | """Function returns current complex-values wavefront array. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | Wavefront class object 21 | 22 | Returns 23 | ------- 24 | numpy ndarray 25 | A 2D, complex valued wavefront array centered in the array 26 | """ 27 | return proper.prop_shift_center(wf.wfarr) 28 | -------------------------------------------------------------------------------- /falco/proper/prop_get_wavelength.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import falco.proper as proper 11 | 12 | def prop_get_wavelength(wf): 13 | """Function returns the wavelength of the current beam in meters. 14 | 15 | Parameters 16 | ---------- 17 | wf : obj 18 | Wavefront class object 19 | 20 | Returns 21 | ------- 22 | float 23 | Current beam's wavelength in meters. 24 | """ 25 | return wf.lamda 26 | -------------------------------------------------------------------------------- /falco/proper/prop_get_z.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | import falco.proper as proper 10 | 11 | def prop_get_z(wf): 12 | """Return the distance from the initialization of the wavefront to the 13 | current surface in meters. 14 | 15 | Parameters 16 | ---------- 17 | wf : obj 18 | WaveFront class object 19 | 20 | Returns 21 | ------- 22 | float 23 | Distance from intialization of the wavefront to the current surface 24 | 25 | """ 26 | return wf.z 27 | -------------------------------------------------------------------------------- /falco/proper/prop_init_savestate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | from time import time 13 | 14 | 15 | def prop_init_savestate(): 16 | """ 17 | Initialize the save state system. This must be called before any calls to 18 | prop_state or prop_is_statesaved. 19 | 20 | Parameters 21 | ---------- 22 | None 23 | 24 | Returns 25 | ------- 26 | statefile : string 27 | Name of state file 28 | """ 29 | proper.save_state = 1 30 | proper.save_state_lam = [] 31 | 32 | # creating a random id number is better this way because the random number 33 | # generator uses seeds based on the current time in integer seconds, and 34 | # that can return the same id number if two processes are started very 35 | # close to one another 36 | 37 | num = time() 38 | num = int(num * 500000. - int(num) * 500000.) 39 | 40 | proper.statefile = "_" + str(num).strip() + "_prop_savestate" 41 | 42 | return 43 | -------------------------------------------------------------------------------- /falco/proper/prop_is_statesaved.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_is_statesaved(wf): 15 | """Determine if a previously saved state exists for the current wavelength. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | Returns 23 | ------- 24 | None 25 | """ 26 | save_state_lam = proper.save_state_lam 27 | 28 | # save_state_lam is a python list 29 | nlam =len(save_state_lam) 30 | if nlam == 0: 31 | return False 32 | else: 33 | for i in range(nlam): 34 | # does a state exist for the current wavelength? 35 | if save_state_lam[i] == wf.lamda: 36 | return True 37 | 38 | return False 39 | -------------------------------------------------------------------------------- /falco/proper/prop_load_fftw_wisdom.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Written by J. Krist - 19 April 2019 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | import os 14 | import pickle 15 | 16 | def prop_load_fftw_wisdom( gridsize, nthreads ): 17 | 18 | if proper.use_ffti == True: 19 | return 20 | 21 | try: 22 | import pyfftw 23 | except ImportError: 24 | raise ImportError("pyfftw is not installed. Stopping.") 25 | 26 | wisdompath = os.path.join( os.path.expanduser('~'), '.proper_{}pix'.format(str(gridsize)) + '{}threads'.format(str(nthreads)) + '_wisdomfile' ) 27 | 28 | if os.path.exists(wisdompath): 29 | pyfftw.forget_wisdom() 30 | with open(wisdompath, 'rb') as infile: 31 | wisdom = pickle.load(infile) 32 | pyfftw.import_wisdom(wisdom) 33 | proper.fftw_use_wisdom = True 34 | else: 35 | proper.fftw_use_wisdom = False 36 | 37 | return 38 | -------------------------------------------------------------------------------- /falco/proper/prop_multiply.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist on 19 April 2019 - switched to *= 10 | 11 | 12 | import falco.proper as proper 13 | import numpy as np 14 | 15 | 16 | def prop_multiply(wf, value): 17 | """Multiply the current wavefront amplitude by a user-provided value or array. 18 | 19 | Parameters 20 | ---------- 21 | wf : obj 22 | WaveFront class object 23 | 24 | value : float or numpy ndarray 25 | Either a scalar or a 2-D array containing the amplitude map by which the 26 | current wavefront will be multiplied. The map is assumed to be centered 27 | at pixel (n/2, n/2). 28 | 29 | Returns 30 | ------- 31 | None 32 | """ 33 | if type(value) != np.ndarray and type(value) != list: 34 | value = float(value) 35 | wf.wfarr *= complex(value, 0.) 36 | else: 37 | value = np.asarray(value) 38 | wf.wfarr *= proper.prop_shift_center(value) 39 | 40 | return 41 | -------------------------------------------------------------------------------- /falco/proper/prop_pixellate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | from numpy.fft import fft2, ifft2 14 | 15 | 16 | def prop_pixellate(image_in, sampling_in, sampling_out, n_out = 0): 17 | """Integrate a sampled PSF onto detector pixels. 18 | 19 | This routine takes as input a sampled PSF and integrates it over pixels of a 20 | specified size. This is done by convolving the Fourier transform of the input 21 | PSF with a sinc function representing the transfer function of an idealized 22 | square pixel and transforming back. This result then represents the PSF 23 | integrated onto detector-sized pixels with the same sampling as the PSF. 24 | The result is interpolated to get detector-sized pixels at detector-pixel 25 | spacing. 26 | 27 | Parameters 28 | ---------- 29 | image_in : numpy ndarray 30 | 2D floating image containing PSF 31 | 32 | sampling_in : float 33 | Sampling of image_in in meters/pixel 34 | 35 | sampling_out : float 36 | Size(=sampling) of detector pixels 37 | 38 | n_out : int 39 | Output image dimension (n_out by n_out) 40 | 41 | Returns 42 | ------- 43 | new : numpy ndarray 44 | Returns image integrated over square detector pixels. 45 | """ 46 | n_in = image_in.shape[0] 47 | 48 | # Compute pixel transfer function (MTF) 49 | psize = 0.5 * (sampling_out / sampling_in) 50 | constant = psize * np.pi 51 | mag = sampling_in / sampling_out 52 | 53 | arr = np.arange(n_in, dtype = np.float64) - int(n_in/2) 54 | x = np.roll(arr, -int(n_in/2), 0) / int(n_in/2) 55 | t = x * constant 56 | y = np.zeros(n_in, dtype = np.float64) 57 | y[1:] = np.sin(t[1:]) / t[1:] 58 | y[0] = 1. 59 | 60 | #pixel_mtf = np.tile(np.vstack(y), (1, n_in)) * y 61 | pixel_mtf = np.dot(y[:,np.newaxis], y[np.newaxis,:]) 62 | 63 | # Convolve image with detector pixel 64 | image_mtf = fft2(np.roll(np.roll(image_in, -int(n_in/2), 0), -int(n_in/2), 1)) / image_in.size 65 | image_mtf *= pixel_mtf 66 | 67 | convolved_image = np.abs(ifft2(image_mtf) * image_mtf.size) 68 | image_mtf = 0 69 | convolved_image = np.roll(np.roll(convolved_image/mag**2, int(n_in/2), 0), int(n_in/2), 1) 70 | 71 | # Image is integrated over pixels but has original sampling; now, resample 72 | # pixel sampling 73 | if n_out != 0: 74 | n_out = int(np.fix(n_in * mag)) 75 | 76 | new = proper.prop_magnify(convolved_image, mag, n_out) 77 | 78 | return new 79 | -------------------------------------------------------------------------------- /falco/proper/prop_print_zernikes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_print_zernikes(maxz): 15 | """Print to the screen the first N number of Noll-ordered Zernike 16 | polynomials for an unobscured circular aperture, up to a specified index. 17 | 18 | Parameters 19 | ---------- 20 | maxz : int 21 | The number of zernike polynomials to print, from 1 to numz 22 | 23 | Returns 24 | ------- 25 | Zernike polynomials : float 26 | Print Zernike polynomials to screen 27 | """ 28 | zernlist = proper.prop_noll_zernikes(maxz) 29 | 30 | for i in range(1,maxz+1): 31 | print("%d = %s" %(i, zernlist[i])) 32 | 33 | return 34 | -------------------------------------------------------------------------------- /falco/proper/prop_qphase.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified 18 April 2019 by J. Krist - switch to *= 10 | 11 | 12 | import falco.proper as proper 13 | import numpy as np 14 | 15 | 16 | def prop_qphase(wf, c): 17 | """Apply a quadratic phase factor to the current wavefront. 18 | 19 | This routine applies a radially-dependent phase alteration caused by a 20 | wavefront curvature. It is used by lens, stw, and wts functions. 21 | 22 | 23 | Parameters 24 | ---------- 25 | wf : obj 26 | WaveFront class object 27 | 28 | c : float 29 | Phase curvature radius 30 | 31 | 32 | Returns 33 | ------- 34 | None 35 | Modifies wavefront. 36 | """ 37 | ngrid = proper.prop_get_gridsize(wf) 38 | sampl = proper.prop_get_sampling(wf) 39 | 40 | if c == 0.0: 41 | return 42 | 43 | i = complex(0., 1.) 44 | 45 | dx = sampl 46 | 47 | xsqr = np.tile(((np.arange(ngrid, dtype = np.float64) - ngrid/2.) * dx)**2, (ngrid, 1)) 48 | rsqr = xsqr + np.transpose(xsqr) 49 | rsqr[:,:] = np.roll(np.roll(rsqr, int(-ngrid/2), 0), int(-ngrid/2), 1) 50 | xsqr = 0. 51 | 52 | wf.wfarr *= np.exp(i*np.pi/(wf.lamda*c) * rsqr) 53 | 54 | return 55 | -------------------------------------------------------------------------------- /falco/proper/prop_radius.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_radius(wf, **kwargs): 16 | """Return a 2D array in which the value of each element corresponds to the 17 | distance of that element from the center of the current wavefront. 18 | 19 | By default, the distance is in meters, unless the /NORM switch is set, in 20 | which case it is normalize to the current radius of the beam. The center 21 | of the wavefront is set to be at the center of the array. 22 | 23 | 24 | Parameters 25 | ---------- 26 | wf : obj 27 | WaveFront class object 28 | 29 | Other Parameters 30 | ---------------- 31 | NORM : bool 32 | Indicates that the returned array contains the distances divided by 33 | the beam radius. This assumes the radius of the pilot tracer beam 34 | accurately reflects the size of the actual beam in the wavefront 35 | array, which will not be true in the case of significant aberrations. 36 | 37 | Returns 38 | ------- 39 | r : numpy ndarray 40 | A 2D array. 41 | """ 42 | ngrid = proper.prop_get_gridsize(wf) 43 | sampling = proper.prop_get_sampling(wf) 44 | beamradius = proper.prop_get_beamradius(wf) 45 | 46 | 47 | r = np.zeros([ngrid, ngrid], dtype = np.float64) 48 | x2 = (np.arange(float(ngrid)) - int(ngrid/2))**2 49 | 50 | # Using map and lambda 51 | #f = lambda j: np.sqrt(x2 + float(j - ngrid/2.)**2) 52 | #r[:,:] = map(f, range(ngrid)) 53 | 54 | # Using list comprehension - which seems to be faster 55 | #r[:,:] = [np.sqrt(x2 + float(j - ngrid/2.)**2) for j in range(ngrid)] 56 | 57 | # Using conventional for loop 58 | for j in range(ngrid): 59 | r[j,:] = np.sqrt(x2 + float(j - int(ngrid/2))**2) 60 | 61 | if proper.switch_set("NORM",**kwargs): 62 | r *= (sampling/beamradius) 63 | else: 64 | r *= sampling 65 | 66 | return r 67 | -------------------------------------------------------------------------------- /falco/proper/prop_rectangular_aperture.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_rectangular_aperture(wf, width, height, xc =0., yc = 0., **kwargs): 15 | """Multiply the current wavefront by a rectangular aperture. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | width, height : float 23 | X and Y widths of aperture in meters, unless the norm switch is 24 | specified, in which case the are normalized to the beam diameter. 25 | 26 | xc, yc : float 27 | Center of aperture relative to the center of the beam in meters (unless 28 | norm is specified); the default is (0,0). 29 | 30 | 31 | Returns 32 | ------- 33 | None 34 | Multiplies the wavefront array in "wf" object by a rectangular mask. 35 | 36 | 37 | Other Parameters 38 | ---------------- 39 | NORM : bool 40 | Indicates that the given aperture sizes and the centers are normalized 41 | to the beam radius. 42 | 43 | ROTATION : float 44 | Angle degrees counter-clockwise to rotate rectangular aperture about 45 | its center. Default is 0 degrees. 46 | """ 47 | 48 | norm = proper.switch_set("NORM",**kwargs) 49 | 50 | if "ROTATION" in kwargs: 51 | rotation = kwargs["ROTATION"] 52 | else: 53 | rotation = 0.0 54 | 55 | wf.wfarr *= proper.prop_shift_center(proper.prop_rectangle(wf, width, height, xc, yc, NORM = norm, ROTATION = rotation)) 56 | 57 | return 58 | -------------------------------------------------------------------------------- /falco/proper/prop_rectangular_obscuration.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | 13 | 14 | def prop_rectangular_obscuration(wf, width, height, xc = 0.0, yc = 0.0, **kwargs): 15 | """Multiply the current wavefront by a rectangular obscuration. 16 | 17 | Parameters 18 | ---------- 19 | wf : obj 20 | WaveFront class object 21 | 22 | width, height : float 23 | X and Y widths of the obscuration in meters (unless the norm switch is 24 | specified, in which case these are normalized to the beam diameter) 25 | 26 | xc, yc : float 27 | Center of obscuration relative to the center of the beam in meters 28 | (unless norm is specified, in which case they are normalized relative 29 | to the beam radius; the default is (0,0) 30 | 31 | 32 | Returns 33 | ------- 34 | None 35 | Multiplies the wavefront array in "wf" object by a dark rectangular mask. 36 | 37 | 38 | Other Parameters 39 | ---------------- 40 | NORM : bool 41 | Indicates that obscuration halfwidths and center are specified relative 42 | to the beam radius. 43 | 44 | ROTATION : float 45 | Degrees counter-clockwise to rotate obscuration about its center. 46 | """ 47 | 48 | norm = proper.switch_set("NORM",**kwargs) 49 | 50 | if "ROTATION" in kwargs: 51 | rotation = kwargs["ROTATION"] 52 | else: 53 | rotation = 0.0 54 | 55 | wf.wfarr *= proper.prop_shift_center(proper.prop_rectangle(wf, width, height, xc, yc, NORM = norm, ROTATION = rotation, DARK = True)) 56 | 57 | return 58 | -------------------------------------------------------------------------------- /falco/proper/prop_resamplemap.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Revised 5 March 2018 - John Krist - Added GRID=True to call to prop_cubic_conv 10 | 11 | 12 | import falco.proper as proper 13 | import numpy as np 14 | 15 | if not proper.use_cubic_conv: 16 | try: 17 | from scipy.ndimage import map_coordinates 18 | except: 19 | from scipy.ndimage.interpolation import map_coordinates 20 | 21 | 22 | def prop_resamplemap(wf, dmap, pixscale, xc, yc, xshift = 0., yshift = 0.): 23 | """Interpolate input map using cubic convolution onto grid with same size 24 | and sampling as the current wavefront array. 25 | 26 | Optionally shift the map. The new, resampled map replaces the old one. 27 | 28 | 29 | Returns 30 | ------- 31 | dmap : numpy ndarray 32 | "map" is replaced with a new version of itself that has equal sampling 33 | as the current wavefront array 34 | 35 | 36 | Parameters 37 | ---------- 38 | wf : obj 39 | WaveFront class object 40 | 41 | dmap : numpy ndarray 42 | Aberration map to be resampled 43 | 44 | pixscale : float 45 | Spacing of "map" in meters 46 | 47 | xc, yc : float 48 | Pixel coordinates of map center (0,0 is center of 1st pixel) 49 | 50 | xshift, yshift : float 51 | Amount of shift map in meters 52 | 53 | 54 | Notes 55 | ----- 56 | Intended for internal use only. 57 | """ 58 | n = proper.prop_get_gridsize(wf) 59 | 60 | x = np.arange(n, dtype = np.float64) - int(n/2) 61 | x = x * proper.prop_get_sampling(wf) / pixscale 62 | x += xc - xshift/pixscale 63 | 64 | y = np.arange(n, dtype = np.float64) - int(n/2) 65 | y = y * proper.prop_get_sampling(wf) / pixscale 66 | y += yc - xshift/pixscale 67 | 68 | if proper.use_cubic_conv: 69 | dmap = proper.prop_cubic_conv(dmap.T, x, y, GRID=True) 70 | else: 71 | xygrid = np.meshgrid(x,y) 72 | dmap = map_coordinates(dmap.T, xygrid, order = 3, mode = "nearest") 73 | 74 | return dmap 75 | -------------------------------------------------------------------------------- /falco/proper/prop_savestate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_savestate(wf): 16 | """Write out the current wavefront state to a file for the current wavelength. 17 | 18 | Parameters 19 | ---------- 20 | wf : obj 21 | WaveFront class object 22 | 23 | Returns 24 | ------- 25 | None 26 | """ 27 | if proper.save_state == False: 28 | return 29 | 30 | statefile_lam = str(int(wf.lamda*1.e15)).strip() + proper.statefile 31 | 32 | np.savez(statefile_lam, total_original_pupil = proper.total_original_pupil, wf = wf) 33 | 34 | proper.save_state_lam.append(wf.lamda) 35 | 36 | return 37 | 38 | -------------------------------------------------------------------------------- /falco/proper/prop_select_propagator.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import numpy as np 12 | import falco.proper as proper 13 | 14 | 15 | def prop_select_propagator(wf, dz): 16 | """Used by propagator to decide which in which propagation regime the next 17 | surface will be (to decide which propagatoion method to use (spherical-to- 18 | planar, planar-to-spherical, or planar-to-planar)) 19 | 20 | 21 | Parameters 22 | ---------- 23 | wf : obj 24 | WaveFront class object 25 | 26 | dz : float 27 | Distance to propagate from current z location 28 | 29 | 30 | Returns 31 | ------- 32 | dzw : float 33 | Distance to new focus position from new position 34 | """ 35 | # w0 = min possible waist radius for current beam 36 | # z_w0 = location of minimum beam waist 37 | # z_Rayleigh = Rayleigh distance from minimum beam waist 38 | # w_at_surface = waist radius at current surface 39 | # R_beam = beam radius of curvature 40 | rayleigh_factor = 1. 41 | 42 | dzw = wf.z_w0 - wf.z 43 | newz = wf.z + dz 44 | 45 | if np.abs(wf.z_w0 - newz) < rayleigh_factor * wf.z_Rayleigh: 46 | beam_type_new = "INSIDE_" 47 | else: 48 | beam_type_new = "OUTSIDE" 49 | 50 | wf.propagator_type = wf.beam_type_old + "_to_" + beam_type_new 51 | 52 | wf.beam_type_old = beam_type_new 53 | 54 | return dzw 55 | -------------------------------------------------------------------------------- /falco/proper/prop_set_antialiasing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Created 28 Jan 2020 - JEK 7 | 8 | import falco.proper as proper 9 | 10 | 11 | def prop_set_antialiasing( nsub=11 ): 12 | """This function sets the pixel subsampling factor (nsub by nsub) used to 13 | antialias the edges of shapes. NOTE: This should be called after prop_run. 14 | 15 | Parameters 16 | ---------- 17 | nsub : 18 | Subsampling factor (must be odd-valued integer) 19 | 20 | """ 21 | 22 | s = int(nsub) 23 | if (s % 2) == 0: 24 | raise ValueError("PROP_SET_ANTIALIASING: subsampling factor must be odd-valued integer.") 25 | else: 26 | proper.antialias_subsampling = s 27 | 28 | -------------------------------------------------------------------------------- /falco/proper/prop_shift_center.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import numpy as np 12 | import falco.proper as proper 13 | 14 | 15 | def prop_shift_center(image): 16 | """Shift a n by n image by (x,y)=(n/2,n/2), either shifting from the image 17 | origin to the center or vice-verse 18 | 19 | Parameters 20 | ---------- 21 | image : numpy ndarray 22 | 2D image to be shifted 23 | 24 | Returns 25 | ------- 26 | shifted image : numpy ndarray 27 | Shifted image 28 | """ 29 | image = np.asarray(image) 30 | 31 | if image.ndim != 2: 32 | raise ValueError("Only 2D images can be shifted. Stopping.") 33 | 34 | s = image.shape 35 | 36 | return np.roll(np.roll(image, int(s[0]/2), 0), int(s[1]/2), 1) 37 | -------------------------------------------------------------------------------- /falco/proper/prop_sinc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019 - added "invalid='ignore'" to 10 | # avoid 0/0 warnings. 11 | 12 | 13 | import numpy as np 14 | import falco.proper as proper 15 | 16 | 17 | def prop_sinc(x): 18 | """Sinc interploation function. 19 | 20 | Parameters 21 | ---------- 22 | x : scalar or numpy ndarray 23 | Input variable 24 | 25 | Returns 26 | ------- 27 | float or numpy array 28 | Sinc interpolated value 29 | """ 30 | if type(x) != np.ndarray and type(x) != list: 31 | if x == 0: 32 | return 1. 33 | else: 34 | return np.sin(x)/x 35 | else: 36 | xarr = np.asarray(x) 37 | 38 | y = np.sin(xarr) 39 | 40 | with np.errstate( divide = "ignore", invalid='ignore' ): 41 | y = y / xarr 42 | 43 | w0 = np.where(xarr == 0) 44 | if w0[0].shape[0] != 0: 45 | y[w0] = 1. 46 | 47 | return y 48 | -------------------------------------------------------------------------------- /falco/proper/prop_state.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | 11 | import falco.proper as proper 12 | import numpy as np 13 | 14 | 15 | def prop_state(wf): 16 | """Save the current state for the current wavelength, if one doesn't already 17 | exist. 18 | 19 | If one does, read it in and use it to define the current wavefront. The 20 | current contents of the wavefront array structure and some other info 21 | necessary to represent the current state of propagation are saved to a file. 22 | 23 | Parameters 24 | ---------- 25 | wf : obj 26 | WaveFront class object 27 | 28 | Returns 29 | ------- 30 | None 31 | """ 32 | if proper.save_state == False: 33 | return 34 | 35 | # If the state for the current wavelength does not exist, write it out 36 | if not proper.prop_is_statesaved(wf): 37 | proper.prop_savestate(wf) 38 | return 39 | 40 | # If it does exist, read it in 41 | statefile_lam = str(int(wf.lamda*1.e15)).strip() + proper.statefile + '.npz' 42 | 43 | proper.total_original_pupil = 0. 44 | proper.n = int(0) 45 | proper.first_pass = np.fix(0) 46 | 47 | statedata = np.load(statefile_lam) 48 | proper.total_original_pupil = statedata["total_original_pupil"] 49 | wf = statedata["wf"].tolist() 50 | proper.n = wf.wfarr.shape[0] 51 | 52 | return 53 | -------------------------------------------------------------------------------- /falco/proper/prop_stw.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified 18 April 2019 by J. Krist - switched to *= and /= and use in-place 10 | # calls for FFTW/FFTI 11 | 12 | 13 | import falco.proper as proper 14 | import numpy as np 15 | from numpy.fft import fft2, ifft2 16 | 17 | 18 | def prop_stw(wf, dz = 0.0): 19 | """Propagate from a spherical reference surface that is outside the Rayleigh 20 | limit from focus to a planar one that is inside. Used by propagate function. 21 | 22 | Parameters 23 | ---------- 24 | wf : obj 25 | WaveFront class object 26 | 27 | dz : float 28 | Distance in meters to propagate 29 | 30 | Returns 31 | ------- 32 | None 33 | Modifies the wavefront. 34 | """ 35 | ngrid = wf.ngrid 36 | 37 | if proper.verbose: 38 | print(" STW: dz = %3.6f" %(dz)) 39 | 40 | if wf.reference_surface != "SPHERI": 41 | if proper.verbose: 42 | print(" STW: Input reference surface not spherical. Using PTP") 43 | proper.prop_ptp(wf, dz) 44 | return 45 | 46 | if dz == 0.0: 47 | dz = wf.z_w0 - wf.z 48 | 49 | wf.z = wf.z + dz 50 | wf.dx = wf.lamda * np.abs(dz) / (ngrid * wf.dx) 51 | 52 | direct = dz >= 0.0 53 | 54 | if direct: # forward transform 55 | if proper.use_fftw: 56 | proper.prop_fftw(wf.wfarr,directionFFTW = 'FFTW_FORWARD') 57 | wf.wfarr /= np.size(wf.wfarr) 58 | else: 59 | wf.wfarr[:,:] = fft2(wf.wfarr) / np.size(wf.wfarr) 60 | wf.wfarr *= ngrid 61 | else: 62 | if proper.use_fftw: 63 | proper.prop_fftw(wf.wfarr, directionFFTW = 'FFTW_BACKWARD') 64 | wf.wfarr *= np.size(wf.wfarr) 65 | else: 66 | wf.wfarr[:,:] = ifft2(wf.wfarr) * np.size(wf.wfarr) 67 | wf.wfarr /= ngrid 68 | 69 | proper.prop_qphase(wf, dz) 70 | 71 | if proper.phase_offset: 72 | wf.wfarr *= np.exp(complex(0.,1.) * 2*np.pi*dz/wf.lamda) 73 | 74 | if proper.verbose: 75 | print(" STW: z = %4.6f dx = %.6e" %(wf.z, wf.dx)) 76 | 77 | wf.reference_surface = "PLANAR" 78 | 79 | return 80 | -------------------------------------------------------------------------------- /falco/proper/prop_szoom_c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //#include 4 | #include 5 | #define K 13 6 | #define DK 6 7 | 8 | /*--------------------------------------------------------------------------------------*/ 9 | double roundval( double x ) 10 | { 11 | if ( x > 0.0 ) 12 | return( floor(x+0.5) ); 13 | else 14 | return( -floor(-x+0.5) ); 15 | } 16 | 17 | /*--------------------------------------------------------------------------------------*/ 18 | int prop_szoom_c(const void *image_input, int size_in, void *image_output, int size_out, double mag ) 19 | { 20 | double val, x, y, x_in, x_phase, y_in; 21 | double **sinc_table; 22 | int i, ik, ikx, iky, ix, iy, x1, x2, x_pix, x_out, y1, y2, y_pix, y_out; 23 | 24 | const double *image_in = (double *)image_input; 25 | double *image_out = (double *)image_output; 26 | 27 | /* Precompute table of sinc kernel coefficients. Because this routine * 28 | * only expands or contracts the square image symmetrically about the * 29 | * center, just the kernel components for one axis are needed. */ 30 | 31 | sinc_table = (double **)malloc( size_out * sizeof(double *) ); 32 | for ( i = 0; i < size_out; ++i ) 33 | sinc_table[i] = (double *)malloc( K * sizeof(double) ); 34 | 35 | for ( x_out = 0; x_out < size_out; ++x_out ) 36 | { 37 | x_in = (x_out - size_out/2) / mag; 38 | x_phase = x_in - roundval(x_in); 39 | for ( ik = 0; ik < K; ++ik ) 40 | { 41 | x = (ik - K/2) - x_phase; 42 | if ( fabs(x) <= DK ) 43 | { 44 | if ( x != 0.0 ) 45 | { 46 | x = x * 3.141592653589793; 47 | sinc_table[x_out][ik] = sin(x)/x * sin(x/DK)/(x/DK); 48 | } 49 | else 50 | sinc_table[x_out][ik] = 1.0; 51 | } 52 | else 53 | { 54 | sinc_table[x_out][ik] = 0.0; 55 | } 56 | } 57 | } 58 | 59 | for ( y_out = 0; y_out < size_out; ++y_out ) 60 | { 61 | y_in = (y_out - size_out/2) / mag; 62 | y_pix = roundval(y_in) + size_in/2; 63 | y1 = y_pix - K/2; 64 | y2 = y_pix + K/2; 65 | if ( (y1 < 0) || (y2 >= size_in) ) 66 | continue; 67 | 68 | for ( x_out = 0; x_out < size_out; ++x_out ) 69 | { 70 | x_in = (x_out - size_out/2) / mag; 71 | x_pix = roundval(x_in) + size_in/2; 72 | x1 = x_pix - K/2; 73 | x2 = x_pix + K/2; 74 | if ( (x1 < 0) || (x2 >= size_in) ) 75 | continue; 76 | 77 | val = 0.0; 78 | iky = 0; 79 | for ( iy = y1; iy <= y2; ++iy ) 80 | { 81 | ikx = 0; 82 | for ( ix = x1; ix <= x2; ++ix ) 83 | { 84 | val = val + image_in[iy*(long)size_in+ix] * sinc_table[y_out][iky] * sinc_table[x_out][ikx]; 85 | ++ikx; 86 | } 87 | ++iky; 88 | } 89 | image_out[y_out*(long)size_out+x_out] = val; 90 | } 91 | } 92 | 93 | for ( i = 0; i < size_out; ++i ) 94 | free( sinc_table[i] ); 95 | free( sinc_table ); 96 | 97 | return( 0 ); 98 | 99 | } /* prop_szoom_c */ 100 | 101 | -------------------------------------------------------------------------------- /falco/proper/prop_table.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019 - Got it to work 10 | 11 | 12 | import falco.proper as proper 13 | 14 | 15 | def prop_table(): 16 | """Print the beam dimensions and sampling at each surface in a prescription. 17 | 18 | Parameters 19 | ---------- 20 | None 21 | 22 | Returns 23 | ------- 24 | None 25 | """ 26 | action_num = proper.action_num 27 | lens_fl_list = proper.lens_fl_list 28 | surface_name_list = proper.surface_name_list 29 | distance_list = proper.distance_list 30 | sampling_list = proper.sampling_list 31 | beam_diam_list = proper.beam_diam_list 32 | lens_eff_fratio_list = proper.lens_eff_fratio_list 33 | 34 | print("") 35 | print(" Dist from Paraxial") 36 | print(" previous Sampling/ beam Grid Lens paraxial Paraxial") 37 | print("Surface Surface surface pixel diameter width focal length focal") 38 | print(" type name (m) (m) (m) (m) (m) ratio") 39 | print("-------- ------------ ------------- ------------ ------------ ------------ ------------- ------------") 40 | 41 | for i in range(action_num): 42 | if lens_fl_list[i] != 0: 43 | print( " LENS %-12s %-14.6e %-14.6e %-14.6e %-14.6e %-14.6e %-14.6e" % 44 | (surface_name_list[i], distance_list[i], sampling_list[i], beam_diam_list[i], proper.n*sampling_list[i], lens_fl_list[i], lens_eff_fratio_list[i]) ) 45 | else: 46 | print( "SURFACE %-12s %-14.6e %-14.6e %-14.6e %-14.6e" % (surface_name_list[i], distance_list[i], sampling_list[i], beam_diam_list[i], proper.n*sampling_list[i]) ) 47 | 48 | print("") 49 | 50 | return 51 | -------------------------------------------------------------------------------- /falco/proper/prop_use_fftw.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | 10 | import os 11 | import falco.proper as proper 12 | 13 | 14 | def prop_use_fftw(**kwargs): 15 | """Instructs PROPER to use the FFTW FFT routines rather than the built-in 16 | numpy FFT. 17 | 18 | See the manual for how to use FFTW. FFTW will be used by PROPER for all 19 | future runs (including after exiting python shell), unless changed by using 20 | the DISABLE switch. 21 | 22 | 23 | Parameters 24 | ---------- 25 | None 26 | 27 | 28 | Returns 29 | ------- 30 | None 31 | 32 | 33 | Other Parameters 34 | ---------------- 35 | DISABLE : bool 36 | If set, the Numpy FFT routine will be used by pyPROPER routines in 37 | future. 38 | """ 39 | 40 | fftw_dummy_file = os.path.join( os.path.expanduser('~'), '.proper_use_fftw' ) 41 | 42 | # Check if pyFFTW is available on user's machine 43 | try: 44 | import pyfftw 45 | except ImportError: 46 | print('pyFFTW not found, using Numpy FT.') 47 | fftw_flag = False 48 | else: 49 | fftw_flag = True 50 | 51 | if proper.switch_set("DISABLE",**kwargs) or not fftw_flag: 52 | if os.path.isfile(fftw_dummy_file): 53 | os.remove(fftw_dummy_file) 54 | proper.use_fftw = False 55 | else: 56 | with open(fftw_dummy_file, 'w') as fd: 57 | fd.write('Using FFTW routines\n') 58 | proper.use_fftw = True 59 | 60 | return 61 | -------------------------------------------------------------------------------- /falco/proper/prop_writemap.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified by J. Krist - 19 April 2019 - switched to astropy.io.fits and 10 | # allow file overwrite 11 | 12 | import astropy.io.fits as pyfits 13 | import falco.proper as proper 14 | 15 | def prop_writemap(dmap, filename, **kwargs): 16 | """Write an aberration (phase or amplitude) map to a FITS file. 17 | 18 | Parameters 19 | ---------- 20 | dmap : numpy ndarray 21 | 2-D error map 22 | 23 | filename : str 24 | Name of file, including extension 25 | 26 | 27 | Returns 28 | ------- 29 | None 30 | 31 | 32 | Other Parameters 33 | ---------------- 34 | AMPLITUDE, MIRROR, WAVEFRONT : bool 35 | Indicates type of map (only one may be specified) 36 | AMPLITUDE : amplitude map 37 | WAVEFRONT : wavefront map in METERS 38 | MIRROR : mirror surface (not wavefront) map in METERS 39 | 40 | Default is WAVEFRONT 41 | 42 | RADIUS_PIX : float 43 | Specifies the beam radius in the map in pixels. If specified, the 44 | value of SAMPLING (if provided) is ignored. When this file is read 45 | by prop_errormap, the map will be resampled as necessary to match the 46 | sampling of the beam. 47 | 48 | SAMPLING : float 49 | Map sampling in METERS; ignored if RADIUS_PIX is specified. 50 | """ 51 | if not "RADIUS_PIX" in kwargs and not "SAMPLING" in kwargs: 52 | raise ValueError('PROP_WRITEMAP: Either SAMPLING or RADIUS_PIX keyword \ 53 | is required. Stopping.') 54 | 55 | nx, ny = dmap.shape 56 | 57 | if proper.switch_set("AMPLITUDE",**kwargs): 58 | maptype = "amplitude" 59 | elif proper.switch_set("MIRROR",**kwargs): 60 | maptype = "mirror" 61 | else: 62 | maptype = "wavefront" 63 | 64 | # assume wavefront map if maptype is not specified 65 | hdu = pyfits.PrimaryHDU() 66 | hdu.header.set("MAPTYPE", maptype, " error map type") 67 | hdu.header.set("X_UNIT", "meters", " X & Y units") 68 | if not maptype == "amplitude": 69 | hdu.header.set("Z_UNIT", "meters", " Error units") 70 | 71 | if "RADIUS_PIX" in kwargs: 72 | hdu.header.set("RADPIX", kwargs["RADIUS_PIX"], " beam radius in pixels") 73 | elif "SAMPLING" in kwargs: 74 | hdu.header.set("PIXSIZE", kwargs["SAMPLING"], " spacing in meters") 75 | 76 | hdu.header.set("XC_PIX", nx//2, " Center X pixel coordinate") 77 | hdu.header.set("YC_PIX", ny//2, " Center Y pixel coordinate") 78 | hdu.data = dmap 79 | hdu.writeto( filename, overwrite=True ) 80 | 81 | return 82 | -------------------------------------------------------------------------------- /falco/proper/prop_wts.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, 2017 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | # 9 | # Modified 18 April 2019 by J. Krist - Switched to /= and *=, new in-place FFT calls 10 | # for FFTW/FFTI 11 | 12 | 13 | import falco.proper as proper 14 | import numpy as np 15 | from numpy.fft import fft2, ifft2 16 | 17 | 18 | def prop_wts(wf, dz): 19 | """Propagate from a planar reference surface that is inside the Rayleigh 20 | distance from focus to a spherical reference surface that is outside. 21 | 22 | Used by propagate function. 23 | 24 | Parameters 25 | ---------- 26 | wf : obj 27 | WaveFront class object 28 | 29 | dz : float 30 | Distance in meters to propagate 31 | 32 | Returns 33 | ------- 34 | None 35 | Wavefront is modified. 36 | """ 37 | if proper.verbose: 38 | print(" WTS: dz = ", dz) 39 | 40 | wf.reference_surface = "SPHERI" 41 | 42 | if dz == 0.0: 43 | return 44 | 45 | wf.z = wf.z + dz 46 | 47 | direct = dz >= 0.0 48 | 49 | proper.prop_qphase(wf, dz) 50 | 51 | if direct == 1: # forward transform 52 | if proper.use_fftw: 53 | proper.prop_fftw(wf.wfarr, directionFFTW = 'FFTW_FORWARD') 54 | wf.wfarr /= np.size(wf.wfarr) 55 | else: 56 | wf.wfarr[:,:] = fft2(wf.wfarr) / np.size(wf.wfarr) 57 | wf.wfarr *= wf.ngrid 58 | else: # inverse transform 59 | if proper.use_fftw: 60 | proper.prop_fftw(wf.wfarr,directionFFTW = 'FFTW_BACKWARD') 61 | wf.wfarr *= np.size(wf.wfarr) 62 | else: 63 | wf.wfarr[:,:] = ifft2(wf.wfarr) * np.size(wf.wfarr) 64 | wf.wfarr /= wf.ngrid 65 | 66 | if proper.phase_offset: 67 | wf.wfarr *= np.exp(complex(0.,1.) * 2*np.pi*dz/wf.lamda) 68 | 69 | wf.dx = wf.lamda * np.abs(dz) / (wf.ngrid * wf.dx) 70 | 71 | if proper.verbose: 72 | print(" WTS: z = ", wf.z, " dx = ", wf.dx) 73 | 74 | return 75 | -------------------------------------------------------------------------------- /falco/proper/switch_set.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 California Institute of Technology 2 | # Users must agree to abide by the restrictions listed in the 3 | # file "LegalStuff.txt" in the PROPER library directory. 4 | # 5 | # PROPER developed at Jet Propulsion Laboratory/California Inst. Technology 6 | # Original IDL version by John Krist 7 | # Python translation by Navtej Saini, with Luis Marchen and Nikta Amiri 8 | 9 | import falco.proper as proper 10 | 11 | def switch_set( name, **kwargs ): 12 | if name in kwargs and kwargs[name]: 13 | return True 14 | else: 15 | return False 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | [build-system] 5 | requires = [ 6 | "setuptools>=42", 7 | "wheel" 8 | ] 9 | build-backend = "setuptools.build_meta" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster==1.0.0 2 | astropy==7.0.0 3 | astropy-iers-data==0.2024.12.2.0.35.34 4 | babel==2.16.0 5 | certifi==2024.8.30 6 | charset-normalizer==3.4.0 7 | contourpy==1.3.1 8 | cycler==0.12.1 9 | deepmerge==2.0 10 | docutils==0.21.2 11 | fonttools==4.55.0 12 | idna==3.10 13 | imagesize==1.4.1 14 | iniconfig==2.0.0 15 | Jinja2==3.1.6 16 | kiwisolver==1.4.7 17 | MarkupSafe==3.0.2 18 | matplotlib==3.9.3 19 | mpi4py 20 | numpy==2.1.3 21 | packaging==24.2 22 | pillow==11.0.0 23 | pluggy==1.5.0 24 | psutil==6.1.0 25 | pyerfa==2.0.1.5 26 | Pygments==2.18.0 27 | pyparsing==3.2.0 28 | pytest==8.3.4 29 | python-dateutil==2.9.0.post0 30 | PyYAML==6.0.2 31 | requests==2.32.3 32 | scipy==1.14.1 33 | six==1.16.0 34 | snowballstemmer==2.2.0 35 | Sphinx==8.1.3 36 | sphinx-rtd-theme==3.0.2 37 | sphinxcontrib-applehelp==2.0.0 38 | sphinxcontrib-devhelp==2.0.0 39 | sphinxcontrib-htmlhelp==2.1.0 40 | sphinxcontrib-jquery==4.1 41 | sphinxcontrib-jsmath==1.0.1 42 | sphinxcontrib-qthelp==2.0.0 43 | sphinxcontrib-serializinghtml==2.0.0 44 | urllib3==2.2.3 45 | -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_design_hlc_band1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_design_hlc_band1.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_design_hlc_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_design_hlc_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_design_hlc_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_design_hlc_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_design_hlc_band4.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_design_hlc_band4.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_flat_hlc_band1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_flat_hlc_band1.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_flat_hlc_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_flat_hlc_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_flat_hlc_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_flat_hlc_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_flat_hlc_band4.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_flat_hlc_band4.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_spc-spec_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_spc-spec_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_spc-spec_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_spc-spec_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_spc-spec_rotated_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_spc-spec_rotated_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_spc-spec_rotated_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_spc-spec_rotated_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_spc-wide_band1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_spc-wide_band1.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm1_m_spc-wide_band4.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm1_m_spc-wide_band4.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_design_hlc_band1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_design_hlc_band1.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_design_hlc_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_design_hlc_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_design_hlc_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_design_hlc_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_design_hlc_band4.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_design_hlc_band4.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_flat_hlc_band1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_flat_hlc_band1.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_flat_hlc_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_flat_hlc_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_flat_hlc_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_flat_hlc_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_flat_hlc_band4.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_flat_hlc_band4.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_spc-spec_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_spc-spec_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_spc-spec_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_spc-spec_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_spc-spec_rotated_band2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_spc-spec_rotated_band2.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_spc-spec_rotated_band3.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_spc-spec_rotated_band3.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_spc-wide_band1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_spc-wide_band1.fits -------------------------------------------------------------------------------- /roman/flatmaps/dm2_m_spc-wide_band4.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/dm2_m_spc-wide_band4.fits -------------------------------------------------------------------------------- /roman/flatmaps/hlc_flattened_dm1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/hlc_flattened_dm1.fits -------------------------------------------------------------------------------- /roman/flatmaps/hlc_flattened_dm2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/hlc_flattened_dm2.fits -------------------------------------------------------------------------------- /roman/flatmaps/hlc_flattened_with_pattern_dm1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/hlc_flattened_with_pattern_dm1.fits -------------------------------------------------------------------------------- /roman/flatmaps/hlc_flattened_with_pattern_dm2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/hlc_flattened_with_pattern_dm2.fits -------------------------------------------------------------------------------- /roman/flatmaps/spc_spec_band3_flattened_dm1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/spc_spec_band3_flattened_dm1.fits -------------------------------------------------------------------------------- /roman/flatmaps/spc_spec_band3_flattened_dm2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/spc_spec_band3_flattened_dm2.fits -------------------------------------------------------------------------------- /roman/flatmaps/spc_wide_band4_flattened_dm1.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/spc_wide_band4_flattened_dm1.fits -------------------------------------------------------------------------------- /roman/flatmaps/spc_wide_band4_flattened_dm2.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/roman/flatmaps/spc_wide_band4_flattened_dm2.fits -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | package_name = pyfalco 3 | description = Fast Linearized Coronagraph Optimizer 4 | long_description = Fast Linearized Coronagraph Optimizer 5 | author = A J Eldorado Riggs and collaborators 6 | author_email = aj.riggs@jpl.nasa.gov 7 | license = Apache 8 | edit_on_github = False 9 | github_project = ajeldorado/falco-python 10 | description-file = README.rst 11 | url = https://github.com/ajeldorado/falco-python 12 | homepage = https://github.com/ajeldorado/falco-python 13 | # version should be PEP386 compatible (http://www.python.org/dev/peps/pep-0386) 14 | version = 3.2.1 15 | 16 | [build-sphinx] 17 | source-dir = docs 18 | build-dir = docs 19 | all_files = 1 20 | 21 | [upload_docs] 22 | upload-dir = docs/_build/html 23 | show-response = 1 24 | 25 | [tool:pytest] 26 | minversion = 3.0 27 | norecursedirs = build docs/_build 28 | 29 | # Python code quality checker 30 | [flake8] 31 | # See http://flake8.pycqa.org/en/latest/user/configuration.html 32 | exclude: .git, __pycache__, test* 33 | max-line-length = 120 34 | -------------------------------------------------------------------------------- /tests/functional/test_wfsc_flc.py: -------------------------------------------------------------------------------- 1 | """FALCO regression test of WFSC with a filtered Lyot coronagraph.""" 2 | from copy import deepcopy 3 | import numpy as np 4 | import os 5 | from math import isclose 6 | 7 | import falco 8 | 9 | import config_wfsc_flc as CONFIG 10 | 11 | 12 | def test_wfsc_flc(): 13 | 14 | mp = deepcopy(CONFIG.mp) 15 | mp.flagPlot = False 16 | LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) 17 | mp.path.falco = os.path.dirname(os.path.dirname(LOCAL_PATH)) 18 | 19 | # Generate the label associated with this trial 20 | mp.runLabel = 'testing_wfsc_flc' 21 | 22 | # Perform the Wavefront Sensing and Control 23 | out = falco.setup.flesh_out_workspace(mp) 24 | falco.wfsc.loop(mp, out) 25 | 26 | # print(out.IrawCorrHist[-1]) 27 | # print(out.IestScoreHist[-1]) 28 | # print(out.IincoCorrHist[-1]) 29 | # print(out.complexProjection[1, 0]) 30 | # print(out.dm1.Spv[-1]) 31 | # print(out.thput[-1]) 32 | # print(out.log10regHist) 33 | 34 | # Tests: 35 | Iend = out.IrawCorrHist[-1] # 1.1157e-5 in matlab, 1.073e-05 in python 36 | assert isclose(Iend, 1.073e-5, abs_tol=1e-6) 37 | 38 | Iest = out.IestScoreHist[-1] # 8.15e-06 in matlab, 8.123e-6 in python 39 | assert isclose(Iest, 8.123e-6, abs_tol=3e-7) 40 | 41 | Iinco = out.IincoCorrHist[-1] # 1.28e-5 in matlab, 1.165e-5 in python 42 | assert isclose(Iinco, 1.165e-5, abs_tol=3e-7) 43 | 44 | complexProj = out.complexProjection[1, 0] # 0.74 in matlab, 0.82 in python 45 | assert isclose(complexProj, 0.82, abs_tol=2e-2) 46 | 47 | dm1pv = out.dm1.Spv[-1] # 5.6956e-08 in matlab, 5.75e-8 in python 48 | assert isclose(dm1pv, 5.75e-8, abs_tol=1e-9) 49 | 50 | thput = out.thput[-1] # 0.1493 in matlab, 0.1492 in python 51 | assert isclose(thput, 0.1492, abs_tol=1e-3) 52 | 53 | assert np.allclose(out.log10regHist, np.array([-2, -2, -2]), rtol=1e-2) 54 | 55 | 56 | if __name__ == '__main__': 57 | test_wfsc_flc() 58 | -------------------------------------------------------------------------------- /tests/functional/test_wfsc_lc.py: -------------------------------------------------------------------------------- 1 | """FALCO regression test of WFSC with a Lyot coronagraph.""" 2 | from copy import deepcopy 3 | import numpy as np 4 | import os 5 | from math import isclose 6 | 7 | import falco 8 | 9 | import config_wfsc_lc as CONFIG 10 | 11 | 12 | def test_wfsc_lc(): 13 | 14 | mp = deepcopy(CONFIG.mp) 15 | mp.flagPlot = False 16 | LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) 17 | mp.path.falco = os.path.dirname(os.path.dirname(LOCAL_PATH)) 18 | 19 | # Generate the label associated with this trial 20 | mp.runLabel = 'testing_wfsc_lc' 21 | 22 | # Perform the Wavefront Sensing and Control 23 | out = falco.setup.flesh_out_workspace(mp) 24 | falco.wfsc.loop(mp, out) 25 | 26 | print(out.IrawScoreHist[-1]) 27 | 28 | print(out.IrawCorrHist[-1]) 29 | print(out.IestScoreHist[-1]) 30 | print(out.dm1.Spv[-1]) 31 | print(out.thput[-1]) 32 | print(out.log10regHist) 33 | 34 | # Tests: 35 | Iend = out.IrawCorrHist[-1] # 1.32e-7 in matlab, 1.34e-7 in python 36 | assert isclose(Iend, 1.34e-7, abs_tol=1e-8) 37 | 38 | Iest = out.IestScoreHist[-1] # 8.8397e-07 in matlab, 8.934e-7 in python 39 | assert isclose(Iest, 8.9e-7, abs_tol=1e-8) 40 | 41 | dm1pv = out.dm1.Spv[-1] # 8.267e-8, 8.271e-8 in python 42 | assert isclose(dm1pv, 8.27e-8, abs_tol=1e-9) 43 | 44 | thput = out.thput[-1] # 0.0740 45 | assert isclose(thput, 0.074, abs_tol=1e-3) 46 | 47 | assert np.allclose(out.log10regHist, np.array([-2, -2, -3]), rtol=1e-2) 48 | 49 | 50 | if __name__ == '__main__': 51 | test_wfsc_lc() 52 | -------------------------------------------------------------------------------- /tests/functional/test_wfsc_vc.py: -------------------------------------------------------------------------------- 1 | """FALCO regression test of WFSC with a vortex coronagraph.""" 2 | from copy import deepcopy 3 | import numpy as np 4 | import os 5 | from math import isclose 6 | 7 | import falco 8 | 9 | import config_wfsc_vc as CONFIG 10 | 11 | 12 | def test_wfsc_vc(): 13 | 14 | mp = deepcopy(CONFIG.mp) 15 | mp.flagPlot = False 16 | LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) 17 | mp.path.falco = os.path.dirname(os.path.dirname(LOCAL_PATH)) 18 | 19 | # Generate the label associated with this trial 20 | mp.runLabel = 'testing_wfsc_vc' 21 | 22 | # Perform the Wavefront Sensing and Control 23 | out = falco.setup.flesh_out_workspace(mp) 24 | falco.wfsc.loop(mp, out) 25 | 26 | # print(out.IrawCorrHist[-1]) 27 | # print(out.dm1.Spv[-1]) 28 | # print(out.thput[-1]) 29 | # print(out.log10regHist) 30 | 31 | # Tests: 32 | Iend = out.IrawCorrHist[-1] # 1.38e-10 in matlab, 1.2958e-10 in python 33 | assert isclose(Iend, 1.2958e-10, abs_tol=1e-11) 34 | 35 | dm1pv = out.dm1.Spv[-1] # 1.5057e-08 in matlab, 1.4218e-08 in python 36 | assert isclose(dm1pv, 1.42e-08, abs_tol=1e-9) 37 | 38 | thput = out.thput[-1] # 28.52% 39 | assert isclose(thput, 0.2852, abs_tol=1e-3) 40 | 41 | assert np.allclose(out.log10regHist, np.array([-4.5, -4, -4]), rtol=1e-2) 42 | 43 | 44 | if __name__ == '__main__': 45 | test_wfsc_vc() 46 | -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | python3 -m pytest -v -s 2 | -------------------------------------------------------------------------------- /tests/unit/pupil_CGI-20200513_8k_binary_noF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/tests/unit/pupil_CGI-20200513_8k_binary_noF.png -------------------------------------------------------------------------------- /tests/unit/test_configure_dark_hole_region.py: -------------------------------------------------------------------------------- 1 | """Unit test suite for falco.setup.falco_configure_dark_hole_region()""" 2 | import numpy as np 3 | from math import isclose 4 | import copy 5 | 6 | import falco 7 | 8 | 9 | def test_single_region(): 10 | 11 | mp = falco.config.ModelParameters() 12 | mp.Fend = falco.config.Object() 13 | mp.Fend.corr = falco.config.Object() 14 | mp.Fend.score = falco.config.Object() 15 | mp.Fend.eval = falco.config.Object() 16 | 17 | # Correction and scoring region definition 18 | mp.Fend.corr.Rin = 2 # inner radius [lambda0/D] 19 | mp.Fend.corr.Rout = 10 # outer radius [lambda0/D] 20 | mp.Fend.corr.ang = 180 # angular opening per side [degrees] 21 | mp.Fend.score = copy.deepcopy(mp.Fend.corr) 22 | 23 | mp.centering = 'pixel' 24 | mp.Fend.sides = 'lr' 25 | mp.Fend.shape = 'circle' 26 | mp.Fend.res = 10 27 | 28 | mp.Fend.eval.res = 20 29 | mp.thput_eval_x = 7 30 | mp.thput_eval_y = 0 31 | 32 | falco.setup.falco_configure_dark_hole_region(mp) 33 | 34 | area = np.sum(mp.Fend.corr.maskBool.astype(float)) 35 | 36 | areaExpected = (np.pi * (mp.Fend.corr.Rout**2 - mp.Fend.corr.Rin**2) * 37 | (2*mp.Fend.corr.ang/360) * mp.Fend.res**2) 38 | areaExpected = areaExpected.item() 39 | 40 | assert isclose(area, areaExpected, rel_tol=1e-3) 41 | 42 | 43 | def test_double_region(): 44 | 45 | mp = falco.config.ModelParameters() 46 | mp.Fend = falco.config.Object() 47 | mp.Fend.corr = falco.config.Object() 48 | mp.Fend.score = falco.config.Object() 49 | mp.Fend.eval = falco.config.Object() 50 | 51 | # Correction and scoring region definition 52 | mp.Fend.corr.Rin = [2, 2] # inner radius [lambda0/D] 53 | mp.Fend.corr.Rout = [5, 5] # outer radius [lambda0/D] 54 | mp.Fend.corr.ang = [150, 180] # angular opening per side [degrees] 55 | mp.Fend.score = copy.deepcopy(mp.Fend.corr) 56 | 57 | mp.centering = 'pixel' 58 | mp.Fend.sides = ['lr', 'lr'] 59 | mp.Fend.shape = ['circle', 'square'] 60 | mp.Fend.res = 10 61 | mp.Fend.xiOffset = [0, 20] 62 | 63 | mp.Fend.eval.res = 20 64 | mp.thput_eval_x = 7 65 | mp.thput_eval_y = 0 66 | 67 | falco.setup.falco_configure_dark_hole_region(mp) 68 | 69 | area = np.sum(mp.Fend.corr.maskBool.astype(int)) 70 | 71 | areaExpected = (np.pi * (mp.Fend.corr.Rout**2 - mp.Fend.corr.Rin**2) * 72 | (2*mp.Fend.corr.ang/360) * mp.Fend.res**2) 73 | 74 | areaExpected = (np.pi*(mp.Fend.corr.Rout[0]**2 - mp.Fend.corr.Rin[0]**2) * 75 | (2*mp.Fend.corr.ang[0]/360)*(mp.Fend.res**2) + 76 | (4*mp.Fend.corr.Rout[1]**2 - 77 | np.pi*mp.Fend.corr.Rin[1]**2)*(mp.Fend.res**2)) 78 | 79 | assert isclose(area, areaExpected, rel_tol=2e-2) 80 | 81 | 82 | if __name__ == '__main__': 83 | test_single_region() 84 | test_double_region() 85 | -------------------------------------------------------------------------------- /tests/unit/test_mask_fpm_annular.py: -------------------------------------------------------------------------------- 1 | """Unit test suite for falco.mask.gen_annular_fpm().""" 2 | import numpy as np 3 | from math import isclose 4 | 5 | from falco.mask import gen_annular_fpm 6 | from falco.util import pad_crop 7 | 8 | 9 | def test_area_spot(): 10 | inputs = {"pixresFPM": 6, 11 | "rhoInner": 3, 12 | "rhoOuter": np.inf, 13 | } 14 | fpm = gen_annular_fpm(inputs) 15 | 16 | area = np.sum(1 - fpm) 17 | areaExpected = np.pi * inputs["rhoInner"]**2 * inputs["pixresFPM"]**2 18 | assert isclose(area, areaExpected, rel_tol=1e-3) 19 | 20 | 21 | def test_area_partially_transmissive_spot(): 22 | inputs = {"pixresFPM": 6, 23 | "rhoInner": 3, 24 | "rhoOuter": np.inf, 25 | "FPMampFac": 0.2, 26 | } 27 | fpm = gen_annular_fpm(inputs) 28 | 29 | area = np.sum(1 - fpm) 30 | areaExpected = (np.pi * inputs["rhoInner"]**2 * inputs["pixresFPM"]**2 * 31 | (1-inputs["FPMampFac"])) 32 | assert isclose(area, areaExpected, rel_tol=1e-3) 33 | 34 | 35 | def test_area_annulus(): 36 | inputs = {"pixresFPM": 6, 37 | "rhoInner": 3, 38 | "rhoOuter": 10, 39 | } 40 | fpm = gen_annular_fpm(inputs) 41 | 42 | area = np.sum(fpm) 43 | areaExpected = (np.pi * inputs["pixresFPM"]**2 * 44 | (inputs["rhoOuter"]**2 - inputs["rhoInner"]**2)) 45 | assert isclose(area, areaExpected, rel_tol=1e-3) 46 | 47 | 48 | def test_translation_spot(): 49 | res = 6 50 | inputs = {"pixresFPM": res, 51 | "rhoInner": 3, 52 | "rhoOuter": np.inf, 53 | } 54 | fpm = gen_annular_fpm(inputs) 55 | 56 | inputs.update({"xOffset": 5.5, "yOffset": -10}) 57 | fpmOffset = gen_annular_fpm(inputs) 58 | 59 | fpmRecentered = np.roll(fpmOffset, 60 | [int(-res*inputs["yOffset"]), 61 | int(-res*inputs["xOffset"])], 62 | axis=(0, 1)) 63 | fpmPad = pad_crop(fpm, fpmRecentered.shape, extrapval=1) 64 | assert np.allclose(fpmPad, fpmRecentered, atol=1e-7) 65 | 66 | 67 | def test_translation_annulus(): 68 | res = 6 69 | inputs = {"pixresFPM": res, 70 | "rhoInner": 3, 71 | "rhoOuter": 10, 72 | } 73 | fpm = gen_annular_fpm(inputs) 74 | 75 | inputs.update({"xOffset": 5.5, "yOffset": -10}) 76 | fpmOffset = gen_annular_fpm(inputs) 77 | 78 | fpmRecentered = np.roll(fpmOffset, 79 | [int(-res*inputs["yOffset"]), 80 | int(-res*inputs["xOffset"])], 81 | axis=(0, 1)) 82 | fpmPad = pad_crop(fpm, fpmRecentered.shape) 83 | assert np.allclose(fpmPad, fpmRecentered, atol=1e-7) 84 | 85 | 86 | if __name__ == '__main__': 87 | test_area_spot() 88 | test_area_partially_transmissive_spot() 89 | test_area_annulus() 90 | test_translation_spot() 91 | test_translation_annulus() 92 | -------------------------------------------------------------------------------- /tests/unit/test_mask_fpm_bowtie.py: -------------------------------------------------------------------------------- 1 | """Unit test suite for falco.mask.gen_bowtie_fpm().""" 2 | import numpy as np 3 | from math import isclose 4 | 5 | from falco.mask import gen_bowtie_fpm 6 | from falco.util import pad_crop 7 | 8 | 9 | def test_area(): 10 | inputs = {"pixresFPM": 6, 11 | "rhoInner": 2.6, 12 | "rhoOuter": 9.4, 13 | "ang": 65, 14 | } 15 | fpm = gen_bowtie_fpm(inputs) 16 | 17 | area = np.sum(fpm) 18 | areaExpected = (np.pi * inputs["pixresFPM"]**2 * inputs["ang"]/180 * 19 | (inputs["rhoOuter"]**2 - inputs["rhoInner"]**2)) 20 | assert isclose(area, areaExpected, rel_tol=1e-3) 21 | 22 | 23 | def test_translation(): 24 | res = 6 25 | inputs = {"pixresFPM": res, 26 | "rhoInner": 2.6, 27 | "rhoOuter": 9.4, 28 | "ang": 65, 29 | "clocking": 20, 30 | } 31 | fpm = gen_bowtie_fpm(inputs) 32 | 33 | inputs.update({"xOffset": 5.5, "yOffset": -10}) 34 | fpmOffset = gen_bowtie_fpm(inputs) 35 | 36 | fpmRecentered = np.roll(fpmOffset, 37 | [int(-res*inputs["yOffset"]), 38 | int(-res*inputs["xOffset"])], 39 | axis=(0, 1)) 40 | fpmPad = pad_crop(fpm, fpmRecentered.shape) 41 | assert np.allclose(fpmPad, fpmRecentered, atol=1e-7) 42 | 43 | 44 | def test_rotation(): 45 | res = 6 46 | inputs = {"pixresFPM": res, 47 | "rhoInner": 2.6, 48 | "rhoOuter": 9.4, 49 | "ang": 65, 50 | } 51 | fpm = gen_bowtie_fpm(inputs) 52 | fpmRot = np.zeros_like(fpm) 53 | fpmRot[1::, 1::] = np.rot90(fpm[1::, 1::], -1) 54 | 55 | inputs.update({"xOffset": 5.5, "yOffset": -10, "clocking": 90}) 56 | fpmRotOffset = gen_bowtie_fpm(inputs) 57 | 58 | fpmRotRecentered = np.roll(fpmRotOffset, 59 | [int(-res*inputs["yOffset"]), 60 | int(-res*inputs["xOffset"])], 61 | axis=(0, 1)) 62 | fpmRotPad = pad_crop(fpmRot, fpmRotRecentered.shape) 63 | assert np.allclose(fpmRotPad, fpmRotRecentered, atol=1e-4) 64 | 65 | 66 | if __name__ == '__main__': 67 | test_area() 68 | test_translation() 69 | test_rotation() 70 | -------------------------------------------------------------------------------- /tests/unit/test_mask_pupil_LUVOIR_B.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from falco.mask import falco_gen_pupil_LUVOIR_B 4 | from falco.util import pad_crop 5 | 6 | 7 | def test_translation(): 8 | Nbeam = 200 9 | inputs = {"Nbeam": Nbeam} 10 | pupil = falco_gen_pupil_LUVOIR_B(inputs) 11 | 12 | inputs.update({"xShear": -11/100, "yShear": 19/100}) 13 | pupilOffset = falco_gen_pupil_LUVOIR_B(inputs) 14 | pupilRecentered = np.roll(pupilOffset, 15 | [int(-Nbeam*inputs["yShear"]), 16 | int(-Nbeam*inputs["xShear"])], 17 | axis=(0, 1)) 18 | diff = pad_crop(pupil, pupilOffset.shape) - pupilRecentered 19 | assert np.max(np.abs(diff)) <= 1e-8 20 | 21 | 22 | def test_translation_and_rotation(): 23 | Nbeam = 200 24 | inputs = {"Nbeam": Nbeam} 25 | pupil = falco_gen_pupil_LUVOIR_B(inputs) 26 | pupilRot = np.zeros_like(pupil) 27 | pupilRot[1::, 1::] = np.rot90(pupil[1::, 1::], -1) 28 | 29 | inputs.update({"xShear": -11/100, "yShear": 19/100, "clock_deg": 90}) 30 | pupilRotOffset = falco_gen_pupil_LUVOIR_B(inputs) 31 | pupilRotRecentered = np.roll(pupilRotOffset, 32 | [int(-Nbeam*inputs["yShear"]), 33 | int(-Nbeam*inputs["xShear"])], axis=(0, 1)) 34 | 35 | diff = pad_crop(pupilRot, pupilRotOffset.shape) - pupilRotRecentered 36 | assert np.max(np.abs(diff)) < 1/33 37 | 38 | 39 | if __name__ == '__main__': 40 | test_translation() 41 | test_translation_and_rotation() 42 | -------------------------------------------------------------------------------- /tests/unit/test_mask_pupil_simple.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from math import isclose 3 | 4 | import falco 5 | 6 | 7 | def test_area_circle(): 8 | inputs = {"Nbeam": 100, "Npad": 180, "OD": 1.0} 9 | pupil = falco.mask.falco_gen_pupil_Simple(inputs) 10 | areaExpected = np.pi/4*(inputs["OD"]*inputs["Nbeam"])**2 11 | area = np.sum(pupil) 12 | assert isclose(area, areaExpected, rel_tol=1e-5) 13 | 14 | 15 | def test_area_annulus(): 16 | inputs = {"Nbeam": 100, "Npad": 180, "OD": 1.0, "ID": 0.20} 17 | pupil = falco.mask.falco_gen_pupil_Simple(inputs) 18 | areaExpected = np.pi/4*(inputs["OD"]**2 - 19 | inputs["ID"]**2)*inputs["Nbeam"]**2 20 | area = np.sum(pupil) 21 | assert isclose(area, areaExpected, rel_tol=1e-5) 22 | 23 | 24 | def test_translation(): 25 | inputs = {"Nbeam": 100, "Npad": 180, "OD": 1.0, "ID": 0.20, 26 | "wStrut": 0.02, "angStrut": (10 + np.array([90, 180, 270, 315]))} 27 | pupilCentered = falco.mask.falco_gen_pupil_Simple(inputs) 28 | 29 | inputs["xShear"] = -11/inputs["Nbeam"] 30 | inputs["yShear"] = 19/inputs["Nbeam"] 31 | pupilOffcenter = falco.mask.falco_gen_pupil_Simple(inputs) 32 | pupilRecentered = np.roll(pupilOffcenter, 33 | (round(-inputs["Nbeam"]*inputs["yShear"]), 34 | round(-inputs["Nbeam"]*inputs["xShear"])), 35 | axis=(0, 1)) 36 | 37 | maxAbsDiff = np.max(np.abs(pupilRecentered - pupilCentered)) 38 | assert maxAbsDiff < 1e-6 39 | 40 | 41 | def test_translation_and_rotation(): 42 | inputs = {"Nbeam": 100, "Npad": 180, "OD": 1.0, "ID": 0.20, 43 | "wStrut": 0.02, "angStrut": (10 + np.array([90, 180, 270, 315]))} 44 | pupilCentered = falco.mask.falco_gen_pupil_Simple(inputs) 45 | pupilCentered = pupilCentered[1:, 1:] 46 | 47 | inputs["xShear"] = -11/inputs["Nbeam"] 48 | inputs["yShear"] = 19/inputs["Nbeam"] 49 | inputs["clocking"] = 90 50 | pupilOffcenterRot = falco.mask.falco_gen_pupil_Simple(inputs) 51 | pupilOffcenterRot = pupilOffcenterRot[1:, 1:] 52 | pupilRecentered = np.rot90(np.roll(pupilOffcenterRot, 53 | (round(-inputs["Nbeam"]*inputs["yShear"]), 54 | round(-inputs["Nbeam"]*inputs["xShear"])), 55 | axis=(0, 1)), 1) 56 | 57 | maxAbsDiff = np.max(np.abs(pupilRecentered - pupilCentered)) 58 | assert maxAbsDiff < 1e-2 59 | 60 | 61 | if __name__ == '__main__': 62 | test_area_circle() 63 | test_area_annulus() 64 | test_translation() 65 | test_translation_and_rotation() 66 | -------------------------------------------------------------------------------- /tests/unit/test_mp.py: -------------------------------------------------------------------------------- 1 | import falco 2 | 3 | 4 | class TestModelParametersClass: 5 | 6 | @classmethod 7 | def setup_class(cls): 8 | print("Setup TestClass!") 9 | 10 | @classmethod 11 | def teardown_class(cls): 12 | print("Teardown TestClass!") 13 | 14 | def setup_method(self, method): 15 | if method == self.test_CanCreateMpObject: 16 | print("\nSetting up for test_CanCreateMpObject...") 17 | else: 18 | print("\nSetting up Unknown test!") 19 | 20 | def teardown_method(self, method): 21 | if method == self.test_CanCreateMpObject: 22 | print("\nTearing down for test_CanCreateMpObject...") 23 | else: 24 | print("\nTearing down Unknown test!") 25 | 26 | def test_CanCreateMpObject(cls): 27 | """ Test creation of MP object """ 28 | mp = falco.config.ModelParameters() 29 | assert mp is not None 30 | -------------------------------------------------------------------------------- /tests/unit/testdata/pupil_CGI-20200513_8k_binary_noF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/tests/unit/testdata/pupil_CGI-20200513_8k_binary_noF.png -------------------------------------------------------------------------------- /tests/unit/testdata/ut_influence_dm5v2_inffix.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeldorado/falco-python/c63de8bc74cd9303e0e4bfe7bc8668d327a8676e/tests/unit/testdata/ut_influence_dm5v2_inffix.fits --------------------------------------------------------------------------------