pmagpy 4.2.125__py3-none-any.whl → 4.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pmagpy/__init__.py +2 -1
- pmagpy/contribution_builder.py +3 -9
- pmagpy/ipmag.py +155 -220
- pmagpy/pmag.py +17 -65
- pmagpy/pmagplotlib.py +14 -257
- pmagpy/rockmag.py +4258 -0
- pmagpy/version.py +2 -2
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/PmagPy-cli.ipynb +77 -106
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/PmagPy_MagIC.ipynb +169 -170
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/PmagPy_calculations.ipynb +2 -6
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/PmagPy_introduction.ipynb +2 -3
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/PmagPy_plots_analysis.ipynb +275 -329
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aarm_magic/aarm_measurements.txt +126 -126
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/measurements.txt +210 -210
- {pmagpy-4.2.125.dist-info → pmagpy-4.3.0.dist-info}/METADATA +1 -1
- pmagpy-4.3.0.dist-info/RECORD +1081 -0
- pmagpy-4.2.125.dist-info/RECORD +0 -1080
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_images.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_mailinglist.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/magic_methods.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/pmag_criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/rmag_anisotropy.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/rmag_hysteresis.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/rmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/2_5/McMurdo/zmab0100049tmp03.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/.ipynb_checkpoints/Parsing_data_model-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/extra_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/images.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/new_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/McMurdo/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/.ipynb_checkpoints/ages_from_samples_to_sites-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_1/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_1/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_1/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_1/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_1/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_1/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_2/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_2/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_2/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_2/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_2/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/Location_2/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/ages_from_samples_to_sites.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/magic_contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Megiddo/test_spec.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/contribution_11087_v2.5.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/contribution_11087_v3.0.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/3_0/Osler/stored.json +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/Cont_rot.svg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/aus_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/col_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/eant_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/eur_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/grn_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/ind_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/mad_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/nam_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/neaf_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/nwaf_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/par_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/FRPTMP/sac_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/af.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/ages.tmp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/ant.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/aus.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/aus_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/balt.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/col_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/congo.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/continents.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/eant_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/eur.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/eur_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/finrot_saf.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/globalapwps.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/gond.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/grn.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/grn_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/ib_eur.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/ind.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/ind.bak +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/ind_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/kala.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/lau.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/mad_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/mkcont.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/mkfrp.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/nam.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/nam_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/neaf_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/nwaf_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/par_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/plates.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/sac_saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/saf.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/sam.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Continents/waf.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/Z35.sam.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/Z35_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/Z35_er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/Z35_er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ErMagicBuilder/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Figures/atrm_meas.png +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Figures/chartmaker.png +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Figures/meas15.png +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Figures/samples.png +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/PmagPy_online.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/Tel-Hazor_Tel-Megiddo_25.Aug.2016.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/ages_from_samples_to_sites.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/demag_orient.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/new_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/new_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/new_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/samples.bak +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/sites.bak +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/specimens.bak +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/3_0/thellier_GUI.log +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/SIOfiles/na_sw.atrm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/SIOfiles/na_sw.cool +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/SIOfiles/na_sw.thel +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/SIOfiles.zip +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/ThisProject/SrExample_AF.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/ThisProject/SrExample_orient.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/ThisProject/SrExample_thellier.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/ThisProject/SrExample_thermal.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/Pmag_GUI/zmab0083201tmp03.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/SVEI_demo.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/.ipynb_checkpoints/Editing-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/.ipynb_checkpoints/U1456A-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/.ipynb_checkpoints/discretes-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/Core_depthplot.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/JR6_data/UTESTA.jr6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/JR6_data/UTESTA_fixed.jr6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/KLY4S_data/UTESTA.kly4s +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/SRM_data/srmdiscrete-XXX-UTEST-A.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/SRM_data/srmsection-XXX-UTEST-A.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/CoreSummary_XXX_UTESTA.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/UTESTA.kly4s.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/UTESTA_er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/UTESTA_fixed.jr6.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/UTESTA_rmag_anisotropy.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/rmag_anisotropy.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/samples-XXX-UTEST-A_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/srmdiscrete-XXX-UTEST-A.csv.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/srmsection-XXX-UTEST-A.csv.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/srmsection-XXX-UTEST-A_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/srmsection-XXX-UTEST-A_er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC/srmsection-XXX-UTEST-A_er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC3/CoreSummary_XXX_UTESTA.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC3/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC3/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC3/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/UTESTA_MagIC3/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/UTESTA/samples-XXX-UTEST-A.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aarm_magic/arm_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aarm_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aarm_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aarm_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aarm_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/angle/angle.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/angle/tmp.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/magic_contribution_12152.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/magic_methods.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/rmag_anisotropy.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/ani_depthplot/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aniso_magic/dike_anisotropy.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aniso_magic/dike_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aniso_magic/sed_anisotropy.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/aniso_magic/sed_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/apwp/apwp_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/atrm_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/atrm_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/atrm_measurements3.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/atrm_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/orig_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/atrm_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/azdip_magic/azdip_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/b_vdm/b_vdm_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/basemap_magic/basemap_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/biplot_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/biplot_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/bootams/bootams_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/cart_dir/cart_dir_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/chi_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/chi_magic_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/chi_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/combine_magic/af_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/combine_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/combine_magic/ns_a.mag +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/combine_magic/ns_t.mag +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/combine_magic/therm_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/common_mean/common_mean_ex_file1.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/common_mean/common_mean_ex_file2.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_asc_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_asc_magic/_2g_asc/DR3B.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_asc_magic/_2g_asc/OK3_15af.asc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_asc_magic/_2g_asc/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/165A.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/165B.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/165C.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/60A.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/60B.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/60C.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/70A.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/70C.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/KodamaFiles/70D.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1ab.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1bb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1c.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1db.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1e.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1f.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1ga.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1ha.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1ib.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1jb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1kb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1la.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton1ma.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29ab.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29bb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29cb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29da.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29db.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29e.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29eb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29f.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29ga.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29gc.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29h.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29ib.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton29j.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2c.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2d.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2e.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2f.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2g.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2h.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton2i.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3aa.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3ba.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3ca.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3da.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3ea.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3fb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3ga.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton3ha.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4ab.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4bb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4c.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4d.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4eb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4fb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4gb.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4ha.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/MolinaFiles/ton4ia.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/MN1.CSV +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/MN_chr_dir.xls +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn001-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn004-2b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn008-2b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn010-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn014-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn017-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn022-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn026-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn031-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn033-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn034-2a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn038-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn041-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn042-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn046-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn049-2a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn056-2a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn061-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn065-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn067-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn071-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn075-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn078-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn081-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn084-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn087-2a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn091-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn093-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn096-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn1.saf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn1.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn100-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn103-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn105-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn106-1a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn107-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn109-2a.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/2g_bin_magic/mn1/mn110-1b.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01a-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01a-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01a-2.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01a-2.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01b-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01b-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01c-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01c-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01d-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01d-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01e-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01e-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01f-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01f-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01f-2.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS01f-2.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02a-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02a-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02a-2.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02a-3.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02b-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02b-1.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02b-2.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02b-2.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_directory/IS02c-1.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_magic_example.agm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/agm_magic/agm_magic_example.irm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/15HHA1-2A +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/15JC4-1A +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/96MT.05.01 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/96MT.05.01.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/BC0-3A +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/BC0-3A.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/bgc_magic/CA14-TA02.05'a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/Craig_Jones_webpage_of_PMag_file_formats_CIT_file_format_source.html +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/Craig_Jones_webpage_of_PMag_file_formats_CIT_file_format_source_files/PaleoMag.gif +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/Craig_Jones_webpage_of_PMag_file_formats_CIT_file_format_source_files/PaleoMag_002.gif +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/Craig_Jones_webpage_of_PMag_file_formats_CIT_file_format_source_files/a-95.gif +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/Craig_Jones_webpage_of_PMag_file_formats_CIT_file_format_source_files/kappa.gif +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/Craig_Jones_webpage_of_PMag_file_formats_CIT_file_format_source_files/phi.gif +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B.LSQ +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B.sam +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B71 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B72 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B73 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B74 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B75 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B76 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B77 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B78 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/MIT/7325B/7325B79 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-.sam +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-1a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-2a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-3a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-4a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-5a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-6a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-7a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-8a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/PI47-9a +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/PI47/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9001-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9001-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9002-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9002-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9003-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9003-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9004-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9004-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9005-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9005-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9006-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9006-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9007-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9007-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9008-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9008-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9009-1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/BL9009-1.rmg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/bl9-1.sam +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/bl9001.dir +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/USGS/bl9-1/command +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/cit_magic/sample_formats.pdf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/fla_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/fla_magic/mejia04.pdf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/fla_magic/pa_thermal.fla +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/fla_magic/pt_af.fla +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/generic_magic/generic_magic_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/huji_magic/Massada_AF_HUJI_new_format.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/huji_magic/Massada_AF_all_old_format.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/huji_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/huji_magic/magdelkrum.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/huji_magic/magdelkrum_datafile.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_jr6_magic/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_jr6_magic/test.jr6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_srm_magic/GCR_U1359_B_coresummary.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_srm_magic/IODP_Janus_312_U1256.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_srm_magic/IODP_LIMS_SRMdiscrete_344_1414A.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_srm_magic/IODP_LIMS_SRMsection_344_1414A.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_srm_magic/SRM_318_U1359_B_A.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/iodp_srm_magic/samples_318_U1359_B.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AF.jr6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AF.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AF_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AF_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AF_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AP12.jr6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AP12.tmp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/AP12.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML01.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML02.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML03.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML04.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML05.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML06.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/SML07.JR6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/TRM.jr6 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/TRM.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/TRM_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/TRM_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/TRM_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/jr6_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/k15_magic/k15_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/kly4s_magic/KLY4S_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/ldeo_magic/ldeo_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_C+/CHEV.livdb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_C+/CHEV.livdb_different_delimiters +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_C+/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_C+/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_C+/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_C+/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_IZZI+andC++/NVPA.livdb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_IZZI+andC++/NVPA.livdb_fifferent_delimiter +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_IZZI+andC++/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_IZZI+andC++/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_IZZI+andC++/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_IZZI+andC++/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/016-01.livdb_old_delimiters +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/017-03.livdb_old_delimiters +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/16-1.livdb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_OT+/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_P/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_P/perp.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_P/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_P/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/MW_P/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/TH_IZZI+/ATPI_Thellier.livdb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/TH_IZZI+/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/TH_IZZI+/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/TH_IZZI+/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/livdb_magic/TH_IZZI+/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/mini_magic/Peru_rev1.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/mini_magic/Peru_rev1_description.rtf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/mst_magic/curie_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/IPGP/0110C.PMD +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/IPGP/0210C.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0101a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0102a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0103a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0104a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0105a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0106a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0107a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0108a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0201a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0202a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0203a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0204a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0205a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0206a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0207a.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/PMD/ss0208c.pmd +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/pmd_magic/UCSC/ssDirAll.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/s_magic/s_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/SantaRosa2006.scz +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy01.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy02.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy03.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy04.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy05.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy06.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy07.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy08.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy09.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy10.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy11.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy12.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy13.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy13A.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy13B.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy14.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/cy15.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/scz_magic/CanyonCreek/santaRosa.pmm +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sio_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sio_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sio_magic/sio_af_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sio_magic/sio_thermal_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sio_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sio_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sufar_asc_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sufar_asc_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sufar_asc_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sufar_asc_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/sufar_asc_magic/sufar4-asc_magic_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/tdt_magic/Krasa_MGH1.tdt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/tdt_magic/Krasa_MGH1_noAC.tdt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/utrecht_magic/Utrecht_Example.af +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/utrecht_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/utrecht_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/utrecht_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/utrecht_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_2_magic/utrecht_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_ages/magic_downloaded_rows.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_samples/Iceland_orient.txt_Northern_Iceland.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_samples/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_samples/convert_samples_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/convert_samples/orient_Northern_Iceland.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/Z35.sam.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/Z35_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/Z35_er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/Z35_er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/copy_er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/pmag_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/copy_ErMagicBuilder/weird_er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/core_depthplot_fixed.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/core_depthplot/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/curie/curie_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dayplot_magic/dayplot_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dayplot_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_eq/di_eq_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_eq/tmp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_eq/tmp1 +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_geo/di_geo.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_geo/di_geo_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_rot/di_rot.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_rot/di_rot_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_rot/fishrot.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_tilt/di_tilt.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_tilt/di_tilt_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/di_vgp/di_vgp_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dipole_pinc/dipole_pinc_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dipole_plat/dipole_plat_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dir_cart/dir_cart_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/contribution.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/dmag_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/magic_contribution_16338.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/dmag_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eigs_s/eigs_s_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eq_di/eq_di_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eq_di/tmp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea/fishrot.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_ell/eqarea_ell_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_ell/tk03.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_magic/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_magic/site_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/eqarea_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/etopo20/etopo20data.gz +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/etopo20/etopo20lats.gz +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/etopo20/etopo20lons.gz +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/find_EI/find_EI_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/find_EI/tmp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/fishqq/fishqq_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/fishrot/fishrot.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest/foldtest_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/magic_contribution_11087.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/magic_methods.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/foldtest_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/forc_diagram/.ipynb_checkpoints/forc_diagram-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/forc_diagram/conventional_example.forc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/forc_diagram/irforc_exmaple.irforc +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/gaussian/gauss.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/ATRM/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/ATRM/generic_ATRM.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/ATRM/generic_ATRM.txt.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/CR/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/CR/generic_CR.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/CR/generic_CR.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/Demag/README.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/Demag/generic_demag.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/PI/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/generic_magic/PI/generic_izzi.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/geomagia/geomagia_sel.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/gobing/gobing_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/gofish/fishrot.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/gokent/gokent_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/gokent/tk03.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/goprinc/goprinc_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/goprinc/tk03.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/grab_magic_key/lats +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/hysteresis_magic/hysteresis_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/hysteresis_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/igrf/igrf.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/igrf/igrf_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/incfish/incfish_example_di.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/incfish/incfish_example_inc.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/.ipynb_checkpoints/PmagPy_iodp_HOLE_template-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/.ipynb_checkpoints/U999A-checkpoint.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/Figures/U999A_1.pdf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/Figures/U999A_anisotropy_xmastree.pdf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/ProcessingPmagData.docx +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/Core Summary_18_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/JR6_data/spinner_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/KLY4S_data/ex-kappa_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/SRM_archive_data/srmsection_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/SRM_discrete_data/srmdiscrete_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/Section Summary_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U1999A_xray_disturbance.xlsx +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/Core Summary_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/dscr_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/jr6_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/kly4s_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/kly4s_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/lims_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/lims_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/lims_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/srm_arch_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/srm_arch_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/srm_arch_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/srm_arch_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_MagIC/srm_dscr_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/U999A_disturbances.xlsx +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A/samples_17_5_2019.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/iodp_magic/U999A.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/irm_unmix/irm_unmix_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/irm_unmix/irm_unmix_example_fit.csv +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/irmaq_magic/U1359A_IRM_coil2.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/irmaq_magic/U1359A_IRM_coil3.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/irmaq_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/k15_magic/k15_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/k15_s/k15_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/kly4s_magic/KLY4S_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lnp_magic/zmab0001193tmp02.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lowrie/lowrie_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lowrie/lowrie_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lowrie_magic/lowrie_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lowrie_magic/lowrie_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/lowrie_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/SrExample_AF.txt.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/SrExample_AF_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/SrExample_thellier.txt.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/SrExample_thellier_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/SrExample_thermal.txt.magic +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/SrExample_thermal_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_gui/3_0/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_select/AF_BFL_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_select/AF_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_select/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/magic_select/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/measurements_normalize/irm_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/measurements_normalize/specimens_weight.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/misc_files/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/misc_files/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/magic_methods.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/pmag_criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/pmag_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/mk_redo/zmab0083201tmp03.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/Importing and using the 3.0 data model.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/Intro to MagIC Contributions.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/Intro to MagicDataFrames.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/Uploading contributions (with validations).ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/Validate Quoted Strings.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/data_model_conversion.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/notebooks/thellier_gui3_0_tester.ipynb +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/nrm_specimens_magic/magic_contribution_15143.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/nrm_specimens_magic/nrm_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/orientation_magic/orient_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/parse_measurements/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pca/pca_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pca/zeq_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plotXY/plotXY.png +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plotXY/plotXY.svg +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plotXY/plotxy_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plotXY/tmp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plot_cdf/gaussian.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plot_map_pts/Map_PTS.png +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plot_map_pts/uniform.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/plotdi_a/plotdi_a_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/Directions.tex +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/Directions.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/Intensities.tex +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/Intensities.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/SiteNfo.tex +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/SiteNfo.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pmag_results_extract/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/polemap_magic/locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/Map_PTS.pdf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/lon_lat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/nam_180-200.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/nam_panA.frp +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/panA.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/pt_rot.input +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/pt_rot.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/pt_rot_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/pt_rot/pt_rot_panA.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/qqplot/gauss.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/quick_hyst/hysteresis_magic_example3.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/quick_hyst2/hysteresis_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/remanence_anisotropy_magic/README +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/revtest/revtest_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/revtest_magic/criteria.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/revtest_magic/revtest_magic_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/revtest_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/s_eigs/s_eigs_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/s_geo/s_geo_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/s_hext/s_geo_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/s_magic/s_magic_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/s_magic/specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/s_tilt/s_tilt_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/scalc/scalc_example.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/scalc_magic/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/scalc_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/scalc_magic/vgp_lat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/site_edit_magic/thellier_redo +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/site_edit_magic/zeq_redo +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/site_edit_magic/zmab0083201tmp03.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/squish/squish_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/stats/gaussian.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/strip_magic/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/strip_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/strip_magic/sites_with_vgps.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/sundec/sundec_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/__init__.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/empty_dir/blank.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/location_09.Oct.2015.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/location_14.Oct.2015.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/location_16.Aug.2015.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/location_16.Aug.2015_1.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/methods/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/__init__.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_samples_orient.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_sites_orient.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_GUI.log +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_interpreter/thellier_interpreter.log +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_interpreter/thellier_interpreter_STDEV-OPT_redo +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_interpreter/thellier_interpreter_STDEV-OPT_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_interpreter/thellier_interpreter_STDEV-OPT_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_interpreter/thellier_interpreter_all.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project/thellier_interpreter/thellier_interpreter_specimens_bounds.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/__init__.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_samples_orient.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_sites_orient.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/er_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/magic_measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/my_project_with_errors/something.py +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/odp_magic/odp_magic_er_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/validation/Jack-Hills_19.Apr.2020_4.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/validation/Jack-Hills_19.Apr.2020_5.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/validation/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/validation/er_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/validation/location1_30.Dec.2015.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/testing/validation/location1_30.Dec.2015_1.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Megiddo_unpublished_example/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Megiddo_unpublished_example/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/SU1_example/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/SU1_example/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/SU1_example/optimizer_test_groups.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/er_expeditions.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/er_test_groups.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/magic_methods.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/pmag_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_interpreter/thellier_interpreter_STDEV-OPT_redo +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_interpreter/thellier_interpreter_STDEV-OPT_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_interpreter/thellier_interpreter_STDEV-OPT_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_interpreter/thellier_interpreter_all.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_interpreter/thellier_interpreter_specimens_bounds.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_redo +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/thellier_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/Tauxe_2006_example/zmab0094380tmp01.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_GUI/thellier_GUI_full_manual_1_0.pdf +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/thellier_magic/zmab0100159tmp01.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/tk03/tk03.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/tsunakawa_shaw/raw_data/mc120c-SA4.d +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/uniform/uniform.out +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/unsquish/unsquish_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/er_ages.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/er_citations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/er_locations.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/magic_methods.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/pmag_samples.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/pmag_sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/pmag_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/thellier_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/upload.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/upload_dos.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/upload_magic/zeq_specimens.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/vdm_b/vdm_b_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/vector_mean/vector_mean_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/vgp_di/vgp_di_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/vgpmap_magic/pmag_results.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/vgpmap_magic/sites.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/watsons_f/watsons_f_example_file1.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/watsons_f/watsons_f_example_file2.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/xpeem_magic/Bryson2019_PVA01-r1.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/xpeem_magic/Maurel2020_TeA01Comma-r1onL.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/xpeem_magic/Maurel2020_TeA01TwoSpace-r1onL.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/zeq/zeq_example.dat +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/zeq_magic/measurements.txt +0 -0
- {pmagpy-4.2.125.data → pmagpy-4.3.0.data}/data/data_files/zeq_magic/zmab0083201tmp03.txt +0 -0
- {pmagpy-4.2.125.dist-info → pmagpy-4.3.0.dist-info}/WHEEL +0 -0
- {pmagpy-4.2.125.dist-info → pmagpy-4.3.0.dist-info}/entry_points.txt +0 -0
- {pmagpy-4.2.125.dist-info → pmagpy-4.3.0.dist-info}/top_level.txt +0 -0
pmagpy/rockmag.py
ADDED
|
@@ -0,0 +1,4258 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
from scipy.optimize import minimize, brent, least_squares, minimize_scalar
|
|
6
|
+
from scipy.signal import savgol_filter
|
|
7
|
+
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
import matplotlib.colors as colors
|
|
10
|
+
import matplotlib.patches as patches
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import ipywidgets as widgets
|
|
14
|
+
from ipywidgets import HBox, VBox, Output, Dropdown, RadioButtons, Checkbox, FloatSlider
|
|
15
|
+
from IPython.display import HTML, display
|
|
16
|
+
|
|
17
|
+
except ImportError:
|
|
18
|
+
widgets = None
|
|
19
|
+
display = None
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from bokeh.plotting import figure, show
|
|
23
|
+
from bokeh.layouts import gridplot
|
|
24
|
+
from bokeh.models import HoverTool, Label
|
|
25
|
+
from bokeh.embed import components
|
|
26
|
+
from bokeh.palettes import Category10
|
|
27
|
+
from bokeh.models import ColumnDataSource
|
|
28
|
+
from bokeh.models.widgets import DataTable, TableColumn
|
|
29
|
+
from bokeh.layouts import column
|
|
30
|
+
_HAS_BOKEH = True
|
|
31
|
+
except ImportError:
|
|
32
|
+
_HAS_BOKEH = False
|
|
33
|
+
try:
|
|
34
|
+
from lmfit import Parameters, Model # for fitting
|
|
35
|
+
from lmfit.models import SkewedGaussianModel
|
|
36
|
+
except ImportError:
|
|
37
|
+
Parameters = None
|
|
38
|
+
Model = None
|
|
39
|
+
SkewedGaussianModel = None
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
import statsmodels.api as sm
|
|
43
|
+
lowess = sm.nonparametric.lowess
|
|
44
|
+
except ImportError:
|
|
45
|
+
sm = None
|
|
46
|
+
lowess = None
|
|
47
|
+
|
|
48
|
+
mpl_to_bokeh_markers = {
|
|
49
|
+
".": "dot",
|
|
50
|
+
",": "dot",
|
|
51
|
+
"o": "circle",
|
|
52
|
+
"v": "inverted_triangle",
|
|
53
|
+
"^": "triangle",
|
|
54
|
+
"<": "triangle",
|
|
55
|
+
">": "triangle",
|
|
56
|
+
"1": "triangle",
|
|
57
|
+
"2": "inverted_triangle",
|
|
58
|
+
"3": "triangle",
|
|
59
|
+
"4": "inverted_triangle",
|
|
60
|
+
"s": "square",
|
|
61
|
+
"p": "square",
|
|
62
|
+
"*": "asterisk",
|
|
63
|
+
"h": "hex",
|
|
64
|
+
"H": "hex",
|
|
65
|
+
"+": "plus",
|
|
66
|
+
"x": "x",
|
|
67
|
+
"X": "x",
|
|
68
|
+
"D": "diamond",
|
|
69
|
+
"d": "diamond",
|
|
70
|
+
"|": "dash",
|
|
71
|
+
"_": "dash",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# general I/O functions
|
|
75
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
def dict_in_native_python(d):
|
|
78
|
+
"""Convert NumPy scalar values in a dict to native Python scalars.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
d : dict
|
|
83
|
+
Dictionary whose values may include NumPy scalar types
|
|
84
|
+
(e.g. np.float64, np.int64, np.bool_).
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
dict
|
|
89
|
+
A new dictionary with the same keys as `d` but with any
|
|
90
|
+
NumPy scalar values replaced by their native Python
|
|
91
|
+
equivalents (float, int, bool). Non‐NumPy values are
|
|
92
|
+
left unchanged.
|
|
93
|
+
"""
|
|
94
|
+
return {k: v.item() if isinstance(v, np.generic) else v for k, v in d.items()}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def interactive_specimen_selection(measurements):
|
|
98
|
+
"""
|
|
99
|
+
Creates and displays a dropdown widget for selecting a specimen from a given
|
|
100
|
+
DataFrame of measurements.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
measurements : pd.DataFrame
|
|
105
|
+
The DataFrame containing measurement data with a column 'specimen'. It is
|
|
106
|
+
expected to have at least this column where 'specimen' identifies the
|
|
107
|
+
specimen name.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
ipywidgets.Dropdown
|
|
112
|
+
A dropdown widget allowing for the selection of a specimen. The initial
|
|
113
|
+
selection in the dropdown is set to the first specimen option.
|
|
114
|
+
"""
|
|
115
|
+
# Extract unique specimen names from the measurements DataFrame
|
|
116
|
+
specimen_options = measurements['specimen'].unique().tolist()
|
|
117
|
+
|
|
118
|
+
# Set the initial selection to the first specimen option, if available
|
|
119
|
+
selected_specimen_name = specimen_options[0] if specimen_options else None
|
|
120
|
+
|
|
121
|
+
# Create a dropdown for specimen selection
|
|
122
|
+
specimen_dropdown = widgets.Dropdown(
|
|
123
|
+
options=specimen_options,
|
|
124
|
+
description='Specimen:',
|
|
125
|
+
value=selected_specimen_name
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Display the dropdown widget
|
|
129
|
+
display(specimen_dropdown)
|
|
130
|
+
|
|
131
|
+
return specimen_dropdown
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def interactive_specimen_experiment_selection(measurements):
|
|
135
|
+
"""
|
|
136
|
+
Creates interactive dropdown widgets for selecting a specimen and its associated
|
|
137
|
+
experiment from a measurements DataFrame.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
measurements : pd.DataFrame
|
|
142
|
+
DataFrame containing measurement data with at least two columns: 'specimen' and
|
|
143
|
+
'experiment'. The 'specimen' column holds the specimen names while the 'experiment'
|
|
144
|
+
column holds the experiment identifiers associated with each specimen.
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
tuple of ipywidgets.Dropdown
|
|
149
|
+
A tuple containing two dropdown widgets. The first widget allows for selecting a
|
|
150
|
+
specimen, and the second widget allows for selecting an experiment associated with
|
|
151
|
+
the chosen specimen. The experiment dropdown is dynamically updated based on the
|
|
152
|
+
specimen selection.
|
|
153
|
+
"""
|
|
154
|
+
specimen_dropdown = widgets.Dropdown(
|
|
155
|
+
options = measurements['specimen'].unique(),
|
|
156
|
+
description = 'specimen:',
|
|
157
|
+
disabled = False,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
experiment_dropdown = widgets.Dropdown(
|
|
161
|
+
options = measurements['experiment'].unique(),
|
|
162
|
+
description = 'Experiment:',
|
|
163
|
+
disabled = False,
|
|
164
|
+
)
|
|
165
|
+
# make sure to set the default value of the experiment dropdown to the first experiment in the specimen dropdown
|
|
166
|
+
experiment_dropdown.options = measurements[measurements['specimen']==specimen_dropdown.value]['experiment'].unique()
|
|
167
|
+
|
|
168
|
+
# make sure to update the experiment dropdown based on the specimen selected
|
|
169
|
+
def update_experiment(*args):
|
|
170
|
+
experiment_dropdown.options = measurements[measurements['specimen']==specimen_dropdown.value]['experiment'].unique()
|
|
171
|
+
|
|
172
|
+
specimen_dropdown.observe(update_experiment, 'value')
|
|
173
|
+
|
|
174
|
+
# display the dropdowns
|
|
175
|
+
display(specimen_dropdown, experiment_dropdown)
|
|
176
|
+
|
|
177
|
+
return specimen_dropdown, experiment_dropdown
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def make_experiment_df(measurements):
|
|
181
|
+
"""
|
|
182
|
+
Creates a DataFrame of unique experiments from the measurements DataFrame.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
measurements (pd.DataFrame): The DataFrame containing measurement data with columns
|
|
186
|
+
'specimen', 'method_codes', and 'experiment'.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
pd.DataFrame: A DataFrame containing unique combinations of 'specimen', 'method_codes',
|
|
190
|
+
and 'experiment'.
|
|
191
|
+
"""
|
|
192
|
+
experiments = measurements.groupby(['specimen', 'method_codes', 'experiment']).size().reset_index().iloc[:, :3]
|
|
193
|
+
return experiments
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def clean_out_na(dataframe):
|
|
197
|
+
"""
|
|
198
|
+
Cleans a DataFrame by removing columns and rows that contain only NaN values.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
dataframe (pd.DataFrame): The DataFrame to be cleaned.
|
|
202
|
+
Returns:
|
|
203
|
+
pd.DataFrame: A cleaned DataFrame with all-NaN columns and rows removed.
|
|
204
|
+
"""
|
|
205
|
+
cleaned_df = dataframe.dropna(axis=1, how='all')
|
|
206
|
+
return cleaned_df
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def ms_t_plot(
|
|
210
|
+
data,
|
|
211
|
+
temperature_column="meas_temp",
|
|
212
|
+
magnetization_column="magn_mass",
|
|
213
|
+
temp_unit="K",
|
|
214
|
+
interactive=False,
|
|
215
|
+
return_figure=False,
|
|
216
|
+
show_plot=True,
|
|
217
|
+
):
|
|
218
|
+
"""
|
|
219
|
+
Plot magnetization vs. temperature, either static or interactive,
|
|
220
|
+
with option to display in K or °C.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
data : pandas.DataFrame or array-like
|
|
225
|
+
Table or array containing temperature and magnetization.
|
|
226
|
+
temperature_column : str
|
|
227
|
+
Name of the temperature column in `data` (assumed in K).
|
|
228
|
+
magnetization_column : str
|
|
229
|
+
Name of the magnetization column in `data`.
|
|
230
|
+
temp_unit : {'K','C'}, default 'K'
|
|
231
|
+
Units for the x-axis: 'K' for Kelvin or 'C' for Celsius.
|
|
232
|
+
interactive : bool, default False
|
|
233
|
+
If True, use Bokeh for an interactive plot.
|
|
234
|
+
return_figure : bool, default False
|
|
235
|
+
If True, return the figure object(s).
|
|
236
|
+
show_plot : bool, default True
|
|
237
|
+
If True, display the plot.
|
|
238
|
+
|
|
239
|
+
Returns
|
|
240
|
+
-------
|
|
241
|
+
(fig, ax) or bokeh layout or None
|
|
242
|
+
Matplotlib Figure and Axes or Bokeh layout if `return_figure` is True;
|
|
243
|
+
otherwise None.
|
|
244
|
+
"""
|
|
245
|
+
# extract data arrays
|
|
246
|
+
T = np.asarray(data[temperature_column], dtype=float)
|
|
247
|
+
M = np.asarray(data[magnetization_column], dtype=float)
|
|
248
|
+
|
|
249
|
+
# convert to Celsius if requested
|
|
250
|
+
if temp_unit == "C":
|
|
251
|
+
T = T - 273.15
|
|
252
|
+
x_label = "Temperature (°C)"
|
|
253
|
+
else:
|
|
254
|
+
x_label = "Temperature (K)"
|
|
255
|
+
|
|
256
|
+
if interactive:
|
|
257
|
+
tools = [HoverTool(tooltips=[("T", "@x"), ("M", "@y")]),
|
|
258
|
+
"pan,box_zoom,wheel_zoom,reset,save"]
|
|
259
|
+
p = figure(
|
|
260
|
+
title="M vs T",
|
|
261
|
+
x_axis_label=x_label,
|
|
262
|
+
y_axis_label="Magnetization",
|
|
263
|
+
tools=tools,
|
|
264
|
+
sizing_mode="stretch_width"
|
|
265
|
+
)
|
|
266
|
+
p.xaxis.axis_label_text_font_style = "normal"
|
|
267
|
+
p.yaxis.axis_label_text_font_style = "normal"
|
|
268
|
+
p.line(T, M, legend_label="M(T)", line_width=2)
|
|
269
|
+
p.circle(T, M, size=6, alpha=0.6, legend_label="M(T)")
|
|
270
|
+
p.legend.location = "top_left"
|
|
271
|
+
p.legend.click_policy = "hide"
|
|
272
|
+
|
|
273
|
+
layout = gridplot([[p]], sizing_mode="stretch_width")
|
|
274
|
+
if show_plot:
|
|
275
|
+
show(layout)
|
|
276
|
+
if return_figure:
|
|
277
|
+
return layout
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
fig, ax = plt.subplots()
|
|
281
|
+
ax.plot(T, M, "o-", label="M(T)")
|
|
282
|
+
ax.set_xlabel(x_label)
|
|
283
|
+
ax.set_ylabel("Magnetization")
|
|
284
|
+
ax.set_title("M vs T")
|
|
285
|
+
ax.legend()
|
|
286
|
+
ax.grid(True)
|
|
287
|
+
if show_plot:
|
|
288
|
+
plt.show()
|
|
289
|
+
if return_figure:
|
|
290
|
+
return fig, ax
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# MPMS functions
|
|
295
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
def extract_mpms_data_dc(df, specimen_name):
|
|
298
|
+
"""
|
|
299
|
+
Extracts and separates MPMS data for a specified specimen from a DataFrame.
|
|
300
|
+
|
|
301
|
+
This function filters data for the given specimen and separates it based on
|
|
302
|
+
MagIC measurement method codes. It specifically extracts data corresponding to
|
|
303
|
+
'LP-FC' (Field Cooled), 'LP-ZFC' (Zero Field Cooled), 'LP-CW-SIRM:LP-MC' (Room
|
|
304
|
+
Temperature SIRM measured upon cooling), and 'LP-CW-SIRM:LP-MW' (Room Temperature
|
|
305
|
+
SIRM measured upon Warming). For each method code, if the data is not available,
|
|
306
|
+
an empty DataFrame with the same columns as the specimen data is returned.
|
|
307
|
+
|
|
308
|
+
Parameters:
|
|
309
|
+
df (pd.DataFrame): DataFrame containing MPMS measurement data.
|
|
310
|
+
specimen_name (str): Name of the specimen to filter data for.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
tuple: A tuple containing four DataFrames:
|
|
314
|
+
- fc_data: Data filtered for 'LP-FC' method if available, otherwise an empty
|
|
315
|
+
DataFrame.
|
|
316
|
+
- zfc_data: Data filtered for 'LP-ZFC' method if available, otherwise an empty
|
|
317
|
+
DataFrame.
|
|
318
|
+
- rtsirm_cool_data: Data filtered for 'LP-CW-SIRM:LP-MC' method if available,
|
|
319
|
+
otherwise an empty DataFrame.
|
|
320
|
+
- rtsirm_warm_data: Data filtered for 'LP-CW-SIRM:LP-MW' method if available,
|
|
321
|
+
otherwise an empty DataFrame.
|
|
322
|
+
"""
|
|
323
|
+
specimen_df = df[df["specimen"] == specimen_name]
|
|
324
|
+
empty_df = pd.DataFrame(columns=specimen_df.columns)
|
|
325
|
+
|
|
326
|
+
# If the 'method_codes' column is missing, return empty DataFrames.
|
|
327
|
+
if "method_codes" not in specimen_df.columns:
|
|
328
|
+
return empty_df, empty_df, empty_df, empty_df
|
|
329
|
+
|
|
330
|
+
# Filter for each method code if available, otherwise use empty DataFrame.
|
|
331
|
+
fc_data = (
|
|
332
|
+
specimen_df[specimen_df["method_codes"].str.contains("LP-FC", na=False)]
|
|
333
|
+
if specimen_df["method_codes"].str.contains("LP-FC", na=False).any()
|
|
334
|
+
else empty_df
|
|
335
|
+
)
|
|
336
|
+
zfc_data = (
|
|
337
|
+
specimen_df[specimen_df["method_codes"].str.contains("LP-ZFC", na=False)]
|
|
338
|
+
if specimen_df["method_codes"].str.contains("LP-ZFC", na=False).any()
|
|
339
|
+
else empty_df
|
|
340
|
+
)
|
|
341
|
+
rtsirm_cool_data = (
|
|
342
|
+
specimen_df[
|
|
343
|
+
specimen_df["method_codes"].str.contains("LP-CW-SIRM:LP-MC", na=False)
|
|
344
|
+
]
|
|
345
|
+
if specimen_df["method_codes"].str.contains("LP-CW-SIRM:LP-MC", na=False).any()
|
|
346
|
+
else empty_df
|
|
347
|
+
)
|
|
348
|
+
rtsirm_warm_data = (
|
|
349
|
+
specimen_df[
|
|
350
|
+
specimen_df["method_codes"].str.contains("LP-CW-SIRM:LP-MW", na=False)
|
|
351
|
+
]
|
|
352
|
+
if specimen_df["method_codes"].str.contains("LP-CW-SIRM:LP-MW", na=False).any()
|
|
353
|
+
else empty_df
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return fc_data, zfc_data, rtsirm_cool_data, rtsirm_warm_data
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def plot_mpms_dc(
|
|
360
|
+
fc_data=None,
|
|
361
|
+
zfc_data=None,
|
|
362
|
+
rtsirm_cool_data=None,
|
|
363
|
+
rtsirm_warm_data=None,
|
|
364
|
+
fc_color="#1f77b4",
|
|
365
|
+
zfc_color="#ff7f0e",
|
|
366
|
+
rtsirm_cool_color="#17becf",
|
|
367
|
+
rtsirm_warm_color="#d62728",
|
|
368
|
+
fc_marker="d",
|
|
369
|
+
zfc_marker="p",
|
|
370
|
+
rtsirm_cool_marker="s",
|
|
371
|
+
rtsirm_warm_marker="o",
|
|
372
|
+
symbol_size=4,
|
|
373
|
+
interactive=False,
|
|
374
|
+
plot_derivative=False,
|
|
375
|
+
return_figure=False,
|
|
376
|
+
show_plot=True,
|
|
377
|
+
drop_first=False,
|
|
378
|
+
drop_last=False,
|
|
379
|
+
):
|
|
380
|
+
"""
|
|
381
|
+
Plots MPMS DC data and optional derivatives, omitting empty panels.
|
|
382
|
+
|
|
383
|
+
Parameters:
|
|
384
|
+
fc_data (DataFrame or None): Field-cooled data.
|
|
385
|
+
zfc_data (DataFrame or None): Zero-field-cooled data.
|
|
386
|
+
rtsirm_cool_data (DataFrame or None): RTSIRM cooling data.
|
|
387
|
+
rtsirm_warm_data (DataFrame or None): RTSIRM warming data.
|
|
388
|
+
fc_color, zfc_color, rtsirm_cool_color, rtsirm_warm_color (str):
|
|
389
|
+
HEX color codes.
|
|
390
|
+
fc_marker, zfc_marker, rtsirm_cool_marker, rtsirm_warm_marker (str):
|
|
391
|
+
Matplotlib-style markers.
|
|
392
|
+
symbol_size (int): Marker size.
|
|
393
|
+
interactive (bool): If True, use Bokeh.
|
|
394
|
+
plot_derivative (bool): If True, plot dM/dT curves.
|
|
395
|
+
return_figure (bool): If True, return the figure/grid.
|
|
396
|
+
show_plot (bool): If True, display the plot.
|
|
397
|
+
drop_first (bool): If True, drop first row of each series.
|
|
398
|
+
drop_last (bool): If True, drop last row of each series.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Bokeh grid or Matplotlib fig/axes tuple, or None.
|
|
402
|
+
"""
|
|
403
|
+
def trim(df):
|
|
404
|
+
if df is None or df.empty:
|
|
405
|
+
return None
|
|
406
|
+
df = df.reset_index(drop=True)
|
|
407
|
+
if drop_first:
|
|
408
|
+
df = df.iloc[1:].reset_index(drop=True)
|
|
409
|
+
if drop_last:
|
|
410
|
+
df = df.iloc[:-1].reset_index(drop=True)
|
|
411
|
+
return df
|
|
412
|
+
|
|
413
|
+
fc = trim(fc_data)
|
|
414
|
+
zfc = trim(zfc_data)
|
|
415
|
+
rc = trim(rtsirm_cool_data)
|
|
416
|
+
rw = trim(rtsirm_warm_data)
|
|
417
|
+
|
|
418
|
+
if plot_derivative:
|
|
419
|
+
def deriv(df):
|
|
420
|
+
return None if df is None else thermomag_derivative(
|
|
421
|
+
df["meas_temp"], df["magn_mass"]
|
|
422
|
+
)
|
|
423
|
+
fcd = deriv(fc)
|
|
424
|
+
zfcd = deriv(zfc)
|
|
425
|
+
rcd = deriv(rc)
|
|
426
|
+
rwd = deriv(rw)
|
|
427
|
+
|
|
428
|
+
fc_zfc_present = (fc is not None) or (zfc is not None)
|
|
429
|
+
rtsirm_present = (rc is not None) or (rw is not None)
|
|
430
|
+
|
|
431
|
+
if interactive:
|
|
432
|
+
tools = [HoverTool(tooltips=[("T","@x"),("M","@y")]), "pan,box_zoom,reset,save"]
|
|
433
|
+
figs = []
|
|
434
|
+
|
|
435
|
+
if fc_zfc_present:
|
|
436
|
+
p0 = figure(title="LTSIRM Data", x_axis_label="Temperature (K)",
|
|
437
|
+
y_axis_label="Magnetization (Am2/kg)", tools=tools,
|
|
438
|
+
sizing_mode="stretch_width",plot_height=400)
|
|
439
|
+
if fc is not None:
|
|
440
|
+
p0.line(fc["meas_temp"], fc["magn_mass"], color=fc_color, legend_label="FC")
|
|
441
|
+
p0.scatter(fc["meas_temp"], fc["magn_mass"], marker=mpl_to_bokeh_markers.get(fc_marker), size=symbol_size, color=fc_color)
|
|
442
|
+
if zfc is not None:
|
|
443
|
+
p0.line(zfc["meas_temp"], zfc["magn_mass"], color=zfc_color, legend_label="ZFC")
|
|
444
|
+
p0.scatter(zfc["meas_temp"], zfc["magn_mass"], marker=mpl_to_bokeh_markers.get(zfc_marker), size=symbol_size, color=zfc_color)
|
|
445
|
+
p0.legend.click_policy="hide"
|
|
446
|
+
p0.xaxis.axis_label_text_font_style = "normal"
|
|
447
|
+
p0.yaxis.axis_label_text_font_style = "normal"
|
|
448
|
+
figs.append(p0)
|
|
449
|
+
|
|
450
|
+
if rtsirm_present:
|
|
451
|
+
p1 = figure(title="RTSIRM Data", x_axis_label="Temperature (K)",
|
|
452
|
+
y_axis_label="Magnetization (Am2/kg)", tools=tools,
|
|
453
|
+
sizing_mode="stretch_width",plot_height=400)
|
|
454
|
+
if rc is not None:
|
|
455
|
+
p1.line(rc["meas_temp"], rc["magn_mass"], color=rtsirm_cool_color, legend_label="cool")
|
|
456
|
+
p1.scatter(rc["meas_temp"], rc["magn_mass"], marker=mpl_to_bokeh_markers.get(rtsirm_cool_marker), size=symbol_size, color=rtsirm_cool_color)
|
|
457
|
+
if rw is not None:
|
|
458
|
+
p1.line(rw["meas_temp"], rw["magn_mass"], color=rtsirm_warm_color, legend_label="warm")
|
|
459
|
+
p1.scatter(rw["meas_temp"], rw["magn_mass"], marker=mpl_to_bokeh_markers.get(rtsirm_warm_marker), size=symbol_size, color=rtsirm_warm_color)
|
|
460
|
+
p1.legend.click_policy="hide"
|
|
461
|
+
p1.xaxis.axis_label_text_font_style = "normal"
|
|
462
|
+
p1.yaxis.axis_label_text_font_style = "normal"
|
|
463
|
+
figs.append(p1)
|
|
464
|
+
|
|
465
|
+
# separate derivative panels
|
|
466
|
+
if plot_derivative and fc_zfc_present:
|
|
467
|
+
p2 = figure(title="LTSIRM Derivative", x_axis_label="Temperature (K)",
|
|
468
|
+
y_axis_label="dM/dT", tools=tools,
|
|
469
|
+
sizing_mode="stretch_width",plot_height=400)
|
|
470
|
+
if fcd is not None: p2.line(fcd["T"], fcd["dM_dT"], color=fc_color, legend_label="FC dM/dT")
|
|
471
|
+
if zfcd is not None: p2.line(zfcd["T"], zfcd["dM_dT"], color=zfc_color, legend_label="ZFC dM/dT")
|
|
472
|
+
p2.legend.click_policy="hide"
|
|
473
|
+
p2.xaxis.axis_label_text_font_style = "normal"
|
|
474
|
+
p2.yaxis.axis_label_text_font_style = "normal"
|
|
475
|
+
figs.append(p2)
|
|
476
|
+
|
|
477
|
+
if plot_derivative and rtsirm_present:
|
|
478
|
+
p3 = figure(title="RTSIRM Derivative", x_axis_label="Temperature (K)",
|
|
479
|
+
y_axis_label="dM/dT", tools=tools,
|
|
480
|
+
sizing_mode="stretch_width",plot_height=400)
|
|
481
|
+
if rcd is not None: p3.line(rcd["T"], rcd["dM_dT"], color=rtsirm_cool_color, legend_label="cool dM/dT")
|
|
482
|
+
if rwd is not None: p3.line(rwd["T"], rwd["dM_dT"], color=rtsirm_warm_color, legend_label="warm dM/dT")
|
|
483
|
+
p3.legend.click_policy="hide"
|
|
484
|
+
p3.xaxis.axis_label_text_font_style = "normal"
|
|
485
|
+
p3.yaxis.axis_label_text_font_style = "normal"
|
|
486
|
+
figs.append(p3)
|
|
487
|
+
|
|
488
|
+
layout = gridplot([figs[:2], figs[2:]], sizing_mode="stretch_width")
|
|
489
|
+
if show_plot: show(layout)
|
|
490
|
+
return layout if return_figure else None
|
|
491
|
+
|
|
492
|
+
# Matplotlib branch
|
|
493
|
+
rows = 1 + (1 if plot_derivative else 0)
|
|
494
|
+
cols = 2
|
|
495
|
+
fig, axes = plt.subplots(rows, cols, figsize=(5*cols, 4*rows))
|
|
496
|
+
axes = axes.reshape(rows, cols)
|
|
497
|
+
|
|
498
|
+
if not fc_zfc_present:
|
|
499
|
+
axes[0,0].set_visible(False)
|
|
500
|
+
if plot_derivative: axes[1,0].set_visible(False)
|
|
501
|
+
if not rtsirm_present:
|
|
502
|
+
axes[0,1].set_visible(False)
|
|
503
|
+
if plot_derivative: axes[1,1].set_visible(False)
|
|
504
|
+
|
|
505
|
+
if fc_zfc_present:
|
|
506
|
+
ax = axes[0,0]
|
|
507
|
+
if fc is not None: ax.plot(fc["meas_temp"], fc["magn_mass"], color=fc_color, marker=fc_marker, label="FC")
|
|
508
|
+
if zfc is not None: ax.plot(zfc["meas_temp"], zfc["magn_mass"], color=zfc_color, marker=zfc_marker, label="ZFC")
|
|
509
|
+
ax.set_title("LTSIRM Data"); ax.set_xlabel("Temperature (K)"); ax.set_ylabel("Magnetization"); ax.legend(); ax.grid(True)
|
|
510
|
+
|
|
511
|
+
if rtsirm_present:
|
|
512
|
+
ax = axes[0,1]
|
|
513
|
+
if rc is not None: ax.plot(rc["meas_temp"], rc["magn_mass"], color=rtsirm_cool_color, marker=rtsirm_cool_marker, label="cool")
|
|
514
|
+
if rw is not None: ax.plot(rw["meas_temp"], rw["magn_mass"], color=rtsirm_warm_color, marker=rtsirm_warm_marker, label="warm")
|
|
515
|
+
ax.set_title("RTSIRM Data"); ax.set_xlabel("Temperature (K)"); ax.set_ylabel("Magnetization"); ax.legend(); ax.grid(True)
|
|
516
|
+
|
|
517
|
+
if plot_derivative and fc_zfc_present:
|
|
518
|
+
ax = axes[1,0]
|
|
519
|
+
if fcd is not None: ax.plot(fcd["T"], fcd["dM_dT"], color=fc_color, marker=fc_marker, label="FC dM/dT")
|
|
520
|
+
if zfcd is not None: ax.plot(zfcd["T"], zfcd["dM_dT"], color=zfc_color, marker=zfc_marker, label="ZFC dM/dT")
|
|
521
|
+
ax.set_title("LTSIRM Derivative"); ax.set_xlabel("Temperature (K)"); ax.set_ylabel("dM/dT"); ax.legend(); ax.grid(True)
|
|
522
|
+
|
|
523
|
+
if plot_derivative and rtsirm_present:
|
|
524
|
+
ax = axes[1,1]
|
|
525
|
+
if rcd is not None: ax.plot(rcd["T"], rcd["dM_dT"], color=rtsirm_cool_color, marker=rtsirm_cool_marker, label="cool dM/dT")
|
|
526
|
+
if rwd is not None: ax.plot(rwd["T"], rwd["dM_dT"], color=rtsirm_warm_color, marker=rtsirm_warm_marker, label="warm dM/dT")
|
|
527
|
+
ax.set_title("RTSIRM Derivative"); ax.set_xlabel("Temperature (K)"); ax.set_ylabel("dM/dT"); ax.legend(); ax.grid(True)
|
|
528
|
+
|
|
529
|
+
fig.tight_layout()
|
|
530
|
+
if show_plot: plt.show()
|
|
531
|
+
return fig if return_figure else None
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def make_mpms_plots_dc(measurements):
|
|
535
|
+
"""Create a UI to select specimen and plot MPMS data in Matplotlib or Bokeh.
|
|
536
|
+
|
|
537
|
+
Parameters:
|
|
538
|
+
measurements (pandas.DataFrame): DataFrame with 'specimen' and
|
|
539
|
+
'method_codes'.
|
|
540
|
+
"""
|
|
541
|
+
experiments = (
|
|
542
|
+
measurements.groupby(["specimen", "method_codes"])
|
|
543
|
+
.size()
|
|
544
|
+
.reset_index()
|
|
545
|
+
.iloc[:, :2]
|
|
546
|
+
)
|
|
547
|
+
filtered = experiments[
|
|
548
|
+
experiments["method_codes"].isin(
|
|
549
|
+
["LP-FC", "LP-ZFC", "LP-CW-SIRM:LP-MC", "LP-CW-SIRM:LP-MW"]
|
|
550
|
+
)
|
|
551
|
+
]
|
|
552
|
+
specimen_options = filtered["specimen"].unique().tolist()
|
|
553
|
+
|
|
554
|
+
specimen_dd = widgets.Dropdown(
|
|
555
|
+
options=specimen_options, description="Specimen:"
|
|
556
|
+
)
|
|
557
|
+
library_rb = widgets.RadioButtons(
|
|
558
|
+
options=["Bokeh", "Matplotlib"], description="Plot with:"
|
|
559
|
+
)
|
|
560
|
+
out = widgets.Output()
|
|
561
|
+
|
|
562
|
+
def _update(change=None):
|
|
563
|
+
spec = specimen_dd.value
|
|
564
|
+
fc_data, zfc_data, rts_c, rts_w = extract_mpms_data_dc(
|
|
565
|
+
measurements, spec
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
with out:
|
|
569
|
+
out.clear_output(wait=True)
|
|
570
|
+
if library_rb.value == "Matplotlib":
|
|
571
|
+
plot_mpms_dc(
|
|
572
|
+
fc_data,
|
|
573
|
+
zfc_data,
|
|
574
|
+
rts_c,
|
|
575
|
+
rts_w,
|
|
576
|
+
use_bokeh=False,
|
|
577
|
+
plot_derivative=True,
|
|
578
|
+
show_plot=True,
|
|
579
|
+
)
|
|
580
|
+
else:
|
|
581
|
+
grid = plot_mpms_dc(
|
|
582
|
+
fc_data,
|
|
583
|
+
zfc_data,
|
|
584
|
+
rts_c,
|
|
585
|
+
rts_w,
|
|
586
|
+
use_bokeh=True,
|
|
587
|
+
plot_derivative=True,
|
|
588
|
+
return_figure=True,
|
|
589
|
+
show_plot=False,
|
|
590
|
+
)
|
|
591
|
+
script, div = components(grid)
|
|
592
|
+
display(HTML(div + script))
|
|
593
|
+
|
|
594
|
+
specimen_dd.observe(_update, names="value")
|
|
595
|
+
library_rb.observe(_update, names="value")
|
|
596
|
+
|
|
597
|
+
ui = widgets.VBox([widgets.HBox([specimen_dd, library_rb]), out])
|
|
598
|
+
display(ui)
|
|
599
|
+
_update()
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def verwey_estimate(temps, mags,
|
|
603
|
+
t_range_background_min=50,
|
|
604
|
+
t_range_background_max=250,
|
|
605
|
+
excluded_t_min=75,
|
|
606
|
+
excluded_t_max=150,
|
|
607
|
+
poly_deg=3,
|
|
608
|
+
plot_zero_crossing=False,
|
|
609
|
+
plot_title=None,
|
|
610
|
+
measurement_marker='o', measurement_color='FireBrick',
|
|
611
|
+
background_fit_marker='s', background_fit_color='Teal',
|
|
612
|
+
magnetite_marker='d', magnetite_color='RoyalBlue',
|
|
613
|
+
verwey_marker='*', verwey_color='Pink',
|
|
614
|
+
verwey_size=10,
|
|
615
|
+
markersize=3.5):
|
|
616
|
+
"""
|
|
617
|
+
Estimate the Verwey transition temperature and remanence loss of magnetite from MPMS data.
|
|
618
|
+
Plots the magnetization data, background fit, and resulting magnetite curve, and
|
|
619
|
+
optionally the zero-crossing.
|
|
620
|
+
|
|
621
|
+
Parameters
|
|
622
|
+
----------
|
|
623
|
+
temps : pd.Series
|
|
624
|
+
Series representing the temperatures at which magnetization measurements were taken.
|
|
625
|
+
mags : pd.Series
|
|
626
|
+
Series representing the magnetization measurements.
|
|
627
|
+
t_range_background_min : int or float, optional
|
|
628
|
+
Minimum temperature for the background fitting range. Default is 50.
|
|
629
|
+
t_range_background_max : int or float, optional
|
|
630
|
+
Maximum temperature for the background fitting range. Default is 250.
|
|
631
|
+
excluded_t_min : int or float, optional
|
|
632
|
+
Minimum temperature to exclude from the background fitting range. Default is 75.
|
|
633
|
+
excluded_t_max : int or float, optional
|
|
634
|
+
Maximum temperature to exclude from the background fitting range. Default is 150.
|
|
635
|
+
poly_deg : int, optional
|
|
636
|
+
Degree of the polynomial for background fitting. Default is 3.
|
|
637
|
+
plot_zero_crossing : bool, optional
|
|
638
|
+
If True, plots the zero-crossing of the second derivative. Default is False.
|
|
639
|
+
plot_title : str, optional
|
|
640
|
+
Title for the plot. Default is None.
|
|
641
|
+
measurement_marker : str, optional
|
|
642
|
+
Marker symbol for measurement data. Default is 'o'.
|
|
643
|
+
measurement_color : str, optional
|
|
644
|
+
Color for measurement data. Default is 'black'.
|
|
645
|
+
background_fit_marker : str, optional
|
|
646
|
+
Marker symbol for background fit data. Default is 's'.
|
|
647
|
+
background_fit_color : str, optional
|
|
648
|
+
Color for background fit data. Default is 'C1'.
|
|
649
|
+
magnetite_marker : str, optional
|
|
650
|
+
Marker symbol for magnetite data. Default is 'd'.
|
|
651
|
+
magnetite_color : str, optional
|
|
652
|
+
Color for magnetite data. Default is 'C0'.
|
|
653
|
+
verwey_marker : str, optional
|
|
654
|
+
Marker symbol used to denote the Verwey transition estimate on the plot. Default is '*'.
|
|
655
|
+
verwey_color : str, optional
|
|
656
|
+
Color of the marker representing the Verwey transition estimate. Default is 'Pink'.
|
|
657
|
+
verwey_size : int, optional
|
|
658
|
+
Size of the marker used for the Verwey transition estimate. Default is 10.
|
|
659
|
+
markersize : float, optional
|
|
660
|
+
Size of the markers. Default is 3.5.
|
|
661
|
+
|
|
662
|
+
Returns
|
|
663
|
+
-------
|
|
664
|
+
verwey_estimate : float
|
|
665
|
+
Estimated Verwey transition temperature.
|
|
666
|
+
remanence_loss : float
|
|
667
|
+
Estimated remanence loss.
|
|
668
|
+
|
|
669
|
+
Examples
|
|
670
|
+
--------
|
|
671
|
+
>>> temps = pd.Series([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
|
672
|
+
>>> mags = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
673
|
+
>>> verwey_estimate(temps, mags)
|
|
674
|
+
(75.0, 0.5)
|
|
675
|
+
"""
|
|
676
|
+
|
|
677
|
+
temps.reset_index(drop=True, inplace=True)
|
|
678
|
+
mags.reset_index(drop=True, inplace=True)
|
|
679
|
+
|
|
680
|
+
dM_dT_df = thermomag_derivative(temps, mags)
|
|
681
|
+
temps_dM_dT = dM_dT_df['T']
|
|
682
|
+
|
|
683
|
+
temps_dM_dT_filtered_indices = [i for i in np.arange(len(temps_dM_dT)) if ((float(temps_dM_dT[i]) > float(t_range_background_min)) and (float(temps_dM_dT[i]) < float(excluded_t_min)) ) or ((float(temps_dM_dT[i]) > float(excluded_t_max)) and (float(temps_dM_dT[i]) < float(t_range_background_max)))]
|
|
684
|
+
temps_dM_dT_filtered = dM_dT_df['T'][temps_dM_dT_filtered_indices]
|
|
685
|
+
dM_dT_filtered = dM_dT_df['dM_dT'][temps_dM_dT_filtered_indices]
|
|
686
|
+
|
|
687
|
+
poly_background_fit = np.polyfit(temps_dM_dT_filtered, dM_dT_filtered, poly_deg)
|
|
688
|
+
dM_dT_filtered_polyfit = np.poly1d(poly_background_fit)(temps_dM_dT_filtered)
|
|
689
|
+
|
|
690
|
+
residuals = dM_dT_filtered - dM_dT_filtered_polyfit
|
|
691
|
+
ss_tot = np.sum((dM_dT_filtered - np.mean(dM_dT_filtered)) ** 2)
|
|
692
|
+
ss_res = np.sum(residuals ** 2)
|
|
693
|
+
r_squared = 1 - (ss_res / ss_tot)
|
|
694
|
+
|
|
695
|
+
temps_dM_dT_background_indices = [i for i in np.arange(len(temps_dM_dT)) if ((float(temps_dM_dT[i]) > float(t_range_background_min)) and (float(temps_dM_dT[i]) < float(t_range_background_max)))]
|
|
696
|
+
temps_dM_dT_background = dM_dT_df['T'][temps_dM_dT_background_indices]
|
|
697
|
+
temps_dM_dT_background.reset_index(drop=True, inplace=True)
|
|
698
|
+
dM_dT_background = dM_dT_df['dM_dT'][temps_dM_dT_background_indices]
|
|
699
|
+
dM_dT_polyfit = np.poly1d(poly_background_fit)(temps_dM_dT_background)
|
|
700
|
+
|
|
701
|
+
mgt_dM_dT = dM_dT_polyfit - dM_dT_background
|
|
702
|
+
mgt_dM_dT.reset_index(drop = True, inplace=True)
|
|
703
|
+
|
|
704
|
+
temps_background_indices = [i for i in np.arange(len(temps)) if ((float(temps[i]) > float(t_range_background_min)) and (float(temps[i]) < float(t_range_background_max)))]
|
|
705
|
+
temps_background = temps[temps_background_indices]
|
|
706
|
+
|
|
707
|
+
poly_func = np.poly1d(poly_background_fit)
|
|
708
|
+
background_curve = np.cumsum(poly_func(temps_background) * np.gradient(temps_background))
|
|
709
|
+
|
|
710
|
+
last_background_temp = temps_background.iloc[-1]
|
|
711
|
+
last_background_mag = background_curve[-1]
|
|
712
|
+
target_temp_index = np.argmin(np.abs(temps - last_background_temp))
|
|
713
|
+
mags_value = mags[target_temp_index]
|
|
714
|
+
background_curve_adjusted = background_curve + (mags_value - last_background_mag)
|
|
715
|
+
|
|
716
|
+
mags_background = mags[temps_background_indices]
|
|
717
|
+
mgt_curve = mags_background - background_curve_adjusted
|
|
718
|
+
|
|
719
|
+
verwey_estimate = zero_crossing(temps_dM_dT_background, mgt_dM_dT,
|
|
720
|
+
make_plot=plot_zero_crossing,
|
|
721
|
+
xlim=(excluded_t_min, excluded_t_max),
|
|
722
|
+
verwey_marker=verwey_marker, verwey_color=verwey_color,
|
|
723
|
+
verwey_size=verwey_size)
|
|
724
|
+
|
|
725
|
+
remanence_loss = np.trapz(mgt_dM_dT, temps_dM_dT_background)
|
|
726
|
+
|
|
727
|
+
fig = plt.figure(figsize=(12,5))
|
|
728
|
+
ax0 = fig.add_subplot(1,2,1)
|
|
729
|
+
ax0.plot(temps, mags, marker=measurement_marker, markersize=markersize, color=measurement_color,
|
|
730
|
+
label='measurement')
|
|
731
|
+
ax0.plot(temps_background, background_curve_adjusted, marker=background_fit_marker, markersize=markersize, color=background_fit_color,
|
|
732
|
+
label='background fit')
|
|
733
|
+
ax0.plot(temps_background, mgt_curve, marker=magnetite_marker, markersize=markersize, color=magnetite_color,
|
|
734
|
+
label='magnetite (meas. minus background)')
|
|
735
|
+
verwey_y_value = np.interp(verwey_estimate, temps_background, mgt_curve)
|
|
736
|
+
ax0.plot(verwey_estimate, verwey_y_value, verwey_marker, color=verwey_color, markersize=verwey_size,
|
|
737
|
+
markeredgecolor='black', markeredgewidth=1,
|
|
738
|
+
label='Verwey estimate' + ' (' + str(round(verwey_estimate,1)) + ' K)')
|
|
739
|
+
ax0.set_ylabel('M (Am$^2$/kg)')
|
|
740
|
+
ax0.set_xlabel('T (K)')
|
|
741
|
+
ax0.legend(loc='upper right')
|
|
742
|
+
ax0.grid(True)
|
|
743
|
+
ax0.ticklabel_format(axis='y', style='scientific', scilimits=(0,0))
|
|
744
|
+
if plot_title is not None:
|
|
745
|
+
ax0.set_title(plot_title)
|
|
746
|
+
|
|
747
|
+
ax1 = fig.add_subplot(1,2,2)
|
|
748
|
+
ax1.plot(dM_dT_df['T'], dM_dT_df['dM_dT'], marker=measurement_marker, markersize=markersize, color=measurement_color,
|
|
749
|
+
label='measurement')
|
|
750
|
+
ax1.plot(temps_dM_dT_background, dM_dT_polyfit, marker=background_fit_marker, markersize=markersize, color=background_fit_color,
|
|
751
|
+
label='background fit'+ ' (r$^2$ = ' + str(round(r_squared,3)) + ')' )
|
|
752
|
+
ax1.plot(temps_dM_dT_background, mgt_dM_dT, marker=magnetite_marker, markersize=markersize, color=magnetite_color,
|
|
753
|
+
label='magnetite (background fit minus measurement)')
|
|
754
|
+
verwey_y_value = np.interp(verwey_estimate, temps_dM_dT_background, mgt_dM_dT)
|
|
755
|
+
ax1.plot(verwey_estimate, verwey_y_value, verwey_marker, color=verwey_color, markersize=verwey_size,
|
|
756
|
+
markeredgecolor='black', markeredgewidth=1,
|
|
757
|
+
label='Verwey estimate' + ' (' + str(round(verwey_estimate,1)) + ' K)')
|
|
758
|
+
rectangle = patches.Rectangle((excluded_t_min, ax1.get_ylim()[0]), excluded_t_max - excluded_t_min,
|
|
759
|
+
ax1.get_ylim()[1] - ax1.get_ylim()[0],
|
|
760
|
+
linewidth=0, edgecolor=None, facecolor='gray',
|
|
761
|
+
alpha=0.3)
|
|
762
|
+
ax1.add_patch(rectangle)
|
|
763
|
+
rect_legend_patch = patches.Patch(color='gray', alpha=0.3, label='excluded from background fit')
|
|
764
|
+
handles, labels = ax1.get_legend_handles_labels()
|
|
765
|
+
handles.append(rect_legend_patch) # Add the rectangle legend patch
|
|
766
|
+
ax1.legend(handles=handles, loc='lower right')
|
|
767
|
+
ax1.set_ylabel('dM/dT (Am$^2$/kg/K)')
|
|
768
|
+
ax1.set_xlabel('T (K)')
|
|
769
|
+
ax1.grid(True)
|
|
770
|
+
ax1.ticklabel_format(axis='y', style='scientific', scilimits=(0,0))
|
|
771
|
+
if plot_title is not None:
|
|
772
|
+
ax1.set_title(plot_title)
|
|
773
|
+
#plt.show()
|
|
774
|
+
|
|
775
|
+
return verwey_estimate, remanence_loss
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def interactive_verwey_estimate(measurements, specimen_dropdown, method_dropdown):
|
|
779
|
+
|
|
780
|
+
selected_specimen_name = specimen_dropdown.value
|
|
781
|
+
selected_method = method_dropdown.value
|
|
782
|
+
|
|
783
|
+
fc_data, zfc_data, rtsirm_cool_data, rtsirm_warm_data = extract_mpms_data_dc(measurements, selected_specimen_name)
|
|
784
|
+
if selected_method == 'LP-FC':
|
|
785
|
+
temps = fc_data['meas_temp']
|
|
786
|
+
mags = fc_data['magn_mass']
|
|
787
|
+
elif selected_method == 'LP-ZFC':
|
|
788
|
+
temps = zfc_data['meas_temp']
|
|
789
|
+
mags = zfc_data['magn_mass']
|
|
790
|
+
|
|
791
|
+
# Determine a fixed width for the descriptions to align the sliders
|
|
792
|
+
description_width = '250px' # Adjust this based on the longest description
|
|
793
|
+
slider_total_width = '600px' # Total width of the slider widget including the description
|
|
794
|
+
|
|
795
|
+
description_style = {'description_width': description_width}
|
|
796
|
+
slider_layout = widgets.Layout(width=slider_total_width) # Set the total width of the slider widget
|
|
797
|
+
|
|
798
|
+
# Update sliders to use IntRangeSlider
|
|
799
|
+
background_temp_range_slider = widgets.IntRangeSlider(
|
|
800
|
+
value=[60, 250], min=0, max=300, step=1,
|
|
801
|
+
description='Background Temperature Range (K):',
|
|
802
|
+
layout=slider_layout, style=description_style
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
excluded_temp_range_slider = widgets.IntRangeSlider(
|
|
806
|
+
value=[75, 150], min=0, max=300, step=1,
|
|
807
|
+
description='Excluded Temperature Range (K):',
|
|
808
|
+
layout=slider_layout, style=description_style
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
poly_deg_slider = widgets.IntSlider(
|
|
812
|
+
value=3, min=1, max=5, step=1,
|
|
813
|
+
description='Background Fit Polynomial Degree:',
|
|
814
|
+
layout=slider_layout, style=description_style
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
# Function to reset sliders to initial values
|
|
818
|
+
def reset_sliders(b):
|
|
819
|
+
background_temp_range_slider.value = (60, 250)
|
|
820
|
+
excluded_temp_range_slider.value = (75, 150)
|
|
821
|
+
poly_deg_slider.value = 3
|
|
822
|
+
|
|
823
|
+
# Create reset button
|
|
824
|
+
reset_button = widgets.Button(description="Reset to Default Values", layout=widgets.Layout(width='200px'))
|
|
825
|
+
reset_button.on_click(reset_sliders)
|
|
826
|
+
|
|
827
|
+
title_label = widgets.Label(value='Adjust Parameters for ' + selected_specimen_name + ' ' + selected_method + ' fit')
|
|
828
|
+
|
|
829
|
+
# Add the reset button to the UI
|
|
830
|
+
ui = widgets.VBox([
|
|
831
|
+
title_label,
|
|
832
|
+
background_temp_range_slider,
|
|
833
|
+
excluded_temp_range_slider,
|
|
834
|
+
poly_deg_slider,
|
|
835
|
+
reset_button
|
|
836
|
+
])
|
|
837
|
+
|
|
838
|
+
out = widgets.interactive_output(
|
|
839
|
+
lambda background_temp_range, excluded_temp_range, poly_deg: verwey_estimate(
|
|
840
|
+
temps, mags,
|
|
841
|
+
background_temp_range[0], background_temp_range[1],
|
|
842
|
+
excluded_temp_range[0], excluded_temp_range[1],
|
|
843
|
+
poly_deg
|
|
844
|
+
), {
|
|
845
|
+
'background_temp_range': background_temp_range_slider,
|
|
846
|
+
'excluded_temp_range': excluded_temp_range_slider,
|
|
847
|
+
'poly_deg': poly_deg_slider,
|
|
848
|
+
}
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
out.layout.height = '400px'
|
|
852
|
+
|
|
853
|
+
display(ui, out)
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def interactive_verwey_specimen_method_selection(measurements):
|
|
857
|
+
"""
|
|
858
|
+
Creates and displays dropdown widgets for selecting a specimen and the corresponding
|
|
859
|
+
available method codes (specifically 'LP-FC' and 'LP-ZFC') from a given DataFrame of measurements.
|
|
860
|
+
This function filters the measurements to include only those with desired method codes,
|
|
861
|
+
dynamically updates the method dropdown based on the selected specimen, and organizes
|
|
862
|
+
the dropdowns vertically in the UI.
|
|
863
|
+
|
|
864
|
+
Parameters:
|
|
865
|
+
measurements (pd.DataFrame): The DataFrame containing measurement data with columns
|
|
866
|
+
'specimen' and 'method_codes'. It is expected to have
|
|
867
|
+
at least these two columns where 'specimen' identifies
|
|
868
|
+
the specimen name and 'method_codes' contains the method
|
|
869
|
+
codes associated with each measurement.
|
|
870
|
+
|
|
871
|
+
Returns:
|
|
872
|
+
tuple: A tuple containing the specimen dropdown widget (`ipywidgets.Dropdown`)
|
|
873
|
+
and the method dropdown widget (`ipywidgets.Dropdown`). The specimen dropdown
|
|
874
|
+
allows for the selection of a specimen, and the method dropdown updates to
|
|
875
|
+
display only the methods available for the selected specimen. The initial
|
|
876
|
+
selection in the specimen dropdown is set to the first specimen option.
|
|
877
|
+
|
|
878
|
+
Note:
|
|
879
|
+
The method dropdown is initially populated based on the methods available for the
|
|
880
|
+
first selected specimen. The available methods are specifically filtered for 'LP-FC'
|
|
881
|
+
and 'LP-ZFC' codes.
|
|
882
|
+
"""
|
|
883
|
+
# Filter to get specimens with desired method codes
|
|
884
|
+
experiments = measurements.groupby(['specimen', 'method_codes']).size().reset_index().iloc[:, :2]
|
|
885
|
+
filtered_experiments = experiments[experiments['method_codes'].isin(['LP-FC', 'LP-ZFC'])]
|
|
886
|
+
specimen_options = filtered_experiments['specimen'].unique().tolist()
|
|
887
|
+
|
|
888
|
+
selected_specimen_name = specimen_options[0] # Example initial selection
|
|
889
|
+
|
|
890
|
+
# Dropdown for specimen selection
|
|
891
|
+
specimen_dropdown = widgets.Dropdown(
|
|
892
|
+
options=specimen_options,
|
|
893
|
+
description='Specimen:',
|
|
894
|
+
value=selected_specimen_name
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
# Method dropdown initialized with placeholder options
|
|
898
|
+
method_dropdown = widgets.Dropdown(
|
|
899
|
+
description='Method:',
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
# Function to update method options based on selected specimen
|
|
903
|
+
def update_method_options(change):
|
|
904
|
+
selected_specimen = change['new']
|
|
905
|
+
# Filter experiments to get methods available for the selected specimen
|
|
906
|
+
available_methods = filtered_experiments[filtered_experiments['specimen'] == selected_specimen]['method_codes'].tolist()
|
|
907
|
+
# Update method dropdown options and reset its value
|
|
908
|
+
method_dropdown.options = available_methods
|
|
909
|
+
if available_methods:
|
|
910
|
+
method_dropdown.value = available_methods[0]
|
|
911
|
+
else:
|
|
912
|
+
method_dropdown.value = None
|
|
913
|
+
|
|
914
|
+
# Register the update function with specimen dropdown
|
|
915
|
+
specimen_dropdown.observe(update_method_options, names='value')
|
|
916
|
+
|
|
917
|
+
# Initially populate method dropdown based on the first selected specimen
|
|
918
|
+
update_method_options({'new': selected_specimen_name})
|
|
919
|
+
|
|
920
|
+
# Creating a UI layout using VBox to organize the dropdowns vertically
|
|
921
|
+
ui_layout = widgets.VBox([specimen_dropdown, method_dropdown])
|
|
922
|
+
|
|
923
|
+
# Display the UI layout
|
|
924
|
+
display(ui_layout)
|
|
925
|
+
|
|
926
|
+
return specimen_dropdown, method_dropdown
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def verwey_estimate_multiple_specimens(specimens_with_params, measurements):
|
|
930
|
+
"""
|
|
931
|
+
Analyze Verwey transitions for a list of specimens with unique parameters.
|
|
932
|
+
|
|
933
|
+
This function uses either field-cooled (FC) or zero-field cooled (ZFC) data depending on the
|
|
934
|
+
method_codes provided in each specimen's parameters. If "LP-FC" is found in the colon-delimited
|
|
935
|
+
method_codes, FC data is used; if "LP-ZFC" is found, ZFC data is used.
|
|
936
|
+
|
|
937
|
+
Parameters
|
|
938
|
+
----------
|
|
939
|
+
specimens_with_params : list of dict
|
|
940
|
+
List of specimen dictionaries. Each dictionary should contain:
|
|
941
|
+
- 'specimen_name' : str
|
|
942
|
+
The name of the specimen.
|
|
943
|
+
- 'params' : dict
|
|
944
|
+
Dictionary containing:
|
|
945
|
+
- 't_range_background_min' : int or float
|
|
946
|
+
- 't_range_background_max' : int or float
|
|
947
|
+
- 'excluded_t_min' : int or float
|
|
948
|
+
- 'excluded_t_max' : int or float
|
|
949
|
+
- 'poly_deg' : int
|
|
950
|
+
- 'method_codes' : str
|
|
951
|
+
Colon-delimited string that must include either "LP-FC" or "LP-ZFC".
|
|
952
|
+
|
|
953
|
+
measurements : object
|
|
954
|
+
Measurements dataframe in MagIC format.
|
|
955
|
+
|
|
956
|
+
Returns
|
|
957
|
+
-------
|
|
958
|
+
pd.DataFrame
|
|
959
|
+
DataFrame containing the Verwey transition estimates and the input parameters for each specimen.
|
|
960
|
+
Columns include:
|
|
961
|
+
- 'specimen'
|
|
962
|
+
- 'critical_temp'
|
|
963
|
+
- 'critical_temp_type'
|
|
964
|
+
- 'remanence_loss'
|
|
965
|
+
plus the additional parameters from the input.
|
|
966
|
+
|
|
967
|
+
Raises
|
|
968
|
+
------
|
|
969
|
+
ValueError
|
|
970
|
+
If neither "LP-FC" nor "LP-ZFC" is found in the method_codes for a specimen.
|
|
971
|
+
Exception
|
|
972
|
+
Propagates exceptions raised during data extraction or analysis.
|
|
973
|
+
"""
|
|
974
|
+
verwey_estimates_and_params = []
|
|
975
|
+
|
|
976
|
+
# Process each specimen with its unique parameters
|
|
977
|
+
for specimen in specimens_with_params:
|
|
978
|
+
specimen_name = specimen['specimen_name']
|
|
979
|
+
params = specimen['params']
|
|
980
|
+
|
|
981
|
+
# Extract method codes and determine whether to use FC or ZFC data
|
|
982
|
+
method_codes = params.get('method_codes', '')
|
|
983
|
+
codes = method_codes.split(':')
|
|
984
|
+
|
|
985
|
+
# Extract the measurement data for the specimen
|
|
986
|
+
fc_data, zfc_data, rtsirm_cool_data, rtsirm_warm_data = extract_mpms_data_dc(measurements, specimen_name)
|
|
987
|
+
|
|
988
|
+
if "LP-FC" in codes:
|
|
989
|
+
data = fc_data
|
|
990
|
+
elif "LP-ZFC" in codes:
|
|
991
|
+
data = zfc_data
|
|
992
|
+
else:
|
|
993
|
+
raise ValueError(f"Specimen {specimen_name} does not have a valid method code ('LP-FC' or 'LP-ZFC').")
|
|
994
|
+
|
|
995
|
+
temps = data['meas_temp']
|
|
996
|
+
mags = data['magn_mass']
|
|
997
|
+
|
|
998
|
+
# Estimate Verwey transition using selected data
|
|
999
|
+
vt_estimate, rem_loss = verwey_estimate(
|
|
1000
|
+
temps,
|
|
1001
|
+
mags,
|
|
1002
|
+
t_range_background_min=params['t_range_background_min'],
|
|
1003
|
+
t_range_background_max=params['t_range_background_max'],
|
|
1004
|
+
excluded_t_min=params['excluded_t_min'],
|
|
1005
|
+
excluded_t_max=params['excluded_t_max'],
|
|
1006
|
+
poly_deg=params['poly_deg'],
|
|
1007
|
+
plot_title=specimen_name
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
record = {
|
|
1011
|
+
'specimen': specimen_name,
|
|
1012
|
+
'critical_temp': vt_estimate,
|
|
1013
|
+
'critical_temp_type': 'Verwey',
|
|
1014
|
+
'remanence_loss': rem_loss
|
|
1015
|
+
}
|
|
1016
|
+
record.update(params)
|
|
1017
|
+
verwey_estimates_and_params.append(record)
|
|
1018
|
+
|
|
1019
|
+
return pd.DataFrame(verwey_estimates_and_params)
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
def thermomag_derivative(temps, mags, drop_first=False, drop_last=False):
|
|
1023
|
+
"""
|
|
1024
|
+
Calculates the derivative of magnetization with respect to temperature and optionally
|
|
1025
|
+
drops the data corresponding to the highest and/or lowest temperature.
|
|
1026
|
+
|
|
1027
|
+
Parameters:
|
|
1028
|
+
temps (pd.Series): A pandas Series representing the temperatures at which
|
|
1029
|
+
magnetization measurements were taken.
|
|
1030
|
+
mags (pd.Series): A pandas Series representing the magnetization measurements.
|
|
1031
|
+
drop_last (bool): Optional; whether to drop the last row from the resulting
|
|
1032
|
+
DataFrame. Defaults to False. Useful when there is an
|
|
1033
|
+
artifact associated with the end of the experiment.
|
|
1034
|
+
drop_first (bool): Optional; whether to drop the first row from the resulting
|
|
1035
|
+
DataFrame. Defaults to False. Useful when there is an
|
|
1036
|
+
artifact associated with the start of the experiment.
|
|
1037
|
+
|
|
1038
|
+
Returns:
|
|
1039
|
+
pd.DataFrame: A pandas DataFrame with two columns:
|
|
1040
|
+
'T' - Midpoint temperatures for each temperature interval.
|
|
1041
|
+
'dM_dT' - The derivative of magnetization with respect to temperature.
|
|
1042
|
+
If drop_last is True, the last temperature point is excluded.
|
|
1043
|
+
If drop_first is True, the first temperature point is excluded.
|
|
1044
|
+
"""
|
|
1045
|
+
temps = temps.reset_index(drop=True)
|
|
1046
|
+
mags = mags.reset_index(drop=True)
|
|
1047
|
+
|
|
1048
|
+
dT = temps.diff()
|
|
1049
|
+
dM = mags.diff()
|
|
1050
|
+
dM_dT = dM / dT
|
|
1051
|
+
dM_dT_real = dM_dT[1:]
|
|
1052
|
+
dM_dT_real.reset_index(drop=True, inplace=True)
|
|
1053
|
+
|
|
1054
|
+
temps_dM_dT = [temps[n] + dT[n + 1] / 2 for n in range(len(temps) - 1)]
|
|
1055
|
+
temps_dM_dT = pd.Series(temps_dM_dT)
|
|
1056
|
+
|
|
1057
|
+
dM_dT_df = pd.DataFrame({'T': temps_dM_dT, 'dM_dT': dM_dT_real})
|
|
1058
|
+
|
|
1059
|
+
# Drop the last row if specified
|
|
1060
|
+
if drop_last:
|
|
1061
|
+
dM_dT_df = dM_dT_df[:-1].reset_index(drop=True)
|
|
1062
|
+
|
|
1063
|
+
# Drop the first row if specified
|
|
1064
|
+
if drop_first:
|
|
1065
|
+
dM_dT_df = dM_dT_df[1:].reset_index(drop=True)
|
|
1066
|
+
|
|
1067
|
+
return dM_dT_df
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
def zero_crossing(dM_dT_temps, dM_dT, make_plot=False, xlim=None,
|
|
1071
|
+
verwey_marker='*', verwey_color='Pink',
|
|
1072
|
+
verwey_size=10,):
|
|
1073
|
+
"""
|
|
1074
|
+
Calculate the temperature at which the second derivative of magnetization with respect to
|
|
1075
|
+
temperature crosses zero. This value provides an estimate of the peak of the derivative
|
|
1076
|
+
curve that is more precise than the maximum value.
|
|
1077
|
+
|
|
1078
|
+
The function computes the second derivative of magnetization (dM/dT) with respect to
|
|
1079
|
+
temperature, identifies the nearest points around the maximum value of the derivative,
|
|
1080
|
+
and then calculates the temperature at which this second derivative crosses zero using
|
|
1081
|
+
linear interpolation.
|
|
1082
|
+
|
|
1083
|
+
Parameters:
|
|
1084
|
+
dM_dT_temps (pd.Series): A pandas Series representing temperatures corresponding to
|
|
1085
|
+
the first derivation of magnetization with respect to temperature.
|
|
1086
|
+
dM_dT (pd.Series): A pandas Series representing the first derivative of
|
|
1087
|
+
magnetization with respect to temperature.
|
|
1088
|
+
make_plot (bool, optional): If True, a plot will be generated. Defaults to False.
|
|
1089
|
+
xlim (tuple, optional): A tuple specifying the x-axis limits for the plot. Defaults to None.
|
|
1090
|
+
verwey_marker : str, optional
|
|
1091
|
+
Marker symbol used to denote the Verwey transition estimate on the plot. Default is '*'.
|
|
1092
|
+
verwey_color : str, optional
|
|
1093
|
+
Color of the marker representing the Verwey transition estimate. Default is 'Pink'.
|
|
1094
|
+
verwey_size : int, optional
|
|
1095
|
+
Size of the marker used for the Verwey transition estimate. Default is 10.
|
|
1096
|
+
|
|
1097
|
+
Returns:
|
|
1098
|
+
float: The estimated temperature at which the second derivative of magnetization
|
|
1099
|
+
with respect to temperature crosses zero.
|
|
1100
|
+
|
|
1101
|
+
Note:
|
|
1102
|
+
The function assumes that the input series `dM_dT_temps` and `dM_dT` are related to
|
|
1103
|
+
each other and are of equal length.
|
|
1104
|
+
"""
|
|
1105
|
+
|
|
1106
|
+
max_dM_dT_temp = dM_dT_temps[dM_dT.idxmax()]
|
|
1107
|
+
|
|
1108
|
+
d2M_dT2 = thermomag_derivative(dM_dT_temps, dM_dT)
|
|
1109
|
+
d2M_dT2_T_array = d2M_dT2['T'].to_numpy()
|
|
1110
|
+
max_index = np.searchsorted(d2M_dT2_T_array, max_dM_dT_temp)
|
|
1111
|
+
|
|
1112
|
+
d2M_dT2_T_before = d2M_dT2['T'][max_index-1]
|
|
1113
|
+
d2M_dT2_before = d2M_dT2['dM_dT'][max_index-1]
|
|
1114
|
+
d2M_dT2_T_after = d2M_dT2['T'][max_index]
|
|
1115
|
+
d2M_dT2_after = d2M_dT2['dM_dT'][max_index]
|
|
1116
|
+
|
|
1117
|
+
zero_cross_temp = d2M_dT2_T_before + ((d2M_dT2_T_after - d2M_dT2_T_before) / (d2M_dT2_after - d2M_dT2_before)) * (0 - d2M_dT2_before)
|
|
1118
|
+
|
|
1119
|
+
if make_plot:
|
|
1120
|
+
fig = plt.figure(figsize=(12,4))
|
|
1121
|
+
ax0 = fig.add_subplot(1,1,1)
|
|
1122
|
+
ax0.plot(d2M_dT2['T'], d2M_dT2['dM_dT'], '.-', color='purple', label='magnetite (background fit minus measurement)')
|
|
1123
|
+
ax0.plot(d2M_dT2_T_before, d2M_dT2_before, marker='o', markerfacecolor='none', markeredgecolor='red')
|
|
1124
|
+
ax0.plot(d2M_dT2_T_after, d2M_dT2_after, marker='o', markerfacecolor='none', markeredgecolor='red')
|
|
1125
|
+
ax0.plot(zero_cross_temp, 0, verwey_marker, color=verwey_color, markersize=verwey_size, markeredgecolor='black')
|
|
1126
|
+
label = f'{zero_cross_temp:.1f} K'
|
|
1127
|
+
ax0.text(zero_cross_temp+2, 0, label, color='black',
|
|
1128
|
+
verticalalignment='center', horizontalalignment='left',
|
|
1129
|
+
bbox=dict(facecolor='white', alpha=0.7, edgecolor='none'))
|
|
1130
|
+
ax0.set_ylabel('d$^2$M/dT$^2$')
|
|
1131
|
+
ax0.set_xlabel('T (K)')
|
|
1132
|
+
ax0.grid(True)
|
|
1133
|
+
ax0.ticklabel_format(axis='y', style='scientific', scilimits=(0,0))
|
|
1134
|
+
if xlim is not None:
|
|
1135
|
+
ax0.set_xlim(xlim)
|
|
1136
|
+
plt.show()
|
|
1137
|
+
|
|
1138
|
+
return zero_cross_temp
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
def goethite_removal(rtsirm_warm_data,
|
|
1142
|
+
rtsirm_cool_data,
|
|
1143
|
+
t_min=150, t_max=290, poly_deg=2,
|
|
1144
|
+
rtsirm_cool_color='#17becf', rtsirm_warm_color='#d62728',
|
|
1145
|
+
symbol_size=4, return_data=False):
|
|
1146
|
+
"""
|
|
1147
|
+
Analyzes and visualizes the removal of goethite signal from Room Temperature Saturation
|
|
1148
|
+
Isothermal Remanent Magnetization (RTSIRM) warming and cooling data. The function fits
|
|
1149
|
+
a polynomial to the RTSRIM warming curve between specified temperature bounds to model
|
|
1150
|
+
the goethite contribution, then subtracts this fit from the original data. The corrected
|
|
1151
|
+
and uncorrected magnetizations are plotted, along with their derivatives, to assess the
|
|
1152
|
+
effect of goethite removal.
|
|
1153
|
+
|
|
1154
|
+
Parameters:
|
|
1155
|
+
rtsirm_warm_data (pd.DataFrame): DataFrame containing 'meas_temp' and 'magn_mass' columns
|
|
1156
|
+
for RTSIRM warming data.
|
|
1157
|
+
rtsirm_cool_data (pd.DataFrame): DataFrame containing 'meas_temp' and 'magn_mass' columns
|
|
1158
|
+
for RTSIRM cooling data.
|
|
1159
|
+
t_min (int, optional): Minimum temperature for polynomial fitting. Default is 150.
|
|
1160
|
+
t_max (int, optional): Maximum temperature for polynomial fitting. Default is 290.
|
|
1161
|
+
poly_deg (int, optional): Degree of the polynomial to fit. Default is 2.
|
|
1162
|
+
rtsirm_cool_color (str, optional): Color code for plotting cooling data. Default is '#17becf'.
|
|
1163
|
+
rtsirm_warm_color (str, optional): Color code for plotting warming data. Default is '#d62728'.
|
|
1164
|
+
symbol_size (int, optional): Size of the markers in the plots. Default is 4.
|
|
1165
|
+
return_data (bool, optional): If True, returns the corrected magnetization data for both
|
|
1166
|
+
warming and cooling. Default is False.
|
|
1167
|
+
|
|
1168
|
+
Returns:
|
|
1169
|
+
Tuple[pd.Series, pd.Series]: Only if return_data is True. Returns two pandas Series
|
|
1170
|
+
containing the corrected magnetization data for the warming
|
|
1171
|
+
and cooling sequences, respectively.
|
|
1172
|
+
"""
|
|
1173
|
+
|
|
1174
|
+
rtsirm_warm_temps = rtsirm_warm_data['meas_temp']
|
|
1175
|
+
rtsirm_warm_mags = rtsirm_warm_data['magn_mass']
|
|
1176
|
+
rtsirm_cool_temps = rtsirm_cool_data['meas_temp']
|
|
1177
|
+
rtsirm_cool_mags = rtsirm_cool_data['magn_mass']
|
|
1178
|
+
|
|
1179
|
+
rtsirm_warm_temps.reset_index(drop=True, inplace=True)
|
|
1180
|
+
rtsirm_warm_mags.reset_index(drop=True, inplace=True)
|
|
1181
|
+
rtsirm_cool_temps.reset_index(drop=True, inplace=True)
|
|
1182
|
+
rtsirm_cool_mags.reset_index(drop=True, inplace=True)
|
|
1183
|
+
|
|
1184
|
+
rtsirm_warm_temps_filtered_indices = [i for i in np.arange(len(rtsirm_warm_temps)) if ((float(rtsirm_warm_temps[i]) > float(t_min)) and (float(rtsirm_warm_temps[i]) < float(t_max)) )]
|
|
1185
|
+
rtsirm_warm_temps_filtered = rtsirm_warm_temps[rtsirm_warm_temps_filtered_indices]
|
|
1186
|
+
rtsirm_warm_mags_filtered = rtsirm_warm_mags[rtsirm_warm_temps_filtered_indices]
|
|
1187
|
+
|
|
1188
|
+
geothite_fit = np.polyfit(rtsirm_warm_temps_filtered, rtsirm_warm_mags_filtered, poly_deg)
|
|
1189
|
+
rtsirm_warm_mags_polyfit = np.poly1d(geothite_fit)(rtsirm_warm_temps)
|
|
1190
|
+
rtsirm_cool_mags_polyfit = np.poly1d(geothite_fit)(rtsirm_cool_temps)
|
|
1191
|
+
|
|
1192
|
+
rtsirm_warm_mags_corrected = rtsirm_warm_mags - rtsirm_warm_mags_polyfit
|
|
1193
|
+
rtsirm_cool_mags_corrected = rtsirm_cool_mags - rtsirm_cool_mags_polyfit
|
|
1194
|
+
|
|
1195
|
+
fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))
|
|
1196
|
+
|
|
1197
|
+
axs[0, 0].plot(rtsirm_warm_temps, rtsirm_warm_mags, color=rtsirm_warm_color,
|
|
1198
|
+
marker='o', linestyle='-', markersize=symbol_size, label='RTSIRM Warming')
|
|
1199
|
+
axs[0, 0].plot(rtsirm_cool_temps, rtsirm_cool_mags, color=rtsirm_cool_color,
|
|
1200
|
+
marker='o', linestyle='-', markersize=symbol_size, label='RTSIRM Cooling')
|
|
1201
|
+
axs[0, 0].plot(rtsirm_warm_temps, rtsirm_warm_mags_polyfit, color=rtsirm_warm_color,
|
|
1202
|
+
linestyle='--', label='goethite fit')
|
|
1203
|
+
axs[0, 1].plot(rtsirm_warm_temps, rtsirm_warm_mags_corrected, color=rtsirm_warm_color,
|
|
1204
|
+
marker='s', linestyle='-', markersize=symbol_size, label='RTSIRM Warming (goethite removed)')
|
|
1205
|
+
axs[0, 1].plot(rtsirm_cool_temps, rtsirm_cool_mags_corrected, color=rtsirm_cool_color,
|
|
1206
|
+
marker='s', linestyle='-', markersize=symbol_size, label='RTSIRM Cooling (goethite removed)')
|
|
1207
|
+
|
|
1208
|
+
ax0 = axs[0, 0]
|
|
1209
|
+
rectangle = patches.Rectangle((t_min, ax0.get_ylim()[0]), t_max - t_min,
|
|
1210
|
+
ax0.get_ylim()[1] - ax0.get_ylim()[0],
|
|
1211
|
+
linewidth=0, edgecolor=None, facecolor='gray',
|
|
1212
|
+
alpha=0.3)
|
|
1213
|
+
ax0.add_patch(rectangle)
|
|
1214
|
+
rect_legend_patch = patches.Patch(color='gray', alpha=0.3, label='excluded from background fit')
|
|
1215
|
+
handles, labels = ax0.get_legend_handles_labels()
|
|
1216
|
+
handles.append(rect_legend_patch) # Add the rectangle legend patch
|
|
1217
|
+
|
|
1218
|
+
for ax in axs[0, :]:
|
|
1219
|
+
ax.set_xlabel("Temperature (K)")
|
|
1220
|
+
ax.set_ylabel("Magnetization (Am$^2$/kg)")
|
|
1221
|
+
ax.legend()
|
|
1222
|
+
ax.grid(True)
|
|
1223
|
+
ax.set_xlim(0, 300)
|
|
1224
|
+
|
|
1225
|
+
rtsirm_cool_derivative = thermomag_derivative(rtsirm_cool_data['meas_temp'],
|
|
1226
|
+
rtsirm_cool_data['magn_mass'], drop_first=True)
|
|
1227
|
+
rtsirm_warm_derivative = thermomag_derivative(rtsirm_warm_data['meas_temp'],
|
|
1228
|
+
rtsirm_warm_data['magn_mass'], drop_last=True)
|
|
1229
|
+
|
|
1230
|
+
rtsirm_cool_derivative_corrected = thermomag_derivative(rtsirm_cool_data['meas_temp'],
|
|
1231
|
+
rtsirm_cool_mags_corrected, drop_first=True)
|
|
1232
|
+
rtsirm_warm_derivative_corrected = thermomag_derivative(rtsirm_warm_data['meas_temp'],
|
|
1233
|
+
rtsirm_warm_mags_corrected, drop_last=True)
|
|
1234
|
+
|
|
1235
|
+
axs[1, 0].plot(rtsirm_cool_derivative['T'], rtsirm_cool_derivative['dM_dT'],
|
|
1236
|
+
marker='o', linestyle='-', color=rtsirm_cool_color, markersize=symbol_size, label='RTSIRM Cooling Derivative')
|
|
1237
|
+
axs[1, 0].plot(rtsirm_warm_derivative['T'], rtsirm_warm_derivative['dM_dT'],
|
|
1238
|
+
marker='o', linestyle='-', color=rtsirm_warm_color, markersize=symbol_size, label='RTSIRM Warming Derivative')
|
|
1239
|
+
axs[1, 1].plot(rtsirm_cool_derivative_corrected['T'], rtsirm_cool_derivative_corrected['dM_dT'],
|
|
1240
|
+
marker='s', linestyle='-', color=rtsirm_cool_color, markersize=symbol_size, label='RTSIRM Cooling Derivative\n(goethite removed)')
|
|
1241
|
+
axs[1, 1].plot(rtsirm_warm_derivative_corrected['T'], rtsirm_warm_derivative_corrected['dM_dT'],
|
|
1242
|
+
marker='s', linestyle='-', color=rtsirm_warm_color, markersize=symbol_size, label='RTSIRM Warming Derivative\n(goethite removed)')
|
|
1243
|
+
for ax in axs[1, :]:
|
|
1244
|
+
ax.set_xlabel("Temperature (K)")
|
|
1245
|
+
ax.set_ylabel("dM/dT")
|
|
1246
|
+
ax.legend()
|
|
1247
|
+
ax.grid(True)
|
|
1248
|
+
ax.set_xlim(0, 300)
|
|
1249
|
+
|
|
1250
|
+
fig.tight_layout()
|
|
1251
|
+
plt.show()
|
|
1252
|
+
|
|
1253
|
+
if return_data:
|
|
1254
|
+
rtsirm_warm_adjusted = pd.DataFrame({'meas_temp': rtsirm_warm_temps, 'corrected_magn_mass': rtsirm_warm_mags_corrected})
|
|
1255
|
+
rtsirm_cool_adjusted = pd.DataFrame({'meas_temp': rtsirm_cool_temps, 'corrected_magn_mass': rtsirm_cool_mags_corrected})
|
|
1256
|
+
return rtsirm_warm_adjusted, rtsirm_cool_adjusted
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
def interactive_goethite_removal(measurements, specimen_dropdown):
|
|
1260
|
+
|
|
1261
|
+
selected_specimen_name = specimen_dropdown.value
|
|
1262
|
+
|
|
1263
|
+
fc_data, zfc_data, rtsirm_cool_data, rtsirm_warm_data = extract_mpms_data_dc(measurements, selected_specimen_name)
|
|
1264
|
+
|
|
1265
|
+
# Determine a fixed width for the descriptions to align the sliders
|
|
1266
|
+
description_width = '250px' # Adjust this based on the longest description
|
|
1267
|
+
slider_total_width = '600px' # Total width of the slider widget including the description
|
|
1268
|
+
|
|
1269
|
+
description_style = {'description_width': description_width}
|
|
1270
|
+
slider_layout = widgets.Layout(width=slider_total_width) # Set the total width of the slider widget
|
|
1271
|
+
|
|
1272
|
+
# Update sliders to use IntRangeSlider
|
|
1273
|
+
temp_range_slider = widgets.IntRangeSlider(
|
|
1274
|
+
value=[150, 290], min=0, max=300, step=1,
|
|
1275
|
+
description='Geothite Fit Temperature Range (K):',
|
|
1276
|
+
layout=slider_layout, style=description_style
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
poly_deg_slider = widgets.IntSlider(
|
|
1280
|
+
value=2, min=1, max=3, step=1,
|
|
1281
|
+
description='Goethite Fit Polynomial Degree:',
|
|
1282
|
+
layout=slider_layout, style=description_style
|
|
1283
|
+
)
|
|
1284
|
+
|
|
1285
|
+
# Function to reset sliders to initial values
|
|
1286
|
+
def reset_sliders(b):
|
|
1287
|
+
temp_range_slider.value = (150, 290)
|
|
1288
|
+
poly_deg_slider.value = 2
|
|
1289
|
+
|
|
1290
|
+
# Create reset button
|
|
1291
|
+
reset_button = widgets.Button(description="Reset to Default Values", layout=widgets.Layout(width='200px'))
|
|
1292
|
+
reset_button.on_click(reset_sliders)
|
|
1293
|
+
|
|
1294
|
+
title_label = widgets.Label(value='Adjust Parameters for ' + selected_specimen_name + ' ' + 'goethite' + ' fit')
|
|
1295
|
+
|
|
1296
|
+
# Add the reset button to the UI
|
|
1297
|
+
ui = widgets.VBox([
|
|
1298
|
+
title_label,
|
|
1299
|
+
temp_range_slider,
|
|
1300
|
+
poly_deg_slider,
|
|
1301
|
+
reset_button
|
|
1302
|
+
])
|
|
1303
|
+
|
|
1304
|
+
out = widgets.interactive_output(
|
|
1305
|
+
lambda temp_range, poly_deg: goethite_removal(
|
|
1306
|
+
rtsirm_warm_data, rtsirm_cool_data,
|
|
1307
|
+
temp_range[0], temp_range[1],
|
|
1308
|
+
poly_deg
|
|
1309
|
+
), {
|
|
1310
|
+
'temp_range': temp_range_slider,
|
|
1311
|
+
'poly_deg': poly_deg_slider,
|
|
1312
|
+
}
|
|
1313
|
+
)
|
|
1314
|
+
|
|
1315
|
+
out.layout.height = '500px'
|
|
1316
|
+
|
|
1317
|
+
display(ui, out)
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
def plot_mpms_ac(
|
|
1321
|
+
experiment,
|
|
1322
|
+
frequency=None,
|
|
1323
|
+
phase='in',
|
|
1324
|
+
figsize=(6, 6),
|
|
1325
|
+
interactive=False,
|
|
1326
|
+
return_figure=False,
|
|
1327
|
+
show_plot=True):
|
|
1328
|
+
"""
|
|
1329
|
+
Plot AC susceptibility data from MPMS-X, optionally as interactive Bokeh.
|
|
1330
|
+
|
|
1331
|
+
Parameters
|
|
1332
|
+
----------
|
|
1333
|
+
experiment : pandas.DataFrame
|
|
1334
|
+
The experiment table from the MagIC contribution.
|
|
1335
|
+
frequency : float or None
|
|
1336
|
+
Frequency of AC measurement in Hz; None plots all frequencies.
|
|
1337
|
+
phase : {'in','out','both'}
|
|
1338
|
+
Which phase to plot.
|
|
1339
|
+
figsize : tuple of float
|
|
1340
|
+
Figure size for Matplotlib (width, height).
|
|
1341
|
+
interactive : bool
|
|
1342
|
+
If True, render with Bokeh for interactive exploration.
|
|
1343
|
+
return_figure : bool
|
|
1344
|
+
If True, return the figure object(s).
|
|
1345
|
+
show_plot : bool
|
|
1346
|
+
If True, display the plot.
|
|
1347
|
+
|
|
1348
|
+
Returns
|
|
1349
|
+
-------
|
|
1350
|
+
fig, ax or (fig, axes) or Bokeh layout or None
|
|
1351
|
+
"""
|
|
1352
|
+
if phase not in ['in', 'out', 'both']:
|
|
1353
|
+
raise ValueError('phase must be "in", "out", or "both"')
|
|
1354
|
+
freqs = ([frequency] if frequency is not None
|
|
1355
|
+
else experiment['meas_freq'].unique().tolist())
|
|
1356
|
+
if frequency is not None and frequency not in freqs:
|
|
1357
|
+
raise ValueError(f'frequency must be one of {freqs}')
|
|
1358
|
+
|
|
1359
|
+
if interactive:
|
|
1360
|
+
tools = [
|
|
1361
|
+
HoverTool(tooltips=[('T', '@x'), ('χ', '@y')]),
|
|
1362
|
+
'pan,box_zoom,wheel_zoom,reset,save']
|
|
1363
|
+
n = len(freqs)
|
|
1364
|
+
palette = Category10[n] if n <= 10 else Category10[10]
|
|
1365
|
+
figs = []
|
|
1366
|
+
|
|
1367
|
+
if phase in ['in', 'out']:
|
|
1368
|
+
p = figure(
|
|
1369
|
+
title=f'AC χ ({phase} phase)',
|
|
1370
|
+
x_axis_label='Temperature (K)',
|
|
1371
|
+
y_axis_label='χ (m³/kg)',
|
|
1372
|
+
tools=tools,
|
|
1373
|
+
width=int(figsize[0] * 100),
|
|
1374
|
+
height=int(figsize[1] * 100))
|
|
1375
|
+
p.xaxis.axis_label_text_font_style = "normal"
|
|
1376
|
+
p.yaxis.axis_label_text_font_style = "normal"
|
|
1377
|
+
for i, f in enumerate(freqs):
|
|
1378
|
+
d = experiment[experiment['meas_freq'] == f]
|
|
1379
|
+
col = 'susc_chi_mass' if phase == 'in' else 'susc_chi_qdr_mass'
|
|
1380
|
+
color = palette[i]
|
|
1381
|
+
p.line(
|
|
1382
|
+
d['meas_temp'], d[col],
|
|
1383
|
+
legend_label=f'{f} Hz',
|
|
1384
|
+
line_width=2,
|
|
1385
|
+
color=color)
|
|
1386
|
+
p.circle(
|
|
1387
|
+
d['meas_temp'], d[col],
|
|
1388
|
+
size=6,
|
|
1389
|
+
alpha=0.6,
|
|
1390
|
+
fill_color=color,
|
|
1391
|
+
line_color=color,
|
|
1392
|
+
legend_label=f'{f} Hz')
|
|
1393
|
+
p.legend.location = 'top_left'
|
|
1394
|
+
p.legend.click_policy = "hide"
|
|
1395
|
+
figs = [p]
|
|
1396
|
+
else:
|
|
1397
|
+
p1 = figure(
|
|
1398
|
+
title='AC χ in phase',
|
|
1399
|
+
x_axis_label='Temperature (K)',
|
|
1400
|
+
y_axis_label='χ (m³/kg)',
|
|
1401
|
+
tools=tools,
|
|
1402
|
+
width=int(figsize[0] * 50),
|
|
1403
|
+
height=int(figsize[1] * 100))
|
|
1404
|
+
p2 = figure(
|
|
1405
|
+
title='AC χ out phase',
|
|
1406
|
+
x_axis_label='Temperature (K)',
|
|
1407
|
+
y_axis_label='χ (m³/kg)',
|
|
1408
|
+
tools=tools,
|
|
1409
|
+
width=int(figsize[0] * 50),
|
|
1410
|
+
height=int(figsize[1] * 100))
|
|
1411
|
+
for p in (p1, p2):
|
|
1412
|
+
p.xaxis.axis_label_text_font_style = "normal"
|
|
1413
|
+
p.yaxis.axis_label_text_font_style = "normal"
|
|
1414
|
+
for i, f in enumerate(freqs):
|
|
1415
|
+
d = experiment[experiment['meas_freq'] == f]
|
|
1416
|
+
color = palette[i]
|
|
1417
|
+
p1.line(
|
|
1418
|
+
d['meas_temp'], d['susc_chi_mass'],
|
|
1419
|
+
legend_label=f'{f} Hz',
|
|
1420
|
+
line_width=2,
|
|
1421
|
+
color=color)
|
|
1422
|
+
p1.circle(
|
|
1423
|
+
d['meas_temp'], d['susc_chi_mass'],
|
|
1424
|
+
size=6,
|
|
1425
|
+
alpha=0.6,
|
|
1426
|
+
fill_color=color,
|
|
1427
|
+
line_color=color,
|
|
1428
|
+
legend_label=f'{f} Hz')
|
|
1429
|
+
p2.line(
|
|
1430
|
+
d['meas_temp'], d['susc_chi_qdr_mass'],
|
|
1431
|
+
legend_label=f'{f} Hz',
|
|
1432
|
+
line_width=2,
|
|
1433
|
+
color=color)
|
|
1434
|
+
p2.circle(
|
|
1435
|
+
d['meas_temp'], d['susc_chi_qdr_mass'],
|
|
1436
|
+
size=6,
|
|
1437
|
+
alpha=0.6,
|
|
1438
|
+
fill_color=color,
|
|
1439
|
+
line_color=color,
|
|
1440
|
+
legend_label=f'{f} Hz')
|
|
1441
|
+
p1.legend.location = p2.legend.location = 'top_left'
|
|
1442
|
+
p1.legend.click_policy = p2.legend.click_policy = "hide"
|
|
1443
|
+
figs = [p1, p2]
|
|
1444
|
+
|
|
1445
|
+
layout = gridplot([figs], sizing_mode='stretch_width')
|
|
1446
|
+
if show_plot:
|
|
1447
|
+
show(layout)
|
|
1448
|
+
if return_figure:
|
|
1449
|
+
return layout
|
|
1450
|
+
return None
|
|
1451
|
+
|
|
1452
|
+
# static Matplotlib
|
|
1453
|
+
if phase in ['in', 'out']:
|
|
1454
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
1455
|
+
col = 'susc_chi_mass' if phase == 'in' else 'susc_chi_qdr_mass'
|
|
1456
|
+
for f in freqs:
|
|
1457
|
+
d = experiment[experiment['meas_freq'] == f]
|
|
1458
|
+
ax.plot(d['meas_temp'], d[col], 'o-', label=f'{f} Hz')
|
|
1459
|
+
ax.set_xlabel('Temperature (K)')
|
|
1460
|
+
ax.set_ylabel('χ (m³/kg)')
|
|
1461
|
+
ax.set_title(f'AC χ ({phase} phase)')
|
|
1462
|
+
ax.legend()
|
|
1463
|
+
if show_plot:
|
|
1464
|
+
plt.show()
|
|
1465
|
+
if return_figure:
|
|
1466
|
+
return fig, ax
|
|
1467
|
+
return None
|
|
1468
|
+
|
|
1469
|
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
|
|
1470
|
+
for f in freqs:
|
|
1471
|
+
d = experiment[experiment['meas_freq'] == f]
|
|
1472
|
+
ax1.plot(d['meas_temp'], d['susc_chi_mass'], 'o-', label=f'{f} Hz')
|
|
1473
|
+
ax2.plot(d['meas_temp'], d['susc_chi_qdr_mass'], 'o-', label=f'{f} Hz')
|
|
1474
|
+
ax1.set_xlabel('Temperature (K)')
|
|
1475
|
+
ax1.set_ylabel('χ (m³/kg)')
|
|
1476
|
+
ax1.set_title('AC χ in phase')
|
|
1477
|
+
ax1.legend()
|
|
1478
|
+
ax2.set_xlabel('Temperature (K)')
|
|
1479
|
+
ax2.set_ylabel('χ (m³/kg)')
|
|
1480
|
+
ax2.set_title('AC χ out phase')
|
|
1481
|
+
ax2.legend()
|
|
1482
|
+
if show_plot:
|
|
1483
|
+
plt.show()
|
|
1484
|
+
if return_figure:
|
|
1485
|
+
return fig, (ax1, ax2)
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
# hysteresis functions
|
|
1489
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
1490
|
+
|
|
1491
|
+
def extract_hysteresis_data(df, specimen_name):
|
|
1492
|
+
"""
|
|
1493
|
+
Extracts and separates Hysteresis data
|
|
1494
|
+
for a specific specimen from a dataframe.
|
|
1495
|
+
|
|
1496
|
+
This function filters data for a given specimen and separates it based on
|
|
1497
|
+
different MagIC measurement method codes. It specifically looks for data
|
|
1498
|
+
corresponding to 'LP-HYS' Hysteresis Data
|
|
1499
|
+
|
|
1500
|
+
Parameters:
|
|
1501
|
+
df (pandas.DataFrame): The dataframe containing MPMS measurement data.
|
|
1502
|
+
specimen_name (str): The name of the specimen to filter data for.
|
|
1503
|
+
|
|
1504
|
+
Returns:
|
|
1505
|
+
tuple: A tuple containing four pandas.DataFrames:
|
|
1506
|
+
- fc_data: Data filtered for 'LP-FC' method if available, otherwise an empty DataFrame.
|
|
1507
|
+
- zfc_data: Data filtered for 'LP-ZFC' method if available, otherwise an empty DataFrame.
|
|
1508
|
+
- rtsirm_cool_data: Data filtered for 'LP-CW-SIRM:LP-MC' method if available, otherwise an empty DataFrame.
|
|
1509
|
+
- rtsirm_warm_data: Data filtered for 'LP-CW-SIRM:LP-MW' method if available, otherwise an empty DataFrame.
|
|
1510
|
+
|
|
1511
|
+
Example:
|
|
1512
|
+
>>> fc, zfc, rtsirm_cool, rtsirm_warm = extract_mpms_data_dc(measurements_df, 'Specimen_1')
|
|
1513
|
+
"""
|
|
1514
|
+
|
|
1515
|
+
specimen_df = df[df['specimen'] == specimen_name]
|
|
1516
|
+
|
|
1517
|
+
hyst_data = specimen_df[specimen_df['method_codes'].str.contains('LP-HYS', na=False)]
|
|
1518
|
+
|
|
1519
|
+
return hyst_data
|
|
1520
|
+
|
|
1521
|
+
def plot_hysteresis_loop(field, magnetization, specimen_name, p=None, line_color='grey', line_width=1, label='', legend_location='bottom_right'):
|
|
1522
|
+
'''
|
|
1523
|
+
function to plot a hysteresis loop
|
|
1524
|
+
|
|
1525
|
+
Parameters
|
|
1526
|
+
----------
|
|
1527
|
+
field : numpy array or list
|
|
1528
|
+
hysteresis loop field values
|
|
1529
|
+
magnetization : numpy array or list
|
|
1530
|
+
hysteresis loop magnetization values
|
|
1531
|
+
|
|
1532
|
+
Returns
|
|
1533
|
+
-------
|
|
1534
|
+
p : bokeh.plotting.figure
|
|
1535
|
+
'''
|
|
1536
|
+
if not _HAS_BOKEH:
|
|
1537
|
+
print("Bokeh is not installed. Please install it to enable hysteresis data processing.")
|
|
1538
|
+
return
|
|
1539
|
+
|
|
1540
|
+
assert len(field) == len(magnetization), 'Field and magnetization arrays must be the same length'
|
|
1541
|
+
if p is None:
|
|
1542
|
+
p = figure(title=f'{specimen_name} hysteresis loop',
|
|
1543
|
+
x_axis_label='Field (T)',
|
|
1544
|
+
y_axis_label='Magnetization (Am\u00B2/kg)',
|
|
1545
|
+
width=600,
|
|
1546
|
+
height=600, aspect_ratio=1)
|
|
1547
|
+
p.axis.axis_label_text_font_size = '12pt'
|
|
1548
|
+
p.axis.axis_label_text_font_style = 'normal'
|
|
1549
|
+
p.title.text_font_size = '14pt'
|
|
1550
|
+
p.title.text_font_style = 'bold'
|
|
1551
|
+
p.title.align = 'center'
|
|
1552
|
+
p.line(field, magnetization, line_width=line_width, color=line_color, legend_label=label)
|
|
1553
|
+
p.legend.click_policy="hide"
|
|
1554
|
+
p.legend.location = legend_location
|
|
1555
|
+
else:
|
|
1556
|
+
p.line(field, magnetization, line_width=line_width, color=line_color, legend_label=label)
|
|
1557
|
+
p.legend.location = legend_location
|
|
1558
|
+
|
|
1559
|
+
return p
|
|
1560
|
+
|
|
1561
|
+
def split_hysteresis_loop(field, magnetization):
|
|
1562
|
+
'''
|
|
1563
|
+
function to split a hysteresis loop into upper and lower branches
|
|
1564
|
+
by the change of sign in the applied field gradient
|
|
1565
|
+
|
|
1566
|
+
Parameters
|
|
1567
|
+
----------
|
|
1568
|
+
field : numpy array or list
|
|
1569
|
+
hysteresis loop field values
|
|
1570
|
+
magnetization : numpy array or list
|
|
1571
|
+
hysteresis loop magnetization values
|
|
1572
|
+
|
|
1573
|
+
Returns
|
|
1574
|
+
-------
|
|
1575
|
+
upper_branch : tuple
|
|
1576
|
+
tuple of field and magnetization values for the upper branch
|
|
1577
|
+
lower_branch : tuple
|
|
1578
|
+
tuple of field and magnetization values for the lower branch
|
|
1579
|
+
'''
|
|
1580
|
+
assert len(field) == len(magnetization), 'Field and magnetization arrays must be the same length'
|
|
1581
|
+
|
|
1582
|
+
# identify loop turning point by change in sign of the field difference
|
|
1583
|
+
# split the data into upper and lower branches
|
|
1584
|
+
field_gradient = np.gradient(field)
|
|
1585
|
+
# There should just be one turning point in the field gradient
|
|
1586
|
+
turning_points = np.where(np.diff(np.sign(field_gradient)))[0]
|
|
1587
|
+
if len(turning_points) > 1:
|
|
1588
|
+
raise ValueError('More than one turning point found in the gradient of the applied field')
|
|
1589
|
+
turning_point = turning_points[0]
|
|
1590
|
+
upper_branch = [field[:turning_point+1], magnetization[:turning_point+1]]
|
|
1591
|
+
# sort the upper branch in reverse order
|
|
1592
|
+
upper_branch = [field[:turning_point+1][::-1], magnetization[:turning_point+1][::-1]]
|
|
1593
|
+
lower_branch = [field[turning_point+1:], magnetization[turning_point+1:]]
|
|
1594
|
+
|
|
1595
|
+
return upper_branch, lower_branch
|
|
1596
|
+
|
|
1597
|
+
def grid_hysteresis_loop(field, magnetization):
|
|
1598
|
+
'''
|
|
1599
|
+
function to grid a hysteresis loop into a regular grid
|
|
1600
|
+
with grid intervals equal to the average field step size calculated from the data
|
|
1601
|
+
|
|
1602
|
+
Parameters
|
|
1603
|
+
----------
|
|
1604
|
+
field : numpy array or list
|
|
1605
|
+
hysteresis loop field values
|
|
1606
|
+
magnetization : numpy array or list
|
|
1607
|
+
hysteresis loop magnetization values
|
|
1608
|
+
|
|
1609
|
+
Returns
|
|
1610
|
+
-------
|
|
1611
|
+
grid_field : numpy array
|
|
1612
|
+
gridded field values
|
|
1613
|
+
grid_magnetization : numpy array
|
|
1614
|
+
gridded magnetization values
|
|
1615
|
+
'''
|
|
1616
|
+
assert len(field) == len(magnetization), 'Field and magnetization arrays must be the same length'
|
|
1617
|
+
|
|
1618
|
+
upper_branch, lower_branch = split_hysteresis_loop(field, magnetization)
|
|
1619
|
+
|
|
1620
|
+
# calculate the average field step size
|
|
1621
|
+
field_step = np.mean(np.abs(np.diff(upper_branch[0])))
|
|
1622
|
+
# grid the hysteresis loop
|
|
1623
|
+
upper_field = np.arange(np.max(field), 0, -field_step)
|
|
1624
|
+
upper_field = np.concatenate([upper_field, -upper_field[::-1]])
|
|
1625
|
+
lower_field = upper_field[::-1]
|
|
1626
|
+
grid_field = np.concatenate([upper_field, lower_field])
|
|
1627
|
+
|
|
1628
|
+
upper_branch_itp = np.interp(upper_field, upper_branch[0], upper_branch[1])
|
|
1629
|
+
lower_branch_itp = np.interp(lower_field, lower_branch[0], lower_branch[1])
|
|
1630
|
+
grid_magnetization = np.concatenate([upper_branch_itp, lower_branch_itp])
|
|
1631
|
+
|
|
1632
|
+
return grid_field, grid_magnetization
|
|
1633
|
+
|
|
1634
|
+
def ANOVA(xs, ys):
|
|
1635
|
+
'''
|
|
1636
|
+
ANOVA statistics for linear regression
|
|
1637
|
+
|
|
1638
|
+
Parameters
|
|
1639
|
+
----------
|
|
1640
|
+
xs : numpy array
|
|
1641
|
+
x values
|
|
1642
|
+
ys : numpy array
|
|
1643
|
+
y values
|
|
1644
|
+
|
|
1645
|
+
Returns
|
|
1646
|
+
-------
|
|
1647
|
+
results : dict
|
|
1648
|
+
dictionary of the results of the ANOVA calculation
|
|
1649
|
+
and intermediate statistics for the ANOVA calculation
|
|
1650
|
+
|
|
1651
|
+
'''
|
|
1652
|
+
|
|
1653
|
+
xs = np.array(xs)
|
|
1654
|
+
ys = np.array(ys)
|
|
1655
|
+
|
|
1656
|
+
ys_mean = np.mean(ys)
|
|
1657
|
+
|
|
1658
|
+
# fit the gridded data by a straight line
|
|
1659
|
+
slope, intercept = np.polyfit(xs, ys, 1)
|
|
1660
|
+
|
|
1661
|
+
# AVOVA calculation
|
|
1662
|
+
# total sum of squares for the dependent variable (magnetization)
|
|
1663
|
+
SST = np.sum((ys - ys_mean)**2)
|
|
1664
|
+
|
|
1665
|
+
# sum of squares due to regression
|
|
1666
|
+
SSR = np.sum((slope * xs + intercept - ys_mean)**2)
|
|
1667
|
+
|
|
1668
|
+
# the remaining unexplained variation (noise and lack of fit)
|
|
1669
|
+
SSD = np.sum((ys - (slope * xs + intercept)) ** 2)
|
|
1670
|
+
R_squared = SSR/SST
|
|
1671
|
+
|
|
1672
|
+
results = {'slope':slope,
|
|
1673
|
+
'intercept':intercept,
|
|
1674
|
+
'SST':SST,
|
|
1675
|
+
'SSR':SSR,
|
|
1676
|
+
'SSD':SSD,
|
|
1677
|
+
'R_squared': R_squared}
|
|
1678
|
+
|
|
1679
|
+
return results
|
|
1680
|
+
|
|
1681
|
+
def hyst_linearity_test(grid_field, grid_magnetization):
|
|
1682
|
+
'''
|
|
1683
|
+
function for testing the linearity of a hysteresis loop
|
|
1684
|
+
|
|
1685
|
+
Parameters
|
|
1686
|
+
----------
|
|
1687
|
+
grid_field : numpy array
|
|
1688
|
+
gridded field values
|
|
1689
|
+
grid_magnetization : numpy array
|
|
1690
|
+
gridded magnetization values
|
|
1691
|
+
|
|
1692
|
+
Returns
|
|
1693
|
+
-------
|
|
1694
|
+
results : dict
|
|
1695
|
+
dictionary of the results of the linearity test
|
|
1696
|
+
and intermediate statistics for the ANOVA calculation
|
|
1697
|
+
'''
|
|
1698
|
+
grid_field = np.array(grid_field)
|
|
1699
|
+
grid_magnetization = np.array(grid_magnetization)
|
|
1700
|
+
|
|
1701
|
+
upper_branch, lower_branch = split_hysteresis_loop(grid_field, grid_magnetization)
|
|
1702
|
+
|
|
1703
|
+
anova_results = ANOVA(grid_field, grid_magnetization)
|
|
1704
|
+
|
|
1705
|
+
# fit the gridded data by a straight line
|
|
1706
|
+
slope, intercept = anova_results['slope'], anova_results['intercept']
|
|
1707
|
+
|
|
1708
|
+
# AVOVA calculation
|
|
1709
|
+
# total sum of squares for the dependent variable (magnetization)
|
|
1710
|
+
SST = anova_results['SST']
|
|
1711
|
+
|
|
1712
|
+
# sum of squares due to regression
|
|
1713
|
+
SSR = anova_results['SSR']
|
|
1714
|
+
|
|
1715
|
+
# the remaining unexplained variation (noise and lack of fit)
|
|
1716
|
+
SSD = anova_results['SSD']
|
|
1717
|
+
|
|
1718
|
+
R_squared = anova_results['R_squared']
|
|
1719
|
+
|
|
1720
|
+
# invert the lower branch to match the upper branch
|
|
1721
|
+
# and calculate the differences between the upper and the inverted lower branch
|
|
1722
|
+
# for any loop shifts and drift that are due to noise alone
|
|
1723
|
+
SSPE = np.sum((upper_branch[1] - (-lower_branch[1][::-1])) ** 2) / 2
|
|
1724
|
+
|
|
1725
|
+
# calculate the lack of fit statistic
|
|
1726
|
+
SSLF = SSD - SSPE
|
|
1727
|
+
|
|
1728
|
+
# mean square pure error
|
|
1729
|
+
MSPE = SSPE / (len(grid_field) / 2)
|
|
1730
|
+
|
|
1731
|
+
# mean square error due to lack of fit
|
|
1732
|
+
MSLF = SSLF / (len(grid_field)/2 - 2)
|
|
1733
|
+
|
|
1734
|
+
# mean squares due to regression
|
|
1735
|
+
MSR = SSR
|
|
1736
|
+
|
|
1737
|
+
# mean squares due to noise
|
|
1738
|
+
MSD = SSD / (len(grid_field) - 2)
|
|
1739
|
+
|
|
1740
|
+
# F-ratio for the linear component
|
|
1741
|
+
FL = MSR / MSD
|
|
1742
|
+
|
|
1743
|
+
# F-ratio for the non-linear component
|
|
1744
|
+
FNL = MSLF / MSPE
|
|
1745
|
+
|
|
1746
|
+
results = {
|
|
1747
|
+
'SST': float(SST),
|
|
1748
|
+
'SSR': float(SSR),
|
|
1749
|
+
'SSD': float(SSD),
|
|
1750
|
+
'R_squared': float(R_squared),
|
|
1751
|
+
'SSPE': float(SSPE),
|
|
1752
|
+
'SSLF': float(SSLF),
|
|
1753
|
+
'MSPE': float(MSPE),
|
|
1754
|
+
'MSLF': float(MSLF),
|
|
1755
|
+
'MSR': float(MSR),
|
|
1756
|
+
'MSD': float(MSD),
|
|
1757
|
+
'FL': float(FL),
|
|
1758
|
+
'FNL': float(FNL),
|
|
1759
|
+
'slope': float(slope),
|
|
1760
|
+
'intercept': float(intercept),
|
|
1761
|
+
'loop_is_linear': bool(FNL < 1.25),
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
return results
|
|
1765
|
+
|
|
1766
|
+
def linefit(xarr, yarr):
|
|
1767
|
+
"""
|
|
1768
|
+
Linear regression fit: y = intercept + slope * x
|
|
1769
|
+
Returns: intercept, slope, R^2
|
|
1770
|
+
"""
|
|
1771
|
+
xarr = np.asarray(xarr)
|
|
1772
|
+
yarr = np.asarray(yarr)
|
|
1773
|
+
|
|
1774
|
+
# Fit a line y = slope * x + intercept
|
|
1775
|
+
slope, intercept = np.polyfit(xarr, yarr, 1)
|
|
1776
|
+
|
|
1777
|
+
# Predict y using the fitted line
|
|
1778
|
+
y_pred = intercept + slope * xarr
|
|
1779
|
+
|
|
1780
|
+
# Total sum of squares
|
|
1781
|
+
ss_tot = np.sum((yarr - np.mean(yarr))**2)
|
|
1782
|
+
|
|
1783
|
+
# Residual sum of squares
|
|
1784
|
+
ss_res = np.sum((y_pred - np.mean(yarr))**2)
|
|
1785
|
+
|
|
1786
|
+
# R^2 score
|
|
1787
|
+
r2 = 1 - ss_res / ss_tot if ss_tot > 0 else 1
|
|
1788
|
+
|
|
1789
|
+
return intercept, slope, r2
|
|
1790
|
+
|
|
1791
|
+
def loop_H_off(loop_fields, loop_moments, H_shift):
|
|
1792
|
+
"""
|
|
1793
|
+
Estimates a vertical shift (V_shift) and returns R² of a reflected loop.
|
|
1794
|
+
|
|
1795
|
+
Arguments:
|
|
1796
|
+
- loop_fields: List or array of magnetic field values.
|
|
1797
|
+
- loop_moments: Corresponding list or array of magnetic moments.
|
|
1798
|
+
- H_shift: Horizontal shift to apply to loop_fields.
|
|
1799
|
+
|
|
1800
|
+
Returns:
|
|
1801
|
+
- r2: R-squared value from linear regression between original and reflected data.
|
|
1802
|
+
- V_shift: Estimated vertical shift (mean of linear fit x-intercept).
|
|
1803
|
+
"""
|
|
1804
|
+
|
|
1805
|
+
n = len(loop_fields)
|
|
1806
|
+
|
|
1807
|
+
# Apply horizontal shift
|
|
1808
|
+
loop_fields = loop_fields - H_shift
|
|
1809
|
+
|
|
1810
|
+
# Define bounds for symmetrical comparison
|
|
1811
|
+
min2 = np.min(loop_fields)
|
|
1812
|
+
max2 = np.max(loop_fields)
|
|
1813
|
+
min1 = -max2
|
|
1814
|
+
max1 = -min2
|
|
1815
|
+
|
|
1816
|
+
n1 = n // 2
|
|
1817
|
+
i2 = 0 # Python uses 0-based indexing
|
|
1818
|
+
x1 = []
|
|
1819
|
+
y1 = []
|
|
1820
|
+
|
|
1821
|
+
for i in range(n1, n):
|
|
1822
|
+
x = loop_fields[i]
|
|
1823
|
+
if min1 < x < max1:
|
|
1824
|
+
while -loop_fields[i2] < x and i2 < n - 1:
|
|
1825
|
+
i2 += 1
|
|
1826
|
+
if i2 > 0:
|
|
1827
|
+
dx = (-loop_fields[i2] - x) / (-loop_fields[i2] + loop_fields[i2 - 1])
|
|
1828
|
+
dy = dx * (-loop_moments[i2] + loop_moments[i2 - 1])
|
|
1829
|
+
y = -loop_moments[i2] - dy
|
|
1830
|
+
x1.append(loop_moments[i])
|
|
1831
|
+
y1.append(-y)
|
|
1832
|
+
|
|
1833
|
+
if len(x1) < 2:
|
|
1834
|
+
return 0.0, 0.0 # Not enough points for regression
|
|
1835
|
+
|
|
1836
|
+
intercept, slope, r2 = linefit(x1, y1)
|
|
1837
|
+
M_shift = intercept / 2
|
|
1838
|
+
|
|
1839
|
+
result = {'slope': slope, 'M_shift': M_shift, 'r2': r2}
|
|
1840
|
+
return result
|
|
1841
|
+
|
|
1842
|
+
def loop_Hshift_brent(loop_fields, loop_moments):
|
|
1843
|
+
def objective(H_shift):
|
|
1844
|
+
result = loop_H_off(loop_fields, loop_moments, H_shift)
|
|
1845
|
+
return result['r2']
|
|
1846
|
+
|
|
1847
|
+
ax = -np.max(loop_fields)/2
|
|
1848
|
+
bx = 0
|
|
1849
|
+
cx = -ax
|
|
1850
|
+
result = minimize_scalar(objective, method='brent', bracket=(ax, bx, cx), tol=1e-6)
|
|
1851
|
+
|
|
1852
|
+
opt_H_off = result.x
|
|
1853
|
+
opt_shift = loop_H_off(loop_fields, loop_moments, opt_H_off)
|
|
1854
|
+
opt_r2 = opt_shift['r2']
|
|
1855
|
+
opt_M_off = opt_shift['M_shift']
|
|
1856
|
+
|
|
1857
|
+
return opt_r2, opt_H_off, opt_M_off
|
|
1858
|
+
|
|
1859
|
+
def calc_Q(H, M, type='Q'):
|
|
1860
|
+
'''
|
|
1861
|
+
function for calculating quality factor Q for a hysteresis loop
|
|
1862
|
+
Q factor is defined by the log10 of the signal to noise ratio
|
|
1863
|
+
where signal is the sum of the square of the data
|
|
1864
|
+
which is the averaged sum over the upper and lower branches for Q
|
|
1865
|
+
and is the sum of the square of the upper branch for Qf
|
|
1866
|
+
'''
|
|
1867
|
+
assert type in ['Q', 'Qf'], 'type must be either Q or Qf'
|
|
1868
|
+
H = np.array(H)
|
|
1869
|
+
M = np.array(M)
|
|
1870
|
+
upper_branch, lower_branch = split_hysteresis_loop(H, M)
|
|
1871
|
+
Me = upper_branch[1] + lower_branch[1][::-1]
|
|
1872
|
+
if type == 'Q':
|
|
1873
|
+
M_sn = np.sqrt((np.sum(upper_branch[1]**2) + np.sum(lower_branch[1][::-1]**2))/2/np.sum(Me**2))
|
|
1874
|
+
elif type == 'Qf':
|
|
1875
|
+
M_sn = np.sqrt(np.sum(upper_branch[1]**2)/np.sum(Me**2))
|
|
1876
|
+
|
|
1877
|
+
Q = np.log10(M_sn)
|
|
1878
|
+
return M_sn, Q
|
|
1879
|
+
|
|
1880
|
+
def hyst_loop_centering(grid_field, grid_magnetization):
|
|
1881
|
+
'''
|
|
1882
|
+
function for finding the optimum applied field offset value for minimizing a linear fit through
|
|
1883
|
+
the Me based on the R2 value. The idea is maximizing the residual noise in the Me gives the best centered loop.
|
|
1884
|
+
|
|
1885
|
+
Parameters
|
|
1886
|
+
----------
|
|
1887
|
+
grid_field : numpy array
|
|
1888
|
+
gridded field values
|
|
1889
|
+
grid_magnetization : numpy array
|
|
1890
|
+
gridded magnetization values
|
|
1891
|
+
|
|
1892
|
+
Returns
|
|
1893
|
+
-------
|
|
1894
|
+
opt_H_offset : float
|
|
1895
|
+
optimized applied field offset value for the loop
|
|
1896
|
+
opt_M_offset : float
|
|
1897
|
+
calculated magnetization offset value for the loop based on the optimized applied field offset
|
|
1898
|
+
(intercept of the fitted line using the upper branch and the inverted and optimally offsetted lower branch)
|
|
1899
|
+
R_squared : float
|
|
1900
|
+
R-squared value of the linear fit between the upper branch and the inverted and offsetted lower branch
|
|
1901
|
+
|
|
1902
|
+
'''
|
|
1903
|
+
grid_field = np.array(grid_field)
|
|
1904
|
+
grid_magnetization = np.array(grid_magnetization)
|
|
1905
|
+
R_squared, H_offset, M_offset = loop_Hshift_brent(grid_field, grid_magnetization)
|
|
1906
|
+
|
|
1907
|
+
M_sn, Q = calc_Q(grid_field, grid_magnetization)
|
|
1908
|
+
|
|
1909
|
+
# re-gridding after offset correction to ensure symmetry
|
|
1910
|
+
centered_H, centered_M = grid_hysteresis_loop(grid_field-H_offset/2, grid_magnetization-M_offset)
|
|
1911
|
+
|
|
1912
|
+
results = {'centered_H':centered_H,
|
|
1913
|
+
'centered_M': centered_M,
|
|
1914
|
+
'opt_H_offset':float(H_offset/2),
|
|
1915
|
+
'opt_M_offset':float(M_offset),
|
|
1916
|
+
'R_squared':float(R_squared),
|
|
1917
|
+
'M_sn':float(M_sn),
|
|
1918
|
+
'Q':float(Q),
|
|
1919
|
+
}
|
|
1920
|
+
return results
|
|
1921
|
+
|
|
1922
|
+
def linear_HF_fit(field, magnetization, HF_cutoff=0.8):
|
|
1923
|
+
'''
|
|
1924
|
+
function to fit a linear function to the high field portion of a hysteresis loop
|
|
1925
|
+
|
|
1926
|
+
Parameters
|
|
1927
|
+
----------
|
|
1928
|
+
field : numpy array or list
|
|
1929
|
+
raw hysteresis loop field values
|
|
1930
|
+
magnetization : numpy array or list
|
|
1931
|
+
raw hysteresis loop magnetization values
|
|
1932
|
+
|
|
1933
|
+
Returns
|
|
1934
|
+
-------
|
|
1935
|
+
slope : float
|
|
1936
|
+
slope of the linear fit
|
|
1937
|
+
can be interpreted to be the paramagnetic/diamagnetic susceptibility
|
|
1938
|
+
intercept : float
|
|
1939
|
+
y-intercept of the linear fit
|
|
1940
|
+
can be interpreted to be the saturation magnetization of the ferromagnetic component
|
|
1941
|
+
'''
|
|
1942
|
+
assert len(field) == len(magnetization), 'Field and magnetization arrays must be the same length'
|
|
1943
|
+
assert HF_cutoff > 0 and HF_cutoff < 1, 'Portion must be between 0 and 1'
|
|
1944
|
+
|
|
1945
|
+
# adopting IRM's max field cutoff at 97% of the max field
|
|
1946
|
+
max_field_cutoff = 0.97
|
|
1947
|
+
|
|
1948
|
+
field = np.array(field)
|
|
1949
|
+
magnetization = np.array(magnetization)
|
|
1950
|
+
|
|
1951
|
+
# filter for the high field portion of each branch
|
|
1952
|
+
|
|
1953
|
+
high_field_index = np.where((np.abs(field) >= HF_cutoff*np.max(np.abs(field))) & (np.abs(field) <= max_field_cutoff*np.max(np.abs(field))))[0]
|
|
1954
|
+
|
|
1955
|
+
# invert points in the third quadrant to the first
|
|
1956
|
+
high_field = np.abs(field[high_field_index])
|
|
1957
|
+
high_field_magnetization = np.abs(magnetization[high_field_index])
|
|
1958
|
+
|
|
1959
|
+
# the slope would be the paramagnetic/diamagnetic susceptibility
|
|
1960
|
+
# the y-intercept would be the Ms value (saturation magnetization of the ferromagnetic component)
|
|
1961
|
+
slope, intercept = np.polyfit(high_field, high_field_magnetization, 1)
|
|
1962
|
+
chi_HF = slope * (4*np.pi/1e7)
|
|
1963
|
+
return chi_HF, intercept
|
|
1964
|
+
|
|
1965
|
+
def hyst_slope_correction(grid_field, grid_magnetization, chi_HF):
|
|
1966
|
+
'''
|
|
1967
|
+
function for subtracting the paramagnetic/diamagnetic slope from a hysteresis loop
|
|
1968
|
+
the input should be gridded field and magnetization values
|
|
1969
|
+
|
|
1970
|
+
Parameters
|
|
1971
|
+
----------
|
|
1972
|
+
grid_field : numpy array
|
|
1973
|
+
gridded field values
|
|
1974
|
+
grid_magnetization : numpy array
|
|
1975
|
+
gridded magnetization values
|
|
1976
|
+
chi_HF : float
|
|
1977
|
+
X_HF
|
|
1978
|
+
|
|
1979
|
+
Returns
|
|
1980
|
+
-------
|
|
1981
|
+
grid_magnetization_ferro: numpy array
|
|
1982
|
+
corrected ferromagnetic component of the magnetization
|
|
1983
|
+
'''
|
|
1984
|
+
slope = chi_HF / (4*np.pi/1e7)
|
|
1985
|
+
assert len(grid_field) == len(grid_magnetization), 'Field and magnetization arrays must be the same length'
|
|
1986
|
+
|
|
1987
|
+
grid_field = np.array(grid_field)
|
|
1988
|
+
grid_magnetization = np.array(grid_magnetization)
|
|
1989
|
+
|
|
1990
|
+
grid_magnetization_ferro = grid_magnetization - slope*grid_field
|
|
1991
|
+
|
|
1992
|
+
return grid_magnetization_ferro
|
|
1993
|
+
|
|
1994
|
+
def find_y_crossing(x, y, y_target=0.0):
|
|
1995
|
+
"""
|
|
1996
|
+
Finds the x-value where y crosses a given y_target, nearest to x = 0.
|
|
1997
|
+
Uses linear interpolation between adjacent points that bracket y_target.
|
|
1998
|
+
|
|
1999
|
+
Parameters:
|
|
2000
|
+
x (array-like): x-values
|
|
2001
|
+
y (array-like): y-values
|
|
2002
|
+
y_target (float): y-value at which to find crossing (default: 0)
|
|
2003
|
+
|
|
2004
|
+
Returns:
|
|
2005
|
+
x_cross (float or None): interpolated x at y = y_target nearest to x = 0, or None if not found
|
|
2006
|
+
"""
|
|
2007
|
+
x = np.asarray(x)
|
|
2008
|
+
y = np.asarray(y)
|
|
2009
|
+
|
|
2010
|
+
for i in range(len(x) - 1):
|
|
2011
|
+
y0, y1 = y[i], y[i + 1]
|
|
2012
|
+
if (y0 - y_target) * (y1 - y_target) < 0: # sign change => crossing
|
|
2013
|
+
x0, x1 = x[i], x[i + 1]
|
|
2014
|
+
# Linear interpolation to find x at y = y_target
|
|
2015
|
+
x_cross = x0 + (y_target - y0) * (x1 - x0) / (y1 - y0)
|
|
2016
|
+
return x_cross
|
|
2017
|
+
|
|
2018
|
+
return None
|
|
2019
|
+
|
|
2020
|
+
def calc_Mr_Mrh_Mih_Brh(grid_field, grid_magnetization):
|
|
2021
|
+
'''
|
|
2022
|
+
function to calculate the Mrh and Mih values from a hysteresis loop
|
|
2023
|
+
|
|
2024
|
+
Parameters
|
|
2025
|
+
----------
|
|
2026
|
+
grid_field : numpy array
|
|
2027
|
+
gridded field values
|
|
2028
|
+
grid_magnetization : numpy array
|
|
2029
|
+
gridded magnetization values
|
|
2030
|
+
|
|
2031
|
+
Returns
|
|
2032
|
+
-------
|
|
2033
|
+
H : numpy array
|
|
2034
|
+
field values of the upper branch (the two branches should have the same field values)
|
|
2035
|
+
Mrh : float
|
|
2036
|
+
remanent magnetization value
|
|
2037
|
+
Mih : float
|
|
2038
|
+
induced magnetization value
|
|
2039
|
+
Me : numpy array
|
|
2040
|
+
error on M(H), calculated as the subtraction of the inverted lower branch from the upper branch
|
|
2041
|
+
Brh : float
|
|
2042
|
+
median field of Mrh
|
|
2043
|
+
|
|
2044
|
+
'''
|
|
2045
|
+
# calculate Mrh bu subtracting the upper and lower branches of a hysterisis loop
|
|
2046
|
+
grid_field = np.array(grid_field)
|
|
2047
|
+
grid_magnetization = np.array(grid_magnetization)
|
|
2048
|
+
|
|
2049
|
+
grid_field = grid_field
|
|
2050
|
+
grid_magnetization = grid_magnetization
|
|
2051
|
+
|
|
2052
|
+
upper_branch, lower_branch = split_hysteresis_loop(grid_field, grid_magnetization)
|
|
2053
|
+
|
|
2054
|
+
Mrh = (upper_branch[1] - lower_branch[1])/2
|
|
2055
|
+
Mih = (upper_branch[1] + lower_branch[1])/2
|
|
2056
|
+
Me = upper_branch[1] + lower_branch[1][::-1]
|
|
2057
|
+
|
|
2058
|
+
H = upper_branch[0]
|
|
2059
|
+
Mr = np.interp(0, H, Mrh)
|
|
2060
|
+
|
|
2061
|
+
# Brh is the field corresponding to the m=Mr/2
|
|
2062
|
+
pos_H = H[np.where(H > 0)]
|
|
2063
|
+
pos_Mrh = Mrh[np.where(H > 0)]
|
|
2064
|
+
neg_H = H[np.where(H < 0)]
|
|
2065
|
+
neg_Mrh = Mrh[np.where(H < 0)]
|
|
2066
|
+
Brh_pos = find_y_crossing(pos_H, pos_Mrh, Mr/2)
|
|
2067
|
+
Brh_neg = find_y_crossing(neg_H, neg_Mrh, Mr/2)
|
|
2068
|
+
Brh = np.abs((Brh_pos - Brh_neg)/2)
|
|
2069
|
+
|
|
2070
|
+
return H, Mr, Mrh, Mih, Me, Brh
|
|
2071
|
+
|
|
2072
|
+
def calc_Bc(H, M):
|
|
2073
|
+
'''
|
|
2074
|
+
function for calculating the coercivity of the ferromagnetic component of a hysteresis loop
|
|
2075
|
+
the final Bc value is calculated as the average of the positive and negative Bc values
|
|
2076
|
+
|
|
2077
|
+
Parameters
|
|
2078
|
+
----------
|
|
2079
|
+
H : numpy array
|
|
2080
|
+
field values
|
|
2081
|
+
M : numpy array
|
|
2082
|
+
magnetization values
|
|
2083
|
+
|
|
2084
|
+
Returns
|
|
2085
|
+
-------
|
|
2086
|
+
Bc : float
|
|
2087
|
+
coercivity of the ferromagnetic component of the hysteresis loop
|
|
2088
|
+
'''
|
|
2089
|
+
upper_branch, lower_branch = split_hysteresis_loop(H, M)
|
|
2090
|
+
|
|
2091
|
+
upper_Bc = find_y_crossing(upper_branch[0], upper_branch[1])
|
|
2092
|
+
lower_Bc = find_y_crossing(lower_branch[0], lower_branch[1])
|
|
2093
|
+
Bc = np.abs((upper_Bc - lower_Bc) / 2)
|
|
2094
|
+
|
|
2095
|
+
return Bc
|
|
2096
|
+
|
|
2097
|
+
def loop_saturation_stats(field, magnetization, HF_cutoff=0.8, max_field_cutoff=0.97):
|
|
2098
|
+
'''
|
|
2099
|
+
ANOVA statistics for the high field portion of a hysteresis loop
|
|
2100
|
+
|
|
2101
|
+
Parameters
|
|
2102
|
+
----------
|
|
2103
|
+
field : numpy array
|
|
2104
|
+
field values
|
|
2105
|
+
magnetization : numpy array
|
|
2106
|
+
magnetization values
|
|
2107
|
+
HF_cutoff : float
|
|
2108
|
+
high field cutoff value
|
|
2109
|
+
default is 0.8
|
|
2110
|
+
|
|
2111
|
+
Returns
|
|
2112
|
+
-------
|
|
2113
|
+
results : dict
|
|
2114
|
+
dictionary of the results of the ANOVA calculation
|
|
2115
|
+
and intermediate statistics for the ANOVA calculation
|
|
2116
|
+
|
|
2117
|
+
'''
|
|
2118
|
+
field = np.array(field)
|
|
2119
|
+
magnetization = np.array(magnetization)
|
|
2120
|
+
upper_branch, lower_branch = split_hysteresis_loop(field, magnetization)
|
|
2121
|
+
Me = upper_branch[1] + lower_branch[1][::-1]
|
|
2122
|
+
# filter for the high field portion of each branch
|
|
2123
|
+
pos_high_field_index = np.where((field >= HF_cutoff*np.max(np.abs(field))) & (field <= max_field_cutoff*np.max(np.abs(field))))[0]
|
|
2124
|
+
neg_high_field_index = np.where((field <= -HF_cutoff*np.max(np.abs(field))) & (field >= -max_field_cutoff*np.max(np.abs(field))))[0]
|
|
2125
|
+
|
|
2126
|
+
# invert points in the third quadrant to the first
|
|
2127
|
+
pos_high_field = field[pos_high_field_index]
|
|
2128
|
+
pos_high_field_magnetization = magnetization[pos_high_field_index]
|
|
2129
|
+
|
|
2130
|
+
neg_high_field = field[neg_high_field_index]
|
|
2131
|
+
neg_high_field_magnetization = magnetization[neg_high_field_index]
|
|
2132
|
+
neg_high_field = -np.array(neg_high_field)
|
|
2133
|
+
neg_high_field_magnetization = -np.array(neg_high_field_magnetization)
|
|
2134
|
+
|
|
2135
|
+
high_field = np.concatenate([pos_high_field, neg_high_field])
|
|
2136
|
+
high_field_magnetization = np.concatenate([pos_high_field_magnetization, neg_high_field_magnetization])
|
|
2137
|
+
|
|
2138
|
+
anova_results = ANOVA(high_field, high_field_magnetization)
|
|
2139
|
+
SST = anova_results['SST']
|
|
2140
|
+
SSR = anova_results['SSR']
|
|
2141
|
+
SSD = anova_results['SSD']
|
|
2142
|
+
R_squared = anova_results['R_squared']
|
|
2143
|
+
|
|
2144
|
+
SSPE = np.sum((upper_branch[1] - (-lower_branch[1][::-1])) ** 2) / 2
|
|
2145
|
+
SSLF = SSD - SSPE
|
|
2146
|
+
MSR = SSR
|
|
2147
|
+
MSD = SSD / (len(high_field) - 2)
|
|
2148
|
+
MSPE = SSPE / (len(high_field) / 2)
|
|
2149
|
+
MSLF = SSLF / (len(high_field)/2 - 2)
|
|
2150
|
+
|
|
2151
|
+
FL = MSR / MSD
|
|
2152
|
+
FNL = MSLF / MSPE
|
|
2153
|
+
|
|
2154
|
+
results = {'SST':SST,
|
|
2155
|
+
'SSR':SSR,
|
|
2156
|
+
'SSD':SSD,
|
|
2157
|
+
'R_squared': R_squared,
|
|
2158
|
+
'SSPE':SSPE,
|
|
2159
|
+
'SSLF':SSLF,
|
|
2160
|
+
'MSPE':MSPE,
|
|
2161
|
+
'MSR':MSR,
|
|
2162
|
+
'MSD':MSD,
|
|
2163
|
+
'FL':FL,
|
|
2164
|
+
'FNL':FNL}
|
|
2165
|
+
return results
|
|
2166
|
+
|
|
2167
|
+
|
|
2168
|
+
def hyst_loop_saturation_test(grid_field, grid_magnetization, max_field_cutoff=0.97):
|
|
2169
|
+
'''
|
|
2170
|
+
function for testing the saturation of a hysteresis loop
|
|
2171
|
+
which is based on the testing of linearity of the loop in field ranges of 60%, 70%, and 80% of the maximum field (<97%)
|
|
2172
|
+
'''
|
|
2173
|
+
|
|
2174
|
+
FNL60 = loop_saturation_stats(grid_field, grid_magnetization, HF_cutoff=0.6, max_field_cutoff = max_field_cutoff)['FNL']
|
|
2175
|
+
FNL70 = loop_saturation_stats(grid_field, grid_magnetization, HF_cutoff=0.7, max_field_cutoff = max_field_cutoff)['FNL']
|
|
2176
|
+
FNL80 = loop_saturation_stats(grid_field, grid_magnetization, HF_cutoff=0.8, max_field_cutoff = max_field_cutoff)['FNL']
|
|
2177
|
+
|
|
2178
|
+
saturation_cutoff = 0
|
|
2179
|
+
if (FNL80 > 2.5) & (FNL70 > 2.5) & (FNL60 > 2.5):
|
|
2180
|
+
saturation_cutoff = 0.92 # IRM default
|
|
2181
|
+
else:
|
|
2182
|
+
if FNL80 < 2.5: #saturated at 80%
|
|
2183
|
+
saturation_cutoff = 0.8
|
|
2184
|
+
if FNL70 < 2.5: #saturated at 70%
|
|
2185
|
+
saturation_cutoff = 0.7
|
|
2186
|
+
if FNL60 < 2.5: #saturated at 60%
|
|
2187
|
+
saturation_cutoff = 0.6
|
|
2188
|
+
results = {'FNL60':FNL60, 'FNL70':FNL70, 'FNL80':FNL80, 'saturation_cutoff':saturation_cutoff, 'loop_is_saturated':(saturation_cutoff != 0.92)}
|
|
2189
|
+
results_dict = dict_in_native_python(results)
|
|
2190
|
+
return results_dict
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
def loop_closure_test(H, Mrh, HF_cutoff=0.8):
|
|
2194
|
+
'''
|
|
2195
|
+
function for testing if the loop is open
|
|
2196
|
+
|
|
2197
|
+
Parameters
|
|
2198
|
+
----------
|
|
2199
|
+
H: array-like
|
|
2200
|
+
field values
|
|
2201
|
+
Mrh: array-like
|
|
2202
|
+
remanence componentt
|
|
2203
|
+
HF_cutoff: float
|
|
2204
|
+
high field cutoff value taken as percentage of the max field value
|
|
2205
|
+
|
|
2206
|
+
Returns
|
|
2207
|
+
-------
|
|
2208
|
+
SNR: float
|
|
2209
|
+
high field signal to noise ratio
|
|
2210
|
+
HAR: float
|
|
2211
|
+
high field area ratio
|
|
2212
|
+
'''
|
|
2213
|
+
assert len(H) == len(Mrh), 'H, Mrh must have the same length'
|
|
2214
|
+
pos_H_index = np.where(H > 0)
|
|
2215
|
+
neg_H_index = np.where(H < 0)
|
|
2216
|
+
pos_H = H[pos_H_index]
|
|
2217
|
+
neg_H = H[neg_H_index]
|
|
2218
|
+
pos_Mrh = Mrh[pos_H_index]
|
|
2219
|
+
neg_Mrh = Mrh[neg_H_index]
|
|
2220
|
+
|
|
2221
|
+
pos_HF_index = np.where(H > HF_cutoff*np.max(H))
|
|
2222
|
+
neg_HF_index = np.where(H < -HF_cutoff*np.max(H))
|
|
2223
|
+
pos_HF = H[pos_HF_index]
|
|
2224
|
+
neg_HF = H[neg_HF_index]
|
|
2225
|
+
pos_HF_Mrh = Mrh[pos_HF_index]
|
|
2226
|
+
neg_HF_Mrh = Mrh[neg_HF_index]
|
|
2227
|
+
|
|
2228
|
+
average_Mrh = (pos_Mrh - neg_Mrh[::-1])/2
|
|
2229
|
+
# replace all negative values with 0
|
|
2230
|
+
average_Mrh[average_Mrh < 0] = 0
|
|
2231
|
+
average_HF_Mrh = (pos_HF_Mrh - neg_HF_Mrh[::-1])/2
|
|
2232
|
+
# replace all negative values with 0
|
|
2233
|
+
average_HF_Mrh[average_HF_Mrh < 0] = 0
|
|
2234
|
+
HF_Mrh_noise = pos_HF_Mrh + neg_HF_Mrh[::-1]
|
|
2235
|
+
# replace all negative values with 0
|
|
2236
|
+
HF_Mrh_noise[HF_Mrh_noise < 0] = 0
|
|
2237
|
+
|
|
2238
|
+
HF_Mrh_signal_RMS = np.sqrt(np.mean(average_HF_Mrh**2))
|
|
2239
|
+
HF_Mrh_noise_RMS = np.sqrt(np.mean(HF_Mrh_noise**2))
|
|
2240
|
+
SNR = 20*np.log10(HF_Mrh_signal_RMS/HF_Mrh_noise_RMS)
|
|
2241
|
+
|
|
2242
|
+
total_Mrh_area = np.trapz(pos_Mrh, pos_H) + np.trapz(-neg_Mrh[::-1], -neg_H[::-1])
|
|
2243
|
+
total_HF_Mrh_area = np.trapz(average_Mrh, pos_H)
|
|
2244
|
+
|
|
2245
|
+
HAR = 20*np.log10(total_HF_Mrh_area/total_Mrh_area)
|
|
2246
|
+
loop_is_closed = (SNR < 8) or (HAR < -48)
|
|
2247
|
+
|
|
2248
|
+
results = {'SNR':float(SNR),
|
|
2249
|
+
'HAR':float(HAR),
|
|
2250
|
+
'loop_is_closed':bool(loop_is_closed),
|
|
2251
|
+
}
|
|
2252
|
+
return results
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
def drift_correction_Me(H, M):
|
|
2256
|
+
'''
|
|
2257
|
+
default IRM drift correction algorithm based on Me
|
|
2258
|
+
|
|
2259
|
+
Parameters
|
|
2260
|
+
----------
|
|
2261
|
+
H : numpy array
|
|
2262
|
+
field values
|
|
2263
|
+
M : numpy array
|
|
2264
|
+
magnetization values
|
|
2265
|
+
'''
|
|
2266
|
+
# split loop branches
|
|
2267
|
+
upper_branch, lower_branch = split_hysteresis_loop(H, M)
|
|
2268
|
+
# calculate Me
|
|
2269
|
+
Me = upper_branch[1][::-1] + lower_branch[1]
|
|
2270
|
+
|
|
2271
|
+
loop_size = len(H) -1
|
|
2272
|
+
half_loop_size = loop_size // 2
|
|
2273
|
+
quarter_loop_size = loop_size // 4
|
|
2274
|
+
# calculate the smoothed Me using Savitzky-Golay filter
|
|
2275
|
+
# which allows inplementation of a polynomial fit to the data within each window
|
|
2276
|
+
smoothed_Me = savgol_filter(Me, window_length=11, polyorder=2, mode='interp')
|
|
2277
|
+
# determine whether the main drift field region
|
|
2278
|
+
main_drift_region = H[np.argmax(np.abs(smoothed_Me[:half_loop_size]))]
|
|
2279
|
+
|
|
2280
|
+
M_cor = copy.deepcopy(M)
|
|
2281
|
+
positive_field_cor = abs(main_drift_region) > np.max(H) * 0.75
|
|
2282
|
+
|
|
2283
|
+
if positive_field_cor:
|
|
2284
|
+
# if the ratio of drift in the high-field range (≥75% of the peak field) to the low-field range.
|
|
2285
|
+
# is high, then the positive field correction is applied
|
|
2286
|
+
for i in range(0, quarter_loop_size):
|
|
2287
|
+
M_cor[i] -= smoothed_Me[i]
|
|
2288
|
+
M_cor[loop_size - i] -= smoothed_Me[half_loop_size - i]
|
|
2289
|
+
|
|
2290
|
+
return M_cor
|
|
2291
|
+
else:
|
|
2292
|
+
# if positive field correctionis not preferred, we do upper branch drift correction
|
|
2293
|
+
window_size = 7
|
|
2294
|
+
# calculate running mean of the upper branch with a window size of 2k+1
|
|
2295
|
+
kernel = np.ones(window_size) / window_size
|
|
2296
|
+
Me_running_mean = np.convolve(Me, kernel, mode='same')
|
|
2297
|
+
|
|
2298
|
+
for i in range(len(Me_running_mean)):
|
|
2299
|
+
|
|
2300
|
+
M_cor[i] = M[i] - Me_running_mean[i]
|
|
2301
|
+
return M_cor
|
|
2302
|
+
|
|
2303
|
+
def prorated_drift_correction(field, magnetization):
|
|
2304
|
+
'''
|
|
2305
|
+
function to correct for the linear drift of a hysteresis loop
|
|
2306
|
+
take the difference between the magnetization measured at the maximum field on the upper and lower branches
|
|
2307
|
+
apply linearly prorated correction of M(H)
|
|
2308
|
+
this should be applied to the gridded data
|
|
2309
|
+
|
|
2310
|
+
Parameters
|
|
2311
|
+
----------
|
|
2312
|
+
field : numpy array
|
|
2313
|
+
field values
|
|
2314
|
+
magnetization : numpy array
|
|
2315
|
+
magnetization values
|
|
2316
|
+
|
|
2317
|
+
Returns
|
|
2318
|
+
-------
|
|
2319
|
+
corrected_magnetization : numpy array
|
|
2320
|
+
corrected magnetization values
|
|
2321
|
+
'''
|
|
2322
|
+
|
|
2323
|
+
field = np.array(field)
|
|
2324
|
+
magnetization = np.array(magnetization)
|
|
2325
|
+
upper_branch, lower_branch = split_hysteresis_loop(field, magnetization)
|
|
2326
|
+
|
|
2327
|
+
# find the maximum field values for the upper and lower branches
|
|
2328
|
+
upper_branch_max_idx = np.argmax(upper_branch[0])
|
|
2329
|
+
lower_branch_max_idx = np.argmax(lower_branch[0])
|
|
2330
|
+
|
|
2331
|
+
# find the difference between the magnetization values at the maximum field values
|
|
2332
|
+
M_ce = upper_branch[1][upper_branch_max_idx] - lower_branch[1][lower_branch_max_idx]
|
|
2333
|
+
|
|
2334
|
+
# apply linearly prorated correction of M(H)
|
|
2335
|
+
corrected_magnetization = [M_ce * ((i-1)/(len(field)-1) - 1/2) + magnetization[i] for i in range(len(field))]
|
|
2336
|
+
|
|
2337
|
+
return np.array(corrected_magnetization)
|
|
2338
|
+
|
|
2339
|
+
def symmetric_averaging_drift_corr(field, magnetization):
|
|
2340
|
+
|
|
2341
|
+
field = np.array(field)
|
|
2342
|
+
magnetization = np.array(magnetization)
|
|
2343
|
+
|
|
2344
|
+
upper_branch, lower_branch = split_hysteresis_loop(field, magnetization)
|
|
2345
|
+
|
|
2346
|
+
# average the upper and inverted lower branches
|
|
2347
|
+
averaged_upper_branch = (upper_branch[1] - lower_branch[1][::-1]) / 2
|
|
2348
|
+
|
|
2349
|
+
# calculate tip-to-tip separation from both the upper and lower branches
|
|
2350
|
+
tip_to_tip_separation = (upper_branch[1][0] - lower_branch[1][0] + upper_branch[1][-1] - lower_branch[1][-1]) / 4
|
|
2351
|
+
# apply the tip-to-tip separation to the upper branch
|
|
2352
|
+
corrected_magnetization = averaged_upper_branch - tip_to_tip_separation
|
|
2353
|
+
|
|
2354
|
+
# append back in the lower branch which should just be the inverted corrected upper branch
|
|
2355
|
+
corrected_magnetization = np.concatenate([corrected_magnetization[::-1], -corrected_magnetization[::-1]])
|
|
2356
|
+
|
|
2357
|
+
return corrected_magnetization
|
|
2358
|
+
|
|
2359
|
+
def IRM_nonlinear_fit(H, chi_HF, Ms, a_1, a_2):
|
|
2360
|
+
'''
|
|
2361
|
+
function for calculating the IRM non-linear fit
|
|
2362
|
+
|
|
2363
|
+
Parameters
|
|
2364
|
+
----------
|
|
2365
|
+
H : numpy array
|
|
2366
|
+
field values
|
|
2367
|
+
chi_HF : float
|
|
2368
|
+
high field susceptibility, converted to Tesla to match the unit of the field
|
|
2369
|
+
Ms : float
|
|
2370
|
+
saturation magnetization
|
|
2371
|
+
a_1 : float
|
|
2372
|
+
coefficient for H^(-1), needs to be negative
|
|
2373
|
+
a_2 : float
|
|
2374
|
+
coefficient for H^(-2), needs to be negative
|
|
2375
|
+
|
|
2376
|
+
'''
|
|
2377
|
+
chi_HF = chi_HF/(4*np.pi/1e7)
|
|
2378
|
+
return chi_HF * H + Ms + a_1 * H**(-1) + a_2 * H**(-2)
|
|
2379
|
+
|
|
2380
|
+
def IRM_nonlinear_fit_cost_function(params, H, M_obs):
|
|
2381
|
+
'''
|
|
2382
|
+
cost function for the IRM non-linear least squares fit optimization
|
|
2383
|
+
|
|
2384
|
+
Parameters
|
|
2385
|
+
----------
|
|
2386
|
+
params : numpy array
|
|
2387
|
+
array of parameters to optimize
|
|
2388
|
+
H : numpy array
|
|
2389
|
+
field values
|
|
2390
|
+
M_obs : numpy array
|
|
2391
|
+
observed magnetization values
|
|
2392
|
+
|
|
2393
|
+
Returns
|
|
2394
|
+
-------
|
|
2395
|
+
residual : numpy array
|
|
2396
|
+
residual between the observed and predicted magnetization values
|
|
2397
|
+
'''
|
|
2398
|
+
|
|
2399
|
+
chi_HF, Ms, a_1, a_2 = params
|
|
2400
|
+
prediction = IRM_nonlinear_fit(H, chi_HF, Ms, a_1, a_2)
|
|
2401
|
+
return M_obs - prediction
|
|
2402
|
+
|
|
2403
|
+
def Fabian_nonlinear_fit(H, chi_HF, Ms, alpha, beta):
|
|
2404
|
+
'''
|
|
2405
|
+
function for calculating the Fabian non-linear fit
|
|
2406
|
+
|
|
2407
|
+
Parameters
|
|
2408
|
+
----------
|
|
2409
|
+
H : numpy array
|
|
2410
|
+
field values
|
|
2411
|
+
chi_HF : float
|
|
2412
|
+
high field susceptibility
|
|
2413
|
+
Ms : float
|
|
2414
|
+
saturation magnetization
|
|
2415
|
+
alpha : float
|
|
2416
|
+
coefficient for H^(beta), needs to be negative
|
|
2417
|
+
beta : float
|
|
2418
|
+
coefficient for H^(beta), needs to be negative
|
|
2419
|
+
|
|
2420
|
+
'''
|
|
2421
|
+
chi_HF = chi_HF/(4*np.pi/1e7) # convert to Tesla
|
|
2422
|
+
return chi_HF * H + Ms + alpha * H**beta
|
|
2423
|
+
|
|
2424
|
+
def Fabian_nonlinear_fit_cost_function(params, H, M_obs):
|
|
2425
|
+
'''
|
|
2426
|
+
cost function for the Fabian non-linear least squares fit optimization
|
|
2427
|
+
|
|
2428
|
+
Parameters
|
|
2429
|
+
----------
|
|
2430
|
+
params : numpy array
|
|
2431
|
+
array of parameters to optimize
|
|
2432
|
+
H : numpy array
|
|
2433
|
+
field values
|
|
2434
|
+
M_obs : numpy array
|
|
2435
|
+
observed magnetization values
|
|
2436
|
+
|
|
2437
|
+
Returns
|
|
2438
|
+
-------
|
|
2439
|
+
residual : numpy array
|
|
2440
|
+
residual between the observed and predicted magnetization values
|
|
2441
|
+
'''
|
|
2442
|
+
|
|
2443
|
+
chi_HF, Ms, alpha, beta = params
|
|
2444
|
+
prediction = Fabian_nonlinear_fit(H, chi_HF, Ms, alpha, beta)
|
|
2445
|
+
return M_obs - prediction
|
|
2446
|
+
|
|
2447
|
+
def Fabian_nonlinear_fit_fix_beta_cost_function(params, H, M_obs):
|
|
2448
|
+
'''
|
|
2449
|
+
cost function for the Fabian non-linear least squares fit optimization
|
|
2450
|
+
with beta fixed at -2
|
|
2451
|
+
|
|
2452
|
+
Parameters
|
|
2453
|
+
----------
|
|
2454
|
+
params : numpy array
|
|
2455
|
+
array of parameters to optimize
|
|
2456
|
+
H : numpy array
|
|
2457
|
+
field values
|
|
2458
|
+
M_obs : numpy array
|
|
2459
|
+
observed magnetization values
|
|
2460
|
+
|
|
2461
|
+
Returns
|
|
2462
|
+
-------
|
|
2463
|
+
residual : numpy array
|
|
2464
|
+
residual between the observed and predicted magnetization values
|
|
2465
|
+
'''
|
|
2466
|
+
beta = -2
|
|
2467
|
+
chi_HF, Ms, alpha = params
|
|
2468
|
+
prediction = Fabian_nonlinear_fit(H, chi_HF, Ms, alpha, beta)
|
|
2469
|
+
return M_obs - prediction
|
|
2470
|
+
|
|
2471
|
+
def hyst_HF_nonlinear_optimization(H, M, HF_cutoff, fit_type, initial_guess=[1, 1, -0.1, -0.1], bounds=([0, 0, -np.inf, -np.inf], [np.inf, np.inf, 0, 0])):
|
|
2472
|
+
'''
|
|
2473
|
+
Optimize a high-field nonlinear fit
|
|
2474
|
+
|
|
2475
|
+
Parameters
|
|
2476
|
+
----------
|
|
2477
|
+
H : numpy.ndarray
|
|
2478
|
+
Array of field values.
|
|
2479
|
+
M : numpy.ndarray
|
|
2480
|
+
Array of magnetization values.
|
|
2481
|
+
HF_cutoff : float
|
|
2482
|
+
Fraction of max(|H|) defining the lower bound of the high-field region.
|
|
2483
|
+
fit_type : {'IRM', 'Fabian', 'Fabian_fixed_beta'}
|
|
2484
|
+
Type of nonlinear model to fit.
|
|
2485
|
+
initial_guess : list of float, optional
|
|
2486
|
+
Initial parameter guess for the optimizer.
|
|
2487
|
+
Defaults to [1, 1, -0.1, -0.1]:
|
|
2488
|
+
χ_HF = 1, Mₛ = 1, a₁ = –0.1, a₂ = –0.1 (or α, β for Fabian).
|
|
2489
|
+
bounds : tuple of array-like, optional
|
|
2490
|
+
Lower and upper bounds for each parameter.
|
|
2491
|
+
Defaults to ([0, 0, -∞, -∞], [∞, ∞, 0, 0]):
|
|
2492
|
+
- Lower: χ_HF ≥ 0, Mₛ ≥ 0, a₁ ≥ –∞, a₂ ≥ –∞
|
|
2493
|
+
- Upper: χ_HF ≤ ∞, Mₛ ≤ ∞, a₁ ≤ 0, a₂ ≤ 0
|
|
2494
|
+
(for Fabian, α and β follow the same positions/limits).
|
|
2495
|
+
|
|
2496
|
+
Returns
|
|
2497
|
+
-------
|
|
2498
|
+
dict
|
|
2499
|
+
Fit results with keys:
|
|
2500
|
+
- 'chi_HF', 'Ms', 'a_1', 'a_2' (for IRM) or
|
|
2501
|
+
'chi_HF', 'Ms', 'alpha', 'beta' (for Fabian variants)
|
|
2502
|
+
- 'Fnl_lin': float, ratio comparing nonlinear vs. linear fit
|
|
2503
|
+
'''
|
|
2504
|
+
HF_index = np.where((np.abs(H) >= HF_cutoff*np.max(np.abs(H))) & (np.abs(H) <= 0.97*np.max(np.abs(H))))[0]
|
|
2505
|
+
|
|
2506
|
+
HF_field = np.abs(H[HF_index])
|
|
2507
|
+
HF_magnetization = np.abs(M[HF_index])
|
|
2508
|
+
|
|
2509
|
+
if fit_type == 'IRM':
|
|
2510
|
+
cost_function = IRM_nonlinear_fit_cost_function
|
|
2511
|
+
results = least_squares(cost_function, initial_guess, bounds=bounds, args=(HF_field, HF_magnetization))
|
|
2512
|
+
elif fit_type == 'Fabian':
|
|
2513
|
+
cost_function = Fabian_nonlinear_fit_cost_function
|
|
2514
|
+
results = least_squares(cost_function, initial_guess, bounds=bounds, args=(HF_field, HF_magnetization))
|
|
2515
|
+
elif fit_type == 'Fabian_fixed_beta':
|
|
2516
|
+
cost_function = Fabian_nonlinear_fit_fix_beta_cost_function
|
|
2517
|
+
results = least_squares(cost_function, initial_guess[:3], bounds=(bounds[0][:3], bounds[1][:3]), args=(HF_field, HF_magnetization))
|
|
2518
|
+
else:
|
|
2519
|
+
raise ValueError('Fit type must be either IRM or Fabian')
|
|
2520
|
+
|
|
2521
|
+
if fit_type == 'IRM':
|
|
2522
|
+
final_result = {'chi_HF': results.x[0], 'Ms': results.x[1], 'a_1': results.x[2], 'a_2': results.x[3]}
|
|
2523
|
+
chi_HF, Ms, a_1, a_2 = results.x
|
|
2524
|
+
nonlinear_fit = IRM_nonlinear_fit(HF_field, chi_HF, Ms, a_1, a_2)
|
|
2525
|
+
elif fit_type == 'Fabian':
|
|
2526
|
+
final_result = {'chi_HF': results.x[0], 'Ms': results.x[1], 'alpha': results.x[2], 'beta': results.x[3]}
|
|
2527
|
+
chi_HF, Ms, alpha, beta = results.x
|
|
2528
|
+
nonlinear_fit = Fabian_nonlinear_fit(HF_field, chi_HF, Ms, alpha, beta)
|
|
2529
|
+
elif fit_type == 'Fabian_fixed_beta':
|
|
2530
|
+
final_result = {'chi_HF': results.x[0], 'Ms': results.x[1], 'alpha': results.x[2], 'beta': -2}
|
|
2531
|
+
chi_HF, Ms, alpha = results.x
|
|
2532
|
+
nonlinear_fit = Fabian_nonlinear_fit(HF_field, chi_HF, Ms, alpha, -2)
|
|
2533
|
+
|
|
2534
|
+
# let's also report the Fnl_lin which is a measure of whether the nonlinear fit is better than a linear fit
|
|
2535
|
+
# let's first make a linear fit
|
|
2536
|
+
linear_fit_ANOVA = ANOVA(HF_field, HF_magnetization)
|
|
2537
|
+
R_squared_l = 1 - linear_fit_ANOVA['R_squared']
|
|
2538
|
+
|
|
2539
|
+
# now calculate the nonlinear fit SST
|
|
2540
|
+
nl_SST = np.sum((HF_magnetization - np.mean(HF_magnetization)) ** 2)
|
|
2541
|
+
|
|
2542
|
+
# sum of squares due to regression
|
|
2543
|
+
nl_SSR = np.sum((nonlinear_fit - np.mean(HF_magnetization)) ** 2)
|
|
2544
|
+
|
|
2545
|
+
# the remaining unexplained variation (noise and lack of fit)
|
|
2546
|
+
nl_SSD = np.sum((HF_magnetization - nonlinear_fit) ** 2)
|
|
2547
|
+
|
|
2548
|
+
R_squared_nl = 1 - nl_SSR/nl_SST
|
|
2549
|
+
|
|
2550
|
+
# calculate the Fnl_lin stat
|
|
2551
|
+
Fnl_lin = R_squared_l / R_squared_nl
|
|
2552
|
+
|
|
2553
|
+
final_result['Fnl_lin'] = Fnl_lin
|
|
2554
|
+
final_result_dict = dict_in_native_python(final_result)
|
|
2555
|
+
return final_result_dict
|
|
2556
|
+
|
|
2557
|
+
|
|
2558
|
+
def process_hyst_loop(field, magnetization, specimen_name, show_results_table=True):
|
|
2559
|
+
'''
|
|
2560
|
+
function to process a hysteresis loop following the IRM decision tree
|
|
2561
|
+
Parameters
|
|
2562
|
+
----------
|
|
2563
|
+
field : array
|
|
2564
|
+
array of field values
|
|
2565
|
+
magnetization : array
|
|
2566
|
+
array of magnetization values
|
|
2567
|
+
|
|
2568
|
+
Returns
|
|
2569
|
+
-------
|
|
2570
|
+
results : dict
|
|
2571
|
+
dictionary with the hysteresis processing results and a Bokeh plot
|
|
2572
|
+
'''
|
|
2573
|
+
# first grid the data into symmetric field values
|
|
2574
|
+
grid_fields, grid_magnetizations = grid_hysteresis_loop(field, magnetization)
|
|
2575
|
+
|
|
2576
|
+
# test linearity of the gridded original loop
|
|
2577
|
+
loop_linearity_test_results = hyst_linearity_test(grid_fields, grid_magnetizations)
|
|
2578
|
+
|
|
2579
|
+
# loop centering
|
|
2580
|
+
loop_centering_results = hyst_loop_centering(grid_fields, grid_magnetizations)
|
|
2581
|
+
# check if the quality factor Q is < 2
|
|
2582
|
+
if loop_centering_results['Q'] < 2:
|
|
2583
|
+
# in case the loop quality is bad, no field correction is applied
|
|
2584
|
+
loop_centering_results['opt_H_offset'] = 0
|
|
2585
|
+
loop_centering_results['centered_H'] = grid_fields
|
|
2586
|
+
|
|
2587
|
+
centered_H, centered_M = loop_centering_results['centered_H'], loop_centering_results['centered_M']
|
|
2588
|
+
|
|
2589
|
+
# drift correction
|
|
2590
|
+
drift_corr_M = drift_correction_Me(centered_H, centered_M)
|
|
2591
|
+
|
|
2592
|
+
# calculate Mr, Mrh, Mih, Me, Brh
|
|
2593
|
+
H, Mr, Mrh, Mih, Me, Brh = calc_Mr_Mrh_Mih_Brh(centered_H, drift_corr_M)
|
|
2594
|
+
|
|
2595
|
+
# check if the loop is closed
|
|
2596
|
+
loop_closure_test_results = loop_closure_test(H, Mrh)
|
|
2597
|
+
|
|
2598
|
+
# check if the loop is saturated (high field linearity test)
|
|
2599
|
+
loop_saturation_stats = hyst_loop_saturation_test(centered_H, drift_corr_M)
|
|
2600
|
+
|
|
2601
|
+
if loop_saturation_stats['loop_is_saturated']:
|
|
2602
|
+
# linear high field correction
|
|
2603
|
+
chi_HF, Ms = linear_HF_fit(centered_H, drift_corr_M, loop_saturation_stats['saturation_cutoff'])
|
|
2604
|
+
Fnl_lin = None
|
|
2605
|
+
else:
|
|
2606
|
+
# do non linear approach to saturation fit
|
|
2607
|
+
NL_fit_result = hyst_HF_nonlinear_optimization(centered_H, drift_corr_M, 0.6, 'IRM')
|
|
2608
|
+
chi_HF, Ms, Fnl_lin = NL_fit_result['chi_HF'], NL_fit_result['Ms'], NL_fit_result['Fnl_lin']
|
|
2609
|
+
|
|
2610
|
+
# apply high field correction
|
|
2611
|
+
slope_corr_M = hyst_slope_correction(centered_H, drift_corr_M, chi_HF)
|
|
2612
|
+
|
|
2613
|
+
# calculate the Msn and Q factor for the ferromagentic component
|
|
2614
|
+
M_sn_f, Qf = calc_Q(centered_H, slope_corr_M)
|
|
2615
|
+
|
|
2616
|
+
# calculate the coercivity Bc
|
|
2617
|
+
Bc = calc_Bc(centered_H, slope_corr_M)
|
|
2618
|
+
|
|
2619
|
+
# calculate the shape parameter of Fabian 2003
|
|
2620
|
+
E_hyst = np.trapz(Mrh, H)
|
|
2621
|
+
sigma = np.log(E_hyst / 2 / Bc / Ms)
|
|
2622
|
+
|
|
2623
|
+
# plot original loop
|
|
2624
|
+
p = plot_hysteresis_loop(grid_fields, grid_magnetizations, specimen_name, line_color='orange', label='raw loop')
|
|
2625
|
+
# plot centered loop
|
|
2626
|
+
p_centered = plot_hysteresis_loop(centered_H, centered_M, specimen_name, p=p, line_color='red', label=specimen_name+' offset corrected')
|
|
2627
|
+
# plot drift corrected loop
|
|
2628
|
+
p_drift_corr = plot_hysteresis_loop(centered_H, drift_corr_M, specimen_name, p=p_centered, line_color='pink', label=specimen_name+' drift corrected')
|
|
2629
|
+
# plot slope corrected loop
|
|
2630
|
+
p_slope_corr = plot_hysteresis_loop(centered_H, slope_corr_M, specimen_name, p=p_drift_corr, line_color='blue', label=specimen_name+' slope corrected')
|
|
2631
|
+
# plot Mrh
|
|
2632
|
+
p_slope_corr.line(H, Mrh, line_color='green', legend_label='Mrh', line_width=1)
|
|
2633
|
+
p_slope_corr.line(H, Mih, line_color='purple', legend_label='Mih', line_width=1)
|
|
2634
|
+
p_slope_corr.line(H, Me, line_color='brown', legend_label='Me', line_width=1)
|
|
2635
|
+
show(p)
|
|
2636
|
+
results = {'gridded_H': grid_fields,
|
|
2637
|
+
'gridded_M': grid_magnetizations,
|
|
2638
|
+
'linearity_test_results': loop_linearity_test_results,
|
|
2639
|
+
'loop_is_linear': loop_linearity_test_results['loop_is_linear'],
|
|
2640
|
+
'FNL': loop_linearity_test_results['FNL'],
|
|
2641
|
+
'loop_centering_results': loop_centering_results,
|
|
2642
|
+
'centered_H': centered_H,
|
|
2643
|
+
'centered_M': centered_M,
|
|
2644
|
+
'drift_corrected_M': drift_corr_M,
|
|
2645
|
+
'slope_corrected_M': slope_corr_M,
|
|
2646
|
+
'loop_closure_test_results': loop_closure_test_results,
|
|
2647
|
+
'loop_is_closed': loop_closure_test_results['loop_is_closed'],
|
|
2648
|
+
'loop_saturation_stats': loop_saturation_stats,
|
|
2649
|
+
'loop_is_saturated': loop_saturation_stats['loop_is_saturated'],
|
|
2650
|
+
'M_sn':loop_centering_results['M_sn'],
|
|
2651
|
+
'Q': loop_centering_results['Q'],
|
|
2652
|
+
'H': H, 'Mr': Mr, 'Mrh': Mrh,
|
|
2653
|
+
'Mih': Mih, 'Me': Me, 'Brh': Brh, 'sigma': sigma,
|
|
2654
|
+
'chi_HF': chi_HF,
|
|
2655
|
+
'FNL60': loop_saturation_stats['FNL60'],
|
|
2656
|
+
'FNL70': loop_saturation_stats['FNL70'],
|
|
2657
|
+
'FNL80': loop_saturation_stats['FNL80'],
|
|
2658
|
+
'Ms': Ms, 'Bc': Bc, 'M_sn_f': M_sn_f,
|
|
2659
|
+
'Qf': Qf, 'Fnl_lin': Fnl_lin,
|
|
2660
|
+
'plot': p}
|
|
2661
|
+
|
|
2662
|
+
if show_results_table:
|
|
2663
|
+
summary = {
|
|
2664
|
+
'Mr': [Mr],
|
|
2665
|
+
'Ms': [Ms],
|
|
2666
|
+
'Bc': [Bc],
|
|
2667
|
+
'Brh': [Brh],
|
|
2668
|
+
'sigma': [sigma],
|
|
2669
|
+
'Q': [loop_centering_results['Q']],
|
|
2670
|
+
'Qf': [Qf],
|
|
2671
|
+
'chi_HF':[chi_HF],
|
|
2672
|
+
'FNL60': [loop_saturation_stats['FNL60']],
|
|
2673
|
+
'FNL70': [loop_saturation_stats['FNL70']],
|
|
2674
|
+
'FNL80': [loop_saturation_stats['FNL80']],
|
|
2675
|
+
}
|
|
2676
|
+
src = ColumnDataSource(summary)
|
|
2677
|
+
cols = [
|
|
2678
|
+
TableColumn(field=param, title=param)
|
|
2679
|
+
for param in summary
|
|
2680
|
+
]
|
|
2681
|
+
data_table = DataTable(
|
|
2682
|
+
source=src, columns=cols,
|
|
2683
|
+
width=p_slope_corr.width, height=100
|
|
2684
|
+
)
|
|
2685
|
+
data_table.index_position = None
|
|
2686
|
+
layout = column(data_table)
|
|
2687
|
+
show(layout)
|
|
2688
|
+
return results
|
|
2689
|
+
|
|
2690
|
+
def process_hyst_loops(
|
|
2691
|
+
hyst_experiments,
|
|
2692
|
+
measurements,
|
|
2693
|
+
field_col="meas_field_dc",
|
|
2694
|
+
magn_col="magn_mass",
|
|
2695
|
+
show_results_table=True,
|
|
2696
|
+
):
|
|
2697
|
+
"""
|
|
2698
|
+
Process multiple hysteresis loops in batch.
|
|
2699
|
+
|
|
2700
|
+
Parameters
|
|
2701
|
+
----------
|
|
2702
|
+
hyst_experiments : DataFrame
|
|
2703
|
+
Must contain columns "experiment" and "specimen".
|
|
2704
|
+
measurements : DataFrame
|
|
2705
|
+
Must contain an "experiment" column and the data columns.
|
|
2706
|
+
field_col : str, optional
|
|
2707
|
+
Name of the column in `measurements` holding field values.
|
|
2708
|
+
Defaults to "meas_field_dc".
|
|
2709
|
+
magn_col : str, optional
|
|
2710
|
+
Name of the column in `measurements` holding magnetization values.
|
|
2711
|
+
Defaults to "magn_mass".
|
|
2712
|
+
show_results_table : bool, optional
|
|
2713
|
+
If True, display the summary table below each plot.
|
|
2714
|
+
|
|
2715
|
+
Returns
|
|
2716
|
+
-------
|
|
2717
|
+
list of dict
|
|
2718
|
+
Each dict is the output of `process_hyst_loop` for one specimen.
|
|
2719
|
+
"""
|
|
2720
|
+
results = []
|
|
2721
|
+
for _, row in hyst_experiments.iterrows():
|
|
2722
|
+
exp = row["experiment"]
|
|
2723
|
+
spec = row["specimen"]
|
|
2724
|
+
df = (
|
|
2725
|
+
measurements[measurements["experiment"] == exp]
|
|
2726
|
+
.reset_index(drop=True)
|
|
2727
|
+
)
|
|
2728
|
+
res = process_hyst_loop(
|
|
2729
|
+
df[field_col].values,
|
|
2730
|
+
df[magn_col].values,
|
|
2731
|
+
spec,
|
|
2732
|
+
show_results_table=show_results_table,
|
|
2733
|
+
)
|
|
2734
|
+
results.append(res)
|
|
2735
|
+
return results
|
|
2736
|
+
|
|
2737
|
+
|
|
2738
|
+
def add_hyst_stats_to_specimens_table(specimens_df, experiment_name, hyst_results):
|
|
2739
|
+
'''
|
|
2740
|
+
function to export the hysteresis data to a MagIC specimen data table
|
|
2741
|
+
|
|
2742
|
+
Parameters
|
|
2743
|
+
----------
|
|
2744
|
+
specimens_df : pandas.DataFrame
|
|
2745
|
+
dataframe with the specimens data
|
|
2746
|
+
experiment_name : str
|
|
2747
|
+
name of the experiment
|
|
2748
|
+
hyst_results : dict
|
|
2749
|
+
dictionary with the hysteresis data
|
|
2750
|
+
as output from the rmag.process_hyst_loop function
|
|
2751
|
+
|
|
2752
|
+
updates the specimen table in place
|
|
2753
|
+
'''
|
|
2754
|
+
|
|
2755
|
+
result_keys_MagIC = ['specimen',
|
|
2756
|
+
'Ms',
|
|
2757
|
+
'Mr',
|
|
2758
|
+
'Bc',
|
|
2759
|
+
'chi_HF']
|
|
2760
|
+
|
|
2761
|
+
MagIC_columns=['specimen',
|
|
2762
|
+
'hyst_ms_mass',
|
|
2763
|
+
'hyst_mr_mass',
|
|
2764
|
+
'hyst_bc',
|
|
2765
|
+
'hyst_xhf']
|
|
2766
|
+
|
|
2767
|
+
# add MagIC available columns to the specimens table
|
|
2768
|
+
for result_key, col in zip(result_keys_MagIC, MagIC_columns):
|
|
2769
|
+
if col not in specimens_df.columns:
|
|
2770
|
+
# add the column to the specimens table
|
|
2771
|
+
specimens_df[col] = np.nan
|
|
2772
|
+
|
|
2773
|
+
specimens_df.loc[specimens_df['experiments'] == experiment_name, col] = hyst_results[result_key]
|
|
2774
|
+
|
|
2775
|
+
# dump the rest of the stats to the description column
|
|
2776
|
+
additional_keys = ['Q', 'Qf', 'sigma',
|
|
2777
|
+
'Brh', 'FNL', 'FNL60', 'FNL70', 'FNL80',
|
|
2778
|
+
'Fnl_lin', 'loop_is_linear', 'loop_is_closed', 'loop_is_saturated']
|
|
2779
|
+
additional_stats_dict = {}
|
|
2780
|
+
for key in additional_keys:
|
|
2781
|
+
additional_stats_dict[key] = hyst_results[key]
|
|
2782
|
+
|
|
2783
|
+
# check if the description cell is type string
|
|
2784
|
+
if isinstance(specimens_df[specimens_df['experiments'] == experiment_name]['description'].iloc[0], str):
|
|
2785
|
+
# unpack the string to a dict, then add the new stats, then pack it back to a string
|
|
2786
|
+
description_dict = eval(specimens_df[specimens_df['experiments'] == experiment_name]['description'].iloc[0])
|
|
2787
|
+
for key in additional_keys:
|
|
2788
|
+
if key in description_dict:
|
|
2789
|
+
# if the key already exists, update it
|
|
2790
|
+
description_dict[key] = hyst_results[key]
|
|
2791
|
+
else:
|
|
2792
|
+
# if the key does not exist, add it
|
|
2793
|
+
description_dict[key] = hyst_results[key]
|
|
2794
|
+
# pack the dict back to a string
|
|
2795
|
+
specimens_df.loc[specimens_df['experiments'] == experiment_name, 'description'] = str(description_dict)
|
|
2796
|
+
else:
|
|
2797
|
+
# if not, create a new dict
|
|
2798
|
+
specimens_df.loc[specimens_df['experiments'] == experiment_name, 'description'] = str(additional_stats_dict)
|
|
2799
|
+
return
|
|
2800
|
+
|
|
2801
|
+
# X-T functions
|
|
2802
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
2803
|
+
|
|
2804
|
+
def split_warm_cool(experiment,temperature_column='meas_temp',
|
|
2805
|
+
magnetic_column='susc_chi_mass'):
|
|
2806
|
+
"""
|
|
2807
|
+
Split a thermomagnetic curve into heating and cooling portions. Default
|
|
2808
|
+
columns are 'meas_temp' and 'susc_chi_mass' for susceptibility measurements.
|
|
2809
|
+
Funcation can also be used for other warming then cooling data.
|
|
2810
|
+
|
|
2811
|
+
Parameters
|
|
2812
|
+
----------
|
|
2813
|
+
experiment : pandas.DataFrame
|
|
2814
|
+
the experiment data
|
|
2815
|
+
temperature_column : str, optional
|
|
2816
|
+
name of the temperature column (default 'meas_temp')
|
|
2817
|
+
magnetic_column : str, optional
|
|
2818
|
+
name of the magnetization/susceptibility column
|
|
2819
|
+
(default 'susc_chi_mass')
|
|
2820
|
+
|
|
2821
|
+
Returns
|
|
2822
|
+
-------
|
|
2823
|
+
warm_T : list[float]
|
|
2824
|
+
temperatures for the heating cycle
|
|
2825
|
+
warm_X : list[float]
|
|
2826
|
+
magnetization/susceptibility for the heating cycle
|
|
2827
|
+
cool_T : list[float]
|
|
2828
|
+
temperatures for the cooling cycle
|
|
2829
|
+
cool_X : list[float]
|
|
2830
|
+
magnetization/susceptibility for the cooling cycle
|
|
2831
|
+
"""
|
|
2832
|
+
Tlist = experiment[temperature_column] # temperature list
|
|
2833
|
+
Xlist = experiment[magnetic_column] # Chi list
|
|
2834
|
+
|
|
2835
|
+
warmorcool = np.array(np.insert((np.diff(Tlist) > 0 )* 1, 0, 1))
|
|
2836
|
+
# print(warmorcool)
|
|
2837
|
+
warm_T = [Tlist[i] for i in range(len(warmorcool)) if warmorcool[i]==1]
|
|
2838
|
+
cool_T = [Tlist[i] for i in range(len(warmorcool)) if warmorcool[i]==0]
|
|
2839
|
+
warm_X = [Xlist[i] for i in range(len(warmorcool)) if warmorcool[i]==1]
|
|
2840
|
+
cool_X = [Xlist[i] for i in range(len(warmorcool)) if warmorcool[i]==0]
|
|
2841
|
+
|
|
2842
|
+
return warm_T, warm_X, cool_T, cool_X
|
|
2843
|
+
|
|
2844
|
+
|
|
2845
|
+
def plot_X_T(
|
|
2846
|
+
experiment,
|
|
2847
|
+
temperature_column="meas_temp",
|
|
2848
|
+
magnetic_column="susc_chi_mass",
|
|
2849
|
+
temp_unit="C",
|
|
2850
|
+
smooth_window=0,
|
|
2851
|
+
remove_holder=True,
|
|
2852
|
+
plot_derivative=True,
|
|
2853
|
+
plot_inverse=False,
|
|
2854
|
+
return_figure=False,
|
|
2855
|
+
):
|
|
2856
|
+
"""
|
|
2857
|
+
Plot the high-temperature X–T curve, and optionally its derivative
|
|
2858
|
+
and reciprocal using Bokeh.
|
|
2859
|
+
|
|
2860
|
+
Parameters:
|
|
2861
|
+
experiment (pandas.DataFrame):
|
|
2862
|
+
The IRM experiment data exported into MagIC format.
|
|
2863
|
+
temperature_column (str, optional):
|
|
2864
|
+
Name of the temperature column. Defaults to "meas_temp".
|
|
2865
|
+
magnetic_column (str, optional):
|
|
2866
|
+
Name of the susceptibility column. Defaults to "susc_chi_mass".
|
|
2867
|
+
temp_unit (str, optional):
|
|
2868
|
+
Unit of temperature, either "K" or "C". Defaults to "C".
|
|
2869
|
+
smooth_window (int, optional):
|
|
2870
|
+
Window size for running-average smoothing. Defaults to 0.
|
|
2871
|
+
remove_holder (bool, optional):
|
|
2872
|
+
If True, subtract the minimum holder signal. Defaults to True.
|
|
2873
|
+
plot_derivative (bool, optional):
|
|
2874
|
+
If True, generate dX/dT plot. Defaults to True.
|
|
2875
|
+
plot_inverse (bool, optional):
|
|
2876
|
+
If True, generate 1/X plot. Defaults to False.
|
|
2877
|
+
return_figure (bool, optional):
|
|
2878
|
+
If True, return the Bokeh figure objects. Defaults to False.
|
|
2879
|
+
|
|
2880
|
+
Returns:
|
|
2881
|
+
tuple[bokeh.plotting.figure.Figure, ...] or None:
|
|
2882
|
+
The requested Bokeh figures if return_figure is True;
|
|
2883
|
+
otherwise, None.
|
|
2884
|
+
"""
|
|
2885
|
+
warm_T, warm_X, cool_T, cool_X = split_warm_cool(
|
|
2886
|
+
experiment,
|
|
2887
|
+
temperature_column=temperature_column,
|
|
2888
|
+
magnetic_column=magnetic_column,
|
|
2889
|
+
)
|
|
2890
|
+
|
|
2891
|
+
if temp_unit == "C":
|
|
2892
|
+
warm_T = [T - 273.15 for T in warm_T]
|
|
2893
|
+
cool_T = [T - 273.15 for T in cool_T]
|
|
2894
|
+
else:
|
|
2895
|
+
raise ValueError('temp_unit must be either "K" or "C"')
|
|
2896
|
+
|
|
2897
|
+
if remove_holder:
|
|
2898
|
+
holder_w = min(warm_X)
|
|
2899
|
+
holder_c = min(cool_X)
|
|
2900
|
+
warm_X = [X - holder_w for X in warm_X]
|
|
2901
|
+
cool_X = [X - holder_c for X in cool_X]
|
|
2902
|
+
|
|
2903
|
+
swT, swX, _, _ = X_T_running_average(warm_T, warm_X, smooth_window)
|
|
2904
|
+
scT, scX, _, _ = X_T_running_average(cool_T, cool_X, smooth_window)
|
|
2905
|
+
|
|
2906
|
+
width = 900
|
|
2907
|
+
height = int(width / 1.618)
|
|
2908
|
+
title = experiment["specimen"].unique()[0]
|
|
2909
|
+
|
|
2910
|
+
p = figure(
|
|
2911
|
+
title=title,
|
|
2912
|
+
width=width,
|
|
2913
|
+
height=height,
|
|
2914
|
+
x_axis_label=f"Temperature (°{temp_unit})",
|
|
2915
|
+
y_axis_label="k (m³ kg⁻¹)",
|
|
2916
|
+
tools="pan,wheel_zoom,box_zoom,reset,save",
|
|
2917
|
+
)
|
|
2918
|
+
|
|
2919
|
+
r_warm_c = p.circle(
|
|
2920
|
+
warm_T, warm_X, legend_label="Heating",
|
|
2921
|
+
color="red", alpha=0.5, size=6,
|
|
2922
|
+
)
|
|
2923
|
+
r_warm_l = p.line(
|
|
2924
|
+
swT, swX, legend_label="Heating – smoothed",
|
|
2925
|
+
line_width=2, color="red",
|
|
2926
|
+
)
|
|
2927
|
+
|
|
2928
|
+
r_cool_c = p.circle(
|
|
2929
|
+
cool_T, cool_X, legend_label="Cooling",
|
|
2930
|
+
color="blue", alpha=0.5, size=6,
|
|
2931
|
+
)
|
|
2932
|
+
r_cool_l = p.line(
|
|
2933
|
+
scT, scX, legend_label="Cooling – smoothed",
|
|
2934
|
+
line_width=2, color="blue",
|
|
2935
|
+
)
|
|
2936
|
+
|
|
2937
|
+
p.add_tools(
|
|
2938
|
+
HoverTool(renderers=[r_warm_c, r_warm_l],
|
|
2939
|
+
tooltips=[("T", "@x"), ("Heating X", "@y")])
|
|
2940
|
+
)
|
|
2941
|
+
p.add_tools(
|
|
2942
|
+
HoverTool(renderers=[r_cool_c, r_cool_l],
|
|
2943
|
+
tooltips=[("T", "@x"), ("Cooling X", "@y")])
|
|
2944
|
+
)
|
|
2945
|
+
|
|
2946
|
+
p.grid.grid_line_color = "lightgray"
|
|
2947
|
+
p.outline_line_color = "black"
|
|
2948
|
+
p.background_fill_color = "white"
|
|
2949
|
+
p.legend.location = "top_left"
|
|
2950
|
+
|
|
2951
|
+
figs = [p]
|
|
2952
|
+
|
|
2953
|
+
if plot_derivative:
|
|
2954
|
+
p_dx = figure(
|
|
2955
|
+
title=f"{title} – dX/dT",
|
|
2956
|
+
width=width,
|
|
2957
|
+
height=height,
|
|
2958
|
+
x_axis_label=f"Temperature (°{temp_unit})",
|
|
2959
|
+
y_axis_label="dX/dT",
|
|
2960
|
+
tools="pan,wheel_zoom,box_zoom,reset,save",
|
|
2961
|
+
)
|
|
2962
|
+
dx_w = np.gradient(swX, swT)
|
|
2963
|
+
dx_c = np.gradient(scX, scT)
|
|
2964
|
+
r_dx_w = p_dx.line(
|
|
2965
|
+
swT, dx_w, legend_label="Heating – dX/dT",
|
|
2966
|
+
line_width=2, color="red"
|
|
2967
|
+
)
|
|
2968
|
+
r_dx_c = p_dx.line(
|
|
2969
|
+
scT, dx_c, legend_label="Cooling – dX/dT",
|
|
2970
|
+
line_width=2, color="blue"
|
|
2971
|
+
)
|
|
2972
|
+
p_dx.add_tools(
|
|
2973
|
+
HoverTool(renderers=[r_dx_w],
|
|
2974
|
+
tooltips=[("T", "@x"), ("dX/dT (heat)", "@y")])
|
|
2975
|
+
)
|
|
2976
|
+
p_dx.add_tools(
|
|
2977
|
+
HoverTool(renderers=[r_dx_c],
|
|
2978
|
+
tooltips=[("T", "@x"), ("dX/dT (cool)", "@y")])
|
|
2979
|
+
)
|
|
2980
|
+
p_dx.grid.grid_line_color = "lightgray"
|
|
2981
|
+
p_dx.outline_line_color = "black"
|
|
2982
|
+
p_dx.background_fill_color = "white"
|
|
2983
|
+
p_dx.legend.location = "top_left"
|
|
2984
|
+
figs.append(p_dx)
|
|
2985
|
+
|
|
2986
|
+
if plot_inverse:
|
|
2987
|
+
p_inv = figure(
|
|
2988
|
+
title=f"{title} – 1/X",
|
|
2989
|
+
width=width,
|
|
2990
|
+
height=height,
|
|
2991
|
+
x_axis_label=f"Temperature (°{temp_unit})",
|
|
2992
|
+
y_axis_label="1/X",
|
|
2993
|
+
tools="pan,wheel_zoom,box_zoom,reset,save",
|
|
2994
|
+
)
|
|
2995
|
+
inv_w = [1.0 / x for x in swX]
|
|
2996
|
+
inv_c = [1.0 / x for x in scX]
|
|
2997
|
+
r_inv_w = p_inv.line(
|
|
2998
|
+
swT, inv_w, legend_label="Heating – 1/X",
|
|
2999
|
+
line_width=2, color="red"
|
|
3000
|
+
)
|
|
3001
|
+
r_inv_c = p_inv.line(
|
|
3002
|
+
scT, inv_c, legend_label="Cooling – 1/X",
|
|
3003
|
+
line_width=2, color="blue"
|
|
3004
|
+
)
|
|
3005
|
+
p_inv.add_tools(
|
|
3006
|
+
HoverTool(renderers=[r_inv_w],
|
|
3007
|
+
tooltips=[("T", "@x"), ("1/X (heat)", "@y")])
|
|
3008
|
+
)
|
|
3009
|
+
p_inv.add_tools(
|
|
3010
|
+
HoverTool(renderers=[r_inv_c],
|
|
3011
|
+
tooltips=[("T", "@x"), ("1/X (cool)", "@y")])
|
|
3012
|
+
)
|
|
3013
|
+
p_inv.grid.grid_line_color = "lightgray"
|
|
3014
|
+
p_inv.outline_line_color = "black"
|
|
3015
|
+
p_inv.background_fill_color = "white"
|
|
3016
|
+
p_inv.legend.location = "top_left"
|
|
3017
|
+
figs.append(p_inv)
|
|
3018
|
+
|
|
3019
|
+
for fig in figs:
|
|
3020
|
+
show(fig)
|
|
3021
|
+
|
|
3022
|
+
if return_figure:
|
|
3023
|
+
return tuple(figs)
|
|
3024
|
+
return None
|
|
3025
|
+
|
|
3026
|
+
|
|
3027
|
+
def X_T_running_average(temp_list, chi_list, temp_window):
|
|
3028
|
+
"""
|
|
3029
|
+
Compute running averages and variances of susceptibility over a sliding
|
|
3030
|
+
temperature window.
|
|
3031
|
+
|
|
3032
|
+
Parameters
|
|
3033
|
+
----------
|
|
3034
|
+
temp_list : Sequence[float]
|
|
3035
|
+
Ordered list of temperatures (must be same length as chi_list).
|
|
3036
|
+
chi_list : Sequence[float]
|
|
3037
|
+
List of susceptibility values corresponding to each temperature.
|
|
3038
|
+
temp_window : float
|
|
3039
|
+
Total width of the temperature window. Each point averages data in
|
|
3040
|
+
[T_i - temp_window/2, T_i + temp_window/2].
|
|
3041
|
+
|
|
3042
|
+
Returns
|
|
3043
|
+
-------
|
|
3044
|
+
avg_temps : List[float]
|
|
3045
|
+
The mean temperature in each window (one per input point).
|
|
3046
|
+
avg_chis : List[float]
|
|
3047
|
+
The mean susceptibility in each window.
|
|
3048
|
+
temp_vars : List[float]
|
|
3049
|
+
The variance of temperatures in each window.
|
|
3050
|
+
chi_vars : List[float]
|
|
3051
|
+
The variance of susceptibility values in each window.
|
|
3052
|
+
"""
|
|
3053
|
+
if not temp_list or not chi_list or temp_window <= 0:
|
|
3054
|
+
return temp_list, chi_list, [], []
|
|
3055
|
+
|
|
3056
|
+
avg_temps = []
|
|
3057
|
+
avg_chis = []
|
|
3058
|
+
temp_vars = []
|
|
3059
|
+
chi_vars = []
|
|
3060
|
+
n = len(temp_list)
|
|
3061
|
+
|
|
3062
|
+
for i in range(n):
|
|
3063
|
+
# Determine the temperature range for the current point
|
|
3064
|
+
temp_center = temp_list[i]
|
|
3065
|
+
start_temp = temp_center - temp_window / 2
|
|
3066
|
+
end_temp = temp_center + temp_window / 2
|
|
3067
|
+
|
|
3068
|
+
# Get the indices within the temperature range
|
|
3069
|
+
indices = [j for j, t in enumerate(temp_list) if start_temp <= t <= end_temp]
|
|
3070
|
+
|
|
3071
|
+
# Calculate the average temperature and susceptibility for the current window
|
|
3072
|
+
if indices:
|
|
3073
|
+
temp_range = [temp_list[j] for j in indices]
|
|
3074
|
+
chi_range = [chi_list[j] for j in indices]
|
|
3075
|
+
avg_temp = sum(temp_range) / len(temp_range)
|
|
3076
|
+
avg_chi = sum(chi_range) / len(chi_range)
|
|
3077
|
+
temp_var = np.var(temp_range)
|
|
3078
|
+
chi_var = np.var(chi_range)
|
|
3079
|
+
else:
|
|
3080
|
+
avg_temp = temp_center
|
|
3081
|
+
avg_chi = chi_list[i]
|
|
3082
|
+
temp_var = 0
|
|
3083
|
+
chi_var = 0
|
|
3084
|
+
|
|
3085
|
+
avg_temps.append(avg_temp)
|
|
3086
|
+
avg_chis.append(avg_chi)
|
|
3087
|
+
temp_vars.append(temp_var)
|
|
3088
|
+
chi_vars.append(chi_var)
|
|
3089
|
+
|
|
3090
|
+
return avg_temps, avg_chis, temp_vars, chi_vars
|
|
3091
|
+
|
|
3092
|
+
|
|
3093
|
+
def optimize_X_T_running_average_window(experiment, min_temp_window=0, max_temp_window=50, steps=50, colormapwarm='tab20b', colormapcool='tab20c'):
|
|
3094
|
+
warm_T, warm_X, cool_T, cool_X = split_warm_cool(experiment)
|
|
3095
|
+
windows = np.linspace(min_temp_window, max_temp_window, steps)
|
|
3096
|
+
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(12, 6))
|
|
3097
|
+
|
|
3098
|
+
# Normalize the colormap
|
|
3099
|
+
norm = colors.Normalize(vmin=min_temp_window, vmax=max_temp_window)
|
|
3100
|
+
|
|
3101
|
+
for window in windows:
|
|
3102
|
+
_, warm_avg_chis, _, warm_chi_vars = X_T_running_average(warm_T, warm_X, window)
|
|
3103
|
+
warm_avg_rms, warm_avg_variance = calculate_avg_variance_and_rms(warm_X, warm_avg_chis, warm_chi_vars)
|
|
3104
|
+
_, cool_avg_chis, _, cool_chi_vars = X_T_running_average(cool_T, cool_X, window)
|
|
3105
|
+
cool_avg_rms, cool_avg_variance = calculate_avg_variance_and_rms(cool_X, cool_avg_chis, cool_chi_vars)
|
|
3106
|
+
|
|
3107
|
+
axs[0].scatter(warm_avg_variance, warm_avg_rms, c=window, cmap=colormapwarm, norm=norm)
|
|
3108
|
+
axs[1].scatter(cool_avg_variance, cool_avg_rms, c=window, cmap=colormapcool, norm=norm)
|
|
3109
|
+
# ax.text(warm_avg_variance, warm_avg_rms, f'{window:.2f}°C', fontsize=12, ha='right')
|
|
3110
|
+
# ax.text(cool_avg_variance, cool_avg_rms, f'{window:.2f}°C', fontsize=12, ha='right')
|
|
3111
|
+
for ax in axs:
|
|
3112
|
+
ax.set_xlabel('Average Variance', fontsize=14)
|
|
3113
|
+
ax.set_ylabel('Average RMS', fontsize=14)
|
|
3114
|
+
|
|
3115
|
+
ax.invert_yaxis()
|
|
3116
|
+
# show the colormaps and make sure the range is correct
|
|
3117
|
+
warm_cbar = plt.colorbar(plt.cm.ScalarMappable(cmap=colormapwarm, norm=norm), orientation='horizontal', ax=axs[0])
|
|
3118
|
+
warm_cbar.set_label('Warm cycle window size (°C)')
|
|
3119
|
+
cool_cbar = plt.colorbar(plt.cm.ScalarMappable(cmap=colormapcool, norm=norm), orientation='horizontal', ax=axs[1])
|
|
3120
|
+
cool_cbar.set_label('Cool cycle window size (°C)')
|
|
3121
|
+
plt.suptitle('Optimization of running average window size', fontsize=16)
|
|
3122
|
+
return fig, axs
|
|
3123
|
+
|
|
3124
|
+
|
|
3125
|
+
def calculate_avg_variance_and_rms(chi_list, avg_chis, chi_vars):
|
|
3126
|
+
rms_list = np.sqrt([(chi - avg_chi)**2 for chi, avg_chi in zip(chi_list, avg_chis)])
|
|
3127
|
+
total_rms = np.sum(rms_list)
|
|
3128
|
+
avg_rms = total_rms / len(rms_list)
|
|
3129
|
+
|
|
3130
|
+
total_variance = np.sum(chi_vars)
|
|
3131
|
+
avg_variance = total_variance / len(chi_vars)
|
|
3132
|
+
|
|
3133
|
+
return avg_rms, avg_variance
|
|
3134
|
+
|
|
3135
|
+
|
|
3136
|
+
# backfield data processing functions
|
|
3137
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
3138
|
+
def backfield_data_processing(experiment, field='treat_dc_field', magnetization='magn_mass', smooth_frac=0.0, drop_first=False):
|
|
3139
|
+
'''
|
|
3140
|
+
Function to process the backfield data including shifting the magnetic
|
|
3141
|
+
moment to be positive values taking the log base 10 of the magnetic
|
|
3142
|
+
field values and writing these new fields into the experiment attribute
|
|
3143
|
+
table
|
|
3144
|
+
|
|
3145
|
+
Parameters
|
|
3146
|
+
----------
|
|
3147
|
+
experiment : DataFrame
|
|
3148
|
+
DataFrame containing the backfield data
|
|
3149
|
+
field : str
|
|
3150
|
+
The name of the treatment field column in the DataFrame
|
|
3151
|
+
magnetization : str
|
|
3152
|
+
The name of the magnetization column in the DataFrame
|
|
3153
|
+
smooth_frac : float
|
|
3154
|
+
Fraction of the data to be used for LOWESS smoothing, value must be between 0 and 1
|
|
3155
|
+
drop_first : bool
|
|
3156
|
+
Whether to drop the first data point or not
|
|
3157
|
+
in some cases you may want to drop the first data point to avoid negative log values
|
|
3158
|
+
|
|
3159
|
+
Returns
|
|
3160
|
+
-------
|
|
3161
|
+
DataFrame
|
|
3162
|
+
The processed experiment DataFrame with new attributes.
|
|
3163
|
+
'''
|
|
3164
|
+
assert smooth_frac >= 0 and smooth_frac <= 1, 'smooth_frac must be between 0 and 1'
|
|
3165
|
+
assert isinstance(drop_first, bool), 'drop_first must be a boolean'
|
|
3166
|
+
|
|
3167
|
+
experiment = experiment.reset_index(drop=True)
|
|
3168
|
+
# check and make sure to force drop first row if the first treat field is in the wrong direction
|
|
3169
|
+
if experiment[field].iloc[0] > 0:
|
|
3170
|
+
drop_first = True
|
|
3171
|
+
if drop_first:
|
|
3172
|
+
experiment = experiment.iloc[1:].reset_index(drop=1)
|
|
3173
|
+
|
|
3174
|
+
if find_y_crossing(experiment[field], experiment[magnetization]) is not None:
|
|
3175
|
+
Bcr = np.abs(find_y_crossing(experiment[field], experiment[magnetization]))
|
|
3176
|
+
else:
|
|
3177
|
+
Bcr = np.nan
|
|
3178
|
+
# to plot the backfield data in the conventional way, we need to shift the magnetization to be positive
|
|
3179
|
+
experiment['magn_mass_shift'] = [i - experiment[magnetization].min() for i in experiment[magnetization]]
|
|
3180
|
+
# we then calculate the log10 of the treatment fields
|
|
3181
|
+
experiment['log_dc_field'] = np.log10(-experiment[field]*1e3)
|
|
3182
|
+
# loess smoothing
|
|
3183
|
+
spl = lowess(experiment['magn_mass_shift'], experiment['log_dc_field'], frac=smooth_frac)
|
|
3184
|
+
experiment['smoothed_magn_mass_shift'] = spl[:, 1]
|
|
3185
|
+
experiment['smoothed_log_dc_field'] = spl[:, 0]
|
|
3186
|
+
return experiment, Bcr
|
|
3187
|
+
|
|
3188
|
+
def plot_backfield_data(
|
|
3189
|
+
experiment,
|
|
3190
|
+
field="treat_dc_field",
|
|
3191
|
+
magnetization="magn_mass",
|
|
3192
|
+
figsize=(5, 12),
|
|
3193
|
+
plot_raw=True,
|
|
3194
|
+
plot_processed=True,
|
|
3195
|
+
plot_spectrum=True,
|
|
3196
|
+
interactive=False,
|
|
3197
|
+
return_figure=True,
|
|
3198
|
+
show_plot=True,
|
|
3199
|
+
):
|
|
3200
|
+
"""
|
|
3201
|
+
Plot backfield data: raw, processed, and coercivity spectrum.
|
|
3202
|
+
|
|
3203
|
+
Data processing steps:
|
|
3204
|
+
- Raw: magnetization vs. field (T).
|
|
3205
|
+
- Processed: magn_mass_shift = magn_mass − min(magn_mass);
|
|
3206
|
+
log_dc_field = log10(−field·1e3) (log10 mT axis).
|
|
3207
|
+
- Spectrum: derivative −ΔM/Δ(log B).
|
|
3208
|
+
|
|
3209
|
+
Parameters
|
|
3210
|
+
----------
|
|
3211
|
+
experiment : DataFrame
|
|
3212
|
+
Must contain raw and, if requested, processed columns.
|
|
3213
|
+
plot_raw : bool
|
|
3214
|
+
plot_processed : bool
|
|
3215
|
+
plot_spectrum : bool
|
|
3216
|
+
interactive : bool
|
|
3217
|
+
return_figure : bool
|
|
3218
|
+
show_plot : bool
|
|
3219
|
+
|
|
3220
|
+
Returns
|
|
3221
|
+
-------
|
|
3222
|
+
Matplotlib (fig, axes) or Bokeh grid or None
|
|
3223
|
+
"""
|
|
3224
|
+
# check columns
|
|
3225
|
+
req = []
|
|
3226
|
+
if plot_raw:
|
|
3227
|
+
req += [field, magnetization]
|
|
3228
|
+
if plot_processed or plot_spectrum:
|
|
3229
|
+
req += [
|
|
3230
|
+
"log_dc_field",
|
|
3231
|
+
"magn_mass_shift",
|
|
3232
|
+
"smoothed_log_dc_field",
|
|
3233
|
+
"smoothed_magn_mass_shift",
|
|
3234
|
+
]
|
|
3235
|
+
missing = [c for c in req if c not in experiment.columns]
|
|
3236
|
+
if missing:
|
|
3237
|
+
raise KeyError(f"Missing columns: {missing}")
|
|
3238
|
+
|
|
3239
|
+
# prepare spectrum
|
|
3240
|
+
if plot_spectrum:
|
|
3241
|
+
log_b = experiment["log_dc_field"]
|
|
3242
|
+
shift_m = experiment["magn_mass_shift"]
|
|
3243
|
+
raw_dy = -np.diff(shift_m) / np.diff(log_b)
|
|
3244
|
+
raw_dx_log = log_b.rolling(2).mean().dropna()
|
|
3245
|
+
smooth_dy = -np.diff(experiment["smoothed_magn_mass_shift"]) / np.diff(
|
|
3246
|
+
experiment["smoothed_log_dc_field"]
|
|
3247
|
+
)
|
|
3248
|
+
smooth_dx_log = experiment["smoothed_log_dc_field"].rolling(2).mean().dropna()
|
|
3249
|
+
# axis: convert log10(mT) → mT for plotting, but axis scale remains log
|
|
3250
|
+
raw_dx = 10**raw_dx_log
|
|
3251
|
+
smooth_dx = 10**smooth_dx_log
|
|
3252
|
+
|
|
3253
|
+
if interactive:
|
|
3254
|
+
tools = [
|
|
3255
|
+
HoverTool(tooltips=[("Field (mT)", "@x"), ("Mag", "@y")]),
|
|
3256
|
+
"pan,box_zoom,reset"
|
|
3257
|
+
]
|
|
3258
|
+
figs = []
|
|
3259
|
+
palette = Category10[3]
|
|
3260
|
+
|
|
3261
|
+
if plot_raw:
|
|
3262
|
+
p0 = figure(
|
|
3263
|
+
title="Raw backfield",
|
|
3264
|
+
x_axis_label="Field (T)",
|
|
3265
|
+
y_axis_label="Magnetization",
|
|
3266
|
+
tools=tools,
|
|
3267
|
+
sizing_mode="stretch_width",
|
|
3268
|
+
)
|
|
3269
|
+
p0.scatter(
|
|
3270
|
+
experiment[field],
|
|
3271
|
+
experiment[magnetization],
|
|
3272
|
+
legend_label="raw",
|
|
3273
|
+
color=palette[0],
|
|
3274
|
+
size=6,
|
|
3275
|
+
)
|
|
3276
|
+
p0.line(experiment[field], experiment[magnetization], color=palette[0])
|
|
3277
|
+
p0.legend.click_policy = "hide"
|
|
3278
|
+
figs.append(p0)
|
|
3279
|
+
|
|
3280
|
+
if plot_processed:
|
|
3281
|
+
x_shifted = 10 ** experiment["log_dc_field"]
|
|
3282
|
+
x_smooth = 10 ** experiment["smoothed_log_dc_field"]
|
|
3283
|
+
p1 = figure(
|
|
3284
|
+
title="Processed backfield",
|
|
3285
|
+
x_axis_label="Field (mT)",
|
|
3286
|
+
y_axis_label="Magnetization",
|
|
3287
|
+
x_axis_type="log",
|
|
3288
|
+
tools=tools,
|
|
3289
|
+
sizing_mode="stretch_width",
|
|
3290
|
+
)
|
|
3291
|
+
p1.scatter(
|
|
3292
|
+
x_shifted,
|
|
3293
|
+
experiment["magn_mass_shift"],
|
|
3294
|
+
legend_label="shifted",
|
|
3295
|
+
color=palette[1],
|
|
3296
|
+
size=6,
|
|
3297
|
+
)
|
|
3298
|
+
p1.line(
|
|
3299
|
+
x_smooth,
|
|
3300
|
+
experiment["smoothed_magn_mass_shift"],
|
|
3301
|
+
color=palette[1],
|
|
3302
|
+
legend_label="smoothed",
|
|
3303
|
+
)
|
|
3304
|
+
p1.legend.click_policy = "hide"
|
|
3305
|
+
figs.append(p1)
|
|
3306
|
+
|
|
3307
|
+
if plot_spectrum:
|
|
3308
|
+
p2 = figure(
|
|
3309
|
+
title="Coercivity spectrum",
|
|
3310
|
+
x_axis_label="Field (mT)",
|
|
3311
|
+
y_axis_label="dM/dB",
|
|
3312
|
+
x_axis_type="log",
|
|
3313
|
+
tools=tools,
|
|
3314
|
+
sizing_mode="stretch_width",
|
|
3315
|
+
)
|
|
3316
|
+
p2.scatter(raw_dx, raw_dy, legend_label="raw spectrum",
|
|
3317
|
+
color=palette[2], size=6)
|
|
3318
|
+
p2.line(smooth_dx, smooth_dy, color=palette[2],
|
|
3319
|
+
legend_label="smoothed spectrum")
|
|
3320
|
+
p2.legend.click_policy = "hide"
|
|
3321
|
+
figs.append(p2)
|
|
3322
|
+
|
|
3323
|
+
grid = gridplot(figs, ncols=1, sizing_mode="stretch_width")
|
|
3324
|
+
if show_plot:
|
|
3325
|
+
show(grid)
|
|
3326
|
+
if return_figure:
|
|
3327
|
+
return grid
|
|
3328
|
+
return None
|
|
3329
|
+
|
|
3330
|
+
# static Matplotlib
|
|
3331
|
+
panels = []
|
|
3332
|
+
if plot_raw:
|
|
3333
|
+
panels.append("raw")
|
|
3334
|
+
if plot_processed:
|
|
3335
|
+
panels.append("processed")
|
|
3336
|
+
if plot_spectrum:
|
|
3337
|
+
panels.append("spectrum")
|
|
3338
|
+
|
|
3339
|
+
n = len(panels)
|
|
3340
|
+
fig, axes = plt.subplots(nrows=n, ncols=1, figsize=figsize)
|
|
3341
|
+
if n == 1:
|
|
3342
|
+
axes = [axes]
|
|
3343
|
+
|
|
3344
|
+
for ax, panel in zip(axes, panels):
|
|
3345
|
+
if panel == "raw":
|
|
3346
|
+
ax.scatter(
|
|
3347
|
+
experiment[field], experiment[magnetization], c="k", s=10, label="raw"
|
|
3348
|
+
)
|
|
3349
|
+
ax.plot(experiment[field], experiment[magnetization], c="k")
|
|
3350
|
+
ax.set(title="raw backfield", xlabel="field (T)", ylabel="magnetization")
|
|
3351
|
+
ax.legend()
|
|
3352
|
+
elif panel == "processed":
|
|
3353
|
+
ax.scatter(
|
|
3354
|
+
experiment["log_dc_field"],
|
|
3355
|
+
experiment["magn_mass_shift"],
|
|
3356
|
+
c="gray",
|
|
3357
|
+
s=10,
|
|
3358
|
+
label="shifted",
|
|
3359
|
+
)
|
|
3360
|
+
ax.plot(
|
|
3361
|
+
experiment["smoothed_log_dc_field"],
|
|
3362
|
+
experiment["smoothed_magn_mass_shift"],
|
|
3363
|
+
c="k",
|
|
3364
|
+
label="smoothed",
|
|
3365
|
+
)
|
|
3366
|
+
ticks = ax.get_xticks()
|
|
3367
|
+
ax.set_xticklabels([f"{round(10**t, 1)}" for t in ticks])
|
|
3368
|
+
ax.set(
|
|
3369
|
+
title="processed", xlabel="field (mT)", ylabel="magnetization"
|
|
3370
|
+
)
|
|
3371
|
+
ax.legend()
|
|
3372
|
+
else: # spectrum
|
|
3373
|
+
ax.scatter(raw_dx_log, raw_dy, c="gray", s=10, label="raw spectrum")
|
|
3374
|
+
ax.plot(smooth_dx_log, smooth_dy, c="k", label="smoothed spectrum")
|
|
3375
|
+
ticks = ax.get_xticks()
|
|
3376
|
+
ax.set_xticklabels([f"{round(10**t, 1)}" for t in ticks])
|
|
3377
|
+
ax.set(title="spectrum", xlabel="field (mT)", ylabel="dM/dB")
|
|
3378
|
+
ax.legend()
|
|
3379
|
+
|
|
3380
|
+
fig.tight_layout()
|
|
3381
|
+
if show_plot:
|
|
3382
|
+
plt.show()
|
|
3383
|
+
if return_figure:
|
|
3384
|
+
return fig, axes
|
|
3385
|
+
return None
|
|
3386
|
+
|
|
3387
|
+
|
|
3388
|
+
def backfield_unmixing(field, magnetization, n_comps=1, parameters=None, iter=True, n_iter=3, skewed=True):
|
|
3389
|
+
'''
|
|
3390
|
+
backfield unmixing for a single experiment
|
|
3391
|
+
|
|
3392
|
+
Parameters
|
|
3393
|
+
----------
|
|
3394
|
+
field : np.array
|
|
3395
|
+
The field values in log10 unit in the experiment
|
|
3396
|
+
magnetization : np.array
|
|
3397
|
+
The magnetization values in the experiment
|
|
3398
|
+
n_comps : int
|
|
3399
|
+
Number of components to unmix the data into
|
|
3400
|
+
params : Pandas DataFrame
|
|
3401
|
+
Initial values for the model parameters
|
|
3402
|
+
should be constructed as the following columns:
|
|
3403
|
+
- amplitude in arbitrary scale
|
|
3404
|
+
- center in unit of mT
|
|
3405
|
+
- sigma in unit of mT
|
|
3406
|
+
- gamma in arbitrary scale
|
|
3407
|
+
|amplitude|center|sigma|gamma|
|
|
3408
|
+
|---|---|---|---|
|
|
3409
|
+
|1.0|100|10|0.0|
|
|
3410
|
+
|...|...|...|...|
|
|
3411
|
+
the program will automatically go through the rows and extract these inital parameter values
|
|
3412
|
+
If the parameters are not given, we will run an automated program to make initial guess
|
|
3413
|
+
|
|
3414
|
+
iter : bool
|
|
3415
|
+
Whether to iterate the fitting process or not. It is useful to iterate the fitting process
|
|
3416
|
+
to make sure the parameters are converged
|
|
3417
|
+
n_iter : int
|
|
3418
|
+
Number of iterations to run the fitting process
|
|
3419
|
+
skewed : bool
|
|
3420
|
+
Whether to use skewed Gaussian model or not
|
|
3421
|
+
if False, the program will use normal Gaussian model
|
|
3422
|
+
|
|
3423
|
+
Returns
|
|
3424
|
+
-------
|
|
3425
|
+
result : lmfit.model.ModelResult
|
|
3426
|
+
The result of the fitting process
|
|
3427
|
+
parameters : DataFrame
|
|
3428
|
+
The updated parameters table
|
|
3429
|
+
'''
|
|
3430
|
+
|
|
3431
|
+
assert n_comps > 0, 'n_component must be greater than 0'
|
|
3432
|
+
assert isinstance(n_comps, int), 'n_component must be an integer'
|
|
3433
|
+
assert isinstance(parameters, pd.DataFrame), f"Expected a pandas DataFrame, but got {type(parameters).__name__}"
|
|
3434
|
+
assert n_comps == parameters.shape[0], 'number of components must match the number of rows in the parameters table'
|
|
3435
|
+
assert n_iter > 0, 'n_iter must be greater than 0'
|
|
3436
|
+
|
|
3437
|
+
if not iter:
|
|
3438
|
+
n_iter = 1
|
|
3439
|
+
|
|
3440
|
+
# re-calculate the derivatives based on the smoothed data columns
|
|
3441
|
+
smoothed_derivatives_y = -np.diff(magnetization)/np.diff(field)
|
|
3442
|
+
smoothed_derivatives_x = pd.Series(field).rolling(window=2).mean().dropna()
|
|
3443
|
+
|
|
3444
|
+
# create the model depending on the number of components specified
|
|
3445
|
+
composite_model = None
|
|
3446
|
+
params = Parameters()
|
|
3447
|
+
for i in range(n_comps):
|
|
3448
|
+
prefix = f'g{i+1}_'
|
|
3449
|
+
model = SkewedGaussianModel(prefix=prefix)
|
|
3450
|
+
|
|
3451
|
+
# Initial parameter guesses
|
|
3452
|
+
params.add(f'{prefix}amplitude', value=parameters['amplitude'][i])
|
|
3453
|
+
params.add(f'{prefix}center', value=np.log10(parameters['center'][i]))
|
|
3454
|
+
params.add(f'{prefix}sigma', value=np.log10(parameters['sigma'][i]))
|
|
3455
|
+
params.add(f'{prefix}gamma', value=parameters['gamma'][i])
|
|
3456
|
+
|
|
3457
|
+
# now let's set bounds to the parameters to help fitting algorithm converge
|
|
3458
|
+
params[f'{prefix}amplitude'].min = 0 # Bounds for amplitude parameters
|
|
3459
|
+
params[f'{prefix}amplitude'].max = 1 # Bounds for proportion/amplitude parameters
|
|
3460
|
+
params[f'{prefix}center'].min = np.min(field) # Bounds for center parameters
|
|
3461
|
+
params[f'{prefix}center'].max = np.max(field) # Bounds for center parameters
|
|
3462
|
+
params[f'{prefix}sigma'].min = 0
|
|
3463
|
+
params[f'{prefix}sigma'].max = np.max(field)-np.min(field) # Bounds for sigma parameters
|
|
3464
|
+
|
|
3465
|
+
# restrict to normal distribution if skewed is False
|
|
3466
|
+
if skewed == False:
|
|
3467
|
+
params[f'{prefix}gamma'].min = 0
|
|
3468
|
+
params[f'{prefix}gamma'].max = 0
|
|
3469
|
+
|
|
3470
|
+
if composite_model is None:
|
|
3471
|
+
composite_model = model
|
|
3472
|
+
else:
|
|
3473
|
+
composite_model += model
|
|
3474
|
+
|
|
3475
|
+
def fitting_function(y, params, x):
|
|
3476
|
+
result = composite_model.fit(y, params, x=x)
|
|
3477
|
+
for i in range(n_comps):
|
|
3478
|
+
prefix = f'g{i+1}_'
|
|
3479
|
+
parameters.loc[i, 'amplitude'] = result.params[f'{prefix}amplitude'].value # convert back to original scale
|
|
3480
|
+
parameters.loc[i, 'center'] = 10**result.params[f'{prefix}center'].value # convert back to mT
|
|
3481
|
+
parameters.loc[i, 'sigma'] = 10**result.params[f'{prefix}sigma'].value # convert back to mT
|
|
3482
|
+
parameters.loc[i, 'gamma'] = result.params[f'{prefix}gamma'].value
|
|
3483
|
+
return result, parameters
|
|
3484
|
+
|
|
3485
|
+
result, parameters = fitting_function(smoothed_derivatives_y/np.max(smoothed_derivatives_y), params, x=smoothed_derivatives_x)
|
|
3486
|
+
|
|
3487
|
+
if iter:
|
|
3488
|
+
for i in range(n_iter):
|
|
3489
|
+
result, parameters = fitting_function(smoothed_derivatives_y/np.max(smoothed_derivatives_y), result.params, x=smoothed_derivatives_x)
|
|
3490
|
+
|
|
3491
|
+
return result, parameters
|
|
3492
|
+
|
|
3493
|
+
|
|
3494
|
+
def plot_backfield_unmixing_result(experiment, result, sigma=2, figsize=(8,6), n=200):
|
|
3495
|
+
'''
|
|
3496
|
+
function for plotting the backfield unmixing results
|
|
3497
|
+
|
|
3498
|
+
Parameters
|
|
3499
|
+
----------
|
|
3500
|
+
experiment : pandas.DataFrame
|
|
3501
|
+
the backfield experiment data
|
|
3502
|
+
result : lmfit.model.ModelResult
|
|
3503
|
+
the result of the backfield unmixing
|
|
3504
|
+
sigma : float
|
|
3505
|
+
the sigma value for the uncertainty band
|
|
3506
|
+
figsize : tuple
|
|
3507
|
+
the figure size
|
|
3508
|
+
n : int
|
|
3509
|
+
the number of points for the x-axis interpolation
|
|
3510
|
+
you may choose a large enough number so that the result components
|
|
3511
|
+
and the best fit curves are smooth
|
|
3512
|
+
|
|
3513
|
+
Returns
|
|
3514
|
+
-------
|
|
3515
|
+
fig : matplotlib.figure.Figure
|
|
3516
|
+
the figure object
|
|
3517
|
+
ax : matplotlib.axes._axes.Axes
|
|
3518
|
+
the axes object
|
|
3519
|
+
'''
|
|
3520
|
+
raw_derivatives_y = -np.diff(experiment['magn_mass_shift'])/np.diff(experiment['log_dc_field'])
|
|
3521
|
+
raw_derivatives_x = experiment['log_dc_field'].rolling(window=2).mean().dropna()
|
|
3522
|
+
smoothed_derivatives_x = experiment['smoothed_log_dc_field'].rolling(window=2).mean().dropna()
|
|
3523
|
+
|
|
3524
|
+
x_interp = np.linspace(smoothed_derivatives_x.min(), smoothed_derivatives_x.max(), n)
|
|
3525
|
+
best_fit_interp = result.eval(x=x_interp) * np.max(raw_derivatives_y)
|
|
3526
|
+
dely_interp = result.eval_uncertainty(x=x_interp, sigma=sigma) * np.max(raw_derivatives_y)
|
|
3527
|
+
# impose bounds on the dely to be smaller than the best fit
|
|
3528
|
+
dely_interp = [min(dely_interp[i], best_fit_interp[i]) for i in range(len(dely_interp))]
|
|
3529
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
3530
|
+
# first plot the scatter raw dMdB data
|
|
3531
|
+
ax.scatter(raw_derivatives_x, raw_derivatives_y, c='grey', marker='o', s=10, label='raw coercivity spectrum')
|
|
3532
|
+
# plot the total best fit
|
|
3533
|
+
ax.plot(x_interp, best_fit_interp, '-', color='k', alpha=0.6, label='total spectrum best fit')
|
|
3534
|
+
ax.fill_between(x_interp,
|
|
3535
|
+
[max(best_fit_interp[j]-dely_interp[j],0) for j in range(len(best_fit_interp))],
|
|
3536
|
+
best_fit_interp+dely_interp,
|
|
3537
|
+
color="#8A8A8A",
|
|
3538
|
+
label=f'total {sigma}-$\sigma$ band', alpha=0.5)
|
|
3539
|
+
if len(result.components) > 1:
|
|
3540
|
+
for i in range(len(result.components)):
|
|
3541
|
+
this_comp_interp = result.eval_components(x=x_interp)[f'g{i+1}_'] * np.max(raw_derivatives_y)
|
|
3542
|
+
this_dely = result.dely_comps[f'g{i+1}_'] * np.max(raw_derivatives_y)
|
|
3543
|
+
# impose bounds on the dely to be smaller than the best fit for the component
|
|
3544
|
+
this_dely = [min(this_dely[j], this_comp_interp[j]) for j in range(len(this_dely))]
|
|
3545
|
+
ax.plot(x_interp, this_comp_interp, c=f'C{i}', label=f'component #{i+1}, {sigma}-$\sigma$ band')
|
|
3546
|
+
lower_bound = [max(this_comp_interp[j]-this_dely[j],0) for j in range(len(this_comp_interp))]
|
|
3547
|
+
upper_bound = this_comp_interp+this_dely
|
|
3548
|
+
ax.fill_between(x_interp,
|
|
3549
|
+
lower_bound,
|
|
3550
|
+
upper_bound,
|
|
3551
|
+
color=f'C{i}', alpha=0.5)
|
|
3552
|
+
|
|
3553
|
+
xticks = ax.get_xticks()
|
|
3554
|
+
ax.set_xticklabels([f'{int(10**i)}' for i in xticks])
|
|
3555
|
+
ax.legend()
|
|
3556
|
+
ax.set_title('coercivity unmixing results')
|
|
3557
|
+
ax.set_xlabel('treatment field (mT)', fontsize=14)
|
|
3558
|
+
ax.set_ylabel('dM/dB', fontsize=14)
|
|
3559
|
+
return fig, ax
|
|
3560
|
+
|
|
3561
|
+
def interactive_backfield_fit(field, magnetization, n_components, figsize=(10, 6)):
|
|
3562
|
+
"""
|
|
3563
|
+
Function for interactive backfield unmixing using skew‑normal distributions.
|
|
3564
|
+
No uncertainty propagation is shown; this function is useful for estimating
|
|
3565
|
+
initial guesses for parameters.
|
|
3566
|
+
|
|
3567
|
+
*Important note:* in a Jupyter notebook use the `%matplotlib widget`
|
|
3568
|
+
command to enable live figure updates.
|
|
3569
|
+
|
|
3570
|
+
Parameters
|
|
3571
|
+
----------
|
|
3572
|
+
field : array‑like
|
|
3573
|
+
The field values in log scale.
|
|
3574
|
+
magnetization : array‑like
|
|
3575
|
+
The magnetization values in log scale.
|
|
3576
|
+
n_components : int
|
|
3577
|
+
The number of skew‑normal components to fit.
|
|
3578
|
+
figsize : tuple, optional
|
|
3579
|
+
The size of the figure to display. Default is (10, 6).
|
|
3580
|
+
|
|
3581
|
+
Returns
|
|
3582
|
+
-------
|
|
3583
|
+
pandas.DataFrame
|
|
3584
|
+
A DataFrame of the most recent fit parameters with columns
|
|
3585
|
+
`amplitude`, `center`, `sigma`, and `gamma`. This DataFrame is
|
|
3586
|
+
updated in place as sliders are moved.
|
|
3587
|
+
"""
|
|
3588
|
+
|
|
3589
|
+
final_fit = {"df": None}
|
|
3590
|
+
|
|
3591
|
+
# Calculate the smoothed derivative
|
|
3592
|
+
smoothed_derivatives_y = -np.diff(magnetization) / np.diff(field)
|
|
3593
|
+
smoothed_derivatives_x = pd.Series(field).rolling(window=2).mean().dropna()
|
|
3594
|
+
|
|
3595
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
3596
|
+
fig.canvas.header_visible = False
|
|
3597
|
+
|
|
3598
|
+
# Store all sliders and text
|
|
3599
|
+
sliders = []
|
|
3600
|
+
texts = []
|
|
3601
|
+
|
|
3602
|
+
def create_slider_dict(name, min_val, max_val, step, description, value=0.0):
|
|
3603
|
+
return {
|
|
3604
|
+
f"{name}_{i}": FloatSlider(
|
|
3605
|
+
value=value,
|
|
3606
|
+
min=min_val,
|
|
3607
|
+
max=max_val,
|
|
3608
|
+
step=step,
|
|
3609
|
+
description=f'{description}_{i+1}',
|
|
3610
|
+
continuous_update=False
|
|
3611
|
+
)
|
|
3612
|
+
for i in range(n_components)
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
amp_slidebars = create_slider_dict('amplitude', 0.0, 1, 0.01, 'amplitude')
|
|
3616
|
+
center_slidebars = create_slider_dict('center', 0.0, 10**np.max(field), 10**np.max(field) / 100, 'center', value=10**np.max(field)/2)
|
|
3617
|
+
sigma_slidebars = create_slider_dict('sigma', 0.0, 10**np.max(field), 10**np.max(field) / 100, 'sigma', value=10**np.max(field)/2)
|
|
3618
|
+
gamma_slidebars = create_slider_dict('gamma', -10.0, 10.0, 0.01, 'gamma')
|
|
3619
|
+
|
|
3620
|
+
# Collect all sliders by component for display and registration
|
|
3621
|
+
|
|
3622
|
+
for i in range(n_components):
|
|
3623
|
+
# Create sliders for each component
|
|
3624
|
+
amp_slider = amp_slidebars[f'amplitude_{i}']
|
|
3625
|
+
center_slider = center_slidebars[f'center_{i}']
|
|
3626
|
+
sigma_slider = sigma_slidebars[f'sigma_{i}']
|
|
3627
|
+
gamma_slider = gamma_slidebars[f'gamma_{i}']
|
|
3628
|
+
# Create a dictionary for each component
|
|
3629
|
+
d = {
|
|
3630
|
+
'amplitude': amp_slider,
|
|
3631
|
+
'center': center_slider,
|
|
3632
|
+
'sigma': sigma_slider,
|
|
3633
|
+
'gamma': gamma_slider
|
|
3634
|
+
}
|
|
3635
|
+
# Add sliders to the list
|
|
3636
|
+
sliders.append(VBox([amp_slider, center_slider, sigma_slider, gamma_slider]))
|
|
3637
|
+
|
|
3638
|
+
# now add the same amount of text boxes to update the best fit parameters on the fly
|
|
3639
|
+
for i in range(n_components):
|
|
3640
|
+
# make text boxes for each parameter
|
|
3641
|
+
amp_text = widgets.Text(value=str(amp_slidebars[f'amplitude_{i}'].value), description=f'amplitude_{i+1}')
|
|
3642
|
+
center_text = widgets.Text(value=str(center_slidebars[f'center_{i}'].value), description=f'center_{i+1}')
|
|
3643
|
+
sigma_text = widgets.Text(value=str(sigma_slidebars[f'sigma_{i}'].value), description=f'sigma_{i+1}')
|
|
3644
|
+
gamma_text = widgets.Text(value=str(gamma_slidebars[f'gamma_{i}'].value), description=f'gamma_{i+1}')
|
|
3645
|
+
# add the text boxes to the texts list
|
|
3646
|
+
texts.append(VBox([amp_text, center_text, sigma_text, gamma_text]))
|
|
3647
|
+
|
|
3648
|
+
# Display sliders
|
|
3649
|
+
display(HBox(sliders))
|
|
3650
|
+
display(HBox(texts))
|
|
3651
|
+
|
|
3652
|
+
def update_plot(*args):
|
|
3653
|
+
ax.clear()
|
|
3654
|
+
ax.scatter(smoothed_derivatives_x, smoothed_derivatives_y, marker='o', s=5, alpha=0.5, color='grey', label='original data')
|
|
3655
|
+
ax.set_xlabel('Field', fontsize=12)
|
|
3656
|
+
ax.set_ylabel('dM/dB', fontsize=12)
|
|
3657
|
+
|
|
3658
|
+
# Get values from sliders
|
|
3659
|
+
amp = [amp_slidebars[f'amplitude_{i}'].value for i in range(n_components)]
|
|
3660
|
+
center = [center_slidebars[f'center_{i}'].value for i in range(n_components)]
|
|
3661
|
+
sigma = [sigma_slidebars[f'sigma_{i}'].value for i in range(n_components)]
|
|
3662
|
+
gamma = [gamma_slidebars[f'gamma_{i}'].value for i in range(n_components)]
|
|
3663
|
+
|
|
3664
|
+
# Create a DataFrame for the parameters
|
|
3665
|
+
parameters = pd.DataFrame({
|
|
3666
|
+
'amplitude': amp,
|
|
3667
|
+
'center': center,
|
|
3668
|
+
'sigma': sigma,
|
|
3669
|
+
'gamma': gamma
|
|
3670
|
+
})
|
|
3671
|
+
|
|
3672
|
+
result, updated_parameters = backfield_unmixing(field, magnetization, n_comps=n_components, parameters=parameters)
|
|
3673
|
+
# update the text boxes with the updated parameters
|
|
3674
|
+
for i in range(n_components):
|
|
3675
|
+
amp_text = texts[i].children[0]
|
|
3676
|
+
center_text = texts[i].children[1]
|
|
3677
|
+
sigma_text = texts[i].children[2]
|
|
3678
|
+
gamma_text = texts[i].children[3]
|
|
3679
|
+
amp_text.value = str(updated_parameters['amplitude'][i].round(4))
|
|
3680
|
+
center_text.value = str(updated_parameters['center'][i].round(4))
|
|
3681
|
+
sigma_text.value = str(updated_parameters['sigma'][i].round(4))
|
|
3682
|
+
gamma_text.value = str(updated_parameters['gamma'][i].round(4))
|
|
3683
|
+
|
|
3684
|
+
ax.plot(field, result.eval(x=field)*np.max(smoothed_derivatives_y), '-', color='k', alpha=0.6, label='total spectrum best fit')
|
|
3685
|
+
if len(result.components) > 1:
|
|
3686
|
+
for i in range(len(result.components)):
|
|
3687
|
+
this_comp = result.eval_components(x=field)[f'g{i+1}_']*np.max(smoothed_derivatives_y)
|
|
3688
|
+
ax.plot(field, this_comp, c=f'C{i}', label=f'component #{i+1}')
|
|
3689
|
+
|
|
3690
|
+
ax.set_xticklabels([f'{int(10**i)}' for i in ax.get_xticks()])
|
|
3691
|
+
ax.legend()
|
|
3692
|
+
|
|
3693
|
+
fig.canvas.draw()
|
|
3694
|
+
if final_fit["df"] is None:
|
|
3695
|
+
final_fit["df"] = updated_parameters.copy()
|
|
3696
|
+
else:
|
|
3697
|
+
# overwrite the existing DataFrame’s values
|
|
3698
|
+
for col in updated_parameters.columns:
|
|
3699
|
+
final_fit["df"][col] = updated_parameters[col].values
|
|
3700
|
+
|
|
3701
|
+
# Attach observers
|
|
3702
|
+
for box in sliders:
|
|
3703
|
+
for slider in box.children:
|
|
3704
|
+
if isinstance(slider, FloatSlider):
|
|
3705
|
+
slider.observe(update_plot, names='value')
|
|
3706
|
+
|
|
3707
|
+
update_plot()
|
|
3708
|
+
return final_fit["df"]
|
|
3709
|
+
|
|
3710
|
+
def backfield_MaxUnmix(field, magnetization, n_comps=1, parameters=None, skewed=True, n_resample=100, proportion=0.95, figsize=(10, 6)):
|
|
3711
|
+
'''
|
|
3712
|
+
function for performing the MaxUnmix backfield unmixing algorithm
|
|
3713
|
+
The components are modelled as skew-normal distributions
|
|
3714
|
+
The uncertainties are calculated based on a bootstrap approach
|
|
3715
|
+
where the given data points are bootstrap resampled with replacement
|
|
3716
|
+
and the unmixing is done with the same original estimates for each iteration
|
|
3717
|
+
|
|
3718
|
+
Parameters
|
|
3719
|
+
----------
|
|
3720
|
+
field : array-like
|
|
3721
|
+
The field values in log10 scale
|
|
3722
|
+
magnetization : array-like
|
|
3723
|
+
The magnetization values
|
|
3724
|
+
parameters : DataFrame
|
|
3725
|
+
The parameters for the unmixing. The DataFrame should contain the following columns:
|
|
3726
|
+
'amplitude', 'center', 'sigma', 'gamma'
|
|
3727
|
+
The number of rows in the DataFrame should be equal to n_comps
|
|
3728
|
+
skewed : bool
|
|
3729
|
+
Whether to use skewed Gaussian model or not
|
|
3730
|
+
if not, the program will use normal Gaussian model
|
|
3731
|
+
n_resample : int, optional
|
|
3732
|
+
The number of bootstrap resamples. The default is 100.
|
|
3733
|
+
proportion : float, optional
|
|
3734
|
+
The proportion of the data to be used for the bootstrap resampling. The default is 0.95.
|
|
3735
|
+
The actual number of resampled points per iteration is calculated as int(len(field) * proportion)
|
|
3736
|
+
n_comps : int, optional
|
|
3737
|
+
The number of components to be used for the unmixing. The default is 1.
|
|
3738
|
+
'''
|
|
3739
|
+
|
|
3740
|
+
assert len(parameters) == n_comps, f"Number of rows in parameters ({len(parameters)}) should be equal to n_comps ({n_comps})"
|
|
3741
|
+
assert proportion > 0 and proportion <= 1, f"proportion should be between 0 and 1, but got {proportion}"
|
|
3742
|
+
assert parameters is not None, f"parameters should not be None"
|
|
3743
|
+
|
|
3744
|
+
field = np.array(field)
|
|
3745
|
+
magnetization = np.array(magnetization)
|
|
3746
|
+
dMdB = -np.diff(magnetization) / np.diff(field)
|
|
3747
|
+
B = pd.Series(field).rolling(window=2).mean().dropna().to_numpy()
|
|
3748
|
+
|
|
3749
|
+
B_high_resolution = np.linspace(np.min(B), np.max(B), 200)
|
|
3750
|
+
# store the total dMdB fits
|
|
3751
|
+
all_total_dMdB = np.zeros((n_resample, len(B_high_resolution)))
|
|
3752
|
+
# store dMdB fits for each component
|
|
3753
|
+
all_component_dMdB = np.zeros((n_resample, n_comps, len(B_high_resolution)))
|
|
3754
|
+
# store distrbution parameters
|
|
3755
|
+
all_parameters = np.zeros((n_resample, n_comps, 4))
|
|
3756
|
+
for iter in range(n_resample):
|
|
3757
|
+
# bootstrap resample with replacement of the data
|
|
3758
|
+
index_resample = np.random.choice(len(B), size=int(len(B) * proportion), replace=True)
|
|
3759
|
+
B_resample = B[index_resample]
|
|
3760
|
+
dMdB_resample = dMdB[index_resample]
|
|
3761
|
+
|
|
3762
|
+
params = Parameters()
|
|
3763
|
+
# Create a composite model
|
|
3764
|
+
composite_model = None
|
|
3765
|
+
for i in range(n_comps):
|
|
3766
|
+
prefix = f'g{i+1}_'
|
|
3767
|
+
model = SkewedGaussianModel(prefix=prefix)
|
|
3768
|
+
# Initial parameter guesses
|
|
3769
|
+
params.add(f'{prefix}amplitude', value=parameters['amplitude'][i])
|
|
3770
|
+
params.add(f'{prefix}center', value=np.log10(parameters['center'][i]))
|
|
3771
|
+
params.add(f'{prefix}sigma', value=np.log10(parameters['sigma'][i]))
|
|
3772
|
+
params.add(f'{prefix}gamma', value=parameters['gamma'][i])
|
|
3773
|
+
|
|
3774
|
+
# now let's set bounds to the parameters to help fitting algorithm converge
|
|
3775
|
+
params[f'{prefix}amplitude'].min = 0 # Bounds for amplitude parameters
|
|
3776
|
+
params[f'{prefix}amplitude'].max = 1 # Bounds for proportion/amplitude parameters
|
|
3777
|
+
params[f'{prefix}center'].min = np.min(B) # Bounds for center parameters
|
|
3778
|
+
params[f'{prefix}center'].max = np.max(B) # Bounds for center parameters
|
|
3779
|
+
params[f'{prefix}sigma'].min = 0
|
|
3780
|
+
params[f'{prefix}sigma'].max = np.max(B)-np.min(B) # Bounds for sigma parameters
|
|
3781
|
+
|
|
3782
|
+
# restrict to normal distribution if skewed is False
|
|
3783
|
+
if skewed == False:
|
|
3784
|
+
params[f'{prefix}gamma'].min = 0
|
|
3785
|
+
params[f'{prefix}gamma'].max = 0
|
|
3786
|
+
|
|
3787
|
+
if composite_model is None:
|
|
3788
|
+
composite_model = model
|
|
3789
|
+
else:
|
|
3790
|
+
composite_model += model
|
|
3791
|
+
|
|
3792
|
+
result = composite_model.fit(dMdB_resample/np.max(dMdB_resample), params, x=B_resample)
|
|
3793
|
+
all_parameters[iter] = np.array([[result.params[f'g{i+1}_amplitude'].value,
|
|
3794
|
+
result.params[f'g{i+1}_center'].value,
|
|
3795
|
+
result.params[f'g{i+1}_sigma'].value,
|
|
3796
|
+
result.params[f'g{i+1}_gamma'].value] for i in range(n_comps)])
|
|
3797
|
+
all_total_dMdB[iter] = result.eval(x=B_high_resolution)*np.max(dMdB_resample)
|
|
3798
|
+
for j in range(n_comps):
|
|
3799
|
+
prefix = f'g{j+1}_'
|
|
3800
|
+
# get the component model
|
|
3801
|
+
this_comp = result.eval_components(x=B_high_resolution)[f'{prefix}']*np.max(dMdB_resample)
|
|
3802
|
+
# store the component model
|
|
3803
|
+
all_component_dMdB[iter][j] = this_comp
|
|
3804
|
+
# calculate the 2.5, 50, and 97.5 percentiles of the bootstrap resamples
|
|
3805
|
+
dMdB_2_5 = np.percentile(all_total_dMdB, 2.5, axis=0)
|
|
3806
|
+
dMdB_50 = np.percentile(all_total_dMdB, 50, axis=0)
|
|
3807
|
+
dMdB_97_5 = np.percentile(all_total_dMdB, 97.5, axis=0)
|
|
3808
|
+
|
|
3809
|
+
# calculate the 2.5, 50, and 97.5 percentiles of the bootstrap resamples for each component
|
|
3810
|
+
dMdB_2_5_components = np.percentile(all_component_dMdB, 2.5, axis=0)
|
|
3811
|
+
dMdB_50_components = np.percentile(all_component_dMdB, 50, axis=0)
|
|
3812
|
+
dMdB_97_5_components = np.percentile(all_component_dMdB, 97.5, axis=0)
|
|
3813
|
+
|
|
3814
|
+
# calculate the mean and std of the parameters
|
|
3815
|
+
mean_parameters = np.mean(all_parameters, axis=0)
|
|
3816
|
+
std_parameters = np.std(all_parameters, axis=0)
|
|
3817
|
+
|
|
3818
|
+
# report a dictionary of the mean and std of the parameters
|
|
3819
|
+
parameters_dict = {}
|
|
3820
|
+
for i in range(n_comps):
|
|
3821
|
+
this_parameters_dict = {
|
|
3822
|
+
f'g{i+1}_amplitude': mean_parameters[i][0],
|
|
3823
|
+
f'g{i+1}_center': 10**mean_parameters[i][1],
|
|
3824
|
+
f'g{i+1}_sigma': 10**mean_parameters[i][2],
|
|
3825
|
+
f'g{i+1}_gamma': mean_parameters[i][3],
|
|
3826
|
+
f'g{i+1}_amplitude_std': std_parameters[i][0],
|
|
3827
|
+
f'g{i+1}_center_std': 10**std_parameters[i][1],
|
|
3828
|
+
f'g{i+1}_sigma_std': 10**std_parameters[i][2],
|
|
3829
|
+
f'g{i+1}_gamma_std': std_parameters[i][3]
|
|
3830
|
+
}
|
|
3831
|
+
parameters_dict.update(this_parameters_dict)
|
|
3832
|
+
|
|
3833
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
3834
|
+
ax.scatter(B, dMdB, marker='o', s=5, alpha=0.5, color='grey', label='original data')
|
|
3835
|
+
ax.plot(B_high_resolution, dMdB_50, '-', color='k', alpha=0.6, label='total best fit')
|
|
3836
|
+
ax.fill_between(B_high_resolution, dMdB_2_5, dMdB_97_5, color='k', alpha=0.2, label='total 95% CI')
|
|
3837
|
+
# plot the components
|
|
3838
|
+
for k in range(n_comps):
|
|
3839
|
+
ax.plot(B_high_resolution, dMdB_50_components[k], c=f'C{k}', label=f'component #{k+1}')
|
|
3840
|
+
ax.fill_between(B_high_resolution, dMdB_2_5_components[k], dMdB_97_5_components[k], color=f'C{k}', alpha=0.2, label=f'component #{k+1} 95% CI')
|
|
3841
|
+
ax.set_xlabel('Field (mT)', fontsize=12)
|
|
3842
|
+
ax.set_ylabel('dM/dB', fontsize=12)
|
|
3843
|
+
ax.set_xticklabels([f'{int(10**i)}' for i in ax.get_xticks()])
|
|
3844
|
+
ax.legend()
|
|
3845
|
+
fig.canvas.header_visible = False
|
|
3846
|
+
|
|
3847
|
+
return ax, parameters_dict
|
|
3848
|
+
|
|
3849
|
+
|
|
3850
|
+
def add_unmixing_stats_to_specimens_table(specimens_df, experiment_name, unmix_result, method='lmfit'):
|
|
3851
|
+
'''
|
|
3852
|
+
function to export the hysteresis data to a MagIC specimen data table
|
|
3853
|
+
|
|
3854
|
+
Parameters
|
|
3855
|
+
----------
|
|
3856
|
+
specimens_df : pd.DataFrame
|
|
3857
|
+
dataframe with the specimen data
|
|
3858
|
+
experiment_name : str
|
|
3859
|
+
name of the experiment
|
|
3860
|
+
unmix_result : dict
|
|
3861
|
+
dictionary with the unmixing results
|
|
3862
|
+
as output from rmag.backfield_MaxUnmix() or
|
|
3863
|
+
from rmag.backfield_unmixing()
|
|
3864
|
+
|
|
3865
|
+
updates the specimen table in place
|
|
3866
|
+
'''
|
|
3867
|
+
if method == 'lmfit':
|
|
3868
|
+
unmix_result_dict = unmix_result.params.valuesdict()
|
|
3869
|
+
elif method == 'MaxUnmix':
|
|
3870
|
+
unmix_result_dict = unmix_result
|
|
3871
|
+
else:
|
|
3872
|
+
raise ValueError(f"method should be either 'lmfit' or 'MaxUnmix', but got {method}")
|
|
3873
|
+
# check if the description cell is type string
|
|
3874
|
+
if isinstance(specimens_df[specimens_df['experiments'] == experiment_name]['description'].iloc[0], str):
|
|
3875
|
+
# unpack the string to a dict, then add the new stats, then pack it back to a string
|
|
3876
|
+
description_dict = eval(specimens_df[specimens_df['experiments'] == experiment_name]['description'].iloc[0])
|
|
3877
|
+
for key in unmix_result_dict:
|
|
3878
|
+
if key in description_dict:
|
|
3879
|
+
# if the key already exists, update it
|
|
3880
|
+
description_dict[key] = unmix_result_dict[key]
|
|
3881
|
+
else:
|
|
3882
|
+
# if the key does not exist, add it
|
|
3883
|
+
description_dict[key] = unmix_result_dict[key]
|
|
3884
|
+
# pack the dict back to a string
|
|
3885
|
+
specimens_df.loc[specimens_df['experiments'] == experiment_name, 'description'] = str(description_dict)
|
|
3886
|
+
else:
|
|
3887
|
+
# if not, create a new dict
|
|
3888
|
+
specimens_df.loc[specimens_df['experiments'] == experiment_name, 'description'] = str(unmix_result_dict)
|
|
3889
|
+
return
|
|
3890
|
+
|
|
3891
|
+
def add_Bcr_to_specimens_table(specimens_df, experiment_name, Bcr):
|
|
3892
|
+
"""
|
|
3893
|
+
Add the Bcr value to the MagIC specimens table
|
|
3894
|
+
the controled vocabulary for backfield derived Bcr is rem_bcr
|
|
3895
|
+
|
|
3896
|
+
Parameters
|
|
3897
|
+
----------
|
|
3898
|
+
specimens_df : pandas.DataFrame
|
|
3899
|
+
The specimens table from the MagIC database
|
|
3900
|
+
experiment_name : str
|
|
3901
|
+
The name of the experiment to which the Bcr value belongs
|
|
3902
|
+
Bcr : float
|
|
3903
|
+
The Bcr value to be added to the specimens table
|
|
3904
|
+
"""
|
|
3905
|
+
# first check if the rem_bcr column exists
|
|
3906
|
+
if 'rem_bcr' not in specimens_df.columns:
|
|
3907
|
+
# add the rem_bcr column to the specimens table
|
|
3908
|
+
specimens_df['rem_bcr'] = np.nan
|
|
3909
|
+
# add the Bcr value to the specimens table
|
|
3910
|
+
specimens_df.loc[specimens_df['experiments'] == experiment_name, 'rem_bcr'] = Bcr
|
|
3911
|
+
|
|
3912
|
+
return
|
|
3913
|
+
|
|
3914
|
+
|
|
3915
|
+
# Day plot function
|
|
3916
|
+
# ------------------------------------------------------------------------------------------------------------------
|
|
3917
|
+
def day_plot_MagIC(specimen_data,
|
|
3918
|
+
by ='specimen',
|
|
3919
|
+
Mr = 'hyst_mr_mass',
|
|
3920
|
+
Ms = 'hyst_ms_mass',
|
|
3921
|
+
Bcr = 'rem_bcr',
|
|
3922
|
+
Bc = 'hyst_bc',
|
|
3923
|
+
**kwargs):
|
|
3924
|
+
"""
|
|
3925
|
+
Function to plot a Day plot from a MagIC specimens table.
|
|
3926
|
+
|
|
3927
|
+
Parameters
|
|
3928
|
+
----------
|
|
3929
|
+
specimen_data : pandas.DataFrame
|
|
3930
|
+
DataFrame containing the specimens data.
|
|
3931
|
+
by : str
|
|
3932
|
+
Column name to group by (default is 'specimen').
|
|
3933
|
+
Mr : str
|
|
3934
|
+
Column name for the remanence (default is 'hyst_mr_mass').
|
|
3935
|
+
Ms : str
|
|
3936
|
+
Column name for the saturation magnetization (default is 'hyst_ms_mass').
|
|
3937
|
+
Bcr : str
|
|
3938
|
+
Column name for the coercivity (default is 'hyst_bcr').
|
|
3939
|
+
Bc : str
|
|
3940
|
+
Column name for the coercivity of remanence (default is 'hyst_bc').
|
|
3941
|
+
**kwargs : keyword arguments
|
|
3942
|
+
Additional arguments to pass to the plotting function.
|
|
3943
|
+
|
|
3944
|
+
Returns
|
|
3945
|
+
-------
|
|
3946
|
+
ax : matplotlib.axes.Axes
|
|
3947
|
+
The axes object containing the plot.
|
|
3948
|
+
"""
|
|
3949
|
+
summary_sats = specimen_data.groupby(by).agg({Mr: 'mean', Ms: 'mean', Bcr: 'mean', Bc: 'mean'}).reset_index()
|
|
3950
|
+
summary_sats = summary_sats.dropna()
|
|
3951
|
+
|
|
3952
|
+
ax = day_plot(Mr = summary_sats[Mr],
|
|
3953
|
+
Ms = summary_sats[Ms],
|
|
3954
|
+
Bcr = summary_sats[Bcr],
|
|
3955
|
+
Bc = summary_sats[Bc],
|
|
3956
|
+
**kwargs)
|
|
3957
|
+
return ax
|
|
3958
|
+
|
|
3959
|
+
def day_plot(Mr, Ms, Bcr, Bc,
|
|
3960
|
+
Mr_Ms_lower=0.05, Mr_Ms_upper=0.5, Bc_Bcr_lower=1.5, Bc_Bcr_upper=4,
|
|
3961
|
+
plot_day_lines = True,
|
|
3962
|
+
plot_MD_slope=True,
|
|
3963
|
+
plot_SP_SD_mixing=[10, 15, 25, 30],
|
|
3964
|
+
plot_SD_MD_mixing=True,
|
|
3965
|
+
color='black', marker='o',
|
|
3966
|
+
label = 'sample', alpha=1,
|
|
3967
|
+
lc='black', lw=0.5,
|
|
3968
|
+
legend=True, figsize=(8,6)):
|
|
3969
|
+
'''
|
|
3970
|
+
function to plot given Ms, Mr, Bc, Bcr values either as single values or list/array of values
|
|
3971
|
+
plots Mr/Ms vs Bc/Bcr.
|
|
3972
|
+
|
|
3973
|
+
Parameters
|
|
3974
|
+
----------
|
|
3975
|
+
Ms : float or array-like
|
|
3976
|
+
saturation magnetization
|
|
3977
|
+
Mr : float or array-like
|
|
3978
|
+
remanent magnetization
|
|
3979
|
+
Bc : float or array-like
|
|
3980
|
+
coercivity
|
|
3981
|
+
Bcr : float or array-like
|
|
3982
|
+
coercivity of remanence
|
|
3983
|
+
color : str, optional
|
|
3984
|
+
color of the points. The default is 'black'.
|
|
3985
|
+
marker : str, optional
|
|
3986
|
+
marker style of the points. The default is 'o'.
|
|
3987
|
+
label : str, optional
|
|
3988
|
+
label for the points. The default is 'sample'.
|
|
3989
|
+
alpha : float, optional
|
|
3990
|
+
transparency of the points. The default is 1.
|
|
3991
|
+
lc : str, optional
|
|
3992
|
+
color of the lines. The default is 'black'.
|
|
3993
|
+
lw : float, optional
|
|
3994
|
+
line width of the lines. The default is 0.5.
|
|
3995
|
+
legend : bool, optional
|
|
3996
|
+
whether to show the legend. The default is True.
|
|
3997
|
+
figsize : tuple, optional
|
|
3998
|
+
size of the figure. The default is (6,6).
|
|
3999
|
+
|
|
4000
|
+
Returns
|
|
4001
|
+
-------
|
|
4002
|
+
ax : matplotlib.axes._axes.Axes
|
|
4003
|
+
the axes object of the plot.
|
|
4004
|
+
|
|
4005
|
+
'''
|
|
4006
|
+
# force numpy arrays
|
|
4007
|
+
Ms = np.asarray(Ms)
|
|
4008
|
+
Mr = np.asarray(Mr)
|
|
4009
|
+
Bc = np.asarray(Bc)
|
|
4010
|
+
Bcr = np.asarray(Bcr)
|
|
4011
|
+
Bcr_Bc = Bcr/Bc
|
|
4012
|
+
Mr_Ms = Mr/Ms
|
|
4013
|
+
_, ax = plt.subplots(figsize = figsize)
|
|
4014
|
+
# plotting SD, PSD, MD regions
|
|
4015
|
+
if plot_day_lines:
|
|
4016
|
+
ax.axhline(Mr_Ms_lower, color = lc, lw = lw)
|
|
4017
|
+
ax.axhline(Mr_Ms_upper, color = lc, lw = lw)
|
|
4018
|
+
ax.axvline(Bc_Bcr_lower, color = lc, lw = lw)
|
|
4019
|
+
ax.axvline(Bc_Bcr_upper, color = lc, lw = lw)
|
|
4020
|
+
ax.text(1.1, 0.55, 'SD', color = 'k', fontsize = 12)
|
|
4021
|
+
ax.text(2.0, 0.06, 'PSD', color = 'k', fontsize = 12)
|
|
4022
|
+
ax.text(5.0, 0.006, 'MD', color = 'k', fontsize = 12)
|
|
4023
|
+
|
|
4024
|
+
if plot_MD_slope:
|
|
4025
|
+
MD_Bcr_Bc = np.linspace(4, 20, 100)
|
|
4026
|
+
MD_Mr_Ms = 1/MD_Bcr_Bc * 45/480
|
|
4027
|
+
ax.plot(MD_Bcr_Bc, MD_Mr_Ms, color = lc, lw = lw, label = 'MD slope')
|
|
4028
|
+
if len(plot_SP_SD_mixing) > 0:
|
|
4029
|
+
# get the SP saturation curve
|
|
4030
|
+
Bcr_Bc_SP, Mr_Ms_SP = SP_saturation_curve()
|
|
4031
|
+
ax.plot(Bcr_Bc_SP, Mr_Ms_SP, color = lc, lw = lw, ls='--', label = 'SP saturation curve')
|
|
4032
|
+
for i, SP_size in enumerate(plot_SP_SD_mixing):
|
|
4033
|
+
mixing_Bcr_Bc, mixing_Mr_Ms = SP_SD_mixture(SP_size)
|
|
4034
|
+
# filter out anything above the SP saturation curve
|
|
4035
|
+
Mr_Ms_SP_cutoff = np.interp(mixing_Bcr_Bc, Bcr_Bc_SP, Mr_Ms_SP)
|
|
4036
|
+
mask = mixing_Mr_Ms < Mr_Ms_SP_cutoff
|
|
4037
|
+
mixing_Bcr_Bc = mixing_Bcr_Bc[mask]
|
|
4038
|
+
mixing_Mr_Ms = mixing_Mr_Ms[mask]
|
|
4039
|
+
ax.plot(mixing_Bcr_Bc, mixing_Mr_Ms, color = 'C'+str(i), lw = lw, ls='--', label = f'SP size {SP_size} nm')
|
|
4040
|
+
if plot_SD_MD_mixing:
|
|
4041
|
+
# get the SD/MD mixing curve
|
|
4042
|
+
mixing_Bcr_Bc, mixing_Mr_Ms = SD_MD_mixture()
|
|
4043
|
+
# filter out anything above the SP saturation curve
|
|
4044
|
+
Mr_Ms_SP_cutoff = np.interp(mixing_Bcr_Bc, Bcr_Bc_SP, Mr_Ms_SP)
|
|
4045
|
+
mask = mixing_Mr_Ms < Mr_Ms_SP_cutoff
|
|
4046
|
+
mixing_Bcr_Bc = mixing_Bcr_Bc[mask]
|
|
4047
|
+
mixing_Mr_Ms = mixing_Mr_Ms[mask]
|
|
4048
|
+
ax.plot(mixing_Bcr_Bc, mixing_Mr_Ms, color = 'k', lw = lw, ls='-.', label = 'SD/MD mixture')
|
|
4049
|
+
# plot the data
|
|
4050
|
+
ax.scatter(Bcr_Bc, Mr_Ms, color = color, marker = marker, label = label, alpha=alpha)
|
|
4051
|
+
|
|
4052
|
+
ax.set_xlim(1, 100)
|
|
4053
|
+
ax.set_ylim(0.005, 1)
|
|
4054
|
+
ax.set_xscale('log')
|
|
4055
|
+
ax.set_yscale('log')
|
|
4056
|
+
ax.set_xticks([1, 2, 5, 10, 20, 50, 100], [1, 2, 5, 10, 20, 50, 100])
|
|
4057
|
+
ax.set_yticks([0.01, 0.02, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1], [0.01, 0.02, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1])
|
|
4058
|
+
ax.set_xlabel('Bcr/Bc', fontsize=12)
|
|
4059
|
+
ax.set_ylabel('Mr/Ms', fontsize=12)
|
|
4060
|
+
ax.set_title('Day plot', fontsize=14)
|
|
4061
|
+
|
|
4062
|
+
if legend:
|
|
4063
|
+
ax.legend(loc='lower right', fontsize=10)
|
|
4064
|
+
return ax
|
|
4065
|
+
|
|
4066
|
+
def squareness_Bc(Ms, Mr, Bc, color = 'black', marker = 'o', label = 'sample', alpha=1, lc = 'black', lw=0.5, legend=True, figsize = (6,6)):
|
|
4067
|
+
'''
|
|
4068
|
+
fuction for making squareness coercivity plot
|
|
4069
|
+
plots Mr/Ms vs Bc
|
|
4070
|
+
'''
|
|
4071
|
+
# force numpy arrays
|
|
4072
|
+
Ms = np.asarray(Ms)
|
|
4073
|
+
Mr = np.asarray(Mr)
|
|
4074
|
+
Bc = np.asarray(Bc)
|
|
4075
|
+
Mr_Ms = Mr/Ms
|
|
4076
|
+
_, ax = plt.subplots(figsize = figsize)
|
|
4077
|
+
ax.scatter(Bc, Mr_Ms, color = color, marker = marker, label = label, alpha=alpha, zorder = 100)
|
|
4078
|
+
|
|
4079
|
+
return ax
|
|
4080
|
+
|
|
4081
|
+
def Langevin_alpha(V, Ms, H, T):
|
|
4082
|
+
'''
|
|
4083
|
+
Langevin alpha calculation
|
|
4084
|
+
|
|
4085
|
+
Parameters
|
|
4086
|
+
----------
|
|
4087
|
+
V : float
|
|
4088
|
+
volume of the particle
|
|
4089
|
+
Ms : float
|
|
4090
|
+
saturation magnetization
|
|
4091
|
+
H : float
|
|
4092
|
+
applied field
|
|
4093
|
+
T : float
|
|
4094
|
+
temperature in Kelvin
|
|
4095
|
+
|
|
4096
|
+
Returns
|
|
4097
|
+
-------
|
|
4098
|
+
alpha : float
|
|
4099
|
+
Langevin alpha value
|
|
4100
|
+
'''
|
|
4101
|
+
mu0 = 4 * np.pi * 1e-7
|
|
4102
|
+
k = 1.38064852e-23
|
|
4103
|
+
|
|
4104
|
+
return mu0*Ms * V * H / (k * T)
|
|
4105
|
+
|
|
4106
|
+
def Langevin(alpha):
|
|
4107
|
+
'''
|
|
4108
|
+
Langevin function
|
|
4109
|
+
|
|
4110
|
+
Parameters
|
|
4111
|
+
----------
|
|
4112
|
+
alpha : float
|
|
4113
|
+
Langevin alpha value
|
|
4114
|
+
|
|
4115
|
+
Returns
|
|
4116
|
+
-------
|
|
4117
|
+
L : float
|
|
4118
|
+
Langevin function value
|
|
4119
|
+
'''
|
|
4120
|
+
return 1 / np.tanh(alpha) - 1 / alpha
|
|
4121
|
+
|
|
4122
|
+
def magnetite_Ms(T):
|
|
4123
|
+
'''
|
|
4124
|
+
Magnetite saturation magnetization calculation
|
|
4125
|
+
|
|
4126
|
+
Parameters
|
|
4127
|
+
----------
|
|
4128
|
+
T : float
|
|
4129
|
+
temperature in Celsius
|
|
4130
|
+
|
|
4131
|
+
Returns
|
|
4132
|
+
-------
|
|
4133
|
+
Ms : float
|
|
4134
|
+
saturation magnetization value
|
|
4135
|
+
'''
|
|
4136
|
+
return 737.384 * 51.876 * (580 - T)**0.4
|
|
4137
|
+
|
|
4138
|
+
def chi_SP(SP_size, T):
|
|
4139
|
+
'''
|
|
4140
|
+
SP size distribution function
|
|
4141
|
+
|
|
4142
|
+
Parameters
|
|
4143
|
+
----------
|
|
4144
|
+
SP_size : float
|
|
4145
|
+
size of the superparamagnetic particle in nm
|
|
4146
|
+
T : float
|
|
4147
|
+
temperature in Kelvin
|
|
4148
|
+
|
|
4149
|
+
Returns
|
|
4150
|
+
-------
|
|
4151
|
+
chi : float
|
|
4152
|
+
susceptibility value
|
|
4153
|
+
'''
|
|
4154
|
+
mu0 = 4 * np.pi * 1e-7
|
|
4155
|
+
k = 1.38064852e-23
|
|
4156
|
+
Ms = magnetite_Ms(T - 273.15)
|
|
4157
|
+
V = 4/3*np.pi*(SP_size/2)**3 / 1e27
|
|
4158
|
+
return mu0 * V * Ms**2 / 3 / k / T
|
|
4159
|
+
|
|
4160
|
+
|
|
4161
|
+
def SP_SD_mixture(SP_size, SD_Mr_Ms = 0.5, SD_Bcr_Bc = 1.25, X_sd = 3, T = 300):
|
|
4162
|
+
'''
|
|
4163
|
+
function to calculate the SP/SD mixture curve according to Dunlop (2002)
|
|
4164
|
+
Parameters
|
|
4165
|
+
----------
|
|
4166
|
+
SP_size : float
|
|
4167
|
+
size of the superparamagnetic particle in nm
|
|
4168
|
+
SD_Mr_Ms : float, optional
|
|
4169
|
+
remanent to saturation magnetization ratio. The default is 0.5.
|
|
4170
|
+
SD_Bcr_Bc : float, optional
|
|
4171
|
+
remanent coercivity to coercivity ratio. The default is 1.25.
|
|
4172
|
+
X_sd : float, optional
|
|
4173
|
+
approximate Mrs/Bc slope. The default is 3 for magnetite
|
|
4174
|
+
T : float, optional
|
|
4175
|
+
temperature in Kelvin. The default is 300.
|
|
4176
|
+
Returns
|
|
4177
|
+
-------
|
|
4178
|
+
Bcr_Bc : numpy.ndarray
|
|
4179
|
+
coercivity ratio array
|
|
4180
|
+
Mrs_Ms : numpy.ndarray
|
|
4181
|
+
saturation magnetization ratio array
|
|
4182
|
+
'''
|
|
4183
|
+
f_sd = 1/np.logspace(0, 2, 100)
|
|
4184
|
+
f_sp = 1 - f_sd
|
|
4185
|
+
Mrs_Ms = f_sd * SD_Mr_Ms
|
|
4186
|
+
X_sp = chi_SP(SP_size, T)
|
|
4187
|
+
Bcr_Bc = 1 / (f_sd * X_sd / (f_sd * X_sd + f_sp * X_sp)) * SD_Bcr_Bc
|
|
4188
|
+
|
|
4189
|
+
return Bcr_Bc, Mrs_Ms
|
|
4190
|
+
|
|
4191
|
+
def SP_saturation_curve(SD_Mr_Ms=0.5, SD_Bcr_Bc = 1.25):
|
|
4192
|
+
'''
|
|
4193
|
+
function to calculate the SP saturation curve according to Dunlop (2002)
|
|
4194
|
+
|
|
4195
|
+
Parameters
|
|
4196
|
+
----------
|
|
4197
|
+
SD_Mr_Ms : float, optional
|
|
4198
|
+
saturation magnetization ratio. The default is 0.5.
|
|
4199
|
+
SD_Bcr_Bc : float, optional
|
|
4200
|
+
remanence coercivity to coercivity ratio. The default is 1.25.
|
|
4201
|
+
Returns
|
|
4202
|
+
-------
|
|
4203
|
+
Bcr_Bc : numpy.ndarray
|
|
4204
|
+
coercivity ratio array
|
|
4205
|
+
Mrs_Ms : numpy.ndarray
|
|
4206
|
+
saturation magnetization ratio array
|
|
4207
|
+
'''
|
|
4208
|
+
f_sp = np.linspace(0, 1/3, 100)
|
|
4209
|
+
f_sd = 1 - f_sp
|
|
4210
|
+
Mrs_Ms = f_sd * SD_Mr_Ms
|
|
4211
|
+
Bcr_Bc = 1 / (1 - (f_sp/f_sd) / SD_Mr_Ms) * SD_Bcr_Bc
|
|
4212
|
+
return Bcr_Bc, Mrs_Ms
|
|
4213
|
+
|
|
4214
|
+
def SD_MD_mixture(Mr_Ms_SD = 0.5, Mr_Ms_MD = 0.019,
|
|
4215
|
+
Bc_SD = 400, Bc_MD = 43,
|
|
4216
|
+
Bcr_SD = 500, Bcr_MD = 230,
|
|
4217
|
+
X_sd = 0.6, X_MD = 0.209,
|
|
4218
|
+
Xr_SD = 0.48, Xr_MD = 0.039):
|
|
4219
|
+
'''
|
|
4220
|
+
function to calculate the SD/MD mixture curve according to Dunlop (2002)
|
|
4221
|
+
Parameters
|
|
4222
|
+
----------
|
|
4223
|
+
Mr_Ms_SD : float
|
|
4224
|
+
remanent to saturation magnetization ratio for SD. The default is 0.5.
|
|
4225
|
+
Mr_Ms_MD : float
|
|
4226
|
+
remanent to saturation magnetization ratio for MD. The default is 0.019.
|
|
4227
|
+
Bc_SD : float
|
|
4228
|
+
coercivity for SD. The default is 400.
|
|
4229
|
+
Bc_MD : float
|
|
4230
|
+
coercivity for MD. The default is 43.
|
|
4231
|
+
Bcr_SD : float
|
|
4232
|
+
coercivity of remanence for SD. The default is 500.
|
|
4233
|
+
Bcr_MD : float
|
|
4234
|
+
coercivity of remanence for MD. The default is 230.
|
|
4235
|
+
X_sd : float
|
|
4236
|
+
approximate Mrs/Bc slope for SD. The default is 0.6.
|
|
4237
|
+
X_MD : float
|
|
4238
|
+
approximate Mrs/Bc slope for MD. The default is 0.209.
|
|
4239
|
+
Xr_SD : float
|
|
4240
|
+
approximate Mrs/Bcr slope for SD. The default is 0.48.
|
|
4241
|
+
Xr_MD : float
|
|
4242
|
+
approximate Mrs/Bcr slope for MD. The default is 0.039.
|
|
4243
|
+
Returns
|
|
4244
|
+
-------
|
|
4245
|
+
Bcr_Bc : numpy.ndarray
|
|
4246
|
+
coercivity ratio array
|
|
4247
|
+
Mrs_Ms : numpy.ndarray
|
|
4248
|
+
saturation magnetization ratio array
|
|
4249
|
+
|
|
4250
|
+
* the default values are fro the IRM database
|
|
4251
|
+
'''
|
|
4252
|
+
f_sd = np.linspace(0, 1, 100)
|
|
4253
|
+
f_md = 1 - f_sd
|
|
4254
|
+
Mrs_Ms = f_sd * Mr_Ms_SD + f_md * Mr_Ms_MD
|
|
4255
|
+
Bc = (f_sd * X_sd * Bc_SD + f_md * X_MD * Bc_MD) / (f_sd * X_sd + f_md * X_MD)
|
|
4256
|
+
Bcr = (f_sd * Xr_SD * Bcr_SD + f_md * Xr_MD * Bcr_MD) / (f_sd * Xr_SD + f_md * Xr_MD)
|
|
4257
|
+
Bcr_Bc = Bcr / Bc
|
|
4258
|
+
return Bcr_Bc, Mrs_Ms
|