# Last edited on 2023-11-29 11:28:12 by stolfi OBTAINING THE DATA FILES In 2013: Copied two session files of the the Ghislain 128-channel EEG data from ~/Dropbox/eeg_sinal_ghislain/2013-09-04-cvargas-raw-128. In 2017: Copied all other sessions (S{10..12,15..21}.raw) from Vargas from Claudia Vargas's external drive at UFRJ Deolindo Couto. Each file raw/s{SS}.raw is the full session recording of subject {SS} (10..21), binary format. Auxiliary files DataDescription.txt -- description of file contents (electrodes, merkers). Touca128.pdf -- electrode names and positions. Net_Station_File_Formats.pdf -- description of ".raw" format. Summary of raw format files: Each line of the files "raw/s{SS}raw" has 139 numbers: * measurements of 128 electrodes "C001" to "C128" (microvolts) * a ``reference electrode'' "CZ" (always zero) * 10 ``marker'' channels ("HB" "HI32" "HN4" "HS" "Q128" "QB" "QN2" "QS" "epoc" "fix1") that are either 0 or something positive. Each file is a session of one subject watching a computer screen in a darkened room. Each sequence comprises a sequence of experimental ``blocks''. Each block consists of 32 ``runs''. Each run lasts ~6 seconds (~3000 frames) and consists of two ``phases'' of ~3 seconds each. In the ``fixation'' phase the screen shows a single white dot in a black background. In the ``stimulus'' phase the subject sees a cloud of dots that may be moving or static, and may be random or form the outline of a person. The start of each phase is marked by a ``start-of-fixation'' pulse in channel 139 = "fix1" and then a ``start-of-stimulus'' pulse in channel "HB", "HS", "QB", or "QS", depending on the type of stimulus displayed to the subject. In each pulse, the channel is nonzero during a single frame. Counting lines subjects=( {10..21} ) for subject in ${subjects[@]} ; do file="S${subject}.raw" cat raw/${file} \ | nmeeg_convert_raw \ -unit 1.00 \ -sourceFile ${file} \ -subject ${subject} \ > /tmp/.eeg.txt nb="`cat /tmp/.eeg.txt | wc -c`" nt1="`egrep -e '^[ ]*[-+0-9]' /tmp/.eeg.txt | wc -l`" nt2="`egrep -e '^nt[ ]*[=]' /tmp/.eeg.txt`" printf "${file} : %s bytes %s frames (%s)\n" "${nb}" "${nt1}" "${nt2}" done see raw/2017-09-14-convert-raw.log The fields "recording date" and "recording time" are inconsistent. S10.raw 2012-06-25 15:45:58.000 854829 S13.raw 2012-06-25 15:50:23.000 811665 S14.raw 2012-06-25 15:51:54.000 805027 S15.raw 2012-06-25 15:52:52.000 806482 S16.raw 2012-06-25 15:55:19.000 807825 S17.raw 2012-06-25 15:57:08.000 820836 S18.raw 2012-06-25 15:58:12.000 805513 S19.raw 2012-06-25 16:00:19.000 808238 S20.raw 2012-07-03 12:04:15.000 819405 S21.raw 2012-07-06 11:17:21.000 912787 S11.raw 2012-08-15 12:07:30.000 839438 S12.raw 2012-08-15 12:10:19.000 718583 Example report: S10.raw : 1901995371 bytes 854829 frames (nt = 854829) sample data type = 4 recording date = 2012-06-25 recording time = 15:45:58.000 sampling rate = 500 Hz number of channels = 129 board gain = 1 conversion bits = 0 amplifier range = 0 uV number of data frames = 854829 number of event channels = 10 event channel names = HB HI32 HN4 HS Q128 QB QN2 QS epoc fix1 skipped 0 frames, copied 854829 frames EXTRACTING THE RUNS We split the raw files "raw/S{SS}.raw" into one text file for each run, named "s{SS}/r{BBB}{RR}.txt". (Note: on 2017-10-18 we changed the naming scheme from "s{SS}_r{BBB}{RR}" to "s{SS}/r{BBB}{RR}". Some scripts and programs may still try to use the old scheme.) Run file format: Each line of the files "s{SS}/r{BBB}{RR}.txt" has 131 numbers: * measurements of 128 electrodes "C001" to "C128" (microvolts) * the ``reference electrode'' "CZ" (always zero) * 2 marker channels "FX" and "ST" that are nonzero during the entire fixation phase and stimulus phase, respectively. These two channels replace the original 10 marker channels ("HB" "HI32" "HN4" "HS" "Q128" "QB" "QN2" "QS" "epoc" "fix1"). Each file contains exactly 3500 frames (7 seconds), that comprise the two phases plus ~250 frames (~0.5 seconds) of the raw file on each side. These padding frames may or may not include fixation and/or stimulus frames from adjacent runs. The start of the stimulus phase is always on frame 1750. The start of the fixation phase is around frame 250, but may vary somewhat. Extracting the runs as separate files: logdate="`yyyy-mm-dd-hhmmss`"; echo "${logdate}" extract_runs.sh "${logdate}" echo 'done extracting.' Logdates: "2017-10-18-162024" (subjects 10..21). Had to re-iterate several times due to bugs. Average removal: The "robust" computation of average elecrode voltage failed in some cases, and did not seem to make much difference in other cases. The problem was fixed by letting the outlier distribution be fixed and very broad ({avg_bad = avg_all, dev_bad = 8*dev_all}) instead of recomputed at each iteration. Possible hand editing: Apparently several of the S{SS}.raw files have been edited by hand to remove "dead" segments between runs. See the dates and times above. The cropping was too harsh, sometimes leaving a bit less than 3 seconds for the stimulus phase before the next fixation phase. In all files except S21.raw, each stimulus pulse was followed by a fixation pulse almost exactly 3 seconds later (from 1494 to 1508 frames); except at the end of each block, when a larger gap occurred. In S21.raw, the spacing between the start-of-stimulus and the next start-of-fixation was greater than 3.5 seconds for all 256 runs, even inside. Missing blocks: In files S11.raw and S12.raw, there were only 7 blocks instead of 8. Note that they were both edited on 2012-08-15. Missing fixation pulses: In file S11.raw, the first "fix1" marker was missing, and the corresponding start-of-stimulus marker occurred only 2.22 seconds after the start of the recording. The latter problem was fixed by implicitly mirroring frames 1..640 about frame 0 (so that we could extract the standard 3.5 seconds before the start of stimulus), and faking a "fix1" pulse at frame 0. That left run s011/r00101 with only 2.2 seconds of fixation. In file S12.raw, the first start-of-fixation pulse in block 006 was missing. There were two stimulus pulses at frames 504141 and 507574 (3433 frames, 6.866 seconds apart) but no fixation pulses between them. Comparing two executions of the split: olddate="2017-09-18-194313" newdate="2017-10-18-162024" for subj in {10..21} ; do echo "=== s0${subj} =========================" ofile="raw-runs/s0${subj}/extract_runs_${olddate}.log" nfile="raw-runs/s0${subj}/extract_runs_${newdate}.log" prdiff -Bb ${ofile} ${nfile} \ | egrep -v -e ' avg | electrode | iteration ' done Gathering statistics on phase lengths: for phase in 0 1 ; do for subj in {10..21} ; do gfile="raw-runs/s0${subj}/extract_runs_${logdate}.log" echo "=== s0${subj} phase ${phase} =========================" cat ${gfile} \ | get_phase_length_histogram.gawk \ -v phase=${phase} done done Extracting the times of markers: for subj in {10..21} ; do echo "=== s0${subj} =========================" gfile="raw-runs/s0${subj}/extract_runs_${logdate}.log" mfile="raw-runs/s0${subj}/markers.txt" rfile="raw-runs/s0${subj}/run_phases.txt" egrep -e '\] pulse in channel' ${gfile} > ${mfile} egrep -e '^ phase *[01] =' ${gfile} > ${rfile} wc -l ${mfile} ${rfile} done 512 raw-runs/s010/run_phases.txt 448 raw-runs/s011/run_phases.txt 448 raw-runs/s012/run_phases.txt 512 raw-runs/s013/run_phases.txt 512 raw-runs/s014/run_phases.txt 512 raw-runs/s015/run_phases.txt 512 raw-runs/s016/run_phases.txt 512 raw-runs/s017/run_phases.txt 512 raw-runs/s018/run_phases.txt 512 raw-runs/s019/run_phases.txt 512 raw-runs/s020/run_phases.txt 512 raw-runs/s021/run_phases.txt 512 raw-runs/s010/markers.txt 447 raw-runs/s011/markers.txt 447 raw-runs/s012/markers.txt 512 raw-runs/s013/markers.txt 512 raw-runs/s014/markers.txt 512 raw-runs/s015/markers.txt 512 raw-runs/s016/markers.txt 512 raw-runs/s017/markers.txt 512 raw-runs/s018/markers.txt 512 raw-runs/s019/markers.txt 512 raw-runs/s020/markers.txt 512 raw-runs/s021/markers.txt Checking that stimulus phase always begins at frame 1750: for subj in {10..21} ; do echo "=== s0${subj} =========================" rfile="raw-runs/s0${subj}/run_phases.txt" egrep -e 'ST:' ${rfile} | egrep -v -e ' 1750 [.][.]' done PLOTTING SPECTRA OF SOME RAW RUNS Plotting the power spectra of selected raw runs: notable_runs=( `cat notable_runs.txt` ) plot_some_runs_and_spectra.sh SHOW ${logdate} raw-runs 0 127 250 101 500 "${notable_runs[@]}" Plotting all the runs: logdate="`yyyy-mm-dd-hhmmss`" echo "${logdate}" plot_all_runs.sh NOSHOW "${logdate}" raw-runs 0 130 260 500 echo 'done plotting.' IDENTIFYING BAD CHANNELS By visual inspection of previous PCA runs, I identified visually some bad channels in each subject and listed them in the files "${subj}/bad-channels.txt". FILTERING THE RUNS Running a bandpass filter {nmeeg_filter} over the extracted runs. The filtered runs are flt-runs-${tag}/*.txt and the removed noise is in nse-runs-${tag}/*.txt In 2013 I ran experiments with subjects S13 and S14, using a "BUR8" filter (${tag} = "A") and a Gaussian filter (${tag} = "B"). The latter seemed to give better results. I also tried the Gaussian filter with trend removal "-trend 2 0" (${tag}="B2"), but "-trend 3 0" seemed to work better. >>> OLD >> Thus in 2017 I filtered all runs of all subjects with the following parameters: ${tag} = "B" filter type = "G" F_LO0 = 0.166667 F_LO1 = 0.333333 F_HI1 = 24.0 F_HI0 = 48.0 Resample step = 5 (thus output sampling freq is 100 Hz) Filtering all runs: logdate="`yyyy-mm-dd-hhmmss`"; echo "${logdate}" filter_runs_B.sh "${logdate}" <<< DLO <<< >>> OLD >>> Saving the results of the PCA on flt-runs-C without channel exclusions, for comparisons: sv="SAVE/2021-08-30" mkdir -p ${sv} mv -vi flt-runs-C/A1_pca.png ${sv} for subj in s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 ; do mkdir -p ${sv}/${subj} for comp in P000 P001 P002 P003 P004 P005 P006 P007 ; do mv -vi flt-runs-C/${subj}/A1_elems/A1_${comp}_eig/f000000.png ${sv}/${subj}/${comp}.png done done sv="SAVE/2021-08-31" mkdir -p ${sv} for subj in s012 ; do mkdir -p ${sv}/${subj} for comp in P000 P001 P002 P003 P004 P005 P006 P007 ; do mv -vi flt-runs-C/${subj}/A1/${comp}_eig_rgb.png ${sv}/${subj}/${comp}.png done done <<< DLO <<< Logdates: "2017-09-21-064245" (subjects 10..21) In 2021 I re-filtered all runs of all subjects with rebasing to the electrode mean, after excluding bad channels: ${tag} = "C" filter type = "G" F_LO0 = 0.166667 F_LO1 = 0.333333 F_HI1 = 24.0 F_HI0 = 48.0 exclude bad channels remove trend, don't put back rebase with simple average Resample step = 5 (thus output sampling freq is 100 Hz) Filtering all runs: logdate="`yyyy-mm-dd-hhmmss`"; echo "${logdate}" # subjects=( s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 ) subjects=( s011 ) for subj in ${subjects[@]} ; do filter_runs_C.sh ${subj} "${logdate}" done Zipping all filtered runs of subject s012: subj=s012 pushd flt-runs-C/${subj} ls r?????.txt | sort > .all cat garbage.dir | gawk '/^r0/{ print $1 }' | sort > .bad bool 1-2 .all .bad \ | gawk 'BEGIN { srand(4615); } // { printf "%10.8f %s\n", rand(), $1; }' \ | sort -k1g \ | cut -d ' ' -f 2 \ | head -n 50 \ | sort \ > .sel zip `yyyy-mm-dd`-${subj}-sel.zip `cat .sel` popd FIRST STAB AT PRINCIPAL COMPONENT ANALYSIS Do a PCA on the filtered runs of each subject, excluding garbage runs, and extract the signal. # subjects=( s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 ) subjects=( s011 ) for subj in ${subjects[@]} ; do do_basic_pca.sh flt-runs-C ${subj} done convert \ SAVE/2021-08-30-1/s012/pca.png \ SAVE/2021-08-31-1/s012/pca.png \ SAVE/2021-08-31-2/s012/pca.png \ SAVE/2021-09-01-1/s012/pca.png \ SAVE/2021-09-02-1/s012/pca.png \ flt-runs-C/s012/A1/pca.png \ -append s012_pca.png display s012_pca.png # subjects=( s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 ) subjects=( s012 ) make_pca_composite_image.sh ${subjects{@]} PLOTTING THE COMPONENTS MAGNITUDES REMOVING THE MAIN COMPONENTS Decomposing the filtered runs into a combination of the first two components (P000,P001) and a residue (RES) that is orthogonal to both patterns: ( cd ~/programs/c/NEUROMAT/nmeeg_spectrum && make uninstall build install ) ( cd ~/programs/c/NEUROMAT/nmeeg_comp_analysis && make uninstall build install ) ( cd ~/programs/c/NEUROMAT/nmeeg_animate && make uninstall build install ) subjects=( s012 ) for subj in ${subjects[@]}; do runs=( r00624 ) for run in ${runs[@]} ; do analyze_components.sh SHOW flt-runs-C ${subj} ${run} 151 51 P000 P003 P004 nmeeg_plot_channel_scatter.sh SHOW flt-runs-C/${subj}/A1/${run}_AMP.txt 1 P000 150 P004 150 nmeeg_plot_channel_scatter.sh SHOW flt-runs-C/${subj}/A1/${run}_AMP.txt 1 P003 150 P004 150 done done for subj in s014; do runs=( r00106 ) for run in ${runs[@]} ; do analyze_components.sh SHOW flt-runs-C ${subj} ${run} 201 51 P000 P001 done done IDENTIFYING THE RUNS Extracting the run types: (cd flt-runs-B && egrep -e '^type *[=]' s???/r?????.txt ) \ | sed -e 's/[.]txt:type *= */ /g' \ > run-types.txt LISTING GARBAGE RUNS By visual inspection of flt-runs-B/s???/r?????.png, created files "flt-runs-C/s0${subj}/garbage.dir" with a list of the run file names ("r${runid}.txt") that look like garbage, with many electrodes dominated by noise. Moving to new filtered runs directory: for subj in `( cd flt-runs-B && ls -d s??? )` ; do mv -vi flt-runs-{B,C}/${subj}/garbage.dir mv -vi flt-runs-{B,C}/${subj}/blink-free.dir done F ############################################################################################ ### BLINKS ################################################################################# LISTING RUNS WITHOUT BLINKS By visual inspection of flt-runs-B/s???/r?????.png, created files "flt-runs-B/s0${subj}/blink-free.dir" with a list of the run file names ("r${runid}.txt") of the runs without blinks in them. LOCATING BLINKS Creating a file blinks/blinks-by-hand.txt with the time ranges (seconds) of blinks. ( cd flt-runs-B && ls s???/r?????.txt | sed -e 's:[.]txt::g' | sort ) > blinks/blinks-by-hand.txt Editing manually the file, extracting visually the intervals with 0.1 precision. Considered only blinks that are completely inside the run's time window (fixation and stimulus phases, plus ~0.5sec buffer on each end). Typically each blink lasts 0.45 seconds, almost never more than 0.6 sec. cleanup_blinks.gawk \ -v trun=7.0 \ blinks/blinks-by-hand.txt \ > blinks/blinks-clean.txt Plotting an histogram of the blink locations: bfile="blinks/blinks-clean.txt" for sid in 013 014 ; do plot_blink_locations.sh SHOW ${sid} blinks/blinks-clean.txt done Locating runs without weirdness flags and without blinks in the stimulus phase or in the end of the fixation phase: list_blink_free_runs.gawk \ -v stini=2.5 \ -v stfin=6.5 \ blinks/blinks-by-hand.txt \ > blinks/blink-safe-runs.txt 107 safe runs in 512 runs (20.90%) subject 013: 91 safe runs in 256 runs (35.55%) subject 014: 16 safe runs in 256 runs (6.25%) EXTRACTING ALL BLINKS OF EACH SUBJECT To model the blinks, we extract all blinks from all good runs of each subject and concatenate them into a single file "flt-runs-B/s${sid}_blinks.txt": vmax=150 bfile="blinks/blinks-clean.txt" for sid in 013 014 ; do xfile="flt-runs-B/s${sid}_blinks.txt" cat ${bfile} \ | egrep -e '^[ ]*'"${sid}" \ | gawk \ -v trun=7.0 \ ' // { sid = $1; rid = $2; nfl = $3; tini = 0+$5; tfin = 0+$6; if ((nfl == 0) && (tini >= 0) && (tfin <= trun)) { print sid, rid, tini, tfin; } } ' \ | extract_run_segments.gawk -v dir="flt-runs-B" \ > .xfr nt=`cat .xfr | egrep -v -e '[=]' | wc -l` printf "nt = %d\n" "${nt}" > ${xfile} cat .xfr >> ${xfile} done for sid in 013 014 ; do xfile="flt-runs-B/s${sid}_blinks.txt" nmeeg_plot_channels.sh SHOW ${xfile%%.*} 0 ${vmax} 0 9999 0 1000 1400 500 done EXTRACTING ALL NON-BLINK IMPORTANT SEGMENTS To analyze the non-blink sections, we extract all the blink-free important segments and concatenate them into a single file "flt-runs-B/s${sid}_nonblinks.txt": bfile="blinks/blink-safe-runs.txt" for sid in 013 014 ; do xfile="flt-runs-B/s${sid}_nonblinks.txt" cat ${bfile} \ | egrep -e '^[ ]*'"${sid}" \ | gawk \ ' // { sid = $1; rid = $2; tini = 2.5; tfin = 6.5; print sid, rid, tini, tfin; } ' \ | extract_run_segments.gawk -v dir="flt-runs-B" \ > .xfr nt=`cat .xfr | egrep -v -e '[=]' | wc -l` printf "nt = %d\n" "${nt}" > ${xfile} cat .xfr >> ${xfile} done vmax=150 for sid in 013 014 ; do xfile="flt-runs-B/s${sid}_nonblinks.txt" nmeeg_plot_channels.sh SHOW ${xfile%%.*} 0 ${vmax} 0 9999 0 2000 1400 500 done IDENTIFYING PRINCIPAL COMPONENTS OF BLINKS for sid in 013 014 ; do xfile="flt-runs-B/s${sid}_blinks.txt" opref="flt-runs-B/s${sid}_blinks" echo "computing components" nmeeg_correl \ -outDirix ${opref} \ -zeroMean T \ -maxComps 5 \ < ${xfile} done btype=2 for sid in 013 014 ; do opref="flt-runs-B/s${sid}_blinks" echo "plotting components" for cfile in ${opref}_P???_eig.txt ; do cpref="${cfile%.*}_b${btype}" nmeeg_animate ${cpref} 0 1 1 ${btype} 560 640 < ${cfile} done done IDENTIFYING PRINCIPAL COMPONENTS OF BLINK-FREE SEGMENTS for sid in 013 014 ; do xfile="flt-runs-B/s${sid}_nonblinks.txt" opref="flt-runs-B/s${sid}_nonblinks" echo "computing components" nmeeg_correl \ -outDirix ${opref} \ -zeroMean F \ -maxComps 5 \ < ${xfile} done btype=2 for sid in 013 014 ; do opref="flt-runs-B/s${sid}_nonblinks" echo "plotting components" for cfile in ${opref}_P???_eig.txt ; do cpref="${cfile%.*}_b${btype}" nmeeg_animate ${cpref} 0 1 1 ${btype} 560 640 < ${cfile} done done SEPARATING THE BLINKS AND THE 10 HZ OSCILLATION Decomposing the filtered runs into a combination of the blink principal component (BL0), the general 10 Hz oscillation component (H10), and a residue (RES) that is orthogonal to both patterns: vmax=150 subruns=( 013:00229 014:00425) for sr in ${subruns[@]} ; do subjid="${sr%%:*}" runid="${sr##*:}" ppref=flt-runs-B/s${subjid} opref=flt-runs-B/s${subjid}/r${runid} nmeeg_comp_analysis \ -pattern BL0 ${ppref}_blinks_P000_eig.txt \ -pattern H10 ${ppref}_nonblinks_P000_eig.txt \ -normalize \ -writeComp BL0 ${opref}_BL0.txt = BL0 \ -writeComp H10 ${opref}_H10.txt = H10 \ -writeComp BLH ${opref}_BLH.txt = BL0 H10 \ -delete BL0 H10 \ < ${opref}.txt \ > ${opref}_RES.txt nmeeg_plot_channels.sh SHOW ${opref}_BL0 0 ${vmax} 0 9999 0 0 1400 500 nmeeg_plot_channels.sh SHOW ${opref}_H10 0 ${vmax} 0 9999 0 0 1400 500 nmeeg_plot_channels.sh SHOW ${opref}_BLH 0 ${vmax} 0 9999 0 0 1400 500 nmeeg_plot_channels.sh SHOW ${opref}_RES 0 50 0 9999 0 0 1400 500 done EXPORTING SOME TO DROPBOX Moving selected files to Dropbox for easy access: projdir="." dropdir="${HOME}/Dropbox/eeg_sinal_ghislain/2013-11-15-stolfi" echo "${notable_runs[@]}" for run in ${notable_runs[@]} ; do cp -av ${projdir}/raw-runs/${run}{.txt,.png,_pwr.png} ${dropdir}/raw-runs/ cp -av ${projdir}/flt-runs-B/${run}{.txt,.png,_pwr.png} ${dropdir}/flt-runs-B/ cp -av ${projdir}/nse-runs-B/${run}{.txt,.png,_pwr.png} ${dropdir}/nse-runs-B/ done >>>> TO FIX AND DO >>>> Tabulating the range of electrodes Fp1 and Fp2 between trigger pulses 1 and 4, and flagging those that vary too much: vfile="flt-runs-B/Fp-variation.txt" rm -f ${vfile} for f in flt-runs-B/s???/r?????.txt ; do printf "%s " "${f}" >> ${vfile} cat ${f} \ | get_electrode_ranges.gawk -v ix1=4 -v ix2=12 -v ixt=21 -v dvmax=150 \ >> ${vfile} done SAMPLE RUNS FOR TESTING Identified two runs from each subject, one "Bio" and one "nonBio", both apparently free from blinks in the inter-pulse region. datadir=${HOME}/programs/c/NEUROMAT/neuromat_show_eeg/tests/data cp -av raw-runs/s001/r005.txt ${datadir} # nonBio cp -av raw-runs/s001/r039.txt ${datadir} # Bio cp -av raw-runs/s002/r016.txt ${datadir} # Bio cp -av raw-runs/s002/r043.txt ${datadir} # nonBio cp -av raw-runs/s003/r009.txt ${datadir} # Bio cp -av raw-runs/s003/r030.txt ${datadir} # nonBio cp -av raw-runs/s004/r030.txt ${datadir} # nonBio cp -av raw-runs/s004/r036.txt ${datadir} # Bio cp -av raw-runs/s005/r014.txt ${datadir} # Bio cp -av raw-runs/s005/r044.txt ${datadir} # nonBio cp -av raw-runs/s006/r024.txt ${datadir} # Bio cp -av raw-runs/s006/r042.txt ${datadir} # nonBio cp -av raw-runs/s007/r028.txt ${datadir} # nonBio cp -av raw-runs/s007/r046.txt ${datadir} # Bio cp -av raw-runs/s008/r015.txt ${datadir} # Bio cp -av raw-runs/s008/r049.txt ${datadir} # nonBio