Commit e535a963 authored by Martin Schorb's avatar Martin Schorb
Browse files

Merge branch 'dev' into main

parents 6cd6b53b 15540ae6
Pipeline #29630 passed with stage
in 13 seconds
......@@ -138,17 +138,17 @@ for idx in range(params.max_tileviews):
# init tile selector
@app.callback([Output({'component':'tile_dd'+idx_str,'module': MATCH},'options'),
Output({'component':'tile_dd'+idx_str,'module': MATCH},'value')],
Input({'component':'tileim_section_in'+idx_str,'module': MATCH},'value'),
[Input({'component':'tileim_section_in'+idx_str,'module': MATCH},'value'),
Input({'component': 'tp_dd', 'module': MATCH},'value')],
[State({'component': 'owner_dd','module': MATCH},'value'),
State({'component': 'project_dd','module': MATCH},'value'),
State({'component': 'stack_dd','module': MATCH},'value'),
State({'component':'tile_dd'+idx_str,'module': MATCH},'value'),
State({'component': 'tp_dd', 'module': MATCH},'value'),
State({'component': 'neighbours', 'module': MATCH},'children'),
State({'component': 'lead_tile', 'module': MATCH},'data'),
State('url', 'pathname')]
,prevent_initial_call=True)
def slicetotiles(slicenum,owner,project,stack,prev_tile,tilepairdir,neighbours,lead_tile,thispage):
def slicetotiles(slicenum,tilepairdir,owner,project,stack,prev_tile,neighbours,lead_tile,thispage):
if None in (slicenum,owner,project,stack,neighbours,lead_tile):
raise PreventUpdate
......@@ -380,7 +380,7 @@ for idx in range(params.max_tileviews):
scale = round(scale, 4)
out_scale = '%0.4f' % scale
url = url + '/box/' + ','.join(map(str,[xmin,ymin,width,height,scale]))
url += '/box/' + ','.join(map(str,[xmin,ymin,width,height,scale]))
imurl = url + '/jpeg-image?scale=' + out_scale
elif 'contrast' in trigger:
......@@ -390,15 +390,19 @@ for idx in range(params.max_tileviews):
imurl = re.sub('/z/[0-9]*','/z/'+str(section),imparams['imurl'])
else:
# bounds = thisstore['stackparams']['stats']['stackBounds']
fullbounds = thisstore['stackparams']['stats']['stackBounds']
url1 = url + '/bounds'
bounds = requests.get(url1).json()
imwidth = bounds['maxX'] - bounds['minX']
scale = float(params.im_width) / float(imwidth)
imparams.update(bounds)
imparams['fullbounds']=bounds
imparams['fullbounds']=fullbounds
imparams['maxZ'] = fullbounds['maxZ']
imparams['minZ'] = fullbounds['minZ']
scale = round(scale,4)
out_scale = '%0.4f' % scale
imurl = url +'/jpeg-image?scale=' + out_scale
......
......@@ -36,7 +36,7 @@ inputmodules = [
main = html.Div(children=[html.H3("Import volume EM datasets - Choose type:", id='conv_head'),
dcc.Dropdown(id={'component': 'import_type_dd', 'module': module}, persistence=True,
options=inputtypes, value='...')
options=inputtypes, value='')
])
stores = [dcc.Store(id={'component': 'store_render_init', 'module': module}, storage_type='session',data=''),
......
......@@ -177,7 +177,7 @@ def bdv_finalize_execute_gobutton(click,jsonfile,launch_store):
run_params['path'] = n5file
# run_params["scale_factors"] = 3 * [[2, 2, 2]],
run_params["resolution"] = str(res)
run_params["resolution"] = res
run_params["unit"] = 'nanometer'
......@@ -191,7 +191,7 @@ def bdv_finalize_execute_gobutton(click,jsonfile,launch_store):
log_file += '.log'
mkxml_p = launch_jobs.run(target='standalone',pyscript=params.rendermodules_dir+'rendermodules/materialize/make_xml.py',jsonfile = param_file,
mkxml_p = launch_jobs.run(target='standalone',pyscript=params.rendermodules_dir+'/materialize/make_xml.py',jsonfile = param_file,
logfile=log_file,errfile=err_file)
......
......@@ -192,7 +192,7 @@ def n5export_stacktodir(#stack_sel,
out['numsections'] = zmax-zmin + 1
url = params.render_base_url + params.render_version + 'owner/' + owner + '/project/' + project + '/stack/' + stack + '/z/'+ str(out['zmin']) +'/render-parameters'
print(url)
tiles0 = requests.get(url).json()
tilefile0 = os.path.abspath(tiles0['tileSpecs'][0]['mipmapLevels']['0']['imageUrl'].strip('file:'))
......@@ -446,7 +446,8 @@ def n5export_execute_gobutton(click,outdir,stack,n_cpu,timelim,comp_sel,owner,pr
spark_p['--worker_cpu'] = params.cpu_pernode_spark
spark_p['--worker_mempercpu'] = params.mem_per_cpu
spark_p['--worker_nodes'] = hf.spark_nodes(n_cpu)
spark_args = {'--jarfile':params.render_sparkjar}
run_params_generate = spsl_p.copy()
......@@ -479,10 +480,13 @@ def n5export_execute_gobutton(click,outdir,stack,n_cpu,timelim,comp_sel,owner,pr
log_file += '.log'
n5export_p = launch_jobs.run(target=comp_sel,pyscript=script,
jsonfile=param_file,run_args=run_args,target_args=target_args,logfile=log_file,errfile=err_file)
# ['sparkslurm__12539018']
n5export_p = launch_jobs.run(target=comp_sel,
pyscript=script,
jsonfile=param_file,
run_args=run_args,
target_args=target_args,
special_args=spark_args,
logfile=log_file,errfile=err_file)
launch_store=dict()
launch_store['logfile'] = log_file
......
......@@ -212,7 +212,7 @@ def sbem_conv_gobutton(stack_sel, in_dir, click, proj_dd_sel, compute_sel, run_s
#launch
# -----------------------
sbem_conv_p = launch_jobs.run(target=compute_sel,pyscript=params.rendermodules_dir+'/rendermodules/dataimport/generate_EM_tilespecs_from_SBEMImage.py',
sbem_conv_p = launch_jobs.run(target=compute_sel,pyscript=params.rendermodules_dir+'/dataimport/generate_EM_tilespecs_from_SBEMImage.py',
jsonfile=param_file,run_args=None,logfile=log_file,errfile=err_file)
run_state['status'] = 'running'
......
......@@ -203,7 +203,7 @@ def serialem_conv_gobutton(stack_sel, in_dir, click, proj_dd_sel, compute_sel, r
#launch
# -----------------------
sbem_conv_p = launch_jobs.run(target=compute_sel,pyscript=params.rendermodules_dir+'/rendermodules/dataimport/generate_EM_tilespecs_from_SerialEMmontage.py',
sbem_conv_p = launch_jobs.run(target=compute_sel,pyscript=params.rendermodules_dir+'/dataimport/generate_EM_tilespecs_from_SerialEMmontage.py',
jsonfile=param_file,run_args=None,logfile=log_file,errfile=err_file)
run_state['status'] = 'running'
......@@ -212,6 +212,7 @@ def serialem_conv_gobutton(stack_sel, in_dir, click, proj_dd_sel, compute_sel, r
run_state['logfile'] = log_file
else:
# outstore = dash.no_update
# check launch conditions and enable/disable button
if any([in_dir=='',in_dir==None]):
......@@ -233,6 +234,7 @@ def serialem_conv_gobutton(stack_sel, in_dir, click, proj_dd_sel, compute_sel, r
else:
if not (run_state['status'] == 'running'):
run_state['status'] = 'input'
but_disabled = False
else:
if not (run_state['status'] == 'running'):
......
......@@ -380,7 +380,7 @@ def mipmaps_gobutton(mipmapdir,click,click2,run_state,comp_sel,runstep_in,owner,
mipmap_generate_p = launch_jobs.run(target=comp_sel,pyscript=params.rendermodules_dir+'/rendermodules/dataimport/generate_mipmaps.py',
mipmap_generate_p = launch_jobs.run(target=comp_sel,pyscript=params.asap_dir+'/dataimport/generate_mipmaps.py',
json=param_file,run_args=None,target_args=target_args,logfile=log_file,errfile=err_file)
......@@ -434,7 +434,7 @@ def mipmaps_gobutton(mipmapdir,click,click2,run_state,comp_sel,runstep_in,owner,
err_file = log_file + '.err'
log_file += '.log'
mipmap_apply_p = launch_jobs.run(target=comp_sel,pyscript=params.rendermodules_dir+'/rendermodules/dataimport/apply_mipmaps_to_render.py',
mipmap_apply_p = launch_jobs.run(target=comp_sel,pyscript=params.asap_dir+'/dataimport/apply_mipmaps_to_render.py',
jsonfile=param_file,run_args=None,logfile=log_file,errfile=err_file)
launch_store=dict()
......
......@@ -5,6 +5,7 @@
import json
import os
import glob
import subprocess
import requests
import socket
......@@ -20,9 +21,8 @@ conda_dir = '/g/emcf/software/python/miniconda'
render_log_dir = '/g/emcf/software/render-logs'
rendermodules_dir = '/g/emcf/schorb/code/render-modules/'
gc3_conffile = os.path.join(base_dir,'launchers/gc3conf/template_gc3pie.conf')
rendermodules_dir = '/g/emcf/schorb/code/rendermodules-addons/rmaddons'
asap_dir = '/g/emcf/schorb/code/asap-modules/asap/'
spark_dir = '/g/emcf/software/spark-3.0.0-bin-hadoop3.2'
......@@ -175,6 +175,9 @@ match_store = {#'init_match':{},
'all_matchcolls':None
}
# match trial owner default ('flyTEM' in built)
mt_owner = 'flyTEM'
#=============================================================
## UI parameters
......@@ -223,7 +226,7 @@ render_base_url = render_json['render']['host']
render_base_url += ':' + str(render_json['render']['port'])
render_base_url += v_base_url
render_sparkjar = glob.glob(render_dir+'/render-ws-spark-client/target/render-ws-spark-client-*-standalone.jar')[0]
# get initial list of owners in Render:
......
......@@ -251,13 +251,13 @@ def pointmatch_comp_set(tilepairdir,matchtime,n_cpu,stack_sel,allstacks,thispage
if thispage == '' or not thispage in hf.trigger(key='module'):
raise PreventUpdate
n_cpu = int(n_cpu)
# n_cpu = int(n_cpu)
out=dict()
factors=dict()
t_fields = ['']*len(status_table_cols)
numtp = 1
# numtp = 1
if (not stack_sel=='-' ) and (not allstacks is None):
stacklist = [stack for stack in allstacks if stack['stackId']['stack'] == stack_sel]
......
......@@ -35,12 +35,14 @@ parent = "pointmatch"
page=[]
matchtrial = html.Div([html.H4("Select appropriate Parameters for the SIFT search"),
html.Div(['Organism: ',
matchtrial = html.Div([pages.tile_view(parent,numpanel=2,showlink=True),
html.Br(),
html.H4("Select appropriate Parameters for the SIFT search"),
html.Div(['Organism template: ',
dcc.Dropdown(id=label+'organism_dd',persistence=True,
clearable=False),
html.Br(),
html.Div(["Select existing Match Trial parameters."
html.Div(["Select template Match Trial parameters."
],
id=label+'mt_sel'),
dcc.Store(id=label+'picks'),
......@@ -48,12 +50,14 @@ matchtrial = html.Div([html.H4("Select appropriate Parameters for the SIFT searc
clearable=False),
html.Br(),
html.Div(id=label+'mtbrowse',
children=[html.A('Explore MatchTrial',
id=label+'mt_link',
target="_blank"),
children=[html.Button('Explore MatchTrial',id=label+'mt_linkbutton'),
html.A(' - ',
id=label + 'mt_link',
target="_blank"),
html.Div('',id=label+'mt_jscaller',style={'display':'none'})
]),
html.Br(),
pages.tile_view(parent,numpanel=2,showlink=True),
html.Br(),
html.Div(["Use this Match Trial as compute parameters:",
dcc.Input(id=label+'mtselect', type="text",
......@@ -146,20 +150,55 @@ def sift_pointmatch_IDs(organism,picks):
return [dd_options]
@app.callback([Output(label+'mt_link','href'),
Output(label+'mtselect','value')],
Input(label+'matchID_dd','value')
# State(label+'picks','data'),
, prevent_initial_call=True)
def sift_browse_matchTrial(matchID):
if matchID is None:
@app.callback([Output(label+'mtselect','value'),
Output(label + 'mt_link','href'),
Output(label+'mt_jscaller','children')],
[Input(label+'matchID_dd','value'),
Input(label+'mt_linkbutton','n_clicks')],
[State({'component': 'tileim_link_0', 'module': parent}, 'children'),
State({'component': 'tileim_link_1', 'module': parent}, 'children'),
State({'component': 'tile_dd_1', 'module': parent}, 'value'),
State({'component': 'tile_dd_1', 'module': parent}, 'options')]
)
def sift_browse_matchTrial(matchID,buttonclick,link1,link2,tile2sel,tile2options):
if None in (matchID,link1,link2):
return dash.no_update
trigger = hf.trigger()
mc_url = params.render_base_url + 'view/match-trial.html?'
for item in tile2options:
if tile2sel in item['value']:
tile2label = item['label']
if 'button' in trigger:
tile_clip = matchTrial.invert_neighbour(tile2label)
matchtrial, matchID = matchTrial.new_matchtrial(matchID,[link1,link2],clippos=tile_clip)
mc_url += 'matchTrialId=' + matchID
return matchID,mc_url,str(buttonclick)
mc_url += 'matchTrialId=' + matchID
return mc_url, matchID
return matchID,mc_url,dash.no_update
app.clientside_callback(
"""
function(trigger, url) {
window.open(arguments[1]);
return {}
}
""",
Output(label+'mt_linkbutton','style'),
Input(label+'mt_jscaller','children'),
State(label + 'mt_link','href')
)
# =============================================
......@@ -278,7 +317,7 @@ def sift_pointmatch_execute_gobutton(click,matchID,matchcoll,comp_sel,mc_owner,t
target_args = None
run_args = None
script = params.rendermodules_dir+'/rendermodules/pointmatch/generate_point_matches_opencv.py'
script = params.asap_dir+'/pointmatch/generate_point_matches_opencv.py'
elif comp_sel == 'sparkslurm':
spsl_p = dict()
......@@ -319,6 +358,8 @@ def sift_pointmatch_execute_gobutton(click,matchID,matchcoll,comp_sel,mc_owner,t
spark_p['--worker_cpu'] = params.cpu_pernode_spark
spark_p['--worker_nodes'] = hf.spark_nodes(n_cpu)
spark_args = {'--jarfile':params.render_sparkjar}
run_params_generate = spsl_p.copy()
run_params_generate.update(mtrun_p)
......@@ -345,8 +386,13 @@ def sift_pointmatch_execute_gobutton(click,matchID,matchcoll,comp_sel,mc_owner,t
sift_pointmatch_p = launch_jobs.run(target=comp_sel,pyscript=script,
jsonfile=param_file,run_args=run_args,target_args=target_args,logfile=log_file,errfile=err_file)
sift_pointmatch_p = launch_jobs.run(target=comp_sel,
pyscript=script,
jsonfile=param_file,
run_args=run_args,
target_args=target_args,
special_args=spark_args,
logfile=log_file,errfile=err_file)
launch_store=dict()
......
......@@ -345,7 +345,7 @@ def solve_execute_gobutton(click,matchcoll,outstack,tform,stype,comp_sel,startse
# time.sleep(5)
solve_generate_p = launch_jobs.run(target=comp_sel,pyscript=params.rendermodules_dir+'/rendermodules/solver/solve.py',
solve_generate_p = launch_jobs.run(target=comp_sel,pyscript=params.asap_dir+'/solver/solve.py',
jsonfile=param_file,run_args=None,target_args=None,logfile=log_file,errfile=err_file)
time.sleep(5)
......
......@@ -195,7 +195,7 @@ def tilepairs_execute_gobutton(click,pairmode,stack,slicedepth,comp_sel,startsec
err_file = log_file + '.err'
log_file += '.log'
tilepairs_generate_p = launch_jobs.run(target=comp_sel,pyscript=params.rendermodules_dir+'rendermodules/pointmatch/create_tilepairs.py',
tilepairs_generate_p = launch_jobs.run(target=comp_sel,pyscript=params.asap_dir+'/pointmatch/create_tilepairs.py',
jsonfile=param_file,run_args=None,target_args=None,logfile=log_file,errfile=err_file)
......
......@@ -217,6 +217,9 @@ def cluster_status(run_state):
else:
if 'FINISHED' in sp_query['completedapps'][0]['state']:
out_stat.append(canceljobs(run_state,'done'))
donefile = run_state['logfile']+'.done'
with open(donefile,'w') as f:
f.write('spark job: '+j_id + ' is done.')
elif 'KILLED' in sp_query['completedapps'][0]['state']:
drop = canceljobs(run_state)
......@@ -264,6 +267,7 @@ def run(target='standalone',
jsonfile='',
run_args='',
target_args=None,
special_args=None,
logfile=os.path.join(params.render_log_dir,'render.out'),
errfile=os.path.join(params.render_log_dir,'render.err')):
......@@ -375,9 +379,12 @@ def run(target='standalone',
# spsl_args += args2string({'--logfile':logfile})
# spsl_args += args2string({'--errfile':errfile})
spsl_args += args2string({'--logdir':logbase})
spark_args = dict()
if type(special_args) is dict:
spark_args.update(special_args)
spark_args['--class'] = pyscript
spark_args['--logdir'] = logbase
spark_args['--spark_home'] = params.spark_dir
......
......@@ -5,18 +5,22 @@ Created on Wed Nov 11 14:24:32 2020
@author: schorb
"""
import json
import requests
import params
def mt_parameters(matchID):
def mt_parameters(matchID,owner=params.mt_owner,raw=False):
out_params = dict()
if not matchID is None:
url = params.render_base_url + params.render_version + 'owner/flyTEM/matchTrial/' + matchID
url = params.render_base_url + params.render_version + 'owner/' + owner + '/matchTrial/' + matchID
matchtrial = requests.get(url).json()
if raw:
return matchtrial
out_params = matchtrial['parameters']['featureAndMatchParameters']
out_params['scale'] = matchtrial['parameters']['pRenderParametersUrl'].partition('scale=')[2].partition('&')[0]
......@@ -36,4 +40,48 @@ def mt_parameters(matchID):
out_params['ptime'] += matchtrial['stats'][tk]
return out_params
\ No newline at end of file
return out_params
def new_matchtrial(matchID,urls,clippos='LEFT',owner=params.mt_owner,):
if not type(urls) is list:
raise TypeError('URLs need to be provided as list.')
if not len(urls) == 2:
raise TypeError('URL list needs to be of length two.')
matchtrial = mt_parameters(matchID,raw=True)
matchtrial['matches']=[]
matchtrial['stats']={}
matchtrial['parameters']['featureAndMatchParameters']['pClipPosition'] = clippos
matchtrial['parameters']['pRenderParametersUrl'] = urls[0]
matchtrial['parameters']['qRenderParametersUrl'] = urls[1]
# json.dump(matchtrial['parameters'],open('test.json','w'),indent=3)
url = params.render_base_url + params.render_version + 'owner/' + owner + '/matchTrial/'
res={}
try: res = requests.post(url, json=matchtrial['parameters']).json()
except: json.decoder.JSONDecodeError
if 'id' in res.keys():
matchtrial = res['parameters']
matchID = res['id']
return matchtrial, matchID
def invert_neighbour(tilelabel):
o_loc = ['LEFT','RIGHT','TOP','BOTTOM']
inv_loc = ['RIGHT','LEFT','BOTTOM','TOP']
for p_loc,q_loc in zip(o_loc,inv_loc):
if p_loc in tilelabel:
return q_loc
return 'NO CLIP'
<mxfile host="Electron" modified="2021-10-01T10:22:10.029Z" agent="5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.5.1 Chrome/89.0.4389.82 Electron/12.0.1 Safari/537.36" etag="DmzuIqdLbkaUH2R4yuNI" version="14.5.1" type="device"><diagram id="D8vMgsMkB-rplZWLKfx1" name="Page-1">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</diagram></mxfile>
\ No newline at end of file
<mxfile host="Electron" modified="2022-01-27T14:24:48.849Z" agent="5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.5.1 Chrome/89.0.4389.82 Electron/12.0.1 Safari/537.36" etag="CA4pjn7pzl3sYHy-dFZO" version="14.5.1" type="device"><diagram id="D8vMgsMkB-rplZWLKfx1" name="Page-1">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</diagram></mxfile>
\ No newline at end of file
docs/sketch/Render_scheme1.png

332 KB | W: | H:

docs/sketch/Render_scheme1.png

336 KB | W: | H:

docs/sketch/Render_scheme1.png
docs/sketch/Render_scheme1.png
docs/sketch/Render_scheme1.png
docs/sketch/Render_scheme1.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -80,7 +80,7 @@ export MASTER_HOST=`hostname`
export MASTER_IP=`host $MASTER_HOST | sed 's/^.*address //'`
export MASTER_WEB="http://$MASTER_IP:8080"
JARFILE=$RENDER_DIR"/render-ws-spark-client/target/render-ws-spark-client-2.3.1-SNAPSHOT-standalone.jar"
#JARFILE=$RENDER_DIR"/render-ws-spark-client/target/render-ws-spark-client-3.0.0-SNAPSHOT-standalone.jar"
mkdir $LOGDIR
mkdir $LOGDIR/$JOB
......@@ -128,4 +128,11 @@ sparksubmitcall="$SPARK_HOME/bin/spark-submit --master $MASTER_URL --driver-memo
echo $sparksubmitcall
$sparksubmitcall
sleep infinity
while [ ! -f $LOGDIR.log.done ]
do
sleep 2
done
rm $LOGDIR.log.done
#sleep infinity
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment