├── .github └── workflows │ ├── build_wheels.yml │ └── run_tests.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── MANIFEST.in ├── README.md ├── RTModel ├── RTModel.py ├── __init__.py ├── data │ ├── ESPL.tbl │ └── TemplateLibrary.txt ├── include │ ├── LevMarFit.h │ └── bumper.h ├── lib │ ├── Finalizer.cpp │ ├── InitCond.cpp │ ├── LevMar.cpp │ ├── LevMarFit.cpp │ ├── LevMarFit.h │ ├── ModelSelector.cpp │ ├── Reader.cpp │ └── bumper.cpp ├── plotmodel │ ├── __init__.py │ └── plotmodel.py └── templates │ ├── __init__.py │ └── templates.py ├── changelog.md ├── docs ├── Animation.md ├── Archive.md ├── Constraints.md ├── DataPreparation.md ├── DataPreprocessing.md ├── FinalAssessment.md ├── Fitting.md ├── InitCond.md ├── LimbDarkening.md ├── ModelCategories.md ├── ModelSelection.md ├── ModelingRun.md ├── PlotModel.md ├── README.md ├── Satellite.md ├── Template.png ├── TemplateLibraries.md ├── ani.gif ├── plotchain.png ├── plotmodel_fig1.png └── plotmodel_fig2.png ├── events ├── OB190033.zip ├── event001.zip ├── event001done.zip ├── event002.zip ├── readme.md └── satellite1.txt ├── fVBM.py ├── jupyter └── Model_event001.ipynb ├── pyproject.toml └── tests ├── build_tests └── test_built_executables_exist.py └── end_to_end_tests ├── test_ps_run.py └── test_ps_run_resources └── example_event.zip /.github/workflows/build_wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.cpp' 7 | - '**.h' 8 | - '**.py' 9 | - '**.toml' 10 | - '**.yml' 11 | 12 | jobs: 13 | build_wheels: 14 | name: Build wheels on ${{ matrix.os }} 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest, macos-13,macos-14] # was macos-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Build wheels 24 | uses: pypa/cibuildwheel@v2.22.0 25 | env: # 26 | MACOSX_DEPLOYMENT_TARGET: "10.15" # 27 | CIBW_SKIP: pp* 28 | # CIBW_SOME_OPTION: value 29 | # ... 30 | #with: 31 | # package-dir: . 32 | # output-dir: wheelhouse 33 | # config-file: "{package}/pyproject.toml 34 | - uses: actions/upload-artifact@v4 35 | with: 36 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} 37 | path: ./wheelhouse/*.whl 38 | build_sdist: 39 | name: Build source distribution 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v4 43 | 44 | - name: Build sdist 45 | run: pipx run build --sdist 46 | 47 | - uses: actions/upload-artifact@v4 48 | with: 49 | name: cibw-sdist 50 | path: dist/*.tar.gz 51 | 52 | 53 | - uses: actions/upload-artifact@v4 54 | with: 55 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} 56 | path: ./wheelhouse/*.whl 57 | -------------------------------------------------------------------------------- /.github/workflows/run_tests.yml: -------------------------------------------------------------------------------- 1 | name: run_tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | run_tests: 7 | name: run_tests_on_${{ matrix.os }}_python_${{ matrix.python_version }}_pip_install_flag_${{ matrix.pip_install_flag }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ubuntu-latest, windows-latest, macos-latest] 13 | python_version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 14 | pip_install_flag: ["none", "editable", "user"] 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: setup_python_${{ matrix.python_version }} 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: ${{ matrix.python_version }} 23 | 24 | - name: install 25 | shell: bash 26 | run: | 27 | python -m pip install --upgrade pip 28 | if [ "${{ matrix.pip_install_flag }}" = "editable" ]; then 29 | pip_install_flag="--editable" 30 | elif [ "${{ matrix.pip_install_flag }}" = "user" ]; then 31 | pip_install_flag="--user" 32 | else 33 | pip_install_flag="" 34 | fi 35 | pip install -v ${pip_install_flag} . 36 | 37 | - name: test 38 | run: | 39 | pip install pytest 40 | cd .. 41 | python -m pytest RTModel/tests 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### C++ template 2 | # Prerequisites 3 | *.d 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Fortran module files 21 | *.mod 22 | *.smod 23 | 24 | # Compiled Static libraries 25 | *.lai 26 | *.la 27 | *.a 28 | *.lib 29 | 30 | # Executables 31 | *.exe 32 | *.out 33 | *.app 34 | 35 | ### Example user template template 36 | ### Example user template 37 | 38 | # IntelliJ project files 39 | .idea 40 | *.iml 41 | out 42 | gen 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | project(RTModel LANGUAGES CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 8 | set(MINIMUM_GCC_VERSION "9.0") 9 | if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MINIMUM_GCC_VERSION) 10 | message(FATAL_ERROR "GCC must be ${MINIMUM_GCC_VERSION} or greater.") 11 | endif() 12 | endif() 13 | 14 | # Find directory of VBMicrolensing 15 | 16 | find_package(Python3 COMPONENTS Interpreter Development) 17 | set(Python3_EXECUTABLE "./python") 18 | 19 | execute_process( 20 | COMMAND ${PYTHON_EXECUTABLE} fVBM.py 21 | #-c "import sys, os, inspect; sys.path.extend(os.environ['PYTHONPATH'].split(os.pathsep)); mod = __import__('VBMicrolensing'); dir1=os.path.dirname(inspect.getfile(mod)); print(dir1.replace(\"\\\",\"/\"))" 22 | OUTPUT_VARIABLE VBMICROLENSING_DIR 23 | OUTPUT_STRIP_TRAILING_WHITESPACE 24 | ) 25 | message(STATUS "Installation directory of VBMicrolensing: ${VBMICROLENSING_DIR}") 26 | 27 | 28 | # Build executables 29 | # ================= 30 | 31 | add_executable(Reader RTModel/lib/Reader.cpp) 32 | target_include_directories(Reader PRIVATE RTModel/include) 33 | 34 | add_executable(InitCond RTModel/lib/InitCond.cpp) 35 | target_include_directories(InitCond PRIVATE RTModel/include) 36 | 37 | add_executable(LevMar RTModel/lib/bumper.cpp RTModel/lib/LevMar.cpp RTModel/lib/LevMarFit.cpp ${VBMICROLENSING_DIR}/lib/VBMicrolensingLibrary.cpp) 38 | target_include_directories(LevMar PRIVATE RTModel/include ${VBMICROLENSING_DIR}/lib) 39 | 40 | add_executable(ModelSelector RTModel/lib/bumper.cpp RTModel/lib/ModelSelector.cpp) 41 | target_include_directories(ModelSelector PRIVATE RTModel/include) 42 | 43 | add_executable(Finalizer RTModel/lib/bumper.cpp RTModel/lib/Finalizer.cpp) 44 | target_include_directories(Finalizer PRIVATE RTModel/include) 45 | 46 | # Install executables 47 | # =================== 48 | 49 | if(SKBUILD) # Only run if called through scikit-build-core. That is, don't run when called through CMake directly. 50 | set(binary_directory "${SKBUILD_PLATLIB_DIR}/RTModel/bin") 51 | 52 | install(TARGETS Reader DESTINATION ${binary_directory}) 53 | install(TARGETS InitCond DESTINATION ${binary_directory}) 54 | install(TARGETS LevMar DESTINATION ${binary_directory}) 55 | install(TARGETS ModelSelector DESTINATION ${binary_directory}) 56 | install(TARGETS Finalizer DESTINATION ${binary_directory}) 57 | 58 | # Ensure data files are present in platlib. 59 | add_custom_command(TARGET Reader POST_BUILD 60 | COMMAND ${CMAKE_COMMAND} -E copy_directory 61 | "${CMAKE_CURRENT_LIST_DIR}/RTModel/data" "${SKBUILD_PLATLIB_DIR}/RTModel/data") 62 | endif() 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | 10 | This version of the GNU Lesser General Public License incorporates 11 | the terms and conditions of version 3 of the GNU General Public 12 | License, supplemented by the additional permissions listed below. 13 | 14 | 0. Additional Definitions. 15 | 16 | As used herein, "this License" refers to version 3 of the GNU Lesser 17 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 18 | General Public License. 19 | 20 | "The Library" refers to a covered work governed by this License, 21 | other than an Application or a Combined Work as defined below. 22 | 23 | An "Application" is any work that makes use of an interface provided 24 | by the Library, but which is not otherwise based on the Library. 25 | Defining a subclass of a class defined by the Library is deemed a mode 26 | of using an interface provided by the Library. 27 | 28 | A "Combined Work" is a work produced by combining or linking an 29 | Application with the Library. The particular version of the Library 30 | with which the Combined Work was made is also called the "Linked 31 | Version". 32 | 33 | The "Minimal Corresponding Source" for a Combined Work means the 34 | Corresponding Source for the Combined Work, excluding any source code 35 | for portions of the Combined Work that, considered in isolation, are 36 | based on the Application, and not on the Linked Version. 37 | 38 | The "Corresponding Application Code" for a Combined Work means the 39 | object code and/or source code for the Application, including any data 40 | and utility programs needed for reproducing the Combined Work from the 41 | Application, but excluding the System Libraries of the Combined Work. 42 | 43 | 1. Exception to Section 3 of the GNU GPL. 44 | 45 | You may convey a covered work under sections 3 and 4 of this License 46 | without being bound by section 3 of the GNU GPL. 47 | 48 | 2. Conveying Modified Versions. 49 | 50 | If you modify a copy of the Library, and, in your modifications, a 51 | facility refers to a function or data to be supplied by an Application 52 | that uses the facility (other than as an argument passed when the 53 | facility is invoked), then you may convey a copy of the modified 54 | version: 55 | 56 | a) under this License, provided that you make a good faith effort to 57 | ensure that, in the event an Application does not supply the 58 | function or data, the facility still operates, and performs 59 | whatever part of its purpose remains meaningful, or 60 | 61 | b) under the GNU GPL, with none of the additional permissions of 62 | this License applicable to that copy. 63 | 64 | 3. Object Code Incorporating Material from Library Header Files. 65 | 66 | The object code form of an Application may incorporate material from 67 | a header file that is part of the Library. You may convey such object 68 | code under terms of your choice, provided that, if the incorporated 69 | material is not limited to numerical parameters, data structure 70 | layouts and accessors, or small macros, inline functions and templates 71 | (ten or fewer lines in length), you do both of the following: 72 | 73 | a) Give prominent notice with each copy of the object code that the 74 | Library is used in it and that the Library and its use are 75 | covered by this License. 76 | 77 | b) Accompany the object code with a copy of the GNU GPL and this license 78 | document. 79 | 80 | 4. Combined Works. 81 | 82 | You may convey a Combined Work under terms of your choice that, 83 | taken together, effectively do not restrict modification of the 84 | portions of the Library contained in the Combined Work and reverse 85 | engineering for debugging such modifications, if you also do each of 86 | the following: 87 | 88 | a) Give prominent notice with each copy of the Combined Work that 89 | the Library is used in it and that the Library and its use are 90 | covered by this License. 91 | 92 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 93 | document. 94 | 95 | c) For a Combined Work that displays copyright notices during 96 | execution, include the copyright notice for the Library among 97 | these notices, as well as a reference directing the user to the 98 | copies of the GNU GPL and this license document. 99 | 100 | d) Do one of the following: 101 | 102 | 0) Convey the Minimal Corresponding Source under the terms of this 103 | License, and the Corresponding Application Code in a form 104 | suitable for, and under terms that permit, the user to 105 | recombine or relink the Application with a modified version of 106 | the Linked Version to produce a modified Combined Work, in the 107 | manner specified by section 6 of the GNU GPL for conveying 108 | Corresponding Source. 109 | 110 | 1) Use a suitable shared library mechanism for linking with the 111 | Library. A suitable mechanism is one that (a) uses at run time 112 | a copy of the Library already present on the user's computer 113 | system, and (b) will operate properly with a modified version 114 | of the Library that is interface-compatible with the Linked 115 | Version. 116 | 117 | e) Provide Installation Information, but only if you would otherwise 118 | be required to provide such information under section 6 of the 119 | GNU GPL, and only to the extent that such information is 120 | necessary to install and execute a modified version of the 121 | Combined Work produced by recombining or relinking the 122 | Application with a modified version of the Linked Version. (If 123 | you use option 4d0, the Installation Information must accompany 124 | the Minimal Corresponding Source and Corresponding Application 125 | Code. If you use option 4d1, you must provide the Installation 126 | Information in the manner specified by section 6 of the GNU GPL 127 | for conveying Corresponding Source.) 128 | 129 | 5. Combined Libraries. 130 | 131 | You may place library facilities that are a work based on the 132 | Library side by side in a single library together with other library 133 | facilities that are not Applications and are not covered by this 134 | License, and convey such a combined library under terms of your 135 | choice, if you do both of the following: 136 | 137 | a) Accompany the combined library with a copy of the same work based 138 | on the Library, uncombined with any other library facilities, 139 | conveyed under the terms of this License. 140 | 141 | b) Give prominent notice with the combined library that part of it 142 | is a work based on the Library, and explaining where to find the 143 | accompanying uncombined form of the same work. 144 | 145 | 6. Revised Versions of the GNU Lesser General Public License. 146 | 147 | The Free Software Foundation may publish revised and/or new versions 148 | of the GNU Lesser General Public License from time to time. Such new 149 | versions will be similar in spirit to the present version, but may 150 | differ in detail to address new problems or concerns. 151 | 152 | Each version is given a distinguishing version number. If the 153 | Library as you received it specifies that a certain numbered version 154 | of the GNU Lesser General Public License "or any later version" 155 | applies to it, you have the option of following the terms and 156 | conditions either of that published version or of any later version 157 | published by the Free Software Foundation. If the Library as you 158 | received it does not specify a version number of the GNU Lesser 159 | General Public License, you may choose any version of the GNU Lesser 160 | General Public License ever published by the Free Software Foundation. 161 | 162 | If the Library as you received it specifies that a proxy can decide 163 | whether future versions of the GNU Lesser General Public License shall 164 | apply, that proxy's public statement of acceptance of any version is 165 | permanent authorization for you to choose that version for the 166 | Library. 167 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft RTModel/data 2 | graft RTModel/lib 3 | graft RTModel/include 4 | graft RTModel/bin 5 | global-exclude RTModel/lib/*.o* 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RTModel 2 | `RTModel` is a package for modeling and interpretation of microlensing events. It uses photometric time series collected from ground and/or space telescopes to propose one or more possible models among the following: 3 | - Single-lens-single-source microlensing (i.e. Paczynski) 4 | - Single-lens-binary-source microlensing (with or without xallarap) 5 | - Binary-lens-single-source microlensing (including planetary microlensing, parallax and orbital motion) 6 | 7 | All models include the finite-size of the source(s). 8 | 9 | The modeling strategy is based on a grid search in the parameter space for single-lens models, whereas a **template library** for binary-lens models is used including all possible geometries of the source trajectory with respect to the caustics. In addition to this global search, planets are searched where maximal deviations from a Paczynski model occurs. 10 | 11 | The library is in the form of a standard Python package that launches specific subprocesses for different tasks. Model fitting is executed in **parallel** exploiting available processors in the machine. The full modeling may take from one to three hours depending on the event and on the machine speed. The results of modeling are given in the form of a text **assessment file**; in addition, **final models** are made available with their parameters and covariance matrices. 12 | 13 | `RTModel` also includes a subpackage **`RTModel.plotmodel`** that allows an immediate visualization of models and the possibility to review each individual fitting process as an animated gif. 14 | 15 | A second subpackage **`RTModel.templates`** helps the user in the visualization and customization of the template library. 16 | 17 | ## Attribution 18 | 19 | `RTModel` has been created by Valerio Bozza (University of Salerno) as a product of many years of direct experience on microlensing modeling (see [RTModel webpage](http://www.fisica.unisa.it/GravitationAstrophysics/RTModel.htm)). 20 | 21 | Any scientific use of `RTModel` should be acknowledged by citing the paper [V.Bozza, A&A 688 (2024) 83](https://ui.adsabs.harvard.edu/abs/2024A%26A...688A..83B/abstract), describing all the algorithms behind the code. 22 | 23 | We are grateful to Greg Olmschenk, who revised the package installation in order to make it as cross-platform as possible. We also thank all the users who are providing suggestions, reporting bugs or failures: Etienne Bachelet, David Bennett, Jonathan Brashear, Stela Ishitani Silva, Yiannis Tsapras, Keto Zhang. 24 | 25 | ## Installation 26 | 27 | The easiest way to install `RTModel` is through `pip`. 28 | 29 | First clone this repository. 30 | 31 | Then go to the repository directory and type 32 | 33 | ``` 34 | pip install . 35 | ``` 36 | 37 | In alternative, you may directly install it from PyPI without cloning this repository: 38 | 39 | ``` 40 | pip install RTModel 41 | ``` 42 | 43 | Currently, `RTModel` works on Linux, Windows and MacOS, requiring Python >= 3.6. 44 | A C++ compiler compatible with C++17 standard is needed for installation. 45 | `RTModel` also incorporates version 4.1 of [`VBMicrolensing`](https://github.com/valboz/VBMicrolensing). You are encouraged to cite the relevant papers listed in that repository as well. 46 | 47 | ## Documentation 48 | Full [documentation for the use of RTModel](docs/README.md) is available. 49 | 50 | In the directory [events](events) we provide some microlensing data on which you may practise with `RTModel`. 51 | 52 | A Jupyter notebook for quick start-up is also available in the [jupyter](jupyter) folder. 53 | 54 | ## License 55 | `RTModel` is freely available to the community under the 56 | GNU Lesser General Public License Version 3 included in this repository. 57 | 58 | 59 | -------------------------------------------------------------------------------- /RTModel/RTModel.py: -------------------------------------------------------------------------------- 1 | import site 2 | import subprocess 3 | import os 4 | import sys 5 | import glob 6 | import time 7 | from pathlib import Path 8 | from tqdm import tqdm 9 | import shutil 10 | 11 | 12 | class RTModel: 13 | def __init__(self, event = None): 14 | # Directory preliminaries 15 | print('*********************') 16 | print('**** RTModel ****') 17 | print('*********************') 18 | self.bindir = self.find_bin_directory() 19 | if(os.path.exists(self.bindir + 'Reader.exe')): 20 | self.readerexe = 'Reader.exe' 21 | self.initcondexe = 'InitCond.exe' 22 | self.levmarexe = 'LevMar.exe' 23 | self.modelselectorexe = 'ModelSelector.exe' 24 | self.finalizerexe = 'Finalizer.exe' 25 | else: 26 | self.readerexe = 'Reader' 27 | self.initcondexe = 'InitCond' 28 | self.levmarexe = 'LevMar' 29 | self.modelselectorexe = 'ModelSelector' 30 | self.finalizerexe = 'Finalizer' 31 | if(event == None): 32 | self.eventname = os.getcwd() 33 | else: 34 | self.eventname = os.path.realpath(event) 35 | print("Event name: " + self.eventname) 36 | self.inidir = "ini" 37 | self.modelcodes = ['PS', 'PX', 'BS', 'BO', 'LS', 'LX', 'LO', 'LK', 'TS', 'TX'] 38 | self.endphase = len(self.modelcodes)*2+3 39 | self.eventinifile = 'event.ini' 40 | self.nprocessors = os.cpu_count() 41 | print('Number of processors: {}'.format(self.nprocessors)) 42 | self.config_Reader() 43 | self.config_InitCond() 44 | self.config_LevMar() 45 | self.config_ModelSelector() 46 | self.satellitedir = '.' 47 | 48 | def set_processors(self, nprocessors): 49 | self.nprocessors = nprocessors 50 | 51 | def set_event(self, event): 52 | self.eventname = os.path.realpath(event) 53 | 54 | def set_satellite_dir(self, satellitedir): 55 | self.satellitedir = satellitedir 56 | 57 | def set_constraints(self, constraints = None): 58 | self.constraints = constraints 59 | if(not os.path.exists(self.eventname + '/' + self.inidir)): 60 | os.makedirs(self.eventname + '/' + self.inidir) 61 | with open(self.eventname + '/' + self.inidir + '/Constraints.ini','w') as f: 62 | for cons in constraints: 63 | f.write(cons[0] + ' = '+ str(cons[1]) + ' '+ str(cons[2]) + ' '+ str(cons[3]) + ' ' + '\n') 64 | 65 | def config_Reader(self, tau = 0.1, binning = 4000, otherseasons = 1, renormalize = 1, thresholdoutliers = 10): 66 | self.Reader_tau= tau # conventional correlation time for consecutive points 67 | self.Reader_binning = binning # maximum number of points left after re-binning 68 | self.Reader_otherseasons = otherseasons # How to use other seasons (0 = Yes, 1 = decrease significance, 2 = remove) 69 | self.Reader_renormalize = renormalize # Re-normalize error bars if non-zero 70 | self.Reader_thresholdoutliers = thresholdoutliers # Threshold in sigmas for removing outliers 71 | 72 | def Reader(self): 73 | if(not os.path.exists(self.eventname + '/' + self.inidir)): 74 | os.makedirs(self.eventname + '/' + self.inidir) 75 | with open(self.eventname + '/' + self.inidir + '/Reader.ini','w') as f: 76 | f.write('tau = ' + str(self.Reader_tau) + '\n') 77 | f.write('binning = ' + str(self.Reader_binning) + '\n') 78 | f.write('otherseasons = ' + str(self.Reader_otherseasons) + '\n') 79 | f.write('renormalize = ' + str(self.Reader_renormalize) + '\n') 80 | f.write('thresholdoutliers = ' + str(self.Reader_thresholdoutliers) + '\n') 81 | print('- Launching: Reader') 82 | print(' Pre-processing data...') 83 | try: 84 | completedprocess=subprocess.run([self.bindir+self.readerexe,self.eventname], cwd = self.bindir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text = True) 85 | print(' OK') 86 | except subprocess.CalledProcessError as e: 87 | print('\033[30;41m! Error in pre-processing. Please check your data!\033[m') 88 | print('\033[30;43m'+e.stdout+'\033[m') 89 | print('\033[30;43m'+e.stderr+'\033[m') 90 | print('\033[30;41m! Program stopped here!\033[m') 91 | self.done = True 92 | 93 | def config_InitCond(self, npeaks = 2, peakthreshold = 10.0, oldmodels = 4, override = None, nostatic = False, onlyorbital = False, usesatellite = 0 94 | , templatelibrary = None, modelcategories = ['PS','PX','BS','BO','LS','LX','LO'], onlyupdate =False): 95 | self.InitCond_npeaks = npeaks # Number of peaks in the observed light curve to be considered for setting initial conditions. 96 | self.InitCond_peakthreshold = peakthreshold # Number of sigmas necessary for a deviation to be identified as a maximum or a minimum. 97 | self.InitCond_oldmodels = oldmodels # Maximum number of old models to include in new run as initial conditions 98 | self.InitCond_override = override # Override peak identification and manually set peak times 99 | self.InitCond_nostatic = nostatic or onlyorbital # No static models will be calculated. 100 | self.InitCond_onlyorbital = onlyorbital # Only orbital motion models will be calculated. 101 | self.InitCond_usesatellite = usesatellite # Satellite to be used for initial conditions. Ground telescopes by default. 102 | self.InitCond_templatelibrary = templatelibrary # Template library to be used in place of the default one. 103 | self.InitCond_modelcategories = modelcategories # Model categories to be fit 104 | self.InitCond_onlyupdate = onlyupdate # No search but only update of previously found best models 105 | 106 | def InitCond(self): 107 | ''' Establishes initial conditions for fitting by executing the InitCond external module. 108 | Options can be assigned through the config_InitCond() method. ''' 109 | if(not os.path.exists(self.eventname + '/' + self.inidir)): 110 | os.makedirs(self.eventname + '/' + self.inidir) 111 | with open(self.eventname + '/' + self.inidir + '/InitCond.ini','w') as f: 112 | f.write('npeaks = ' + str(self.InitCond_npeaks) + '\n') 113 | f.write('peakthreshold = ' + str(self.InitCond_peakthreshold) + '\n') 114 | f.write('oldmodels = ' + str(self.InitCond_oldmodels) + '\n') 115 | f.write('usesatellite = ' + str(self.InitCond_usesatellite) + '\n') 116 | if(self.InitCond_nostatic): 117 | f.write('nostatic = 1\n') 118 | if(self.InitCond_onlyorbital): 119 | f.write('onlyorbital = 1\n') 120 | if(self.InitCond_override != None): 121 | f.write('override = ' + str(self.InitCond_override[0])+ ' ' + str(self.InitCond_override[1]) + '\n') 122 | if(self.InitCond_templatelibrary != None): 123 | f.write('templatelibrary = ' + self.InitCond_templatelibrary + '\n') 124 | if(self.InitCond_modelcategories != None): 125 | f.write('modelcategories = '+ ''.join(self.InitCond_modelcategories) + '\n') 126 | if(self.InitCond_onlyupdate): 127 | f.write('onlyupdate = 1\n') 128 | print('- Launching: InitCond') 129 | print(' Setting initial conditions...') 130 | try: 131 | completedprocess=subprocess.run([self.bindir+self.initcondexe,self.eventname], cwd = self.bindir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text = True) 132 | peaksearch = True 133 | imod=0 134 | while(peaksearch): 135 | initfils=glob.glob(self.eventname + '/InitCond/InitCond'+ self.modelcodes[imod] + '*') 136 | if(len(initfils)!=0): 137 | peaksearch = False 138 | with open(initfils[0], 'r') as f: 139 | npeaks = int(f.readline().split()[0]) 140 | print('Peaks: ',end ='') 141 | for i in range(0,npeaks): 142 | print(f'{float(f.readline().split()[0]):.4f}',end = ' ') 143 | imod+=1 144 | print('\n OK') 145 | except subprocess.CalledProcessError as e: 146 | print('\033[30;41m! Error in setting initial conditions!\033[m') 147 | print('\033[30;43m'+e.stdout+'\033[m') 148 | print('\033[30;43m'+e.stderr+'\033[m') 149 | print('\033[30;41m! Program stopped here!\033[m') 150 | self.done = True 151 | 152 | def config_LevMar(self, nfits = 6, offsetdegeneracy = 3, timelimit = 600.0, maxsteps = 50, bumperpower = 2.0): 153 | self.LevMar_nfits = nfits # Number of models to be calculated from the same initial condition using the bumper method 154 | self.LevMar_offsetdegeneracy = offsetdegeneracy # Number of models to be fit after applying offset degeneracy to best model found so far 155 | self.LevMar_maxsteps = maxsteps # Maximum number of steps in each fit 156 | self.LevMar_timelimit = timelimit # Maximum time in seconds for total execution 157 | self.LevMar_bumperpower = bumperpower # Repulsion factor of bumpers 158 | 159 | def LevMar(self,strmodel, parameters_file = None, parameters = None): 160 | if(not os.path.exists(self.eventname + '/' + self.inidir)): 161 | os.makedirs(self.eventname + '/' + self.inidir) 162 | if(parameters != None): 163 | parameters_file = self.eventname + '/' + self.inidir + '/parameters.ini' 164 | with open(parameters_file,'w') as f: 165 | line ='' 166 | for fl in parameters: 167 | line = line + str(fl) + ' ' 168 | f.write(line) 169 | with open(self.eventname + '/' + self.inidir + '/LevMar.ini','w') as f: 170 | f.write('nfits = ' + str(self.LevMar_nfits) + '\n') 171 | f.write('offsetdegeneracy = ' + str(self.LevMar_offsetdegeneracy) + '\n') 172 | f.write('maxsteps = ' + str(self.LevMar_maxsteps) + '\n') 173 | f.write('timelimit = ' + str(self.LevMar_timelimit) + '\n') 174 | f.write('bumperpower = ' + str(self.LevMar_bumperpower) + '\n') 175 | if(parameters_file != None): 176 | f.write('parametersfile = ' + parameters_file) 177 | print('- Launching: LevMar') 178 | print(' Fitting ' + strmodel + ' ...') 179 | try: 180 | completedprocess=subprocess.run([self.bindir+self.levmarexe,self.eventname, strmodel,self.satellitedir], cwd = self.bindir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text = True) 181 | print(' OK') 182 | except subprocess.CalledProcessError as e: 183 | print('\033[30;41m! Error in fit!\033[m') 184 | print('\033[30;43m'+e.stdout+'\033[m') 185 | print('\033[30;43m'+e.stderr+'\033[m') 186 | print('\033[30;41m! Program stopped here!\033[m') 187 | self.done = True 188 | 189 | def launch_fits(self,modelcode): 190 | if(not os.path.exists(self.eventname + '/' + self.inidir)): 191 | os.makedirs(self.eventname + '/' + self.inidir) 192 | with open(self.eventname + '/' + self.inidir + '/LevMar.ini','w') as f: 193 | f.write('nfits = ' + str(self.LevMar_nfits) + '\n') 194 | f.write('offsetdegeneracy = ' + str(self.LevMar_offsetdegeneracy) + '\n') 195 | f.write('maxsteps = ' + str(self.LevMar_maxsteps) + '\n') 196 | f.write('timelimit = ' + str(self.LevMar_timelimit) + '\n') 197 | f.write('bumperpower = ' + str(self.LevMar_bumperpower) + '\n') 198 | stringfits = {'PS' : '- Single-lens-Single-source fits', 199 | 'PX' : '- Single-lens-Single-source fits with parallax', 200 | 'BS' : '- Single-lens-Binary-source fits', 201 | 'BO' : '- Single-lens-Binary-source fits with xallarap', 202 | 'LS' : '- Binary-lens-Single-source fits', 203 | 'LX' : '- Binary-lens-Single-source fits with parallax', 204 | 'LO' : '- Binary-lens-Single-source fits with orbital motion', 205 | 'LK' : '- Binary-lens-Single-source fits with eccentric orbital motion', 206 | 'TS' : '- Triple-lens-Single-source fits', 207 | 'TX' : '- Triple-lens-Single-source fits with parallax'} 208 | print(stringfits[modelcode]) 209 | initcondfile = self.eventname + '/InitCond/' + 'InitCond'+ modelcode + '.txt' 210 | if(os.path.exists(initcondfile)): 211 | with open(self.eventname + '/InitCond/' + 'InitCond'+ modelcode + '.txt') as f: 212 | line = f.readline().split() 213 | npeaks = int(line[0]) 214 | ninitconds = int(line[1]) 215 | processes = [] 216 | procnumbers = [] 217 | procepochs = [] 218 | iinitcond = 0 219 | finitcond = 0 220 | finitcondold = -1 221 | timeouts = 0 222 | crashes = 0 223 | pbar = tqdm(total = ninitconds,desc = 'Fits completed',file=sys.stdout, colour='GREEN', smoothing = 0) 224 | while(finitcond < ninitconds): 225 | i=0 226 | while i < len(processes): 227 | if(time.time() - procepochs[i] > self.LevMar_timelimit): 228 | processes[i].kill() 229 | timeouts += 1 230 | crashes -= 1 231 | premodfiles = glob.glob(self.eventname +'/PreModels/*.txt') 232 | strmodel = modelcode + '{:0>4}'.format(str(procnumbers[i])) 233 | with open(self.eventname +'/PreModels/' + strmodel + '/t' + strmodel + '.dat','w') as f: 234 | f.write(f'{len(premodfiles)} {self.LevMar_nfits}') 235 | if(processes[i].poll() != None): 236 | if(processes[i].returncode!=0): 237 | crashes +=1 238 | processes.pop(i) 239 | procnumbers.pop(i) 240 | procepochs.pop(i) 241 | finitcond += 1 242 | else: 243 | i += 1 244 | while(iinitcond < ninitconds and len(processes) < self.nprocessors): 245 | strmodel = modelcode + '{:0>4}'.format(str(iinitcond)) 246 | if(glob.glob(self.eventname +'/PreModels/' + strmodel + '/t' + strmodel + '.dat')==[]): 247 | processes.append(subprocess.Popen([self.bindir+self.levmarexe,self.eventname, strmodel,self.satellitedir], cwd = self.bindir, shell = False, stdout=subprocess.DEVNULL)) 248 | procnumbers.append(iinitcond) 249 | procepochs.append(time.time()) 250 | else: 251 | finitcond += 1 252 | iinitcond += 1 253 | if(finitcond != finitcondold): 254 | #print(' Fits launched: {}; completed: {}/{}'.format(iinitcond, finitcond, ninitconds)) 255 | pbar.update(finitcond - max(finitcondold,0)) 256 | finitcondold =finitcond 257 | time.sleep(0.1) 258 | pbar.close() 259 | if(crashes>0): 260 | print('crashed fits: ' + str(crashes)) 261 | if(timeouts>0): 262 | print('timed out fits: ' + str(timeouts)) 263 | print(' OK') 264 | else: 265 | print('- No initial conditions for this category') 266 | 267 | def config_ModelSelector(self, sigmasoverlap = 3.0, sigmachisquare = 1.0, maxmodels = 10): 268 | self.ModelSelector_sigmasoverlap = sigmasoverlap # factor multiplying the inverse covariance in search for superpositions (models are incompatible if farther than sigmasoverlap*sigma) 269 | self.ModelSelector_sigmachisquare = sigmachisquare # number of sigmas in the chi square distribution for accepting alternative models after the best one 270 | self.ModelSelector_maxmodels = maxmodels # maximum number of models returned 271 | 272 | def ModelSelector(self, modelcode): 273 | if(not os.path.exists(self.eventname + '/' + self.inidir)): 274 | os.makedirs(self.eventname + '/' + self.inidir) 275 | with open(self.eventname + '/' + self.inidir + '/ModelSelector.ini','w') as f: 276 | f.write('sigmasoverlap = ' + str(self.ModelSelector_sigmasoverlap) + '\n') 277 | f.write('sigmachisquare = ' + str(self.ModelSelector_sigmachisquare) + '\n') 278 | f.write('maxmodels = ' + str(self.ModelSelector_maxmodels) + '\n') 279 | stringmodels = {'PS' : '- Selecting models for Single-lens-Single-source fits', 280 | 'PX' : '- Selecting models for Single-lens-Single-source fits with parallax', 281 | 'BS' : '- Selecting models for Single-lens-Binary-source fits', 282 | 'BO' : '- Selecting models for Single-lens-Binary-source fits with xallarap', 283 | 'LS' : '- Selecting models for Binary-lens-Single-source fits', 284 | 'LX' : '- Selecting models for Binary-lens-Single-source fits with parallax', 285 | 'LO' : '- Selecting models for Binary-lens-Single-source fits with orbital motion', 286 | 'LK' : '- Selecting models for Binary-lens-Single-source fits with eccentric orbital motion', 287 | 'TS' : '- Selecting models for Triple-lens-Single-source fits', 288 | 'TX' : '- Selecting models for Triple-lens-Single-source fits with parallax'} 289 | print(stringmodels[modelcode]) 290 | try: 291 | completedprocess=subprocess.run([self.bindir+self.modelselectorexe,self.eventname, modelcode], cwd = self.bindir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text = True) 292 | print(' OK') 293 | except subprocess.CalledProcessError as e: 294 | print('\033[30;41m! Error in model selection!\033[m') 295 | print('\033[30;43m'+e.stdout+'\033[m') 296 | print('\033[30;43m'+e.stderr+'\033[m') 297 | print('\033[30;41m! Program stopped here!\033[m') 298 | self.done = True 299 | 300 | def Finalizer(self): 301 | print('- Launching: Finalizer') 302 | print(' Making final assessment for this event') 303 | try: 304 | completedprocess=subprocess.run([self.bindir+self.finalizerexe,self.eventname], cwd = self.bindir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text = True) 305 | with open(self.eventname + '/Nature.txt') as f: 306 | for line in f.readlines(): 307 | print(" " + line,end='') 308 | print(" OK") 309 | except subprocess.CalledProcessError as e: 310 | print('\033[30;41m! Error in finalization!\033[m') 311 | print('\033[30;43m'+e.stdout+'\033[m') 312 | print('\033[30;43m'+e.stderr+'\033[m') 313 | print('\033[30;41m! Program stopped here!\033[m') 314 | self.done = True 315 | 316 | def run(self, event = None, cleanup = False): 317 | phase =0 318 | if(event!= None): 319 | self.eventname = os.path.realpath(event) 320 | self.done = False 321 | print("o " + time.asctime()) 322 | while not(self.done): 323 | # Check that event directory exists 324 | if phase == 0: 325 | if(os.path.exists(self.eventname + '/Data')): 326 | print('- Analyzing event: ',self.eventname) 327 | phase = 1 328 | else: 329 | print('! Event data for ' + self.eventname + ' not found !') 330 | self.done = True 331 | # Launch Reader 332 | elif phase == 1: 333 | self.Reader() 334 | print("o " + time.asctime()) 335 | phase = 2 336 | # Launch InitCond 337 | elif phase == 2: 338 | self.InitCond() 339 | print("o " + time.asctime()) 340 | phase = 3 341 | # Launch Finalizer 342 | elif phase == self.endphase: 343 | self.Finalizer() 344 | print("o " + time.asctime()) 345 | phase += 1 346 | # Conclude analysis 347 | elif phase > self.endphase: 348 | if(cleanup): 349 | print('- Cleaning up preliminary models') 350 | self.cleanup_preliminary_models() 351 | print("- Analysis of " + self.eventname + " successfully completed!") 352 | print("o " + time.asctime()) 353 | self.done = True 354 | # Launch LevMar for next class 355 | elif phase%2 == 1: 356 | if(self.InitCond_modelcategories == None or self.modelcodes[phase//2-1] in self.InitCond_modelcategories): 357 | self.launch_fits(self.modelcodes[phase//2-1]) 358 | print("o " + time.asctime()) 359 | phase += 1 360 | # Launch ModelSelector for this class 361 | else: 362 | if(self.InitCond_modelcategories == None or self.modelcodes[phase//2-2] in self.InitCond_modelcategories): 363 | self.ModelSelector(self.modelcodes[phase//2-2]) 364 | print("o " + time.asctime()) 365 | phase += 1 366 | 367 | def cleanup_preliminary_models(self): 368 | os.chdir(self.eventname) 369 | if(os.path.exists('PreModels')): 370 | shutil.rmtree('PreModels') 371 | 372 | def archive_run(self, destination = None): 373 | olddir = os.getcwd() 374 | os.chdir(self.eventname) 375 | if(os.path.exists('LCToFit.txt')): 376 | previousrunslist = glob.glob('run-*') 377 | previousrunslist.sort() 378 | if(destination == None): 379 | if(len(previousrunslist)>0): 380 | lastrun = int(previousrunslist[-1].split('-')[-1]) 381 | else: 382 | lastrun = 0 383 | rundir = 'run-' + str(lastrun+1). zfill(4) 384 | else: 385 | rundir = destination 386 | alllist = glob.glob('*') 387 | alllist.remove('Data') 388 | filelist = list(set(alllist) - set(previousrunslist)) 389 | os.mkdir(rundir) 390 | shutil.copytree('Data',rundir+'/Data') 391 | for nam in filelist: 392 | shutil.move(nam,rundir) 393 | os.chdir(olddir) 394 | 395 | def recover_options(self,run = None): 396 | if(self.eventname == None): 397 | print('! No event chosen') 398 | if(run!=None): 399 | pathname = run 400 | else: 401 | pathname = self.eventname 402 | if(not(os.path.exists(pathname))): 403 | print("Invalid path!") 404 | return 405 | if(os.path.exists(pathname + '/' + self.inidir + '/Constraints.ini')): 406 | with open(pathname + '/' + self.inidir + '/Constraints.ini','r') as f: 407 | lines = f.read().splitlines() 408 | print('Constraints --- ',lines) 409 | self.constraints =[] 410 | for line in lines: 411 | chunks =line.split() 412 | self.constraints.append([chunks[0], float(chunks[2]), float(chunks[3]), float(chunks[4])]) 413 | self.set_constraints(self.constraints) 414 | if(os.path.exists(pathname + '/' + self.inidir + '/Reader.ini')): 415 | with open(pathname + '/' + self.inidir + '/Reader.ini','r') as f: 416 | lines = f.read().splitlines() 417 | print('Reader --- ',lines) 418 | for line in lines: 419 | chunks = line.split() 420 | if(chunks[0]=='tau'): 421 | self.Reader_tau = float(chunks[2]) 422 | elif(chunks[0]=='binning'): 423 | self.Reader_binning = int(chunks[2]) 424 | elif(chunks[0]=='otherseasons'): 425 | self.Reader_otherseasons = int(chunks[2]) 426 | elif(chunks[0]=='renormalize'): 427 | self.Reader_renormalize = int(chunks[2]) 428 | elif(chunks[0]=='thresholdoutliers'): 429 | self.Reader_thresholdoutliers = float(chunks[2]) 430 | if(os.path.exists(pathname + '/' + self.inidir + '/InitCond.ini')): 431 | with open(pathname + '/' + self.inidir + '/InitCond.ini','r') as f: 432 | lines = f.read().splitlines() 433 | print('InitCond --- ',lines) 434 | self.InitCond_nostatic = False 435 | self.InitCond_onlyorbital = False 436 | self.InitCond_override = None 437 | self.InitCond_onlyupdate = False 438 | for line in lines: 439 | chunks = line.split() 440 | if(chunks[0]=='npeaks'): 441 | self.InitCond_npeaks = int(chunks[2]) 442 | elif(chunks[0]=='peakthreshold'): 443 | self.InitCond_peakthreshold = float(chunks[2]) 444 | elif(chunks[0]=='oldmodels'): 445 | self.InitCond_oldmodels = int(chunks[2]) 446 | elif(chunks[0]=='usesatellite'): 447 | self.InitCond_usesatellite = int(chunks[2]) 448 | elif(chunks[0]=='nostatic'): 449 | self.InitCond_nostatic = (int(chunks[2])!=0) 450 | elif(chunks[0]=='onlyorbital'): 451 | self.InitCond_onlyorbital = (int(chunks[2])!=0) 452 | elif(chunks[0]=='onlyupdate'): 453 | self.InitCond_onlyupdate = (int(chunks[2])!=0) 454 | elif(chunks[0]=='override'): 455 | self.InitCond_override = (float(chunks[2]),float(chunks[3])) 456 | elif(chunks[0]=='templatelibrary'): 457 | self.InitCond_templatelibrary = chunks[2] 458 | elif(chunks[0]=='modelcategories'): 459 | self.InitCond_modelcategories = [chunks[2][i:i+2] for i in range(0, len(chunks[2]), 2)] 460 | if(os.path.exists(pathname + '/' + self.inidir + '/LevMar.ini')): 461 | with open(pathname + '/' + self.inidir + '/LevMar.ini','r') as f: 462 | lines = f.read().splitlines() 463 | print('LevMar --- ',lines) 464 | for line in lines: 465 | chunks = line.split() 466 | if(chunks[0]=='nfits'): 467 | self.LevMar_nfits = int(chunks[2]) 468 | if(chunks[0]=='offsetdegeneracy'): 469 | self.LevMar_offsetdegeneracy = int(chunks[2]) 470 | elif(chunks[0]=='maxsteps'): 471 | self.LevMar_maxsteps = int(chunks[2]) 472 | elif(chunks[0]=='timelimit'): 473 | self.LevMar_timelimit = float(chunks[2]) 474 | elif(chunks[0]=='bumperpower'): 475 | self.LevMar_bumperpower = float(chunks[2]) 476 | if(os.path.exists(pathname + '/' + self.inidir + '/ModelSelector.ini')): 477 | with open(pathname + '/' + self.inidir + '/ModelSelector.ini','r') as f: 478 | lines = f.read().splitlines() 479 | print('ModelSelector --- ',lines) 480 | for line in lines: 481 | chunks = line.split() 482 | if(chunks[0]=='sigmasoverlap'): 483 | self.ModelSelector_sigmasoverlap = float(chunks[2]) 484 | elif(chunks[0]=='sigmachisquare'): 485 | self.ModelSelector_sigmachisquare = float(chunks[2]) 486 | elif(chunks[0]=='maxmodels'): 487 | self.ModelSelector_maxmodels = int(chunks[2]) 488 | 489 | @staticmethod 490 | def find_bin_directory() -> str: 491 | """ 492 | Searches for the RTModel/bin directory in the available site-packages directories. 493 | 494 | :return: The string of the parth to the bin directory. 495 | """ 496 | bin_directory_string = None 497 | site_packages_directories = site.getsitepackages() 498 | site_packages_directories.append(site.getusersitepackages()) 499 | for site_packages_directory in site_packages_directories: 500 | bin_directory_path = Path(site_packages_directory).joinpath('RTModel/bin') 501 | if bin_directory_path.exists(): 502 | bin_directory_string = str(bin_directory_path) + '/' 503 | break 504 | if bin_directory_string is None: 505 | raise FileNotFoundError(f'RTModel binary directory not found. Searched {site_packages_directories} ' 506 | f'site-packages directories.') 507 | return bin_directory_string 508 | -------------------------------------------------------------------------------- /RTModel/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.4.2" 2 | __author__ = 'Valerio Bozza' 3 | __credits__ = 'University of Salerno, Italy' 4 | 5 | from .RTModel import RTModel 6 | -------------------------------------------------------------------------------- /RTModel/data/ESPL.tbl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/RTModel/data/ESPL.tbl -------------------------------------------------------------------------------- /RTModel/data/TemplateLibrary.txt: -------------------------------------------------------------------------------- 1 | 113 0.7 0.5 0.15 3.5 0.01 -0.18254 0.01625 0.7 0.5 0.15 3.5 0.01 -0.18254 0.08566 0.7 0.5 0.15 3.5 0.01 0.01625 0.08566 0.7 0.1 0.0 5.38 0.01 -0.06613 0.02524 0.7 0.1 0.0 5.38 0.01 -0.06613 0.30473 0.7 0.1 0.0 5.38 0.01 0.02524 0.30473 0.7 0.5 0.0 2.0 0.01 -1.20214 -0.11323 0.7 0.5 0.0 2.0 0.01 -1.20214 0.13806 0.7 0.5 0.0 2.0 0.01 -0.11323 0.13806 0.7 0.5 -0.1 1.57 0.01 -0.507 -0.01281 0.7 0.5 -0.1 1.57 0.01 -0.507 0.01297 0.7 0.5 -0.1 1.57 0.01 -0.01281 0.01297 0.7 0.5 0.1 1.35 0.01 -0.08231 0.07511 0.7 0.5 0.1 1.35 0.01 -0.08231 1.12127 2 | 0.7 0.5 0.1 1.35 0.01 0.07511 1.12127 0.65 0.1 0.52 5.0 0.01 -0.29069 0.87108 0.65 0.1 0.52 5.0 0.01 -0.29069 0.87928 0.65 0.1 0.52 5.0 0.01 0.87108 0.87928 0.7 0.5 -0.35 1.5 0.01 -1.01649 -0.97465 0.7 0.5 -0.35 1.5 0.01 -1.01649 0.01484 0.7 0.5 -0.35 1.5 0.01 -0.97465 0.01484 0.7 0.5 0.8 4.0 0.01 0.045 0.55186 0.7 0.5 0.8 4.0 0.01 0.045 0.60478 0.7 0.5 0.8 4.0 0.01 0.55186 0.60478 0.7 0.1 0.1 4.6 0.01 -0.20823 -0.00676 0.7 0.1 0.1 4.6 0.01 -0.20823 0.1493 3 | 0.7 0.1 0.1 4.6 0.01 -0.00676 0.1493 0.7 0.5 -0.2 1.4 0.01 -0.51524 0.03098 0.7 0.5 -0.2 1.4 0.01 -0.51524 1.12915 0.7 0.5 -0.2 1.4 0.01 0.03098 1.12915 0.7 0.5 0.3 1.93 0.01 -1.11878 0.10593 0.7 0.1 0.25 5.3 0.01 -0.1024 0.91391 0.7 0.5 1.12 3.2 0.01 -0.32584 -0.09533 0.7 0.1 0.75 4.6 0.01 -0.74331 0.03092 4 | 0.7 0.1 0.75 4.6 0.01 -0.74331 0.53977 0.7 0.1 0.75 4.6 0.01 0.03092 0.53977 0.9 0.03 -0.15 2.0 0.01 -0.27698 0.06576 0.9 0.03 -0.15 2.0 0.01 -0.27698 0.09019 5 | 0.9 0.03 -0.15 2.0 0.01 0.06576 0.09019 0.9 0.03 -0.1 1.6 0.01 -0.15107 -0.12148 0.9 0.03 -0.1 1.6 0.01 -0.15107 0.11163 0.9 0.03 -0.1 1.6 0.01 -0.12148 0.11163 6 | 0.9 0.03 -0.2 2.3 0.01 -0.47211 0.01011 0.9 0.03 -0.2 2.3 0.01 -0.47211 0.04044 0.9 0.03 -0.2 2.3 0.01 0.01011 0.04044 1.5 0.5 0.1 3.2 0.01 -0.45923 -0.32531 1.5 0.5 0.1 3.2 0.01 -0.45923 0.63255 7 | 1.5 0.5 0.1 3.2 0.01 -0.32531 0.63255 1.5 0.5 0.0 3.0 0.01 -0.44535 -0.34781 1.5 0.5 0.0 3.0 0.01 -0.44535 0.6563 1.5 0.5 0.0 3.0 0.01 -0.34781 0.6563 0.9 0.03 -0.26 1.65 0.01 -0.22177 0.16748 0.9 0.03 -0.26 1.65 0.01 -0.22177 0.19405 0.9 0.03 -0.26 1.65 0.01 0.16748 0.19405 1.5 0.5 0.3 2.8 0.01 0.31411 0.78162 8 | 1.5 0.5 0.3 2.8 0.01 0.58817 0.78162 0.9 0.03 -0.3 1.8 0.01 -0.28464 0.1291 9 | 0.9 0.03 -0.3 1.8 0.01 -0.28464 0.14202 0.9 0.03 -0.3 1.8 0.01 0.1291 0.14202 1.5 0.5 0.3 2.9 0.01 -0.48885 0.39245 10 | 1.5 0.5 0.3 2.9 0.01 -0.48885 0.56195 11 | 1.5 0.5 0.3 2.9 0.01 0.39245 0.56195 1.5 0.5 0.25 2.75 0.01 0.20834 0.73845 12 | 1.5 0.5 0.25 2.75 0.01 0.66775 0.73845 13 | 1.5 0.5 0.35 2.2 0.01 0.06007 0.6458 14 | 1.5 0.5 0.35 2.2 0.01 0.5378 0.6458 15 | 1.5 0.5 0.25 2.5 0.01 -0.45776 0.09626 16 | 1.5 0.5 0.25 2.5 0.01 -0.45776 0.58665 17 | 1.5 0.5 0.25 2.5 0.01 0.09626 0.58665 18 | 1.5 0.5 0.25 2.0 0.01 -0.04058 0.2958 19 | 1.5 0.5 0.25 2.0 0.01 -0.04058 0.47182 20 | 1.5 0.5 0.25 2.0 0.01 0.2958 0.47182 21 | 1.5 0.5 0.7 1.5 0.01 -0.07965 -0.01574 22 | 1.5 0.5 0.7 1.5 0.01 -0.07965 0.34594 23 | 1.5 0.5 0.7 1.5 0.01 -0.01574 0.34594 1.5 0.5 0.45 4.6 0.01 -0.40928 -0.04946 24 | 1.5 0.5 0.45 4.6 0.01 -0.40928 0.27708 25 | 1.5 0.5 0.45 4.6 0.01 -0.04946 0.27708 26 | 1.5 0.5 0.33 3.2 0.01 -0.36736 0.65181 27 | 0.9 0.1 -0.45 1.65 0.01 -0.4663 -0.00103 28 | 0.9 0.1 -0.45 1.65 0.01 -0.4663 0.37168 29 | 0.9 0.1 -0.45 1.65 0.01 -0.00103 0.37168 2.2 0.5 0.1 3.0 0.01 0.74611 1.06918 30 | 2.2 0.5 0.1 3.0 0.01 0.74611 1.18948 31 | 2.2 0.5 0.1 3.0 0.01 1.06918 1.18948 32 | 2.2 0.5 0.35 2.9 0.01 1.07297 1.14242 33 | 2.2 0.5 0.35 2.9 0.01 1.07297 1.28772 34 | 2.2 0.5 0.35 2.9 0.01 1.14242 1.28772 35 | 2.2 0.5 0.4 2.8 0.01 0.98474 1.16883 36 | 2.2 0.5 0.4 2.8 0.01 0.98474 1.21577 37 | 2.2 0.5 0.4 2.8 0.01 1.16883 1.21577 38 | 2.2 0.5 1.05 2.0 0.01 0.39398 0.56261 39 | 2.2 0.5 1.05 2.0 0.01 0.39398 0.6566 40 | 2.2 0.5 1.05 2.0 0.01 0.56261 0.6566 41 | 2.2 0.5 0.45 2.7 0.01 -0.67542 0.89586 42 | 2.2 0.5 0.45 2.7 0.01 -0.67542 1.10689 43 | 2.2 0.5 0.45 2.7 0.01 0.89586 1.10689 44 | 2.2 0.5 1.0 1.9 0.01 0.30045 0.39828 45 | 2.2 0.5 1.0 1.9 0.01 0.30045 0.52621 46 | 2.2 0.5 1.0 1.9 0.01 0.39828 0.52621 47 | 2.2 0.5 1.23 1.6 0.01 -0.17028 0.01821 48 | 2.2 0.5 1.23 1.6 0.01 -0.17028 0.05286 49 | 2.2 0.5 1.23 1.6 0.01 0.01821 0.05286 50 | 2.2 0.5 0.35 2.7 0.01 -0.66716 0.7449 51 | 2.2 0.5 0.35 2.7 0.01 -0.66716 1.14629 52 | 2.2 0.5 0.35 2.7 0.01 0.7449 1.14629 53 | 2.2 0.5 0.05 2.95 0.01 -0.64485 0.23071 54 | 2.2 0.5 0.05 2.95 0.01 -0.64485 1.20146 55 | 2.2 0.5 0.05 2.95 0.01 0.23071 1.20146 56 | 2.2 0.5 0.35 3.0 0.01 -0.68509 1.15393 57 | 1.8 0.03 1.0 2.3 0.01 -0.08517 0.72053 58 | 1.8 0.03 1.0 2.3 0.01 -0.08517 0.88924 59 | 1.8 0.03 1.0 2.3 0.01 0.72053 0.88924 -------------------------------------------------------------------------------- /RTModel/include/LevMarFit.h: -------------------------------------------------------------------------------- 1 | // LevMarFit.h 2 | // Definition of the LevMar and bumper classes for Levenberg-Marquardt fitting 3 | 4 | #include 5 | #include "bumper.h" 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace std::filesystem; 11 | 12 | #ifndef _LevMarFit 13 | #define _LevMarFit 14 | #define __unmanaged 15 | 16 | #include 17 | 18 | class LevMar { 19 | 20 | VBMicrolensing* VBM; 21 | 22 | char eventname[512], filename[30], filnum[10], outdir[30], satellitedir[256]; 23 | char modelcode[16]; 24 | int error; 25 | int flagblending; 26 | path exedir; 27 | 28 | double tim0, tm; 29 | 30 | double (VBMicrolensing::* model)(double*, double); 31 | int nps; 32 | double* sigmapr, * leftlim, * rightlim; 33 | 34 | int* filter, * satel, nfil, np, OGLE; 35 | double* t, * y, * w, * delta, * maxdelta, * Curv, * A, * B, * B0, * Cov, * fb, ** Gr, * dFdp, * errs; 36 | double* pr, * prn, * sumy, * sumy2, * sumsigma, * sumfy, * sumf, * sumf2, * limbdarks; 37 | 38 | int consnumber, *consindex; 39 | double* constraints, * consleft, * consright, *consvars; 40 | int modnumber; 41 | 42 | double Tol; 43 | 44 | void (LevMar::* PrintOut)(double*); 45 | void (LevMar::* PrintFile)(FILE*, double, bool); 46 | 47 | bumper* stepchain, * bumperlist, * laststep; 48 | 49 | 50 | public: 51 | LevMar(int, char**); 52 | ~LevMar(); 53 | 54 | void ReadFiles(int, char**); 55 | int InitCond(double* presigmapr, double* preleftlim, double* prerightlim); 56 | void ReadCurve(); 57 | void ReadOptions(); 58 | void Run(); 59 | double ChiSquared(double*); 60 | void Grad(); 61 | void Covariance(); 62 | double ComputeConstraint(double *pr, int i); 63 | 64 | void PrintOutPS(double*); 65 | void PrintOutPX(double*); 66 | void PrintOutBS(double*); 67 | void PrintOutBO(double*); 68 | void PrintOutLS(double*); 69 | void PrintOutLX(double*); 70 | void PrintOutLO(double*); 71 | void PrintOutLK(double*); 72 | void PrintOutTS(double*); 73 | void PrintOutTX(double*); 74 | 75 | void PrintFilePS(FILE*, double, bool); 76 | void PrintFilePX(FILE*, double, bool); 77 | void PrintFileBS(FILE*, double, bool); 78 | void PrintFileBO(FILE*, double, bool); 79 | void PrintFileLS(FILE*, double, bool); 80 | void PrintFileLX(FILE*, double, bool); 81 | void PrintFileLO(FILE*, double, bool); 82 | void PrintFileLK(FILE*, double, bool); 83 | void PrintFileTS(FILE*, double, bool); 84 | void PrintFileTX(FILE*, double, bool); 85 | 86 | }; 87 | 88 | double Determinant(double*, int); 89 | void Inverse(double*, double*, int); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /RTModel/include/bumper.h: -------------------------------------------------------------------------------- 1 | // bumper.h 2 | // Definition of the bumper class containing information on a single model 3 | 4 | #ifndef _bumper 5 | #define _bumper 6 | 7 | // Bumper class is the penalty function to be used to fill minima in chi square 8 | class bumper{ 9 | public: 10 | double *p0; 11 | double *dp; 12 | double *curv, *cov; 13 | double Amp; 14 | int nps; 15 | char modelcode[16]; 16 | int il; 17 | bool duplicate; 18 | bumper(double *,int); 19 | ~bumper(); 20 | void SetCurvature(double *,double); 21 | void SetCovariance(double*, double); 22 | void UpdateCurvature(double); 23 | void signCovariance(int); 24 | void flipCovariance(int, int); 25 | double distance(double *); 26 | bumper *next; 27 | }; 28 | 29 | double Determinant(double *,int); 30 | void Inverse(double*, double*, int); 31 | void CombineCovariances(bumper*, bumper*, double *Cov, double * Curv, int); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /RTModel/lib/Finalizer.cpp: -------------------------------------------------------------------------------- 1 | // Finalizer.cpp : main project file. 2 | 3 | #define _USE_MATH_DEFINES 4 | #define _CRT_SECURE_NO_WARNINGS 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "bumper.h" 13 | 14 | using namespace std; 15 | using namespace std::filesystem; 16 | 17 | const double failthr = 1.0e100; // threshold for chisquare/dof for declaring failure 18 | const int ncategories = 10; 19 | 20 | int main(int argc, char* argv[]) { 21 | 22 | char eventname[512] = ""; 23 | char filename[30] = ""; 24 | char command[256], buffer[256]; 25 | double value; 26 | int nfil, * satel; 27 | double* t, * y, * w, * pr, * sigmapr, thsigma; 28 | double thrs[20] = { 36., 40.0872, 43.4518, 46.4625, 49.2497, 51.878, 54.3854, 56.7964, \ 29 | 59.1282, 61.3932, 63.601, 65.7588, 67.8725, 69.9471, 71.9863, \ 30 | 73.9937, 75.9719, 77.9236, 79.8506 }; // thresholds at 6 sigma for n more parameters 31 | string modelcodes[ncategories] = { "PS","PX","BS","BO","LS","LX","LO","LK","TS","TX"}; 32 | string modelnames[ncategories] = { "Single-Lens-Single-Source","Single-Lens-Single-Source with parallax",\ 33 | "Binary source","Binary source with xallarap",\ 34 | ""," with parallax"," with orbital motion", "with eccentric orbital motion",\ 35 | "Triplelens", "Triple lens with parallax"}; 36 | int npss[ncategories] = { 4,6,7,10,7,9,12, 14, 10, 12}; 37 | double chis[ncategories]; 38 | double cmin, c0, c1, c2, cflat; 39 | double chiblp = 1.e100, chiblb = 1.e100; 40 | int mn; 41 | char final[2000], stringa[1024]; 42 | int flag, nmod, ngoodmod, * filter, np; 43 | FILE* f, * g; 44 | bumper* bumperlist = 0, * scanbumper, * scanbumper2; 45 | 46 | 47 | setbuf(stdout, nullptr); 48 | printf("******************************************\n"); 49 | printf("********** Finalizer *********\n"); 50 | printf("******************************************\n\n\n"); 51 | printf("This program proposes a final interpretation for the event\n\n"); 52 | 53 | // Directory preliminaries. Reads event name from arguments. 54 | 55 | auto exedir = current_path(); 56 | 57 | if (argc > 1) { 58 | strcpy(eventname, argv[1]); 59 | } 60 | else { 61 | printf("\n\nEvent name? "); 62 | scanf("%s", eventname); 63 | } 64 | 65 | printf("\n\n- Event: %s\n", eventname); 66 | 67 | current_path(eventname); 68 | 69 | if (exists("Nature.txt")) { 70 | printf("\n\nEvent already finalized"); 71 | return 0; 72 | } 73 | 74 | if (exists("ini")) { 75 | current_path("ini"); 76 | f = fopen("Finalizer.ini", "r"); 77 | if (f != 0) { 78 | printf("\n\n- Reading options in Finalizer.ini"); 79 | while (!feof(f)) { 80 | int red = fscanf(f, "%s %s %lf", command, buffer, &value); 81 | if (red < 1) { 82 | command[0] = 0; 83 | //if (red != 0) { 84 | // printf("\n\n!!! Bad command in Reader.ini"); 85 | // return -1; 86 | //}; 87 | } 88 | //if (strcmp(command, "maxmodels") == 0) { 89 | // maxmodels = value; 90 | //} 91 | 92 | } 93 | fclose(f); 94 | } 95 | else { 96 | printf("\n\n- Default options:"); 97 | } 98 | } 99 | 100 | /*printf("\nNumber of sigmas used to declare overlap between two models: %lf", supfac); 101 | printf("\nNumber of sigmas in chi square distribution for model acceptance: %lf", chifac); 102 | printf("\nMaximum number of models reported: %d", maxmodels);*/ 103 | 104 | 105 | // Read curve to fit 106 | 107 | current_path(eventname); 108 | 109 | printf("\n\nReading data\n"); 110 | 111 | f = fopen("LCToFit.txt", "r"); 112 | fscanf(f, "%d", &np); 113 | filter = (int*)malloc(sizeof(int) * np); 114 | satel = (int*)malloc(sizeof(int) * np); 115 | t = (double*)malloc(sizeof(double) * np); 116 | y = (double*)malloc(sizeof(double) * np); 117 | w = (double*)malloc(sizeof(double) * np); 118 | 119 | nfil = 1; 120 | for (int i = 0; i < np; i++) { 121 | fscanf(f, "%d %lf %lf %lf %d", &(filter[i]), &(t[i]), &(y[i]), &(w[i]), &(satel[i])); 122 | if ((i != 0) && (filter[i] != filter[i - 1])) { 123 | nfil++; 124 | } 125 | w[i] = 1 / (w[i]); 126 | } 127 | fclose(f); 128 | 129 | 130 | pr = (double*)malloc(sizeof(double) * (14 + 2 * nfil)); 131 | sigmapr = (double*)malloc(sizeof(double) * (14 + 2 * nfil)); 132 | 133 | // Calculate flat chi square 134 | 135 | c0 = c1 = c2 = cflat = 0; 136 | nfil = 0; 137 | for (int i = 0; i < np; i++) { 138 | if (filter[i] == nfil) { 139 | c0 += w[i] * w[i]; 140 | c1 += y[i] * w[i] * w[i]; 141 | c2 += y[i] * y[i] * w[i] * w[i]; 142 | } 143 | else { 144 | nfil++; 145 | cflat += c2 - c1 * c1 / c0; 146 | c0 = c1 = c2 = 0; 147 | } 148 | } 149 | nfil++; 150 | cflat += c2 - c1 * c1 / c0; 151 | 152 | // Compare all models and draw conclusions 153 | 154 | printf("\n\n********************"); 155 | printf("\n Finalization "); 156 | printf("\n********************\n"); 157 | 158 | current_path(eventname); 159 | 160 | auto selectmodelsname = path("FinalModels"); 161 | create_directory(selectmodelsname); 162 | 163 | 164 | g = fopen("Nature.txt", "w"); 165 | 166 | current_path("Models"); 167 | 168 | // Load chi square of best models of each type 169 | 170 | printf("\n- Reading models\n\n"); 171 | 172 | nmod = 0; 173 | for (int icat = 0; icat < ncategories; icat++) { 174 | chis[icat] = 1.e100; 175 | auto searchstring = regex(modelcodes[icat] + ".*-[0-9]*.txt"); 176 | for (auto const& itr : directory_iterator(".")) { 177 | string curfile = (itr).path().filename().string(); 178 | if (regex_match(curfile, searchstring)) { 179 | strcpy(filename, curfile.c_str()); 180 | f = fopen(filename, "r"); 181 | for (int j = 0; j < npss[icat] + 2 * nfil; j++) { 182 | fscanf(f, "%le", &(pr[j])); 183 | } 184 | fscanf(f, "%le", &(c0)); 185 | fclose(f); 186 | 187 | if (c0 < chis[icat]) chis[icat] = c0; 188 | 189 | if (modelcodes[icat][0] == 'L') { 190 | if (pr[1] <= 0.03) { 191 | if (c0 < chiblp) chiblp = c0; 192 | } 193 | else { 194 | if (c0 < chiblb) chiblb = c0; 195 | } 196 | } 197 | if (nmod) { 198 | if (c0 < bumperlist->Amp) { 199 | scanbumper = bumperlist; 200 | bumperlist = new bumper(pr, npss[icat]); 201 | strcpy(bumperlist->modelcode, (char*)(itr).path().filename().string().c_str()); 202 | bumperlist->il = icat; 203 | bumperlist->Amp = c0; 204 | bumperlist->next = scanbumper; 205 | } 206 | else { 207 | scanbumper = bumperlist; 208 | while ((scanbumper->next) && (scanbumper->next->Amp < c0)) scanbumper = scanbumper->next; 209 | scanbumper2 = new bumper(pr, npss[icat]); 210 | strcpy(scanbumper2->modelcode, (char*)(itr).path().filename().string().c_str()); 211 | scanbumper2->il = icat; 212 | scanbumper2->Amp = c0; 213 | scanbumper2->next = scanbumper->next; 214 | scanbumper->next = scanbumper2; 215 | } 216 | } 217 | else { 218 | bumperlist = new bumper(pr, npss[icat]); 219 | strcpy(bumperlist->modelcode, (char*)(itr).path().filename().string().c_str()); 220 | bumperlist->il = icat; 221 | bumperlist->Amp = c0; 222 | } 223 | nmod++; 224 | } 225 | } 226 | //printf("%s: ", modelcodes[icat].c_str()); 227 | //fprintf(g, "%s: ", modelcodes[icat].c_str()); 228 | if (chis[icat] < 1.e99) { 229 | printf("%s: ", modelcodes[icat].c_str()); 230 | fprintf(g, "%s: ", modelcodes[icat].c_str()); 231 | printf("%lf\n", chis[icat]); 232 | fprintf(g, "%lf\n", chis[icat]); 233 | } 234 | //else { 235 | // printf("N/A\n"); 236 | // fprintf(g, "N/A\n"); 237 | //} 238 | } 239 | 240 | 241 | //// Check that there are no weird chi squares 242 | //for (int i = 0; i<9; i++) { 243 | // if (chis[i]Amp; 252 | 253 | thsigma = cmin + cmin / np * sqrt(2 * np); 254 | for (int i = 0; i < 10; i++) { 255 | thrs[i] *= cmin / np; 256 | } 257 | 258 | if (chiblp < 1.e99) { 259 | printf("\nBestPlanetary: %lf", chiblp); 260 | fprintf(g, "\nBestPlanetary: %lf", chiblp); 261 | } 262 | else { 263 | printf("\nBestPlanetary: N/A"); 264 | fprintf(g, "\nBestPlanetary: N/A"); 265 | } 266 | 267 | if (chiblb < 1.e99) { 268 | printf("\nBestBinary: %lf\n\n", chiblb); 269 | fprintf(g, "\nBestBinary: %lf\n\n", chiblb); 270 | } 271 | else { 272 | printf("\nBestBinary: N/A\n\n"); 273 | fprintf(g, "\nBestBinary: N/A\n\n"); 274 | } 275 | 276 | if (chiblb + thsigma - cmin < chiblp) { 277 | strcpy(stringa, "Binary lens"); 278 | } 279 | else { 280 | if (chiblp + thsigma - cmin < chiblb) { 281 | strcpy(stringa, "Planetary lens"); 282 | } 283 | else { 284 | strcpy(stringa, "Binary lens or Planetary lens"); 285 | } 286 | } 287 | 288 | // Making assessment. Each category is tested against nested ones 289 | vector dependencies[] = { {}, 290 | {0}, 291 | {0}, 292 | {0,1,2}, 293 | {0}, 294 | {0,1,4}, 295 | {0,1,4,5}, 296 | {0,1,4,5,6}, 297 | {0,4}, 298 | {0,1,4,5}, 299 | }; 300 | double modelthrs[ncategories]; 301 | 302 | for (int icat = 0; icat < ncategories; icat++) { 303 | modelthrs[icat] = thsigma; 304 | for (int jdep = 0; jdep < dependencies[icat].size(); jdep++) { 305 | double thr= chis[dependencies[icat][jdep]] - thrs[npss[icat] - npss[dependencies[icat][jdep]]]; 306 | if (thr < modelthrs[icat]) modelthrs[icat] = thr; 307 | } 308 | if (chis[icat] > modelthrs[icat]) { 309 | chis[icat] = 1.e100; 310 | } 311 | } 312 | 313 | //if (chis[1] > chis[0] - thrs[npss[1] - npss[0]]) { 314 | // chis[1] = 1.e100; 315 | //} 316 | //if (chis[2] > chis[0] - thrs[npss[2] - npss[0]]) { 317 | // chis[2] = 1.e100; 318 | //} 319 | //if (chis[3] > chis[0] - thrs[npss[3] - npss[0]] || chis[3] > chis[1] - thrs[npss[3] - npss[1]] || chis[3] > chis[2] - thrs[npss[3] - npss[2]]) { 320 | // chis[3] = 1.e100; 321 | //} 322 | //if (chis[4] > chis[0] - thrs[npss[4] - npss[0]]) { 323 | // chis[4] = 1.e100; 324 | //} 325 | //if (chis[5] > chis[0] - thrs[npss[5] - npss[0]] || chis[5] > chis[1] - thrs[npss[5] - npss[1]] || chis[5] > chis[4] - thrs[npss[5] - npss[4]]) { 326 | // chis[5] = 1.e100; 327 | //} 328 | //if (chis[6] > chis[0] - thrs[npss[6] - npss[0]] || chis[6] > chis[1] - thrs[npss[6] - npss[1]] || chis[6] > chis[4] - thrs[npss[6] - npss[4]] || chis[6] > chis[5] - thrs[npss[6] - npss[5]]) { 329 | // chis[6] = 1.e100; 330 | //} 331 | //if (chis[7] > chis[0] - thrs[npss[7] - npss[0]] || chis[7] > chis[1] - thrs[npss[7] - npss[1]] || chis[7] > chis[4] - thrs[npss[7] - npss[4]] || chis[7] > chis[5] - thrs[npss[7] - npss[5]] || chis[7] > chis[6] - thrs[npss[7] - npss[6]]) { 332 | // chis[7] = 1.e100; 333 | //} 334 | //if (chis[8] > chis[0] - thrs[npss[8] - npss[0]] || chis[8] > chis[4] - thrs[npss[8] - npss[4]]) { 335 | // chis[8] = 1.e100; 336 | //} 337 | //if (chis[9] > chis[0] - thrs[npss[9] - npss[0]] || chis[9] > chis[1] - thrs[npss[9] - npss[1]] || chis[9] > chis[4] - thrs[npss[9] - npss[4]] || chis[9] > chis[5] - thrs[npss[9] - npss[5]]) { 338 | // chis[9] = 1.e100; 339 | //} 340 | 341 | // If more complicated category has survived, all nested categories are removed 342 | 343 | for (int icat = ncategories - 1; icat > 0; icat--) { 344 | if (chis[icat] < 1.e99) { 345 | for (int jdep = 0; jdep < dependencies[icat].size(); jdep++) { 346 | chis[dependencies[icat][jdep]] = 1.e100; 347 | } 348 | } 349 | } 350 | //if (chis[9] < 1.e99) { 351 | // chis[5] = chis[4] = chis[1] = chis[0] = 1.e100; 352 | //} 353 | //if (chis[8] < 1.e99) { 354 | // chis[4] = chis[0] = 1.e100; 355 | //} 356 | //if (chis[7] < 1.e99) { 357 | // chis[6] = chis[5] = chis[4] = chis[1] = chis[0] = 1.e100; 358 | //} 359 | //if (chis[6] < 1.e99) { 360 | // chis[5] = chis[4] = chis[1] = chis[0] = 1.e100; 361 | //} 362 | //if (chis[5] < 1.e99) { 363 | // chis[4] = chis[1] = chis[0] = 1.e100; 364 | //} 365 | //if (chis[4] < 1.e99) { 366 | // chis[0] = 1.e100; 367 | //} 368 | //if (chis[3] < 1.e99) { 369 | // chis[2] = chis[1] = chis[0] = 1.e100; 370 | //} 371 | //if (chis[2] < 1.e99) { 372 | // chis[0] = 1.e100; 373 | //} 374 | //if (chis[1] < 1.e99) { 375 | // chis[0] = 1.e100; 376 | //} 377 | 378 | // Models of discarded categories or with chi square higher than threshold are removed 379 | 380 | for (scanbumper = bumperlist; scanbumper; scanbumper = scanbumper->next) { 381 | if (scanbumper->Amp > modelthrs[scanbumper->il] || chis[scanbumper->il] > 1.e99) scanbumper->modelcode[0] = 'N'; 382 | } 383 | 384 | // Counting good models 385 | 386 | ngoodmod = 0; 387 | for (scanbumper = bumperlist; scanbumper; scanbumper = scanbumper->next) { 388 | if (scanbumper->modelcode[0] != 'N') ngoodmod++; 389 | } 390 | 391 | // Decide designation 392 | 393 | if (cflat < thsigma) { 394 | strcpy(final, "Flat"); 395 | } 396 | else { 397 | if (cmin > np * failthr) { 398 | strcpy(final, "Uncertain: variable or failed"); 399 | } 400 | else { 401 | strcpy(final, "Successful: "); 402 | flag = 0; 403 | for (int icat = 0; icat < ncategories; icat++) { 404 | for (scanbumper = bumperlist; scanbumper; scanbumper = scanbumper->next) { 405 | if (scanbumper->il == icat && scanbumper->modelcode[0] != 'N') { 406 | if (flag > 0) { 407 | strncat(final, " or ", 60); 408 | } 409 | else { 410 | flag = 1; 411 | } 412 | if (icat >= 4) strncat(final, stringa, 60); 413 | strncat(final, modelnames[icat].c_str(), 60); 414 | break; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | printf("%s\n", final); 422 | // Writing file Nature.txt with the conclusion drawn before 423 | fprintf(g, "%s\n", final); 424 | 425 | printf("----\n"); 426 | fprintf(g, "----\n"); 427 | 428 | 429 | printf("Number of alternative models: %d\n\nchisquare model\n", ngoodmod); 430 | fprintf(g, "Number of alternative models: %d\n\nchisquare model\n", ngoodmod); 431 | 432 | 433 | // Writing the names of the files containing alternative models in Nature.txt 434 | current_path(".."); 435 | for (scanbumper = bumperlist; scanbumper; scanbumper = scanbumper->next) { 436 | if (scanbumper->modelcode[0] != 'N') { 437 | fprintf(g, "%lf %s\n", scanbumper->Amp, scanbumper->modelcode); 438 | printf("%lf %s", scanbumper->Amp, scanbumper->modelcode); 439 | copy_file(path("Models") / path(string(scanbumper->modelcode)), path("FinalModels") / path(string(scanbumper->modelcode)), copy_options::overwrite_existing); 440 | } 441 | } 442 | fclose(g); 443 | 444 | 445 | 446 | printf("\n\n- Done"); 447 | 448 | // Sleep(5000l); 449 | 450 | scanbumper = bumperlist; 451 | while (scanbumper) { 452 | scanbumper2 = scanbumper->next; 453 | delete scanbumper; 454 | scanbumper = scanbumper2; 455 | } 456 | 457 | free(pr); 458 | free(sigmapr); 459 | free(filter); 460 | free(satel); 461 | free(t); 462 | free(y); 463 | free(w); 464 | 465 | 466 | return 0; 467 | } 468 | -------------------------------------------------------------------------------- /RTModel/lib/LevMar.cpp: -------------------------------------------------------------------------------- 1 | // LevMar.cpp : main project file. 2 | // This program performs Levenberg-Marquardt fitting on a given initial condition 3 | 4 | #include "LevMarFit.h" 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | LevMar* MyLevMar = new LevMar(argc, argv); 9 | int err = MyLevMar->Run(); 10 | delete MyLevMar; 11 | return err; 12 | } 13 | -------------------------------------------------------------------------------- /RTModel/lib/LevMarFit.h: -------------------------------------------------------------------------------- 1 | // LevMarFit.h 2 | // Definition of the LevMar and bumper classes for Levenberg-Marquardt fitting 3 | 4 | #include 5 | #include "bumper.h" 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace std::filesystem; 11 | 12 | #ifndef _LevMarFit 13 | #define _LevMarFit 14 | #define __unmanaged 15 | 16 | #include 17 | 18 | class LevMar { 19 | 20 | VBMicrolensing* VBM; 21 | 22 | char eventname[512], filename[30], filnum[10], outdir[30], satellitedir[256]; 23 | char modelcode[16]; 24 | int error; 25 | int flagblending; 26 | path exedir; 27 | 28 | double tim0, tm; 29 | 30 | double (VBMicrolensing::* model)(double*, double); 31 | int nps; 32 | double* sigmapr, * leftlim, * rightlim; 33 | 34 | int* filter, * satel, nfil, np, OGLE; 35 | double* t, * y, * w, * delta, * maxdelta, * Curv, * A, * B, * B0, * Cov, * fb, ** Gr, * dFdp, * errs; 36 | double* pr, * prn, * sumy, * sumy2, * sumsigma, * sumfy, * sumf, * sumf2, * limbdarks; 37 | 38 | int consnumber, * consindex; 39 | double* constraints, * consleft, * consright, * consvars; 40 | int modnumber; 41 | 42 | double Tol; 43 | 44 | void (LevMar::* PrintOut)(double*); 45 | void (LevMar::* PrintFile)(FILE*, double, bool); 46 | 47 | bumper* stepchain, * bumperlist, * laststep; 48 | 49 | 50 | public: 51 | LevMar(int, char**); 52 | ~LevMar(); 53 | 54 | void ReadFiles(int, char**); 55 | int InitCond(double* presigmapr, double* preleftlim, double* prerightlim); 56 | void ReadCurve(); 57 | void ReadOptions(); 58 | int Run(); 59 | double ChiSquared(double*); 60 | void Grad(); 61 | void Covariance(); 62 | double ComputeConstraint(double* pr, int i); 63 | 64 | void PrintOutPS(double*); 65 | void PrintOutPX(double*); 66 | void PrintOutBS(double*); 67 | void PrintOutBO(double*); 68 | void PrintOutLS(double*); 69 | void PrintOutLX(double*); 70 | void PrintOutLO(double*); 71 | void PrintOutLK(double*); 72 | void PrintOutTS(double*); 73 | void PrintOutTX(double*); 74 | 75 | void PrintFilePS(FILE*, double, bool); 76 | void PrintFilePX(FILE*, double, bool); 77 | void PrintFileBS(FILE*, double, bool); 78 | void PrintFileBO(FILE*, double, bool); 79 | void PrintFileLS(FILE*, double, bool); 80 | void PrintFileLX(FILE*, double, bool); 81 | void PrintFileLO(FILE*, double, bool); 82 | void PrintFileLK(FILE*, double, bool); 83 | void PrintFileTS(FILE*, double, bool); 84 | void PrintFileTX(FILE*, double, bool); 85 | 86 | }; 87 | 88 | double Determinant(double*, int); 89 | void Inverse(double*, double*, int); 90 | 91 | #endif -------------------------------------------------------------------------------- /RTModel/lib/Reader.cpp: -------------------------------------------------------------------------------- 1 | // Reader.cpp : main project file. 2 | // This program formats data for a specific event for following work. 3 | // for following work. 4 | 5 | #define _CRT_SECURE_NO_WARNINGS 6 | #define _USE_MATH_DEFINES 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | using namespace std::filesystem; 16 | 17 | #ifdef _WIN32 18 | char systemslash = '\\'; 19 | #else 20 | char systemslash = '/'; 21 | #endif 22 | 23 | double tau = 0.1; // conventional correlation time for consecutive points 24 | int npmax = 4000; // maximum number of points left after re-binning 25 | int otherseasons = 1; // How to use other seasons 26 | int renormalize = 1; // Re-normalize error bars 27 | double thresholdoutliers = 10; // Threshold for removing outliers 28 | 29 | struct datapoint { 30 | datapoint* prev, * next; 31 | double t, y, err, sig, basesig; 32 | int dn; 33 | }; 34 | 35 | struct dataset { 36 | dataset* prev, * next; 37 | datapoint* first, * last; 38 | int length; 39 | char label[100]; 40 | 41 | dataset(); 42 | ~dataset(); 43 | void addpoint(double, double, double); 44 | }; 45 | 46 | #define _computesig\ 47 | p->sig=(p->y-p->prev->y);\ 48 | pc=(p->t-p->prev->t)/tau;\ 49 | p->sig*=p->sig/(p->err*p->err + p->prev->err*p->prev->err);\ 50 | p->sig+=pc*pc;\ 51 | p->sig *= p->basesig; 52 | 53 | 54 | 55 | int main(int argc, char* argv[]) 56 | { 57 | char exedir[256] = ""; 58 | char eventname[512] = ""; 59 | char filename[256] = ""; 60 | char titstring[256] = "", nostr[2], * undersc; 61 | char command[256], buffer[256]; 62 | double value; 63 | double t, y, err, yr, errr, w1, w2; 64 | FILE* f; 65 | int ifile, flag, nps, normalized = 0, satellite; 66 | double pc, residual, residual1, residual2, outlier, crosscheck, weight, minfac, maxlength; 67 | dataset* datalist = 0, * curdataset, * pmaxdataset; 68 | datapoint* p, * pmax, * p1, * p2; 69 | 70 | // Directory preliminaries. Reads event name from arguments. 71 | 72 | setbuf(stdout, nullptr); 73 | printf("******************************************\n"); 74 | printf("************* Reader **********\n"); 75 | printf("******************************************\n\n"); 76 | printf("*** This program formats data for a specific event\n\n"); 77 | 78 | 79 | if (argc > 1) { 80 | strcpy(eventname, argv[1]); 81 | } 82 | else { 83 | printf("\n\nEvent name? "); 84 | scanf("%s", eventname); 85 | //sprintf(eventname, "WDC10193"); 86 | } 87 | 88 | printf("\n\n- Event: %s\n", eventname); 89 | 90 | current_path(eventname); 91 | 92 | if (exists("LCToFit.txt")) { 93 | printf("\n\n- Data already processed"); 94 | return 0; 95 | } 96 | 97 | // Reading Reader.ini and set parameters accordingly 98 | 99 | if (exists("ini")) { 100 | current_path("ini"); 101 | f = fopen("Reader.ini", "r"); 102 | if (f != 0) { 103 | printf("\n\n- Reading options in Reader.ini"); 104 | while (!feof(f)) { 105 | int red = fscanf(f, "%s %s %lf", command, buffer, &value); 106 | if (red != 3) { 107 | command[0] = 0; 108 | //if (red != 0) { 109 | // printf("\n\n!!! Bad command in Reader.ini"); 110 | // return -1; 111 | //}; 112 | } 113 | if (strcmp(command, "binning") == 0) { 114 | npmax = (int)value; 115 | } 116 | if (strcmp(command, "tau") == 0) { 117 | tau = value; 118 | } 119 | if (strcmp(command, "otherseasons") == 0) { 120 | otherseasons = (int)value; 121 | } 122 | if (strcmp(command, "renormalize") == 0) { 123 | renormalize = (int)value; 124 | } 125 | if (strcmp(command, "thresholdoutliers") == 0) { 126 | thresholdoutliers = value; 127 | } 128 | } 129 | fclose(f); 130 | } 131 | else { 132 | printf("\n\n- Default options:"); 133 | } 134 | current_path(eventname); 135 | } 136 | 137 | 138 | 139 | printf("\nUse of other seasons %d", otherseasons); 140 | printf("\nBinning down to %d", npmax); 141 | printf("\nThreshold for outliers removal: %lf", thresholdoutliers); 142 | printf("\nTimescale for scatter evaluation %lf", tau); 143 | printf("\nRenormalize error bars: %s", (renormalize > 0) ? "yes" : "no"); 144 | if (renormalize == 0) normalized = 1; 145 | 146 | 147 | // Read data from files and create structured lists 148 | 149 | 150 | 151 | printf("\n\n- Reading data from files\n"); 152 | 153 | curdataset = datalist = 0; 154 | current_path("Data"); 155 | auto searchstring = regex(".*\\.dat"); 156 | for (auto const& itr : directory_iterator(".")) { 157 | string curfile = itr.path().filename().string(); 158 | if (regex_match(curfile, searchstring)) { 159 | strcpy(filename, curfile.c_str()); 160 | if (!datalist) { 161 | curdataset = datalist = new dataset; 162 | } 163 | else { 164 | curdataset->next = new dataset; 165 | curdataset->next->prev = curdataset; 166 | curdataset = curdataset->next; 167 | } 168 | printf("\n%s", filename); 169 | strcpy(curdataset->label, filename); 170 | f = fopen(filename, "r"); 171 | fscanf(f, "%[^\n]", titstring); 172 | fscanf(f, "%[\n]", nostr); 173 | while (fscanf(f, "%[^\n]", titstring) == 1) { 174 | fscanf(f, "%[\n]", nostr); 175 | sscanf(titstring, "%lf %lf %lf", &y, &err, &t); 176 | // Sanity checks on individual point 177 | if (y < 90 && err >0) { 178 | curdataset->addpoint(t, y, err); 179 | } 180 | } 181 | fclose(f); 182 | printf("\npoints: %d", curdataset->length); 183 | } 184 | } 185 | //auto itr = directory_iterator("."); 186 | //while (!itr._At_end()) { 187 | // string curfile = (*itr).path().filename().string(); 188 | // if (regex_match(curfile, searchstring)) { 189 | // strcpy(filename, curfile.c_str()); 190 | // if (!datalist) { 191 | // curdataset = datalist = new dataset; 192 | // } 193 | // else { 194 | // curdataset->next = new dataset; 195 | // curdataset->next->prev = curdataset; 196 | // curdataset = curdataset->next; 197 | // } 198 | // printf("\n%s", filename); 199 | // strcpy(curdataset->label,filename); 200 | // f = fopen(filename, "r"); 201 | // fscanf(f, "%[^\n]", titstring); 202 | // fscanf(f, "%[\n]", nostr); 203 | // while (fscanf(f, "%[^\n]", titstring) == 1) { 204 | // fscanf(f, "%[\n]", nostr); 205 | // sscanf(titstring, "%lf %lf %lf", &y, &err, &t); 206 | // // Sanity checks on individual point 207 | // if (y < 90 && err >0) { 208 | // curdataset->addpoint(t, y, err); 209 | // } 210 | // } 211 | // fclose(f); 212 | // printf("\npoints: %d", curdataset->length); 213 | // fclose(f); 214 | // } 215 | // itr++; 216 | //} 217 | current_path(eventname); 218 | 219 | // Sort lists 220 | 221 | printf("\n\n- Time ordering of data\n"); 222 | 223 | curdataset = datalist; 224 | while (curdataset) { 225 | printf("\n%s", curdataset->label); 226 | p = curdataset->first; 227 | while (p) { 228 | pmax = p; 229 | p1 = pmax->next; 230 | while (p1) { 231 | if (p1->t == pmax->t) { 232 | p2 = p1->prev; 233 | if (p1->prev) p1->prev->next = p1->next; 234 | if (p1->next) p1->next->prev = p1->prev; 235 | delete p1; 236 | curdataset->length--; 237 | p1 = p2; 238 | } 239 | else { 240 | if (p1->t < pmax->t) { 241 | pmax = p1; 242 | } 243 | } 244 | p1 = p1->next; 245 | } 246 | if (p != pmax) { 247 | pc = pmax->t; 248 | pmax->t = p->t; 249 | p->t = pc; 250 | pc = pmax->y; 251 | pmax->y = p->y; 252 | p->y = pc; 253 | pc = pmax->err; 254 | pmax->err = p->err; 255 | p->err = pc; 256 | } 257 | p = p->next; 258 | } 259 | 260 | curdataset = curdataset->next; 261 | //if (curdataset->length <= 3) { 262 | // printf(" discarded!"); 263 | // if (curdataset->prev) { 264 | // dataset* predataset = curdataset->prev; 265 | // curdataset = curdataset->next; 266 | // delete predataset->next; 267 | // predataset->next = curdataset; 268 | // if (curdataset) curdataset->prev = predataset; 269 | // } 270 | // else { 271 | // datalist = curdataset->next; 272 | // if (datalist) datalist->prev = 0; 273 | // delete curdataset; 274 | // curdataset = datalist; 275 | // } 276 | //} 277 | //else { 278 | // curdataset = curdataset->next; 279 | //} 280 | //pmax=curdataset->first->next; 281 | //while(pmax){ 282 | // flag=0; 283 | // p=curdataset->first; 284 | // while(p->t<=pmax->t && p!=pmax) p=p->next; 285 | // if(p!=pmax){ 286 | // if(p->t!=pmax->t){ 287 | // pc=pmax->t; 288 | // pmax->t=p->t; 289 | // p->t=pc; 290 | // pc=pmax->y; 291 | // pmax->y=p->y; 292 | // p->y=pc; 293 | // pc=pmax->err; 294 | // pmax->err=p->err; 295 | // p->err=pc; 296 | // }else{ 297 | // if(p->prev) p->prev->next=p->next; 298 | // if(p->next) p->next->prev=p->prev; 299 | // delete p; 300 | // curdataset->length--; 301 | // } 302 | // flag=1; 303 | // } 304 | // if(!flag) pmax=pmax->next; 305 | //} 306 | } 307 | 308 | 309 | // Estimating error bars for each dataset 310 | 311 | double ft1, ft2; 312 | printf("\n\n- Assessing error bars\n"); 313 | 314 | minfac = 1.e100; 315 | maxlength = -minfac; 316 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 317 | residual = weight = 0; 318 | for (p = curdataset->first->next; p; p = p->next) { 319 | residual1 = residual2 = 0; 320 | if (p->next) { 321 | p1 = p->next; 322 | if (p1->next) { 323 | p2 = p1->next; 324 | ft2 = (p->t - p1->t) / (p2->t - p1->t); 325 | ft1 = 1 - ft2; 326 | y = p1->y + ((p2->t == p1->t) ? 0 : (p2->y - p1->y) * ft2); 327 | pc = (p2->t - p->t) / tau; 328 | err = (p1->err * p1->err * ft1 * ft1 + p2->err * p2->err * ft2 * ft2 + p->err * p->err) * exp(pc / tau); 329 | w1 = 1 / exp(pc / tau); 330 | pc = (p->y - y); 331 | residual1 = pc / sqrt(err); 332 | yr = y; 333 | errr = err; 334 | } 335 | } 336 | if (p->prev) { 337 | p1 = p->prev; 338 | if (p1->prev) { 339 | p2 = p1->prev; 340 | ft2 = (p->t - p1->t) / (p2->t - p1->t); 341 | ft1 = 1 - ft2; 342 | y = p1->y + ((p2->t == p1->t) ? 0 : (p2->y - p1->y) / (p2->t - p1->t) * (p->t - p1->t)); 343 | pc = (p2->t - p->t) / tau; 344 | err = (p1->err * p1->err * ft1 * ft1 + p2->err * p2->err * ft2 * ft2 + p->err * p->err) * exp(-pc / tau); 345 | w2 = 1 / exp(-pc / tau); 346 | pc = (p->y - y); 347 | residual2 = pc / sqrt(err); 348 | } 349 | } 350 | outlier = residual1 * residual1 + residual2 * residual2; 351 | crosscheck = (y - yr); 352 | crosscheck *= crosscheck; 353 | crosscheck /= (err + errr); 354 | if (residual1 != 0 && residual2 != 0 && crosscheck <9 && sqrt(outlier) >thresholdoutliers) { 355 | printf("\nOutlier found: %lf %lf %lf", p->t, p->y, sqrt(outlier)); 356 | p1 = p->prev; 357 | p2 = p->next; 358 | p1->next = p2; 359 | p2->prev = p1; 360 | delete p; 361 | curdataset->length--; 362 | p = p1; 363 | } 364 | else { 365 | residual += outlier; 366 | weight += w1 + w2; 367 | } 368 | } 369 | // residual*=0.5*curdataset->length/weight; 370 | residual = (residual + 1.) / (weight + 1.); 371 | pc = sqrt(residual); 372 | printf("\n%s", curdataset->label); 373 | printf("\nResidual: %le Length: %d Weight: %le Normalization factor: %le", residual, curdataset->length, weight, pc); 374 | curdataset->first->sig = pc; 375 | if (curdataset->length > maxlength) { 376 | minfac = pc; 377 | maxlength = curdataset->length; 378 | } 379 | } 380 | 381 | // Re-normalizing error bars for each dataset 382 | 383 | if (normalized < 0.5) { 384 | printf("\n\n- Re-normalizing error bars"); 385 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 386 | pc = curdataset->first->sig;// / minfac; 387 | for (p = curdataset->first; p; p = p->next) { 388 | p->err *= pc; 389 | } 390 | curdataset->first->sig = 0; 391 | } 392 | } 393 | 394 | 395 | // Calculate peak season 396 | 397 | if (otherseasons > 0) { 398 | 399 | printf("\n\n- Calculate peak season\n"); 400 | 401 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 402 | datapoint** chunkfirst, ** chunklast; 403 | int nchunks, ns, imaxdev; 404 | double* devs; 405 | double sy, sy2, ss, maxdev; 406 | printf("\n%s", curdataset->label); 407 | // Divide dataset in chunks separated by gaps longer than 30 days 408 | chunkfirst = (datapoint**)malloc(sizeof(chunkfirst)); 409 | chunklast = (datapoint**)malloc(sizeof(chunkfirst)); 410 | chunkfirst[0] = curdataset->first; 411 | nchunks = 0; 412 | do { 413 | p = chunkfirst[nchunks]; 414 | while (p && p->next && p->next->t - p->t < 30) p = p->next; 415 | chunklast[nchunks] = p; 416 | if (p->next) { 417 | nchunks++; 418 | chunkfirst = (datapoint**)realloc(chunkfirst, sizeof(chunkfirst) * (nchunks + 1)); 419 | chunklast = (datapoint**)realloc(chunklast, sizeof(chunkfirst) * (nchunks + 1)); 420 | chunkfirst[nchunks] = p->next; 421 | } 422 | } while (p->next); 423 | nchunks++; 424 | 425 | // Calculate standard deviation from flat light curve for each chunk 426 | devs = (double*)malloc(sizeof(double) * nchunks); 427 | imaxdev = 0; 428 | maxdev = -1; 429 | for (int ichunk = 0; ichunk < nchunks; ichunk++) { 430 | sy = ss = sy2 = 0; 431 | ns = 0; 432 | for (p = chunkfirst[ichunk]; p != chunklast[ichunk]->next; p = p->next) { 433 | sy += p->y / (p->err * p->err); 434 | sy2 += p->y * p->y / (p->err * p->err); 435 | ss += 1 / (p->err * p->err); 436 | ns++; 437 | } 438 | devs[ichunk] = sqrt((sy2 - sy * sy / ss) / ns); 439 | // Note chunk with maximal deviation (should be peak season) 440 | if (devs[ichunk] > maxdev) { 441 | maxdev = devs[ichunk]; 442 | imaxdev = ichunk; 443 | } 444 | printf("\n%lf --- %lf : dev: %lf", chunkfirst[ichunk]->t, chunklast[ichunk]->t, devs[ichunk]); 445 | } 446 | 447 | if (otherseasons == 1) { // Diminish significance of seasons other than peak season 448 | printf("\nSeasons other than peak season considered less significant in re-binning"); 449 | for (int ichunk = 0; ichunk < nchunks; ichunk++) { 450 | if (ichunk != imaxdev) { 451 | double fac = devs[ichunk] / maxdev; 452 | fac *= fac; 453 | for (p = chunkfirst[ichunk]; p != chunklast[ichunk]->next; p = p->next) { 454 | p->basesig = fac; 455 | } 456 | } 457 | } 458 | } 459 | else { // Otherwise remove seasons other than peak season 460 | printf("\nSeasons other than peak season are removed"); 461 | for (p = curdataset->first; p != chunkfirst[imaxdev]; p = p1) { 462 | p1 = p->next; 463 | delete p; 464 | curdataset->length--; 465 | } 466 | curdataset->first = chunkfirst[imaxdev]; 467 | curdataset->first->prev = 0; 468 | for (p = chunklast[imaxdev]->next; p; p = p1) { 469 | p1 = p->next; 470 | delete p; 471 | curdataset->length--; 472 | } 473 | curdataset->last = chunklast[imaxdev]; 474 | curdataset->last->next = 0; 475 | } 476 | 477 | free(devs); 478 | free(chunkfirst); 479 | free(chunklast); 480 | } 481 | } 482 | 483 | 484 | 485 | // Calculate significance of each point with respect to the previous one 486 | 487 | printf("\n- Calculate significance of each data point\n"); 488 | 489 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 490 | for (p = curdataset->first->next; p; p = p->next) { 491 | _computesig 492 | } 493 | } 494 | 495 | 496 | // Eliminating short datasets 497 | 498 | printf("\n\n- Removing short datasets\n"); 499 | 500 | pmaxdataset = datalist; 501 | nps = 0; 502 | while (pmaxdataset) { 503 | curdataset = pmaxdataset->next; 504 | if (pmaxdataset->length < 4) { 505 | printf("\n- Discarding: %s", pmaxdataset->label); 506 | if (pmaxdataset->prev) { 507 | pmaxdataset->prev->next = pmaxdataset->next; 508 | } 509 | else { 510 | datalist = pmaxdataset->next; 511 | } 512 | if (pmaxdataset->next) pmaxdataset->next->prev = pmaxdataset->prev; 513 | pmaxdataset->next = 0; 514 | delete pmaxdataset; 515 | } 516 | else { 517 | nps += pmaxdataset->length; 518 | } 519 | pmaxdataset = curdataset; 520 | } 521 | 522 | // Rebin useless points, reducing the number of points to npmax 523 | 524 | printf("\n- Rebinning data down to %d\n", npmax); 525 | while (nps > npmax) { 526 | pmax = datalist->first->next; 527 | pmaxdataset = datalist; 528 | ifile = flag = 0; 529 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 530 | for (p = curdataset->first->next; p; p = p->next) { 531 | if (p->sig < pmax->sig) { 532 | pmax = p; 533 | pmaxdataset = curdataset; 534 | flag = ifile; 535 | } 536 | } 537 | ifile++; 538 | } 539 | p = pmax->prev; 540 | // printf("\n%d %d %lf\n%lf %le %le\n%lf %le %le",nps,flag,pmax->sig,p->t,p->y,p->err,pmax->t,pmax->y,pmax->err); 541 | w1 = 1 / (p->err * p->err); 542 | w2 = 1 / (pmax->err * pmax->err); 543 | p->t = (p->t * w1 + pmax->t * w2) / (w1 + w2); 544 | p->y = (p->y * w1 + pmax->y * w2) / (w1 + w2); 545 | p->err = 1 / sqrt(w1 + w2); 546 | p->next = pmax->next; 547 | // printf("\n%lf %le %le",p->t,p->y,p->err); 548 | 549 | if (pmax->next) { 550 | pmax->next->prev = p; 551 | } 552 | else { 553 | pmaxdataset->last = p; 554 | } 555 | pmaxdataset->length--; 556 | delete pmax; 557 | nps--; 558 | 559 | if (p->t > -1.e99 && p->y > -1.e99) { 560 | } 561 | else { 562 | if (p->prev) { 563 | p->prev->next = p->next; 564 | } 565 | else { 566 | datalist->first = p->next; 567 | } 568 | if (p->next) { 569 | p->next->prev = p->prev; 570 | } 571 | else { 572 | datalist->last = p->prev; 573 | } 574 | delete p; 575 | pmaxdataset->length--; 576 | nps--; 577 | } 578 | 579 | 580 | if (pmaxdataset->length < 4) { 581 | if (pmaxdataset->prev) { 582 | pmaxdataset->prev->next = pmaxdataset->next; 583 | } 584 | else { 585 | datalist = pmaxdataset->next; 586 | } 587 | if (pmaxdataset->next) pmaxdataset->next->prev = pmaxdataset->prev; 588 | nps -= pmaxdataset->length; 589 | pmaxdataset->next = 0; 590 | delete pmaxdataset; 591 | } 592 | else { 593 | if (p->prev) { 594 | _computesig 595 | } 596 | p = p->next; 597 | if (p) { 598 | _computesig 599 | } 600 | } 601 | } 602 | 603 | 604 | 605 | // Write rebinned datasets to file 606 | 607 | printf("\n- Writing final data to LCToFit.txt\n"); 608 | 609 | remove("LCToFit.bkp"); 610 | rename("LCToFit.txt", "LCToFit.bkp"); 611 | 612 | f = fopen("LCToFit.txt", "w"); 613 | ifile = 0; 614 | fprintf(f, "%d\n", nps); 615 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 616 | printf("\n%d", curdataset->length); 617 | undersc = curdataset->label - 5 + strlen(curdataset->label); 618 | satellite = (*undersc <= '9' && *undersc > '0') ? (*undersc) - '0' : 0; // satellite data should have names like ZOB1501241.dat where 1.dat distinguishes satellites data 619 | for (p = curdataset->first; p; p = p->next) { 620 | y = pow(10., -0.4 * p->y); 621 | err = p->err * y * 0.9210340371976184; 622 | fprintf(f, "%d %.10le %.10le %.10le %d\n", ifile, p->t, y, err, satellite); 623 | } 624 | ifile++; 625 | } 626 | fclose(f); 627 | 628 | f = fopen("FilterToData.txt", "w"); 629 | for (curdataset = datalist; curdataset; curdataset = curdataset->next) { 630 | fprintf(f, "%s\n", curdataset->label); 631 | } 632 | fclose(f); 633 | 634 | // Exiting 635 | 636 | delete datalist; 637 | 638 | //printf("\nHello!"); 639 | // getchar(); 640 | 641 | return 0; 642 | } 643 | 644 | 645 | dataset::dataset() { 646 | length = 0; 647 | prev = next = 0; 648 | first = last = 0; 649 | } 650 | 651 | dataset::~dataset() { 652 | datapoint* p, * q; 653 | 654 | delete next; 655 | for (p = first; p; p = q) { 656 | q = p->next; 657 | delete p; 658 | } 659 | } 660 | 661 | void dataset::addpoint(double t, double y, double err) { 662 | datapoint* p; 663 | p = new datapoint; 664 | p->t = t; 665 | p->y = y; 666 | p->err = err; 667 | if (length) { 668 | p->prev = last; 669 | last->next = p; 670 | last = p; 671 | } 672 | else { 673 | first = last = p; 674 | p->prev = 0; 675 | } 676 | p->next = 0; 677 | p->sig = 0; 678 | p->basesig = 1; 679 | length++; 680 | } 681 | -------------------------------------------------------------------------------- /RTModel/lib/bumper.cpp: -------------------------------------------------------------------------------- 1 | // bumper.cpp 2 | // Implementation of all methods in bumper.h 3 | 4 | #define _CRT_SECURE_NO_WARNINGS 5 | #define _USE_MATH_DEFINES 6 | #include "bumper.h" 7 | #include 8 | #include 9 | 10 | const double epsilon = 1.e-99; 11 | 12 | bumper::bumper(double* pr, int ns) { 13 | nps = ns; 14 | p0 = (double*)malloc(sizeof(double) * nps); 15 | dp = (double*)malloc(sizeof(double) * nps); 16 | for (int i = 0; i < nps; i++) { 17 | p0[i] = pr[i]; 18 | } 19 | curv = cov = 0; 20 | duplicate = false; 21 | 22 | next = 0; 23 | } 24 | 25 | bumper::~bumper() { 26 | free(p0); 27 | free(dp); 28 | if (curv) free(curv); 29 | if (cov) free(cov); 30 | } 31 | 32 | void bumper::SetCurvature(double* sr, double nrm) { 33 | curv = (double*)malloc(sizeof(double) * nps * nps); 34 | for (int i = 0; i < nps * nps; i++) { 35 | curv[i] = sr[i] * nrm; 36 | } 37 | } 38 | 39 | void bumper::SetCovariance(double* sr, double nrm) { 40 | cov = (double*)malloc(sizeof(double) * nps * nps); 41 | for (int i = 0; i < nps * nps; i++) { 42 | cov[i] = sr[i] / nrm; 43 | } 44 | //curv = (double*)malloc(sizeof(double) * nps * nps); 45 | //Inverse(sr, curv, nps); 46 | } 47 | 48 | void bumper::UpdateCurvature(double bumperpower) { 49 | for (int i = 0; i < nps * nps; i++) { 50 | curv[i] /= (bumperpower * bumperpower); 51 | } 52 | } 53 | 54 | double bumper::distance(double* pr) { 55 | double dist; 56 | for (int i = 0; i < nps; i++) { 57 | dp[i] = pr[i] - p0[i] + epsilon; 58 | } 59 | dist = 0; 60 | for (int i = 0; i < nps; i++) { 61 | dist += dp[i] * curv[i * nps + i] * dp[i]; 62 | for (int j = 0; j < i; j++) { 63 | dist += 2 * dp[i] * curv[i * nps + j] * dp[j]; 64 | } 65 | } 66 | return dist; 67 | } 68 | 69 | void bumper::signCovariance(int j) { 70 | for (int i = 0; i < nps; i++) { 71 | cov[j * nps + i] = -cov[j * nps + i]; 72 | cov[i * nps + j] = -cov[i * nps + j]; 73 | } 74 | } 75 | 76 | void bumper::flipCovariance(int i, int j) { 77 | double sc; 78 | for (int k = 0; k < nps; k++) { 79 | sc = cov[k * nps + i]; 80 | cov[k * nps + i] = cov[k * nps + j]; 81 | cov[k * nps + j] = sc; 82 | } 83 | for (int k = 0; k < nps; k++) { 84 | sc = cov[i * nps + k]; 85 | cov[i * nps + k] = cov[j * nps + k]; 86 | cov[j * nps + k] = sc; 87 | } 88 | } 89 | 90 | double Determinant(double* A, int n) { 91 | double* U, L, M; 92 | double det = 1.; 93 | int iM; 94 | 95 | U = (double*)malloc(sizeof(double) * n * n); 96 | 97 | for (int i = 0; i < n; i++) { 98 | for (int j = 0; j < n; j++) { 99 | U[i * n + j] = A[i * n + j]; 100 | } 101 | } 102 | 103 | for (int j = 0; j < n - 1; j++) { 104 | for (int i = j + 1; i < n; i++) { 105 | // find row with largest pivot 106 | M = 0.; 107 | iM = j; 108 | for (int i1 = j; i1 < n; i1++) { 109 | if (M < fabs(U[i1 * n + j])) { 110 | M = fabs(U[i1 * n + j]); 111 | iM = i1; 112 | } 113 | } 114 | if (M == 0.) { 115 | free(U); 116 | return 0.; 117 | } 118 | // select row with largest pivot as the next to be processed 119 | if (iM != j) { 120 | for (int j1 = j; j1 < n; j1++) { 121 | L = U[j * n + j1]; 122 | U[j * n + j1] = U[iM * n + j1]; 123 | U[iM * n + j1] = -L; 124 | } 125 | } 126 | L = (fabs(U[j * n + j]) > 1.e-100) ? U[i * n + j] / U[j * n + j] : 1.0; 127 | for (int k = j; k < n; k++) { 128 | U[i * n + k] -= L * U[j * n + k]; 129 | } 130 | } 131 | } 132 | 133 | for (int i = 0; i < n; i++) { 134 | det *= U[i * n + i]; 135 | } 136 | 137 | free(U); 138 | return det; 139 | } 140 | 141 | 142 | void Inverse(double* source, double* dest, int n) { 143 | double p1; 144 | int j1, k1; 145 | double* minor; 146 | minor = (double*)malloc(sizeof(double) * (n - 1) * (n - 1)); 147 | p1 = Determinant(source, n) + 1.e-100; 148 | for (int i = 0; i < n; i++) { 149 | for (int i2 = 0; i2 < n; i2++) { 150 | for (int j = 0; j < n - 1; j++) { 151 | for (int k = 0; k < n - 1; k++) { 152 | j1 = (j < i2) ? j : j + 1; 153 | k1 = (k < i) ? k : k + 1; 154 | minor[j * (n - 1) + k] = source[j1 * n + k1]; 155 | } 156 | } 157 | dest[i * n + i2] = Determinant(minor, n - 1) / p1 * (((i + i2) % 2 == 0) ? 1 : -1); 158 | } 159 | } 160 | free(minor); 161 | } 162 | 163 | void CombineCovariances(bumper* scanbumper, bumper* scanbumper2, double* Cov, double* Curv, int nps) { 164 | for (int i = 0; i < nps; i++) { 165 | for (int j = 0; j < nps; j++) { 166 | Cov[i * nps + j] = scanbumper->cov[i * nps + j] + scanbumper2->cov[i * nps + j]; 167 | } 168 | } 169 | Inverse(Cov, Curv, nps); 170 | } 171 | -------------------------------------------------------------------------------- /RTModel/plotmodel/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.4.1" 2 | __author__ = 'Valerio Bozza' 3 | __credits__ = 'University of Salerno, Italy' 4 | 5 | from .plotmodel import * 6 | -------------------------------------------------------------------------------- /RTModel/templates/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.3" 2 | __author__ = 'Valerio Bozza' 3 | __credits__ = 'University of Salerno, Italy' 4 | 5 | from .templates import * 6 | -------------------------------------------------------------------------------- /RTModel/templates/templates.py: -------------------------------------------------------------------------------- 1 | import site 2 | from pathlib import Path 3 | import RTModel.plotmodel as plm 4 | import shutil 5 | import math 6 | import numpy as np 7 | 8 | template_library_path = None 9 | site_packages_directories = site.getsitepackages() 10 | site_packages_directories.append(site.getusersitepackages()) 11 | for site_packages_directory in site_packages_directories: 12 | template_library_path = Path(site_packages_directory).joinpath('RTModel/data/TemplateLibrary.txt') 13 | if template_library_path is None: 14 | raise FileNotFoundError(f'RTModel binary directory not found. Searched {site_packages_directories} ' 15 | f'site-packages directories.') 16 | 17 | def clone_default_library(destination): 18 | shutil.copy(template_library_path, destination) 19 | return 20 | 21 | def load_library(source = None): 22 | templates = None 23 | if(source == None): 24 | source = template_library_path 25 | with open(source) as f: 26 | lines = f.readlines() 27 | templates = [] 28 | for i in range(1,len(lines)): 29 | template = [float(par) for par in lines[i].split()] 30 | templates.append(template) 31 | return templates 32 | 33 | def save_library(destination, templates): 34 | with open(destination, 'w') as f: 35 | f.write(str(len(templates)) + '\n') 36 | for temp in templates: 37 | for num in temp: 38 | f.write(str(num) + ' ') 39 | f.write('\n') 40 | return 41 | 42 | def show_template(parameters, tmin = -3, tmax = +3, tstep = 0.001, accuracy = 0.01): 43 | logs = math.log(parameters[0]) 44 | logq = math.log(parameters[1]) 45 | u0 = parameters[2] 46 | alpha = parameters[3] 47 | logrho = math.log(parameters[4]) 48 | logtE = 0 49 | t0 = 0 50 | print('s: ' + str(parameters[0]) + ' q: ' + str(parameters[1]) + ' u0: ' + str(parameters[2]) + ' alpha: ' + str(parameters[3]) + ' rho: '+ str(parameters[4])) 51 | parnew = parameters[0:5] + [1,0] 52 | pl = plm.plotmodel(None, model = 'LS', parameters = parnew, tmin = tmin, tmax = tmax, timesteps = math.floor((tmax-tmin)/tstep+1) ,accuracy = accuracy, printpars = False) 53 | times = pl.t 54 | mags = pl.results[0] 55 | 56 | peaks =[] 57 | lastmin = mags[0] 58 | lastmax = -1 59 | timmax = -1 60 | derivative = -1 61 | i = 1 62 | for i in range(1,len(mags)): 63 | if(derivative == -1): 64 | if(mags[i]lastmin + 5*accuracy): 67 | derivative = +1 68 | lastmax = mags[i] 69 | else: 70 | if(mags[i]>mags[i-1]): 71 | lastmax = mags[i] 72 | timmax = times[i] 73 | elif(mags[i] 27 | 28 | The `plotchain()` function shows the chains of steps taken by the Levenberg-Marquardt fit from the initial condition to the minima found. The blue line is the first fit, while the following fits after the bumping mechanism described in [Fitting](Fitting.md) are shown with different colors. Each minimum is marked by a filled circle. The parameter space is identified by the two values given as `parameter1,parameter2`. The order of parameters is the one specified in [Model categories](ModelCategories.md). 29 | 30 | ## Animating the fit 31 | 32 | The fit process can be animated by the following code 33 | 34 | ``` 35 | stepchainfile = 'PreModels/LS0190/LS0190-stepchain0.dat' 36 | plm.plotmodel(eventname = eventname, modelfile = stepchainfile, printpars = False, animate = 1,interval = 800) 37 | ``` 38 | 39 | The output is a gif file `ani.gif` generated in the directory `/event001` which looks like this 40 | 41 | 42 | 43 | The individual frames are contained in a subdirectory `/tmpssteps`. 44 | 45 | The `plotmodel()` function works in the same way as illustrated in [Plot model](PlotModel.md). The additional options we see here with their default values are the following: 46 | - `animate = 0`: If non-zero, an animated gif is generated. This only works with files containing a sequence of models line by line, as the step chains generated by the `LevMar` module described in [Fitting](Fitting.md). 47 | - `interval = 1000`: The time interval in milliseconds between frames in the generated gif. 48 | 49 | If you want to show the gif file in a Jupyter notebook you may use the following lines: 50 | 51 | ``` 52 | from IPython.display import Image 53 | Image(filename = 'ani.gif') 54 | ``` 55 | 56 | [Go to **Template libraries**](TemplateLibraries.md) 57 | -------------------------------------------------------------------------------- /docs/Archive.md: -------------------------------------------------------------------------------- 1 | [Back to **Plotting models**](PlotModel.md) 2 | 3 | # Archiving and Updating models 4 | 5 | ## Archiving a run 6 | 7 | After the completion of a modeling run, it is possible to save and **archive the results** and run a new modeling run on the same event with different options or data files. This is useful if you have additional data for some event and you want to keep track of previous results. Another possibility is that the first modeling run with the default options was not satisfactory and you want to try different options for the model search but you want to compare old and new results. 8 | 9 | ``` 10 | import RTModel 11 | rtm = RTModel.RTModel() 12 | rtm.set_event('/event001') 13 | rtm.archive_run() 14 | ``` 15 | 16 | In this code the function `archive_run()` moves all the files and subdirectories inside `/event001` to a new subdirectory called `/run-0001`. Only the `/Data` subdirectory is left in the event directory. The event is then ready for a new modeling run. 17 | 18 | All consecutive archived runs are stored in directories with increasing number. Optionally, the user may specify a different archive directory by `rtm.archive_run(destination = 'myarchive')`. 19 | 20 | We note that if you have not cleaned up your event directory from the preliminary models, you will rapidly exhaust your disk space if you archive runs still including them. We suggest to clean up the directory before archiving 21 | ``` 22 | rtm.cleanup_preliminary_models() 23 | rtm.archive_run() 24 | ``` 25 | 26 | 27 | ## Previously found best models 28 | 29 | An important feature of archived runs is that the best models found in the most recent archived run (identified by its progressive number) are included in the set of initial conditions of the new run. In this way, `RTModel` will also have simple **updates of the previous best models** available in the model selection in the new run. 30 | 31 | ## Re-using options from previous runs 32 | 33 | If you want to use the same options as in the previous run, you can use the `recover_options` function. Supposing you have an event with a completed run, the following code 34 | 35 | ``` 36 | import RTModel 37 | rtm = RTModel.RTModel() 38 | rtm.set_event('/event001') 39 | rtm.recover_options() 40 | ``` 41 | 42 | will generate the output 43 | ``` 44 | Reader --- ['tau = 0.1', 'binning = 20000', 'otherseasons = 1', 'renormalize = 1', 'thresholdoutliers = 10'] 45 | InitCond --- ['npeaks = 2', 'peakthreshold = 10.0', 'oldmodels = 4', 'usesatellite = 0', 'override = 6757.903 6753.5'] 46 | LevMar --- ['nfits = 5', 'maxsteps = 50', 'timelimit = 600.0', 'bumperpower = 2.0'] 47 | ModelSelector --- ['sigmasoverlap = 3.0', 'sigmachisquare = 1.0', 'maxmodels = 10'] 48 | ``` 49 | 50 | This is the list of all options used in the previous run. You will find detailed explanations for each of these options in the sections for advanced users, starting from [Data pre-processing](DataPreprocessing.md). At the moment, we note that by this function you can repeat a run using exactly the same options you used in the previous run. In fact, all options are automatically loaded in your object `rtm` and will be used as they are unless you explicitly change them. 51 | 52 | If any [constraints](Constraints.md) have been specified, they will also be recovered. 53 | 54 | You may also pick up the options from one of the archived runs by specifying the optional argument `run` 55 | ``` 56 | rtm.recover_options(run = '/eventoo1/run-0001') 57 | ``` 58 | 59 | [Go to **Satellite datasets**](Satellite.md) 60 | -------------------------------------------------------------------------------- /docs/Constraints.md: -------------------------------------------------------------------------------- 1 | [Back to **Limb darkening**](LimbDarkening.md) 2 | 3 | # Constrained fits 4 | 5 | It is possible to impose constraints on the fit parameters or some pre-defined combinations of them that are particularly important in microlensing observations. Let $f(\mathbf{p})$ be a function of the fit parameters. Suppose we want to force $f(\mathbf{p}) = f_0$ with some tolerance $\sigma$. Then we make the following addition to the $\chi^2$ function: 6 | 7 | $\tilde\chi^2 =\chi^2 + \left(\frac{f(\mathbf{p})-f_0}{\sigma}\right)^2$ 8 | 9 | The modified $\tilde\chi^2$ including a gaussian constraint on $f$ is then used in minimization. In `RTModel` it is also possible to consider asymmetric constraints with difference tolerances on either side of $f_0$ following he form 10 | 11 | $\tilde\chi^2 =\chi^2 + \left(\frac{f(\mathbf{p})-f_0}{\sigma_l}\right)^2\Theta(f_0-f(\mathbf{p})) + \left(\frac{f(\mathbf{p})-f_0}{\sigma_r}\right)^2\Theta(f(\mathbf{p})-f_0)$ 12 | 13 | ## Implementation 14 | 15 | The practical implementation in `RTModel` occurs through the function `set_constraints` as in the following example 16 | 17 | ``` 18 | import RTModel 19 | rtm = RTModel.RTModel('/event001') 20 | 21 | myconstraints = [['tE', 23.0, -1.0, +1.0], 22 | ['log_rho', -3, -2, +0.1], 23 | ['g_1', 0.0, -0.01, 1.e100]] 24 | rtm.set_constraints(myconstraints) 25 | 26 | rtm.run() 27 | ``` 28 | 29 | Here we have built a list of constraints on several parameters or functions that will be included in the modified $\tilde\chi^2$ as above. Each constraint is in the form of a list following the general form `[function_name, f_0, sigma_l, sigma_r]`. The numbers for `f_0, sigma_l, sigma_r` express the asymmetric constraint as explained above. Note that one-sided constraints can be easily obtained if one of the sigmas is set to extremely large values, as in the third constraint in the example above. 30 | 31 | The following sections explain the syntax for the `function_name` in detail. 32 | 33 | ## Names of the constrained functions 34 | 35 | Each entry in the list of constraints is a list starting with the name of the parameter or the function that is going to be constrained. We have four possibilities available to the user. 36 | 37 | ### Constraining a parameter 38 | 39 | A single fit parameter can be just indicated as constrained function. For the names of the parameters, refer to [Model Categories](ModelCategories.md). Note that if the constraint is defined on a parameter that is not relevant for a given model category, the constraint will just be ignored for that specific model category. 40 | 41 | ### Constraining the logarithm of a parameter 42 | 43 | In the second constraint in the example above we see how to constrain the logarithm (base 10) of a given parameter. The name of the parameter should just be preceded by the prefix `'log_'`. 44 | 45 | ### Constraining blending 46 | 47 | The third constraint in the example above shows how to constrain the blending parameter for telescope number 1 (the telescope list starts from 0). We remind that in `RTModel` the blending parameter is defined as the ratio of the background flux to the source flux: $g_1 = F_{background,1}/F_{source,1}$. Note that in the specific case, non-negative blending has been requested. 48 | 49 | ### Other functions 50 | 51 | To constrain other functions of the parameters, you may choose from the following list, to be updated on demand by users: 52 | 53 | - 'muangle': The angle $\psi_\mu$ in radians of the relative lens-source proper motion from the North direction taken counterclockwise. This angle affects the parallax parameters according to 54 | 55 | $\arctan \frac{\pi_E}{\pi_N} =\psi_\mu$ 56 | 57 | This direction can be obtained from high-resolution observations which have been able to resolve the lens and the source individually. The constraint on the magnitude of the proper motion becomes a constraint on $t_E$ when combined with constraints on $\theta_E$ coming from the source study and the finite source effect. 58 | 59 | - 't*': The source radius crossing time 60 | 61 | $t_E \rho_* =t_*$ 62 | 63 | ## Recovering previous constraints 64 | 65 | The constraints used in a given modeling run are stored in the file `Constraints.ini` in the subdirectory `/ini` in the event directory. 66 | 67 | The function `recover_options()` illustrated in [Archiving and updating models](Archive.md) recovers previously used constraints for a given event. These can be modified by manipulating the `rtm.constraints` object and will be used in the next modeling run. 68 | 69 | [Go to **Data pre-processing**](DataPreprocessing.md) 70 | -------------------------------------------------------------------------------- /docs/DataPreparation.md: -------------------------------------------------------------------------------- 1 | [Back to **Summary**](README.md) 2 | 3 | # Data preparation 4 | 5 | In this page we explain how the data should be prepared for the analysis by `RTModel`. You can find some sample events already prepared in the directory [events](/events). These can be useful to see how an event directory should look like at the beginning or at the end of the modeling run. 6 | 7 | ## Directory structure 8 | 9 | Each microlensing event should have its own dedicated directory somewhere on your PC (e.g. `/event001/`). 10 | 11 | This directory should contain a subdirectory named `/Data`. 12 | 13 | The `/Data` directory should contain all photometric time series available for the analysis. Each series (data collected by a single telescope in one filter) corresponds to one file with extension `.dat`. 14 | 15 | ## Photometry files 16 | 17 | The content of each `.dat` file should be as in the following example: 18 | 19 | ``` 20 | # Mag err HJD-2450000 21 | 19.0232 0.012 8370.1223 22 | 19.0150 0.011 8370.2421 23 | 19.0034 0.011 8370.3697 24 | 18.9712 0.010 8370.4911 25 | 18.9592 0.011 8370.6114 26 | 18.9109 0.009 8370.8234 27 | 18.8798 0.009 8371.0092 28 | ... 29 | 30 | ``` 31 | 32 | The first line is just a header and is ignored by `RTModel`. 33 | 34 | Each data point contains magnitude, error and Heliocentric Julian Date - 2450000. 35 | 36 | ## Event coordinates 37 | 38 | Event coordinates are contained in a file with extension `.coordinates` (e.g. `event001.coordinates`) placed in the same `/Data` directory along with the photometry files. 39 | 40 | The content of the file should be in the form `HH:MM:SS.S +DD:PP:SS.S` for right ascension and declination respectively, e.g. `18:00:23.32 -32:11:09.7`. 41 | 42 | ## Optional input files 43 | 44 | Other optional input files are observations from [satellite](Satellite.md) and [limb darkening coefficients](LimbDarkening.md), described in the corresponding pages. 45 | 46 | [Go to **Modeling Run**](ModelingRun.md) 47 | -------------------------------------------------------------------------------- /docs/DataPreprocessing.md: -------------------------------------------------------------------------------- 1 | [Back to **Constrained fits**](Constraints.md) 2 | 3 | # Data pre-processing 4 | 5 | As explained in [Full modeling run](ModelingRun.md), all steps in the modeling run are performed by external pre-compiled executables. Each of them can be launched separately through the corresponding function available in `RTModel`. 6 | 7 | ## The `Reader` module 8 | 9 | The first step in the modeling run is the data pre-processing, which is performed by a specific external module called `Reader`. This can be launched by the corresponding function called `Reader()`: 10 | 11 | ``` 12 | import RTModel 13 | rtm = RTModel.RTModel('/event001') 14 | rtm.Reader() 15 | ``` 16 | 17 | With this code, we just perform the data pre-processing by the `Reader` module without proceeding to the following steps. In the `/event001` directory you will see that the following products appear: 18 | - a new subfolder called `ini/`. This contains the file `Reader.ini` file, which contains the current options with which `Reader` has been launched; 19 | - a file named `LCToFit.txt` containing all data points that will be used for modeling after combining all photometry files found in `/Data`; 20 | - a file named `FilterToData.txt` containing the ordered list of names of the datasets used to build `LCToFit.txt`. 21 | 22 | After the execution of `Reader`, you may call the `run()` function to complete the modeling run or the `InitCond()` function if you just want to check the results of the next step, which is [Initial conditions](InitCond.md) settings. 23 | 24 | ## Pre-processing operations 25 | 26 | The `Reader` module performs several operations on the original data to improve the chances of a successful modeling run and limit the computational time. All these operations can be fully controlled by the user through specific options. In particular: 27 | 28 | - Data are re-binned until the number of data points is contained within the desired amount; 29 | - Error bars are re-normalized based on the assessment of local scatter; 30 | - Outliers are removed; 31 | - Datasets left with less than 4 points are ignored. 32 | 33 | For details about the algorithms used in this pre-processing, please refer to the [RTModel paper](https://ui.adsabs.harvard.edu/abs/2024A%26A...688A..83B/abstract). 34 | 35 | ## Options for pre-processing 36 | 37 | ### The `config_Reader()` function 38 | 39 | The user may specify his/her own options to drive the pre-processing to the desired result by calling the `config_Reader()` function with the proper options: 40 | 41 | ``` 42 | import RTModel 43 | rtm = RTModel.RTModel('/event001') 44 | rtm.config_Reader(binning = 4000, tau = 0.1, otherseasons = 1, renormalize = 1, thresholdoutliers = 10) 45 | rtm.run() 46 | ``` 47 | 48 | The call to `config_Reader()` will affect all following executions of the `Reader` module, whether called through `run()` or `Reader()`. If you want to change your options, you may call `config_Reader()` again. 49 | 50 | ### Description of the options 51 | 52 | Here we describe the options in detail with their default values: 53 | 54 | - `binning = 4000`: the maximum number of data points you want to model. If the original datasets total to less than `binning` they are left untouched. 55 | - `tau = 0.1`: The timescale (in days) used for the assessment of local scatter and for re-binning. In a first approximation, `RTModel` considers variations below `tau` as possible scatter. 56 | - `otherseasons = 1`: how to treat seasons other than the season containing the peak value: 0 for including all seasons, 1 to downgrade the significance of other seasons in the re-binning process, 2 to remove all seasons other than the peak season. 57 | - `renormalize = 1`: if non-zero, all datasets are re-normalized based on the scatter assessment. 58 | - `thresholdoutliers = 10`: threshold in sigmas to remove outliers. 59 | 60 | All options are also accessible separately as properties of the `RTModel` class. The user may thus modify the value of each option one by one. The names of the properties are the same as the options in `config_Reader()` with the prefix `Reader_`, as shown in the example below: 61 | 62 | ``` 63 | rtm.Reader_binning = 2000 64 | rtm.Reader_otherseasons = 0 65 | ``` 66 | 67 | An empty call to `config_Reader()` with no parameters will reset all variables to the default values. Notice that the options that are not explicitly specified in the call to `config_Reader()` are always reset to their default values. This is also true if you previously used the `recover_options()` function to inherit the options from a previous run (see [Archiving and updating](Archive.md)). 68 | 69 | ### How to switch off pre-processing 70 | 71 | If you want to avoid any modifications to the original data, you may switch off all pre-processing by the following call 72 | ``` 73 | rtm.config_Reader(binning = 1000000, renormalize = 0, thresholdoutliers = 1000000) 74 | ``` 75 | 76 | Re-binning and outliers removal do not intervene if set to very high numbers and renormalization is switched off. 77 | 78 | ### Recording the options 79 | 80 | In each modeling run, the options for `Reader` are stored in the file `Reader.ini` in the `/ini` subdirectory within the event directory for later reference. If the modeling run is [archived](Archive.md), also the whole `/ini` subdirectory is saved so that the user may check the options used in each modeling run. The function `recover_options()` can be used to load the options from a previous run. 81 | 82 | ## Forcing error-bar normalization 83 | 84 | With the `renormalize` option, the user may choose between no re-normalization and error-bar re-normalization based on local scatter. However, the user may wish to use his/her own normalization factors. These can be provided by adding the file `Normalizations.txt` to the `/Data` subdirectory. This file should contain the normalization factors for each dataset one per line: 85 | ``` 86 | 1.23 87 | 0.97 88 | ``` 89 | 90 | In this example, there are just two datasets. After switching-off re-normalization with the `renormalize = 0` option, the error bars will be multiplied by these factors in all fits. The order of the datasets is the same that can be found in the `FilterToData.txt` file, generated by the `Reader` module. 91 | 92 | [Go to **Initial conditions**](InitCond.md) 93 | -------------------------------------------------------------------------------- /docs/FinalAssessment.md: -------------------------------------------------------------------------------- 1 | [Back to **Model selection**](ModelSelection.md) 2 | 3 | # Final assessment 4 | 5 | ## The `Finalizer` module 6 | 7 | The final step in the modeling run is the classification of the microlensing event through comparison of models obtained for each category. This task is performed by a specific external module called `Finalizer`. This module can be launched by the corresponding function called `Finalizer()`: 8 | 9 | ``` 10 | import RTModel 11 | rtm = RTModel.RTModel('/event001') 12 | rtm.Reader() 13 | rtm.InitCond() 14 | rtm.launch_fits('PS') 15 | rtm.ModelSelector('PS') 16 | ... 17 | rtm.Finalizer() 18 | ``` 19 | 20 | With this code, we first perform all steps as detailed in the previous sections and then we make the final assessment through `Finalizer()`. 21 | 22 | In the `/event001` directory you will see that the following products appear: 23 | - A new subdirectory called `FinalModels/` is created. This will contain the final proposed models for the microlensing event, which are just copies of those appearing in the directory `Models/` that have passed all critieria. 24 | - A file `nature.txt` containing a summary of the chi square achieved in each model category, a final assessment, and the list of the competing models that have passed all criteria and copied to the directory `FinalModels/`. 25 | 26 | The execution of `Finalizer` closes the modeling run. You may then proceed to [plotting](PlotModel.md) the best models. 27 | 28 | ## Making the final assessment 29 | 30 | The `Finalizer` module compares models of different categories according to Wilks' theorem, which states that the level at which a model with p additional parameters is preferred can be assessed by the chi square distribution with p degrees of freedom. More details can be read in the [RTModel paper](https://ui.adsabs.harvard.edu/abs/2024A%26A...688A..83B/abstract). Models that are not nested one within the other are compared with the softer threshold given by the 1-sigma level in the chi square distribution. 31 | 32 | There are no available options for `Finalizer()` since an unsatisfied user may easily vet the models in the directory `Models/` according to his/her own criteria. 33 | 34 | [Go to **Animating fits**](Animation.md) 35 | -------------------------------------------------------------------------------- /docs/Fitting.md: -------------------------------------------------------------------------------- 1 | [Back to **Initial conditions**](InitCond.md) 2 | 3 | # Fitting 4 | 5 | The third step in the modeling run is fitting of all models from the given initial conditions. This task is performed by a specific external module called `LevMar`. For more direct control, the user may either choose to launch all fits of a given category or launch one individual fit from a specified initial condition. We will examine both possibilities in the following. 6 | 7 | ## The `LevMar` module 8 | 9 | The basic fitting module can be launched by the corresponding function called `LevMar()`: 10 | 11 | ``` 12 | import RTModel 13 | rtm = RTModel.RTModel('/event001') 14 | rtm.Reader() 15 | rtm.InitCond() 16 | rtm.LevMar('PS0000') 17 | ``` 18 | 19 | With this code, we first perform the data pre-processing by `Reader`, we set the initial conditions by `InitCond` and finally launch the fit of the single-lens-single-source model from the first initial condition found in the file `InitCondPS.txt` (see [Initial conditions](InitCond.md)). Initial condition seeds in each `InitCondXX.txt` file are numbered starting from zero (e.g. `PS0011` would be the 12th initial condition). 20 | 21 | In the `/event001` directory you will see that the following products appear: 22 | - A new subdirectory called `PreModels/` is created with a file `minchi.dat`. This file contains the value of the minimum chi square among all preliminary models contained in the subdirectories within `PreModels/`. 23 | - In the subdirectory `PreModels/` there will be a subsubdirectory called `PS0000/`, dedicated to the models resulting from this run of `LevMar`. 24 | - Inside `PS0000/` we will find some files numbered `0.txt`, `1.txt`, ... containing the preliminary models found by this run of `LevMar`. 25 | - There will also be files named `PS0000-stepchain0.txt`, `PS0000-stepchain1.txt` ... containing all steps of the Levenberg-Marquardt fit taken for each of the corresponding models. 26 | - Besides these, there will also be a file `nlc.dat` containing the numbers of models calculated so far and a file `tPS0000.dat` generated at the exit of the `LevMar` module to mark the successful closure of the module. 27 | 28 | After the execution of `LevMar`, you may call the `run()` function to complete the modeling run or continue with other calls to `LevMar()`, depending on your intentions. 29 | 30 | ## Launching all fits for a specific category 31 | 32 | The `LevMar()` function only launches one fit and will be rarely useful to a generic user, except for checking or repeating a specific initial condition. More interesting is the `launch_fits()` function, that launches all fits for a specific model category: 33 | 34 | ``` 35 | import RTModel 36 | rtm = RTModel.RTModel('/event001') 37 | rtm.Reader() 38 | rtm.InitCond() 39 | rtm.launch_fits('PS') 40 | ``` 41 | 42 | In this code, the `launch_fits()` will launch all fits of the `'PS'` [model category](ModelCategories.md) using all available processors or those that have been indicated by the users through the `set_processors()` function (see [Modeling Run](ModelingRun.md)). A progress bar will inform the user of the fits completed and those yet to be done. 43 | 44 | At the end of the execution of the `launch_fits()` function, the `PreModels/` directory will be populated by all subdirectories corresponding to fits from all initial conditions in `InitCondPS.txt`. The file `minchi.dat` will contain the minimum chi square found so far, with the name of the subdirectory containing the best model. 45 | 46 | The following step would be the [Model selection](ModelSelection.md) within the fitted model category. After that, the preliminary models in `/PreModels` will no longer be used and can be deleted to save disk space by a call to `rtm.cleanup_preliminary_models()` 47 | 48 | ## Launching a fit from a user-defined initial condition 49 | 50 | If you want to run a fit from a set of parameters outside the set of initial conditions generated by `InitCond`, you may write the parameters in a file or directly give the parameters to `LevMar` as an argument: 51 | 52 | ``` 53 | rtm.LevMar('LSmyfit', parameters_file = 'myinitialcondition.txt') 54 | ``` 55 | 56 | In this case, `LevMar` will perform a static binary-lens fit starting from the initial conditions found in the file 'myinitialcondition.txt'. The results will still be placed in `PreModels/LSmyfit` similarly to the previous case. It is important to notice that the model category is read from the first two characters of the first argument. We also note that the obtained results will be taken in consideration in the process of [Model selection](ModelSelection.md) on equal footing as standard fits obtained from the initial conditions generated by `InitCond`. 57 | 58 | The parameters file should just contain the starting parameters in the first line separated by spaces. The order of the parameters is specified in [Model categories](ModelCategories.md). Any additional lines are ignored. Indeed, the output models generated by `LevMar` are perfectly valid initial conditions for the same model category and can be used in consecutive runs. 59 | 60 | In alternative, we may specify the parameters directly in the call to `LevMar`: 61 | ``` 62 | rtm.LevMar('LSmyfit', parameters = [1.2, 0.01, 0.23, 1.5, 0.001, 25.2, 6573.2]) 63 | ``` 64 | 65 | The order of parameters for each model category is specified in [Model categories](ModelCategories.md). 66 | 67 | ## The Levenberg-Marquardt fit 68 | 69 | The `LevMar` module executes a number of Levenberg-Marquardt fits from the specified initial condition in order to minimize the chi square. Every time a minimum is found, it is filled with a 'bumper'. Any subsequent run hitting the bumper will be bounced off in a different direction in the parameter space. In this way, new minima can be found from the same initial condition. 70 | 71 | If any [constraints](Constraints.md) have been specified by the `set_constraints()` function, they will be taken into account at this stage. 72 | 73 | The details of this fitting strategy are illustrated in the [RTModel paper](https://ui.adsabs.harvard.edu/abs/2024A%26A...688A..83B/abstract). 74 | 75 | ## Options for fitting 76 | 77 | ### The `config_LevMar()` function 78 | 79 | The user may specify his/her own options to drive the initial conditions to the desired result by calling the `config_InitCond()` function with the proper options: 80 | 81 | ``` 82 | import RTModel 83 | rtm = RTModel.RTModel('/event001') 84 | rtm.config_LevMar(nfits = 5, timelimit = 600.0, maxsteps = 50, bumperpower = 2.0) 85 | rtm.run() 86 | ``` 87 | 88 | The call to `config_LevMar()` will affect all following executions of the `LevMar` module, whether called through `run()` or `launch_fits()` or `LevMar()`. If you want to change your options, you may call `config_LevMar()` again. 89 | 90 | ### Description of the options 91 | 92 | Here we describe the options for `LevMar` in detail indicating their default values. 93 | 94 | - `nfits = 6`: Number of fits executed from the same initial condition. 95 | - `offsetdegeneracy = 3`: Number of fits to be executed after applying the [offset degeneracy](https://ui.adsabs.harvard.edu/abs/2022NatAs...6..782Z/abstract) reflection. 96 | - `maxsteps = 50`: Maximum number of steps for each fit. 97 | - `timelimit = 600.0`: Maximum time in seconds allowed for the execution of `LevMar`. If the limit is reached, the execution of the individual fitting is stopped and only the minima found so far will be saved. 98 | - `bumperpower = 2.0`: Size of the bumper in the parameter space expressed in sigmas. The bumper is created with the shape determined by the local covariance matrix. and the size given by this parameter. 99 | 100 | All options are also accessible separately as properties of the `RTModel` class. The user may thus modify the value of each option one by one. The names of the properties are the same as the options in `config_LevMar()` with the prefix `LevMar_`, as shown in the example below: 101 | 102 | ``` 103 | rtm.LevMar_nfits = 1 104 | rtm.LevMar_timelimit = 1200.0 105 | ``` 106 | 107 | An empty call to `config_LevMar()` with no parameters will reset all variables to the default values. Notice that the options that are not explicitly specified in the call to `config_LevMar()` are always reset to their default values. This is also true if you previously used the `recover_options()` function to inherit the options from a previous run (see [Archiving and updating](Archive.md)). 108 | 109 | ### Recording the options 110 | 111 | In each modeling run, the options for `LevMar` are stored in the file `LevMar.ini` in the `/ini` subdirectory within the event directory for later reference. If the modeling run is [archived](Archive.md), also the whole `/ini` subdirectory is saved so that the user may check the options used in each modeling run. The function `recover_options()` can be used to load the options from a previous run. 112 | 113 | [Go to **Model selection**](ModelSelection.md) 114 | -------------------------------------------------------------------------------- /docs/InitCond.md: -------------------------------------------------------------------------------- 1 | [Back to **Data pre-processing**](DataPreprocessing.md) 2 | 3 | # Initial conditions 4 | 5 | ## The `InitCond` module 6 | 7 | The second step in the modeling run is the determination of the initial conditions for the following fits. This task is performed by a specific external module called `InitCond`. This can be launched by the corresponding function called `InitCond()`: 8 | 9 | ``` 10 | import RTModel 11 | rtm = RTModel.RTModel('/event001') 12 | rtm.Reader() 13 | rtm.InitCond() 14 | ``` 15 | 16 | With this code, we first perform the data pre-processing by `Reader` and then we set the initial conditions by `InitCond`. In the `/event001` directory you will see that the following products appear: 17 | - In the subfolder `ini/`, the file `InitCond.ini` appears, which contains the current options with which `InitCond` has been launched. 18 | - A new subdirectory called `InitCond/` is created. In this subdirectory there are several text files named `InitCondXX.txt` and some `PreInitCondXX.txt`, with XX replaced by the label of the corresponding [model category](ModelCategories.md). 19 | - Each `InitCondXX.txt` contains the number of initial conditions and the parameters of each initial conditions line by line. 20 | - A file `spline.txt` in the base event folder containing the points of the spline models built by `InitCond` for each dataset. 21 | 22 | After the execution of `InitCond`, you may call the `run()` function to complete the modeling run or the `launch_fits()` function for the fits you are interested in, as described in [Fitting](Fitting.md). 23 | 24 | ## Setting initial conditions 25 | 26 | In order to set initial conditions for modeling, `InitCond` executes the following steps: 27 | 28 | - Spline approximation of each dataset; 29 | - Identification of peaks in each datasets; 30 | - Removal of duplicate peaks; 31 | - Definition of initial conditions for single-lens models by using the main peak(s) and a grid search over the shape parameters; 32 | - Definition of initial conditions for binary-lens models by matching detected peaks to peaks of templates from a library; 33 | - Addition of old models from previous runs (if any). 34 | 35 | The details of each step are illustrated in the [RTModel paper](https://ui.adsabs.harvard.edu/abs/2024A%26A...688A..83B/abstract). 36 | 37 | ## Options for initial conditions 38 | 39 | ### The `config_InitCond()` function 40 | 41 | The user may specify his/her own options to drive the initial conditions to the desired result by calling the `config_InitCond()` function with the proper options: 42 | 43 | ``` 44 | import RTModel 45 | rtm = RTModel.RTModel('/event001') 46 | rtm.config_InitCond(npeaks = 2, peakthreshold = 10.0, oldmodels = 4, override = None, nostatic = False, onlyorbital = False, usesatellite = 0) 47 | rtm.run() 48 | ``` 49 | 50 | The call to `config_InitCond()` will affect all following executions of the `InitCond` module, whether called through `run()` or `InitCond()`. If you want to change your options, you may call `config_InitCond()` again. 51 | 52 | ### Description of the options 53 | 54 | Here we describe the options for `InitCond` in detail indicating their default values. 55 | 56 | - `peakthreshold = 10.0`: Number of sigmas necessary for a deviation to be identified as a peak in a concave section with respect to a straight line joining the left and right boundaries of the section. A too low value will include noise in the baseline among peaks. A too high value will ignore small anomalies. 57 | - `npeaks = 2`: Number of peaks in the observed light curve to be considered for setting initial conditions. If you choose to use more than 2 peaks you will have many more fits to be run, with greater chances of success but longer computational time. 58 | - `templatelibrary = None`: Alternative user-defined template library file to be used to build initial conditions for binary-lens fits. You may learn more about the customization of template libraries in [Template libraries](TemplateLibraries.md). 59 | - `override = None`: If a t-uple is specified here (e.g `(8760.1, 8793.1)`), the elements of the t-uple are taken as peak positions in the data and directly used to define the initial conditions. The whole spline and peak identification procedure is then skipped. 60 | - `usesatellite = 0`: Initial conditions are set only considering peaks in the indicated [satellite](Satellite.md). If zero, ground datasets are used for initial conditions. 61 | - `nostatic = False`: If `True`, static models will not be calculated. This is useful if higher orders are significant and cannot be treated as a simple perturbation of static models. Furthermore, this option is recommended if you have observations from a satellite spaced by a distance of the order of au. 62 | - `onlyorbital = False`: If `True`, only orbital motion models will be calculated. 63 | - `modelcategories = ['PS','PX','BS','BO','LS','LX','LO']`: The list of model categories to be fit in this modeling run. For example `rtm.config_InitCond(modelcategories = ['PS','PX','LS','LX','LO'])` will exclude binary source models from fitting. Refer to [Model categories](ModelCategories.md) for the meaning of all labels. 64 | - `oldmodels = 4`: If previous runs have been [archived](Archive.md), the chosen number of best models from the last previous run are included as initial conditions. This can be useful for refining old models with new data or options. 65 | - `onlyupdate = False`: If `True`, the modeling run will be limited to a quick update of old models found in the last [archived run](Archive.md) as specified by the `oldmodels` option. 66 | 67 | All options are also accessible separately as properties of the `RTModel` class. The user may thus modify the value of each option one by one. The names of the properties are the same as the options in `config_InitCond()` with the prefix `InitCond_`, as shown in the example below: 68 | 69 | ``` 70 | rtm.InitCond_peakthreshold = 20.0 71 | rtm.InitCond_nostatic = True 72 | ``` 73 | 74 | An empty call to `config_InitCond()` with no parameters will reset all variables to the default values. Notice that the options that are not explicitly specified in the call to `config_InitCond()` are always reset to their default values. This is also true if you previously used the `recover_options()` function to inherit the options from a previous run (see [Archiving and updating](Archive.md)). 75 | 76 | ### Recording the options 77 | 78 | In each modeling run, the options for `InitCond` are stored in the file `InitCond.ini` in the `/ini` subdirectory within the event directory for later reference. If the modeling run is [archived](Archive.md), also the whole `/ini` subdirectory is saved so that the user may check the options used in each modeling run. The function `recover_options()` can be used to load the options from a previous run. 79 | 80 | [Go to **Fitting**](Fitting.md) 81 | -------------------------------------------------------------------------------- /docs/LimbDarkening.md: -------------------------------------------------------------------------------- 1 | [Back to **Satellite datasets**](Satellite.md) 2 | 3 | # Limb Darkening 4 | 5 | The main purpose of `RTModel` is to find preliminary models of microlensing events that can be later studied and refined using any available additional information. In general, to meet this goal, it is not necessary to consider the source limb darkening and a uniform brightness profile is adopted in all calculations. 6 | 7 | However, there are known exceptions in which limb darkening is essential to avoid claiming planet detections from improperly modeled finite-source effects. In fact, in high-magnification single-lens events it may happen that the difference between the uniform and limb-darkened model can be mimicked by a distortion of the central caustic induced by a planet. 8 | 9 | In such cases, it is recommended to include limb darkening in the whole `RTModel` modeling run. This is achieved just by adding one more file named `LimbDarkening.txt` to the `/Data` subdirectory of the event. 10 | 11 | The file `LimbDarkening.txt` should contain the values of the limb darkening coefficients for each telescope, one per line. The following example shows the typical content of `LimbDarkening.txt`: 12 | ``` 13 | 0.57 14 | 0.41 15 | ``` 16 | 17 | Here we have two photometric datasets and we specify their respective linear limb darkening coefficients. Only linear limb darkening is implemented in `RTModel`. The order of the telescopes here is the same as that found in the file `FilterToData.txt`, which is generated by the [Data pre-processing module](DataPreprocessing.md). 18 | 19 | [Go to **Constrained fits**](Constraints.md) 20 | -------------------------------------------------------------------------------- /docs/ModelCategories.md: -------------------------------------------------------------------------------- 1 | [Back to **Modeling run**](ModelingRun.md) 2 | 3 | # Model categories 4 | 5 | In the current version, `RTModel` fits 7 different model categories by default. Each of them has a speficic label with two characters. This table summarizes the main information 6 | 7 | | Label | Model | Number of parameters | 8 | | --- | --- | --- | 9 | | PS | Single-lens-single-source | 4 | 10 | | PX | Single-lens-single-source with parallax | 6 | 11 | | BS | Single-lens-binary-source | 7 | 12 | | BO | Single-lens-binary-source with xallarap | 10 | 13 | | LS | Binary-lens-single-source | 7 | 14 | | LX | Binary-lens-single-source with parallax | 9 | 15 | | LO | Binary-lens-single-source with circular orbital motion | 12 | 16 | 17 | ## Including or excluding model categories 18 | 19 | By default, `RTModel` fits all the model categories listed above to the data, providing models for all of them and comparing their chi square to make its final assessment. However, the user may specify which model categories to fit and even include additional models not proposed in the default modeling run by specifying the corresponding option, as described in [Initial conditions](InitCond.md). 20 | 21 | ## Additional model categories 22 | 23 | Some additional model categories are available but not included in the default modeling run. They can be included by adding their label to the list of model categories as detailed in [Initial conditions](InitCond.md). At the moment, we have only one additional category, but more are on their ways. 24 | 25 | | Label | Model | Number of parameters | 26 | | --- | --- | --- | 27 | | LK | Binary-lens-single-source with eccentric orbital motion | 14 | 28 | 29 | The following sections provide details about parameters and conventions of all categories listed above. 30 | 31 | Note that some parameters are fit in logarithmic scale by `RTModel` for better performance. The only consequence for users is that the covariance matrix contained in model files is written for the ln parameters. 32 | 33 | ## Single-lens-single-source (PS) 34 | 35 | This is the basic microlensing model with a single-lens and a single-source. Note that `RTModel` only considers finite-source models. In most cases, there will be no constraints on the source radius anyway. 36 | 37 | The parameters of this model are the following. We also indicate if the parameter is internally fit in logarithmic (ln) scale (see above). 38 | 39 | | Number | Parameter | Meaning | ln | 40 | | --- | --- | --- | --- | 41 | | 1 | u0 | Impact parameter normalized to Einstein angle | X | 42 | | 2 | tE | Einstein time in days | X | 43 | | 3 | t0 | Closest approach time in HJD | | 44 | | 4 | rho | Source radius normalized to Einstein angle | X | 45 | 46 | We note that only positive values of `u0` are considered in this model since a distinction only comes when parallax is included. 47 | 48 | ## Single-lens-single-source with parallax (PX) 49 | 50 | This is the same as before with parallax vector included. 51 | 52 | | Number | Parameter | Meaning | ln | 53 | | --- | --- | --- | --- | 54 | | 1 | u0 | Impact parameter normalized to Einstein angle | | 55 | | 2 | tE | Einstein time in days | X | 56 | | 3 | t0 | Closest approach time in HJD | | 57 | | 4 | rho | Source radius normalized to Einstein angle | X | 58 | | 5 | piN| Parallax component along North | | 59 | | 6 | piE | Parallax component along East | | 60 | 61 | Note that the positive and negative impact parameters correspond to different models when we include parallax. 62 | The parallax components are calculated with respect to the reference time t0. 63 | 64 | ## Single-lens-binary-source (BS) 65 | 66 | Here we consider two sources and one lens. The two sources have different fluxes and different finite radii. Here are the parameters: 67 | 68 | | Number | Parameter | Meaning | ln | 69 | | --- | --- | --- | --- | 70 | | 1 | tE | Einstein time in days | X | 71 | | 2 | FR | Flux ratio of the secondary to the primary source | X | 72 | | 3 | u01 | Impact parameter of the primary source | | 73 | | 4 | u02 | Impact parameter of the secondary source | | 74 | | 5 | t01 | Closest approach time of the primary source | | 75 | | 6 | t02 | Closest approach time of the secondary source | | 76 | | 7 | rho1 | Source radius for the primary | X | 77 | 78 | Note that both sources may have positive or negative impact parameters. 79 | 80 | The source radius is only given for the primary star, while the secondary star has a source radius calculated by the relation rho2 = rho1 * FR^(0.225), coming from approximate stellar mass-luminosity-radius relations for solar-type stars. In general, we do not expect that both sources have a detectable finite-size effect, so this relation is enforced with the only purpose to avoid the exploration of grossly unphysical models. 81 | 82 | ## Single-lens-binary-source with xallarap (BO) 83 | 84 | Here we also include circular orbital motion of the two sources around a common center of mass: 85 | 86 | | Number | Parameter | Meaning | ln | 87 | | --- | --- | --- | --- | 88 | | 1 | u01 | Impact parameter of the primary source | | 89 | | 2 | t01 | Closest approach time of the primary source | | 90 | | 3 | tE | Einstein time in days | X | 91 | | 4 | rho1 | Source radius for the primary | X | 92 | | 5 | xi1 | Xallarap component parallel to the source velocity | | 93 | | 6 | xi2 | Xallarap component orthogonal to the source velocity | | 94 | | 7 | omega | Orbital angular velocity in days^-1 | | 95 | | 8 | i | Inclination of the orbital plane in radians | | 96 | | 9 | phi | Phase of the orbit from the passage on the line of nodes | | 97 | | 10 | qs | Mass ratio of the secondary to the primary | X | 98 | 99 | The position of the secondary source is calculated from the position of the primary at any time. See [VBMicrolensing Binary Sources](https://github.com/valboz/VBMicrolensing/blob/master/docs/python/BinarySources.md) for a detailed explanation. 100 | 101 | Annual parallax is not considered in this model because it can be mimicked by xallarap, as well known and would only induce bad degeneracies in a higher dimensional parameter space. However, satellite observations may distinguish between parallax and xallarap. 102 | 103 | Note that the flux ratio is calculated as FR = qs^4 and the radius of the secondary is rho2 = rho1 * qs^0.9. As before, we do not expect that these mass-radius-luminosity relations are strictly needed except for avoiding the exploration of unphysical models. 104 | 105 | ## Binary-lens-single-source (LS) 106 | 107 | This is the "static" binary-lens model: 108 | 109 | | Number | Parameter | Meaning | ln | 110 | | --- | --- | --- | --- | 111 | | 1 | s | Separation between the lenses in Einstein radii | X | 112 | | 2 | q | Mass ratio of the secondary to the primary lens | X | 113 | | 3 | u0 | Impact parameter normalized to Einstein angle | | 114 | | 4 | alpha | Angle between the source velocity and the vector pointing from the secondary to the primary lens | | 115 | | 5 | rho | Source radius normalized to Einstein angle | X | 116 | | 6 | tE | Einstein time in days | X | 117 | | 7 | t0 | Closest approach time in HJD to the barycenter | | 118 | 119 | For details about the source trajectory parameterization, please check [VBMicrolensing Light Curves](https://github.com/valboz/VBMicrolensing/blob/master/docs/python/LightCurves.md). 120 | 121 | ## Binary-lens-single-source with parallax (LX) 122 | 123 | Here we also include parallax as we did before for the single-lens case: 124 | 125 | | Number | Parameter | Meaning | ln | 126 | | --- | --- | --- | --- | 127 | | 1 | s | Separation between the lenses in Einstein radii | X | 128 | | 2 | q | Mass ratio of the secondary to the primary lens | X | 129 | | 3 | u0 | Impact parameter normalized to Einstein angle | | 130 | | 4 | alpha | Angle between the source velocity and the vector pointing from the secondary to the primary lens | | 131 | | 5 | rho | Source radius normalized to Einstein angle | X | 132 | | 6 | tE | Einstein time in days | X | 133 | | 7 | t0 | Closest approach time in HJD to the barycenter | | 134 | | 8 | piN| Parallax component along North | | 135 | | 9 | piE | Parallax component along East | | 136 | 137 | 138 | ## Binary-lens-single-source with circular orbital motion (LO) 139 | 140 | Finally, we also explore circular orbital motion for our binary lens: 141 | 142 | | Number | Parameter | Meaning | ln | 143 | | --- | --- | --- | --- | 144 | | 1 | s | Separation between the lenses in Einstein radii | X | 145 | | 2 | q | Mass ratio of the secondary to the primary lens | X | 146 | | 3 | u0 | Impact parameter normalized to Einstein angle | | 147 | | 4 | alpha | Angle between the source velocity and the vector pointing from the secondary to the primary lens | | 148 | | 5 | rho | Source radius normalized to Einstein angle | X | 149 | | 6 | tE | Einstein time in days | X | 150 | | 7 | t0 | Closest approach time in HJD to the barycenter | | 151 | | 8 | piN| Parallax component along North | | 152 | | 9 | piE | Parallax component along East | | 153 | | 10 | gamma1 | Angular velocity parallel to the lens axis | | 154 | | 11 | gamma2 | Angular velocity perpendicular to the lens axis | | 155 | | 12 | gammaz | Angular velocity along the line of sight | | 156 | 157 | The three components of the orbital motion are expressed at time t0 in units of days^-1. 158 | 159 | The component gamma1 is equivalent to ds/dt/s. The component gamma2 is dalpha/dt. The third component gammaz is generally poorly constrained, but is required to move along a physical circular orbit. 160 | 161 | More details are available at [VBMicrolensing Orbital Motion](https://github.com/valboz/VBMicrolensing/blob/master/docs/python/OrbitalMotion.md). 162 | 163 | The subpackage [plotmodel](PlotModel.md) contains a function for translating from the fitting parameters to conventional orbital elements. 164 | 165 | ## Binary-lens-single-source with eccentric orbital motion (LK) 166 | 167 | Eccentric orbital motion is not fit by default, but can be requested by setting the corresponding option in the [Initial Conditions](InitCond.md). 168 | 169 | | Number | Parameter | Meaning | ln | 170 | | --- | --- | --- | --- | 171 | | 1 | s | Separation between the lenses in Einstein radii | X | 172 | | 2 | q | Mass ratio of the secondary to the primary lens | X | 173 | | 3 | u0 | Impact parameter normalized to Einstein angle | | 174 | | 4 | alpha | Angle between the source velocity and the vector pointing from the secondary to the primary lens | | 175 | | 5 | rho | Source radius normalized to Einstein angle | X | 176 | | 6 | tE | Einstein time in days | X | 177 | | 7 | t0 | Closest approach time in HJD to the barycenter | | 178 | | 8 | piN| Parallax component along North | | 179 | | 9 | piE | Parallax component along East | | 180 | | 10 | gamma1 | Angular velocity parallel to the lens axis | | 181 | | 11 | gamma2 | Angular velocity perpendicular to the lens axis | | 182 | | 12 | gammaz | Angular velocity along the line of sight | | 183 | | 13 | sz_s | Separation along the line of sight over projected separation | | 184 | | 14 | a_s3d | Semimajor axis over total separation | | 185 | 186 | The two additional parameters are sufficient to completely define an eccentric orbit. More details are available at [VBMicrolensing Orbital Motion](https://github.com/valboz/VBMicrolensing/blob/master/docs/python/OrbitalMotion.md). 187 | 188 | The subpackage [plotmodel](PlotModel.md) contains a function for translating from the fitting parameters to conventional orbital elements. 189 | 190 | [Go to **Plotting models**](PlotModel.md) 191 | 192 | -------------------------------------------------------------------------------- /docs/ModelSelection.md: -------------------------------------------------------------------------------- 1 | [Back to **Fitting**](Fitting.md) 2 | 3 | # Model selection 4 | 5 | ## The `ModelSelector` module 6 | 7 | The fourth step in the modeling run is the selection of best models for a given model category. This task is performed by a specific external module called `ModelSelector`. This module can be launched by the corresponding function called `ModelSelector()`: 8 | 9 | ``` 10 | import RTModel 11 | rtm = RTModel.RTModel('/event001') 12 | rtm.Reader() 13 | rtm.InitCond() 14 | rtm.launch_fits('PS') 15 | rtm.ModelSelector('PS') 16 | ``` 17 | 18 | With this code, we first perform the data pre-processing by `Reader`, we set the initial conditions by `InitCond`, we launch all fits of the single-lens-single-source model with `launch_fits` and then we select the best models within this category with `ModelSelector`. 19 | 20 | In the `/event001` directory you will see that the following products appear: 21 | - A new subdirectory called `Models/` is created. This will contain the best models for each category. 22 | - One or more files named `PSXXXX-X.txt` containing the details of the selected models. Each model is identified by the label for the model category followed by the number of initial condition and then by the fit number. Any model obtained by user-defined initial conditions will carry the same label specified by the user. 23 | - In addition, in the `InitCond/` subdirectory, some initial conditions files are updated to include more initial conditions obtained by perturbing the best models found in this category. For example, after the single-lens-single-source fits, initial conditions for binary lenses starting from best models found with single lens are added. These are particularly useful to model small anomalies due to planets. 24 | 25 | After the execution of `ModelSelector`, you may call the `run()` function to complete the modeling run or continue with other calls to `launch_fits()` and `ModelSelector()`, or going to [final assessment](FinalAssessment.md) with `Finalizer()`, depending on your intentions. 26 | 27 | After the model selection, the preliminary models calculated by `launch_fits` will no longer be used. If you do not need to perform any additional fits for the same model category, it is wise to clean up the preliminary models by `rtm.cleanup_preliminary_models()`. 28 | 29 | ## Model files 30 | 31 | Each model file contains: 32 | 33 | - The parameters of the model, starting from the non-linear parameters as described in [Model categories](ModelCategories.md), followed by the blend and source fluxes for each dataset, in the order shown in `FilterToData.txt`, and closing with the chi square. 34 | - The 1-sigma error for each parameter as listed in the first line, except for the chi square. 35 | - The covariance matrix for the parameters as used in the fit. Some of them are fit in log scale (see [Model categories](ModelCategories.md)). 36 | 37 | ## The model selection 38 | 39 | The `ModelSelector` module sorts all preliminary model of the chosen category by their chi square. Models with overlapping covariance ellipsoid are discarded as duplicates. Reflections are considered according to the symmetries of the model category. 40 | 41 | ## Options for model selection 42 | 43 | ### The `config_ModelSelector()` function 44 | 45 | The user may specify his/her own options to drive the initial conditions to the desired result by calling the `config_ModelSelector()` function with the proper options: 46 | 47 | ``` 48 | import RTModel 49 | rtm = RTModel.RTModel('/event001') 50 | rtm.config_ModelSelector(sigmasoverlap = 3.0, sigmachisquare = 1.0, maxmodels = 10) 51 | rtm.run() 52 | ``` 53 | 54 | The call to `config_ModelSelector()` will affect all following executions of the `ModelSelector` module, whether called through `run()` or `ModelSelector()`. If you want to change your options, you may call `config_ModelSelector()` again. 55 | 56 | ### Description of the options 57 | 58 | Here we describe the options for `LevMar` in detail indicating their default values. 59 | 60 | - `sigmasoverlap = 3.0`: Maximum number of sigmas to declare overlap. 61 | - `sigmachisquare = 1.0`: Besides the best model, `ModelSelector` retains competing models up to a threshold given by sqrt(2*chisqure), which represents one sigma in the chi square distribution. This threshold can be changed by this option to include less or more competing models in the final selection. 62 | - `maxmodels = 10`: Maximum number of competing models to report 63 | 64 | All options are also accessible separately as properties of the `RTModel` class. The user may thus modify the value of each option one by one. The names of the properties are the same as the options in `config_ModelSelector()` with the prefix `ModelSelector_`, as shown in the example below: 65 | 66 | ``` 67 | rtm.ModelSelector_sigmasoverlap = 5.0 68 | rtm.ModelSelector_maxmodels = 2 69 | ``` 70 | 71 | An empty call to `config_ModelSelector()` with no parameters will reset all variables to the default values. Notice that the options that are not explicitly specified in the call to `config_ModelSelector()` are always reset to their default values. This is also true if you previously used the `recover_options()` function to inherit the options from a previous run (see [Archiving and updating](Archive.md)). 72 | 73 | ### Recording the options 74 | 75 | In each modeling run, the options for `ModelSelector` are stored in the file `ModelSelector.ini` in the `/ini` subdirectory within the event directory for later reference. If the modeling run is [archived](Archive.md), also the whole `/ini` subdirectory is saved so that the user may check the options used in each modeling run. The function `recover_options()` can be used to load the options from a previous run. 76 | 77 | [Go to **Final assessment**](FinalAssessment.md) 78 | -------------------------------------------------------------------------------- /docs/ModelingRun.md: -------------------------------------------------------------------------------- 1 | [Back to **Data preparation**](DataPreparation.md) 2 | 3 | # Modeling run 4 | 5 | ## RTModel start-up 6 | 7 | Once data have been prepared according to the format specified in [Data preparation](DataPreparation.md), we can go to Python and write a simple code to perform the full modeling run. 8 | 9 | ``` 10 | import RTModel 11 | rtm = RTModel.RTModel('/event001') 12 | ``` 13 | Here we have first imported the `RTModel` package, then we have defined an instance to an `RTModel` object. We may optionally specify the name of the event to analyze already in the constructor or leave it blank and indicate it later. The event is identified through the path to the directory prepared before. 14 | 15 | The output of the constructor looks like this: 16 | ``` 17 | ********************* 18 | **** RTModel **** 19 | ********************* 20 | Number of processors: 24 21 | ``` 22 | 23 | It is useful to know that by default `RTModel` will use all available processors to run fits in parallel. If you are concerned that your CPU will burn, you may explicitly choose the number of processors you want to use by the following line 24 | 25 | ``` 26 | nprocessors = 8 27 | rtm.set_processors(nprocessors) 28 | ``` 29 | 30 | where `nprocessors` is the number you want to use. 31 | 32 | ## Launching modeling run(s) 33 | 34 | After that, we are ready to launch modeling through the `run()` function. In this example, we are assuming that we just want to use all available processors (see above). 35 | 36 | ``` 37 | import RTModel 38 | rtm = RTModel.RTModel('/event001') 39 | rtm.run() 40 | ``` 41 | 42 | Note that an alternative equivalent writing is the following: 43 | 44 | ``` 45 | import RTModel 46 | rtm = RTModel.RTModel() 47 | rtm.run('/event001') 48 | ``` 49 | 50 | The event name can be specified directly in the `run()` function. In principle, more events can be modeled by the same `RTModel` object one after the other: 51 | 52 | ``` 53 | import RTModel 54 | rtm = RTModel.RTModel() 55 | rtm.run('/event001') 56 | rtm.run('/event002') 57 | ... 58 | ``` 59 | 60 | Or we can have a list of events to model in a campaign and perform a single loop to model them all. 61 | 62 | ``` 63 | import RTModel 64 | import glob 65 | 66 | rtm = RTModel.RTModel() 67 | 68 | eventlist = glob.glob(/campaign2024/*) 69 | for event in eventlist: 70 | rtm.run(event) 71 | ``` 72 | 73 | If you just want to change the name of the event without launching a modeling run, you may use the `set_event()` function 74 | 75 | ``` 76 | rtm.set_event('/event002') 77 | ``` 78 | 79 | A modeling run may take from minutes to hours depending on the number of data points, the complexity of the event, the available CPU. Progress bars are shown to inform the user of the current status of the modeling run. The slowest step is the Binary-lens-single-source fitting, which takes 90% of the time. 80 | 81 | ## Output of a modeling run 82 | 83 | ### Assessment on the nature of the event 84 | 85 | At the end of the modeling run, a summary is displayed with the best chi square achieved for each model category, the final assessment and the list of proposed models. The same content can be found in a file named `nature.txt` that is created inside the event directory. 86 | 87 | ### Best models 88 | 89 | The best models listed in `nature.txt` are available as text files in the subdirectory `event001/FinalModels`. 90 | 91 | Each file contains the list of parameters in the first line, including the background and source fluxes for each telescope and the total chi square. This means that the first line contains `nps + 2 * ntel + 1` values, where `nps` is the number of parameters in the model category (e.g. 7 for binary-lens-single-source) and `ntel` is the number of datasets. 92 | 93 | A detailed explanation of parameters for each model category is available in [Model categories](ModelCategories.md) 94 | 95 | The second line contains the uncertainty on each of the above listed parameters (except for the chi square). Therefore, this line contains `nps + 2 * ntel` values. 96 | 97 | The remaining lines contain the covariance matrix between the model parameters. Therefore, there are `nps` additional lines containing `nps` values each. Note that some parameters are internally fit in logarithmic scale (see [Model categories](ModelCategories.md) for which parameters are fit as ln). The reported covariance matrix is thus calculated on these internal parameters. 98 | 99 | ### Additional products 100 | 101 | Additional partial products of modeling are stored in the event directory. At the end of the modeling run we will find the following files and directories 102 | ``` 103 | Data/ # Directory containing the original input data files as described in data preparation 104 | ini/ # Directory containing text files specifying the options for individual modules 105 | InitCond/ # Directory containing the text files with the initial conditions for fitting 106 | PreModels/ # Directory containing subdirectories with all models calculated by all fits 107 | Models/ # Directory containing selected models for each category 108 | FinalModels/ # Directory containing the best models as proposed in the final assessment (see above) 109 | LCToFit.txt # Text file containing the formatted and pre-processed data points 110 | FilterToData.txt # Table of datasets names 111 | spline.txt # List of points in the spline approximation used for initial conditions 112 | nature.txt # Text file containing the final assessment on the event and the list of best models 113 | ``` 114 | 115 | These files will be explained in the following chapters in due time. They can be useful for careful diagnostics of the modeling process. You may vision an event with a completed run among the provided examples: [event001done.zip](/events/event001done.zip). 116 | 117 | ### Saving disk space 118 | 119 | The preliminary models calculated by `RTModel` may occupy 30MB of disk space in nearly 10000 files. Unless you need some specific debugging or some deeper investigation, we suggest to cleanup the event directory from the directory `/PreModels`, thus saving 99% disk space. You may do it after the modeling run by executing the following line: 120 | 121 | ``` 122 | rtm.cleanup_preliminary_models() 123 | ``` 124 | 125 | In alternative, you may including the `cleanup = True` option when you call the `run()` function in order to request a cleanup at the end of the modeling run: 126 | ``` 127 | rtm.run('/event002', cleanup = True) 128 | ``` 129 | 130 | ## Structure of a modeling run 131 | 132 | The `run()` function performs a precise sequence of operations on the selected event. The individual operations are performed by external pre-compiled modules called by the function. Here we summarize the individual steps: 133 | 134 | 1. Data pre-processing 135 | 2. Initial conditions setting 136 | 3. Fits for a specific category of models 137 | 4. Model selection within the category 138 | 5. Final assessment on the event 139 | 140 | Steps 3 and 4 are repeated for each model category. Details on the model categories and their parameters are given in the [following page](ModelCategories.md) 141 | 142 | Each step is described in a dedicated documentation page (see [Summary](README.md)). Each step can be executed separately through a dedicated function. Numerous options are available to change the behavior of each step. 143 | 144 | In case the modeling run is interrupted, it can be resumed by executing the `run()` function again on the same event. The function checks whether the expected output of each step is present and in such case it moves to the next step. 145 | 146 | If one of the child processes fails (typically for corrupted data), the output of the process is shown to help the user understand the problem. 147 | 148 | [Go to **Model categories**](ModelCategories.md) 149 | -------------------------------------------------------------------------------- /docs/PlotModel.md: -------------------------------------------------------------------------------- 1 | [Back to **Model categories**](ModelCategories.md) 2 | 3 | # Plotting models 4 | 5 | `RTModel` comes with a subpackage that is intended for fast and basic visualization of results with a minimal number of options. 6 | 7 | Assuming you have a completed run on some event in its directory `/event001`, we may plot the best models with the following code 8 | 9 | ``` 10 | import RTModel.plotmodel as plm 11 | import glob 12 | 13 | event = '/event001' 14 | models = glob.glob(event +'/FinalModels/*') 15 | model = models[0] # let's plot the first of the best models 16 | 17 | myplot = plm.plotmodel(eventname = event, modelfile = model) 18 | ``` 19 | 20 | The output will look like this 21 | 22 | 23 | 24 | On the left, we have the model light curve with the data points. Residuals are also shown below. the source trajectory and the caustics are shown on the right. The plots are followed by the list of parameters with their errors. For each telescope we also have the blending fraction $F_{background}/F_{source}$ and the baseline magnitude. Finally, the chi square for the model is displayed. 25 | 26 | ## Options 27 | 28 | Here is a list of arguments available for the `plotmodel` function: 29 | - `eventname`: Directory of the event prepared according to the indications in [Data preparation](DataPreparation.md) 30 | - `modelfile = None`: The file name containing the model we want to plot. The type of model is identified by the first two characters in the filename. For example, 'LX0000-0.txt' is a file containing a binary lens with parallax (see [Model categories](ModelCategories.md)). The parameters are read from the file. If `modelfile` is not specified, you may plot any kind of models specifying your parameters in input by providing the arguments `model` and `parameters`. 31 | - `model = None`: If modelfile is left blank, you may specify here the model you want to plot following the labels given in [Model categories](ModelCategories.md). Example: `model = 'LS'`. The parameters of the model should be given through the argument `parameters` 32 | - `parameters = []`: parameters of a user-defined model. The order and meaning of the parameters depends on the model chosen through the argument `model` (see [Model categories](ModelCategories.md)). 33 | - `tmin = None`, `tmax = None`: Minimum and maximum time for the plot in units of HJD-2450000. If not specified, the plot is made between t0-2tE and t0+2tE. 34 | - `timesteps = 300`: Number of steps in time axis. 35 | - `referencephot = 0`: Dataset to be used as photometric reference for the magnitude axis. All other datasets are rescaled to magnitudes in the system of the chosen dataset. By default the first dataset is chosen. 36 | - `printpars = True`: If left True, the parameters are printed below the figure, otherwise only the figure is shown. 37 | - 'accuracy = 0.01`: The accuracy of magnification calculations in the plot. 38 | 39 | ## Properties of the ```plotmodel``` object 40 | 41 | Further customization of the plots is possible by manipulation of the object returned by the `plotmodel` function as shown in the following example: 42 | 43 | ``` 44 | myplot.causticcolor = 'g' 45 | myplot.telescopes = ['Everest', 'Aconcagua'] 46 | myplot.showall() 47 | ``` 48 | 49 | Here we have changed the color of the caustic to green and changed the displayed names of the two telescopes in the legend. To see the plot with these changes, you may call the `showall()` function. Some changes require to recalculate the model and you should precede the `showall()` with a call to `calculate()`: 50 | 51 | ``` 52 | myplot.tmin = 9300 53 | myplot.calculate() 54 | myplot.showall() 55 | ``` 56 | 57 | Here is a list of properties of a `plotmodel` object that are available for customization: 58 | - `telescopes`: List of telescope names to be shown in the legend. By default, the names of the data files are used. 59 | - `legendlocation`: Location of the legend according to the options available in matplotlib. 60 | - `colors`: List of colors of the datasets. 61 | - `satellitecolors`: List of colors of the light curve model as seen by ground telescopes (the first entry) and the light curves as seen by the satellites. The corresponding source tracks in the right plot take the same colors. 62 | - `sourcecolor`: Color of the source disk in the plot showing the source trajectory and the caustic. 63 | - `causticcolor`: Color of the caustic. 64 | 65 | Finally, all optional parameters in the call of the `plotmodel` can be also manipulated as in the case of `tmin` shown above. 66 | 67 | ## Retrieving and saving the figures 68 | 69 | It is possible to generate separate plots for the light curve and for the caustic with the source trajectory using the following functions 70 | 71 | ``` 72 | myplot.showlightcurve() 73 | myplot.showcaustics() 74 | ``` 75 | 76 | Finally, each plot can be saved to a file. In fact, after the execution of `showlightcurve()`, `showcaustics()`, `showall()` or the constructor of `plotmodel`, the property `figure` contains the figure just created. Therefore, you can save it with the line 77 | 78 | ``` 79 | myplot.figure.savefig('myfile.png') 80 | ``` 81 | 82 | ## Orbital elements 83 | 84 | The fit parameters for [models with orbital motion](ModelCategories.md) may not be as transparent. The subpackage `plotmodel` offers the function `orbital_elements` for a translation to conventional orbital elements. Suppose you have obtained a model with circular orbital motion `LO0001-1.txt'. If we just use `plotmodel` as before, we can read the fitting parameters, including the three components of the orbital velocity. However, if we write 85 | 86 | ``` 87 | import RTModel.plotmodel as plm 88 | orbitalelements = plm.orbital_elements(`LO0001-1.txt') 89 | for oe in orbitalelements: 90 | print(oe,' = ',orbitalelements[oe]) 91 | ``` 92 | 93 | the output will be the list of conventional orbital elements calculated from the components of the orbital motion 94 | 95 | ``` 96 | T = 3362.6154510332913 97 | a = 0.5269665151055616 98 | e = 0 99 | inc = 1.3715375471903625 100 | OM = 0.35 101 | om = 0 102 | phi0 = 0.45222968275074405 103 | epoch = 9816.2023437747 104 | ``` 105 | 106 | The elements are as follows 107 | - T: orbital period in days 108 | - a: semimajor axis in Einstein radii 109 | - e: eccentricity (0 for circular orbital motion) 110 | - inc: inclination in radians (0 is face-on, 1.57 is edge-on) 111 | - OM: longitude of the ascending node in radians, taken from the binary axis at time t0. 112 | - om: argument of periastron in radians (0 for circular orbital motion) 113 | - phi0: true anomaly in radians (taken from the ascending node for circular orbital motion) 114 | - epoch: epoch of the periastron in HJD (for circular orbital motion this coincides with the epoch of the ascending node) 115 | 116 | The same function can also be used for models obtained with [eccentric orbital motion](ModelCategories.md), for which the eccentricity and the argument of the periastron will take non-zero values. 117 | 118 | [Go to **Archiving and updating models**](Archive.md) 119 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Documentation 3 | 4 | In this document we will describe all use cases of the `RTModel` library and provide ready-to-use examples that you can copy/paste to your code. 5 | 6 | ## Quick start 7 | 8 | Provided you have prepared a folder for your microlensing event (e.g. `/event001`) with all datasets in the subfolder `/event001/Data` as detailed in the section about [data preparation](DataPreparation.md), in order to analyze the microlensing event with the default settings, just type 9 | 10 | ``` 11 | import RTModel 12 | rtm = RTModel.RTModel() 13 | rtm.run('/event001') 14 | ``` 15 | 16 | Then the modeling starts and you may sit back and watch the progress bars tracking the status of modeling. You will notice that all fits are nearly instantaneous except for the Binary-lens-single-source fit that takes 1-2 hours. At the end of all fits, you will see the final assessment of `RTModel` for the event, along with a summary of the best chi squares found for each category of models. 17 | 18 | The final model(s) proposed by `RTModel` are collected as separate files in the directory `/event001/FinalModels`. You may plot them using the `RTModel.plotmodel` package: 19 | 20 | ``` 21 | import RTModel.plotmodel as plm 22 | import glob 23 | 24 | event = '/event001' 25 | models = glob.glob(event +'/FinalModels/*') 26 | model = models[0] 27 | 28 | plm.plotmodel(eventname = event, modelfile = model) 29 | ``` 30 | 31 | ## Summary 32 | 33 | In this documentation, we describe all functionalities of `RTModel` in detail. A novel user should read the following pages at least. 34 | 35 | - [Data preparation](DataPreparation.md) 36 | 37 | - [Full modeling run](ModelingRun.md) 38 | 39 | - [Model Categories](ModelCategories.md) 40 | 41 | - [Plotting models and results](PlotModel.md) 42 | 43 | Additional useful functionalities are discussed in the following pages. 44 | 45 | - [Archiving and updating models](ArchivingUpdating.md) 46 | 47 | - [Satellite datasets](Satellite.md) 48 | 49 | - [Limb darkening](LimbDarkening.md) 50 | 51 | - [Constrained fits](Constraints.md) 52 | 53 | Advanced users may attempt a deeper understanding of the modeling steps and optimize `RTModel` by numerous options. 54 | 55 | - [Data pre-processing](DataPreprocessing.md) 56 | 57 | - [Initial conditions](InitCond.md) 58 | 59 | - [Fitting](Fitting.md) 60 | 61 | - [Model selection](ModelSelection.md) 62 | 63 | - [Final assessment and results](FinalAssessment.md) 64 | 65 | - [Animating fits](Animation.md) 66 | 67 | - [Template library customization](TemplateLibraries.md) 68 | 69 | ## Success rate 70 | 71 | The success rate of `RTModel` has been evaluated on the simulated events created for the [WFIRST data challenge](https://roman.ipac.caltech.edu/docs/street_data_challenge1_results.pdf) by Matthew Penny. Here we report the results from the current version. 72 | 73 | ### Planetary regime 74 | 75 | In this challenge there were 51 binary events with q<0.03 (planetary regime). The results obtained by the current version of `RTModel` with the default options were the following: 76 | - 44 full successes; 77 | - 5 cases in which an s<1 solution was found instead of the s>1 (or the opposite); 78 | - 1 cases in which a different binary or planetary solution was preferred; 79 | - 1 case in which the anomaly was not detected at all (too much noise) 80 | 81 | ### Binary regime 82 | 83 | In the stellar binary regime q>0.03 there were 75 events simulated in the data challenge. We note that orbital motion was simulated by assigning the two transverse components, which leads to non-physical trajectories not reproducible by circular orbital motion. Anyway, the results obtained are the following: 84 | 85 | - 62 full successes; 86 | - 4 cases in which an s<1 solution was found instead of the s>1; 87 | - 7 cases in which a different binary model was found; 88 | - 2 cases in which the anomaly was too weak or perfectly reproduced by a single-lens with parallax 89 | 90 | The success rate is observed to decrease significantly for events with high orbital motion. This should be partly due to the different way this effect is taken into account in the simulation and in our fitting code. 91 | 92 | In addition, we note that such events with strong higher order effects would have been better fit with the `nostatic = True` option (see [Initial conditions](InitCond.md)). 93 | 94 | -------------------------------------------------------------------------------- /docs/Satellite.md: -------------------------------------------------------------------------------- 1 | [Back to **Archiving and updating models**](Archive.md) 2 | 3 | # Satellite datasets 4 | 5 | Observations from satellites, if available, are identified through their photometry filename ending with a number, e.g. `Spitzer1.dat`. Each satellite is identified by a different number that is used to select the correct ephemerides table. All other files ending by anything that is not a number is considered as taken from a ground telescope. 6 | 7 | In all models without parallax ('PS', 'BS', 'BO', 'LS' (see [Model categories](ModelCategories.md))), satellite datasets are treated in the same way as ground datasets. For models including parallax ('PX', 'LX', 'LO', 'LK'), the position of the satellite in space is taken into account through its respective ephemerides table. This leads to a different source trajectory with respect to the caustics for the event as seen from the satellite. Therefore, the light curve model for the satellite data can be profoundly different. This difference is extremely useful to fix the parallax parameters and retrieve physical information on the lens distance. 8 | 9 | ## Ephemerides tables 10 | 11 | In order to exploit satellite observations, we need satellite ephemerides covering the observation period. `RTModel` conforms to `VBMicrolensing` standard, using ephemerides of the satellite in the format given by the [NASA Horizons system](http://ssd.jpl.nasa.gov/horizons.cgi). 12 | 13 | In particular, we assume five columns: 14 | - JD 15 | - RA (degrees) 16 | - Dec (degrees) 17 | - Distance from Earth (AU) 18 | - Distance rate change (not really needed but included by default in Horizons). 19 | 20 | In order to obtain the table in the correct format in [Horizons](http://ssd.jpl.nasa.gov/horizons.cgi), in Table Settings you should only check flags 1. Astrometric RA & Dec and 20. Observer range & range-rate. Date/time format should be Julian Day calendar, angle format should be decimal degrees, range units should be astronomical units. 21 | 22 | An example of a valid satellite ephemerid tables is in [/events/satellite1.txt](/events/satellite1.txt). 23 | 24 | The satellite table(s) should be named "satellite*.txt" (with * replaced by a number) and placed in some directory `/satellitedir', which does not need to be inside the event directory. 25 | 26 | As satellite datasets are identified by a number preceding the extension, they are matched to the ephemerides tables with the same number. 27 | 28 | In order to let `RTModel` find the ephemerides tables, you should include a call to the function `set_satellite_dir` in your code before the start of the modeling run: 29 | 30 | ``` 31 | import RTModel 32 | rtm = RTModel.RTModel('/OB190033') 33 | rtm.set_satellite_dir('/satellitedir') 34 | rtm.run() 35 | ``` 36 | 37 | ## Modeling strategies 38 | 39 | By default `RTModel` looks for static models first (without parallax) and then only performs fits with parallax on the best static models found. In case of satellite observations, this strategy may work if the satellite is very close to Earth (Earth or Moon orbiting and L2 are good examples) or for very small parallax values. The small difference between the source trajectories as seen from the Earth and from the satellite can be easily recovered as a higher order refinement. 40 | 41 | However, for au-separated satellites, static models are often useless or even driven to completely wrong results if satellite data are forced to follow the same light curve model as for ground telescopes. Then, we should skip static models and start the search with parallax included from the beginning of the search. This is achieved by the `nostatic = True` option in the `config_InitCond()` function: 42 | 43 | ``` 44 | import RTModel 45 | rtm = RTModel.RTModel('/OB190033') 46 | rtm.set_satellite_dir('/satellitedir') 47 | rtm.config_InitCond(nostatic = True) 48 | rtm.run() 49 | ``` 50 | 51 | Another important note is that if you have no ground data and only satellites, initial conditions must be set using satellite data. This is achieved by the line 52 | 53 | ``` 54 | rtm.config_InitCond(nostatic = True, usesatellite = 1) 55 | ``` 56 | 57 | Without the `usesatellite` option set to the chosen satellite, `RTModel` will look for ground datasets for setting initial conditions and will fail if no ground datasets are present. 58 | 59 | Further discussion and details are provided in the [Initial conditions](InitCond.md) page. 60 | 61 | ## Plotting satellite observations 62 | 63 | For events including satellite observations, the `plotmodel` call should include the indication of the directory containing the ephemerides tables: 64 | 65 | ``` 66 | plm.plotmodel(eventname = event, modelfile = model, satellitedir = '/satellitedir') 67 | ``` 68 | 69 | The plots will include the light curves for ground datasets and for satellite datasets with different colors. The same colors are used to represent the source trajectories as seen from ground and satellite in the plot on the right: 70 | 71 | 72 | 73 | 74 | [Go to **Limb darkening**](LimbDarkening.md) 75 | -------------------------------------------------------------------------------- /docs/Template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/docs/Template.png -------------------------------------------------------------------------------- /docs/TemplateLibraries.md: -------------------------------------------------------------------------------- 1 | [Back to **Animating fits**](Animation.md) 2 | 3 | # Template libraries 4 | 5 | As explained before, the module [InitCond](InitCond.md) sets the initial conditions for binary-lens fits by matching the peaks found in the observed datasets to the peaks of the templates in a library, following an original idea by [Mao & Di Stefano (1995)](https://ui.adsabs.harvard.edu/abs/1995ApJ...440...22M/abstract). This matching provides the values for $t_0$ and $t_E$, while all remaining parameters are read from the template. 6 | 7 | ## Default template library 8 | 9 | The default library used by `InitCond` is available [here](/RTModel/data/TemplateLibrary.txt). It is the product of many years of modeling of many different microlensing events. It is based on the idea that a seed is needed in each region of the parameter space where the sequence of peaks in the light curve remains the same (see [Liebig et al. (2015)](https://ui.adsabs.harvard.edu/abs/2015MNRAS.450.1565L/abstract)). 10 | 11 | The first line in the library indicates the number of templates, while the following lines contain the information for each template one by one. Each line contains the parameters $s$ $q$ $u_0$ $\alpha$ $\rho$ followed by the time of two peaks $t_1$ $t_2$. If the template has more than two peaks, the template is repeated in the following line with different choices of the times of the peaks. The order of the two peaks does not matter, since `InitCond` always include each template and then its time-reversal. Here is an excerpt from our [template library](/RTModel/data/TemplateLibrary.txt): 12 | 13 | ``` 14 | 113 15 | 0.7 0.5 0.15 3.5 0.01 -0.18254 0.01625 16 | 0.7 0.5 0.15 3.5 0.01 -0.18254 0.08566 17 | 0.7 0.5 0.15 3.5 0.01 0.01625 0.08566 18 | 0.7 0.1 0.0 5.38 0.01 -0.06613 0.02524 19 | 0.7 0.1 0.0 5.38 0.01 -0.06613 0.30473 20 | 0.7 0.1 0.0 5.38 0.01 0.02524 0.30473 21 | 0.7 0.5 0.0 2.0 0.01 -1.20214 -0.11323 22 | ... 23 | ``` 24 | 25 | ## Changing the template library 26 | 27 | `RTModel` offers the possibility to use a different template library, which can be constructed by hand or using the tools offered by the subpackage `RTModel.templates`. In order to change the template library, you should include the corresponding option in `config_InitCond()`: 28 | 29 | ``` 30 | rtm.config_InitCond(templatelibrary = 'MyLibrary.txt') 31 | ``` 32 | 33 | By providing the full path to your library, `InitCond` will use it to determine the initial seeds for binary-lens fitting. A valid library should conform to the same format of the default library, with the first line containing the number of templates and the following lines with the parameters and peak times as explained above. 34 | 35 | ## The `RTModel.templates` subpackage 36 | 37 | The `RTModel.templates` contains useful tools to visualize the templates of the default library and elaborate your own templates. 38 | 39 | ### Cloning the default template library 40 | 41 | After importing the subpackage, we may start by cloning the default library to a local file 42 | 43 | ``` 44 | import RTModel.templates as tmpl 45 | 46 | tmpl.clone_default_library('MyLibrary.txt') 47 | ``` 48 | 49 | ### Loading and saving libraries 50 | 51 | The content of a template library can be loaded to Python by the function 52 | 53 | ``` 54 | mytemplates = tmpl.load_library('MyLibrary.txt') 55 | ``` 56 | 57 | As a result, `mytemplates` will contain a standard Python list of all templates found in `'MyLibrary.txt'`: 58 | 59 | ``` 60 | print(mytemplates) 61 | 62 | [[0.7, 0.5, 0.15, 3.5, 0.01, -0.183, 0.016], [0.7, 0.5, 0.15, 3.5, 0.01, -0.183, 0.086], [0.7, 0.5, 0.15, 3.5, 0.01, 0.016, 0.086], ... 63 | ``` 64 | 65 | A call to `mytemplates = tmpl.load_library()` without arguments will load the default template library. 66 | 67 | At this point, you are free to manipulate the list with standard Python tools. 68 | 69 | When you are happy with your new list of templates, you can save it with the function 70 | 71 | ``` 72 | tmpl.save_library('MyNewLibrary.txt', mytemplates) 73 | ``` 74 | 75 | The new file `'MyNewLibrary.txt'` will be in the format accepted by `RTModel` and ready for use in your modeling runs. 76 | 77 | ### Visualization and elaboration of the templates 78 | 79 | The most useful tool in the `RTModel.templates` subpackage is the `show_template()` function. Here we see an example followed by its output: 80 | 81 | ``` 82 | newtemplates = tmpl.show_template(mytemplates[0], tmin = -1, tmax = +1, tstep = 0.00001, accuracy = 0.001) 83 | ``` 84 | 85 | 86 | The `show_template(mytemplate)` function calculates and shows the lightcurve corresponding to the parameters found in `mytemplate`, which is a standard list containing at least 5 values for $s$ $q$ $u_0$ $\alpha$ $\rho$. The light curve is shown in units of $t/t_E$ with $t_0=0$ from `tmin` to `tmax` (default values are -3 and +3 respectively). The time step for the plot is specified by `tstep` (default value is 0.001) and the accuracy in the magnification calculation is given by `accuracy` (default value is 0.01). The source trajectory and the caustic are shown in the right panel. 87 | 88 | In addition to the visualization, actually `show_template()` also shows the values of the parameters found and calculates the peak positions in the template that were found between `tmin` and `tmax`. The peaks found are also reported in the output. It is important to underline that the time accuracy for these peaks depends on the time steps specified through `tstep`. Also the `accuracy` option is important to locate the peak more precisely on relatively flat maxima. 89 | 90 | The return value of the `show_template()` function is a list of templates built by combining the parameters found in `mytemplate` and all peaks found in the calculation. So the length of `newtemplates` for a light curve with $n$ peaks will be $n(n-1)/2$. The output templates of `show_template()` can then be easily included in a new library of templates. 91 | -------------------------------------------------------------------------------- /docs/ani.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/docs/ani.gif -------------------------------------------------------------------------------- /docs/plotchain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/docs/plotchain.png -------------------------------------------------------------------------------- /docs/plotmodel_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/docs/plotmodel_fig1.png -------------------------------------------------------------------------------- /docs/plotmodel_fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/docs/plotmodel_fig2.png -------------------------------------------------------------------------------- /events/OB190033.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/events/OB190033.zip -------------------------------------------------------------------------------- /events/event001.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/events/event001.zip -------------------------------------------------------------------------------- /events/event001done.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/events/event001done.zip -------------------------------------------------------------------------------- /events/event002.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/events/event002.zip -------------------------------------------------------------------------------- /events/readme.md: -------------------------------------------------------------------------------- 1 | This directory contains some simulated events with data prepared in the format required by `RTModel` (see [Data preparation](/docs/DataPreparation.md)). 2 | 3 | The zipped directory `event001done.zip` contains the same data as `event001.zip` with the full modeling run already completed. It can be used as reference for what you should obtain at the end of a modeling run. 4 | 5 | The event in `OB190033.zip` is OGLE-2019-BLG-0033, published in [Herald et al. (2022)](https://ui.adsabs.harvard.edu/abs/2022A%26A...663A.100H/abstract). The zip file contains all the data used for the analysis, including those from the *Spitzer* satellite. It is a good event to start practising with satellite data. The corresponding ephemerides for *Spitzer* are in the file `satellite1.txt` available in this directory. If you run `RTModel` on this event, make sure to select the option `nostatic = True` in `config_InitCond()` (see [Initial conditions](InitCond.md)). 6 | -------------------------------------------------------------------------------- /fVBM.py: -------------------------------------------------------------------------------- 1 | import os 2 | import inspect 3 | 4 | try: 5 | import VBMicrolensing 6 | package_dir = os.path.dirname(inspect.getfile(VBMicrolensing)).replace('\\','/') 7 | 8 | # with open("vbmp.txt", "w") as f: 9 | # f.write(package_dir) 10 | 11 | print(f"{package_dir}") 12 | 13 | except ImportError: 14 | print("! Error: VBMicrolensing non installed!") 15 | raise 16 | 17 | -------------------------------------------------------------------------------- /jupyter/Model_event001.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "42a3aa04-f93b-4ec7-a3ad-bd088fea980e", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import RTModel" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "id": "789ed4ce-0f74-4d29-8767-49f6ddc260ab", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "rtm = RTModel.RTModel()" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "id": "aec7428b-6550-4e01-9098-fe239044047e", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "eventname = '../events/event001' # Remember to unzip the file event001.zip first!\n", 31 | "rtm.set_event(eventname)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "id": "e79523ad-2263-4db5-9e49-e770717c359c", 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "rtm.run()" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "id": "98a3fc46-e5b6-45fa-b680-8d54f8489959", 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "import RTModel.plotmodel as plm" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "id": "fd1ddcc9-6976-46bc-a429-b8cd291cc0f1", 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "plm.plotmodel(eventname = eventname,modelfile = 'Models/PS0032-1.txt')" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "6d5011fe-8ccd-43dc-81d1-7ecbf172afa4", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [] 71 | } 72 | ], 73 | "metadata": { 74 | "kernelspec": { 75 | "display_name": "Python 3 (ipykernel)", 76 | "language": "python", 77 | "name": "python3" 78 | }, 79 | "language_info": { 80 | "codemirror_mode": { 81 | "name": "ipython", 82 | "version": 3 83 | }, 84 | "file_extension": ".py", 85 | "mimetype": "text/x-python", 86 | "name": "python", 87 | "nbconvert_exporter": "python", 88 | "pygments_lexer": "ipython3", 89 | "version": "3.11.8" 90 | } 91 | }, 92 | "nbformat": 4, 93 | "nbformat_minor": 5 94 | } 95 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["scikit-build-core>=0.10","VBMicrolensing>=4.1"] 3 | build-backend = "scikit_build_core.build" 4 | 5 | [project] 6 | name = "RTModel" 7 | description = "RTModel is a tool for microlensing event interpretation." 8 | version = "2.4.2" 9 | keywords = ['Microlensing analysis and fitting'] 10 | authors = [ 11 | { name = "Valerio Bozza", email = "valboz@sa.infn.it" }, 12 | ] 13 | license = { text = "GPL-3.0" } 14 | requires-python = ">=3.7,<4" 15 | readme = "README.md" 16 | classifiers = [ 17 | 'Development Status :: 5 - Production/Stable', 18 | 'Intended Audience :: Science/Research', 19 | 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)', 20 | 'Programming Language :: Python :: 3', 21 | ] 22 | dependencies = [ 23 | "numpy", 24 | "pytest", 25 | "matplotlib", 26 | "tqdm", 27 | "tabulate", 28 | "VBMicrolensing>=4.1.0" 29 | ] 30 | 31 | [project.urls] 32 | Homepage = "https://github.com/valboz/RTModel" 33 | 34 | [tool.scikit-build] 35 | build-dir = "build" 36 | minimum-version = "build-system.requires" 37 | 38 | [tool.cibuildwheel] 39 | build-verbosity = "3" 40 | 41 | [tool.setuptools.package-data] 42 | "RTModel.data" = ["data/*"] 43 | -------------------------------------------------------------------------------- /tests/build_tests/test_built_executables_exist.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from RTModel import RTModel 4 | 5 | 6 | def test_reader_executable_exists(): 7 | rt_model = RTModel() 8 | reader_executable_path = Path(rt_model.bindir + rt_model.readerexe) 9 | assert reader_executable_path.exists() 10 | 11 | 12 | def test_init_cond_executable_exists(): 13 | rt_model = RTModel() 14 | init_cond_executable_path = Path(rt_model.bindir + rt_model.initcondexe) 15 | assert init_cond_executable_path.exists() 16 | 17 | 18 | def test_lev_mar_executable_exists(): 19 | rt_model = RTModel() 20 | lev_mar_executable_path = Path(rt_model.bindir + rt_model.levmarexe) 21 | assert lev_mar_executable_path.exists() 22 | 23 | 24 | def test_model_selector_executable_exists(): 25 | rt_model = RTModel() 26 | model_selector_executable_path = Path(rt_model.bindir + rt_model.modelselectorexe) 27 | assert model_selector_executable_path.exists() 28 | 29 | 30 | def test_finalizer_executable_exists(): 31 | rt_model = RTModel() 32 | finalizer_executable_path = Path(rt_model.bindir + rt_model.finalizerexe) 33 | assert finalizer_executable_path.exists() 34 | -------------------------------------------------------------------------------- /tests/end_to_end_tests/test_ps_run.py: -------------------------------------------------------------------------------- 1 | import tempfile 2 | import zipfile 3 | from pathlib import Path 4 | 5 | from RTModel import RTModel 6 | 7 | 8 | def test_ps_run(): 9 | temporary_directory = Path(tempfile.gettempdir()) 10 | event_zip_path = Path(__file__).parent.joinpath('test_ps_run_resources/example_event.zip') 11 | with zipfile.ZipFile(event_zip_path, 'r') as zip_file_handle: 12 | zip_file_handle.extractall(temporary_directory) 13 | rtm = RTModel(str(temporary_directory.joinpath('event001'))) 14 | rtm.Reader() 15 | rtm.InitCond() 16 | rtm.launch_fits('PS') 17 | rtm.ModelSelector('PS') 18 | -------------------------------------------------------------------------------- /tests/end_to_end_tests/test_ps_run_resources/example_event.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valboz/RTModel/919af0eef1626e2c3919f25823029bdcf95f6d77/tests/end_to_end_tests/test_ps_run_resources/example_event.zip --------------------------------------------------------------------------------