""" Substrate settings for force map prediction. Loads from config/substrate_settings.json - users can edit this file to add/modify substrates. """ import os import json def _default_config_path(): """Default path to substrate settings config (S2F/config/substrate_settings.json).""" this_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(this_dir) # S2F root return os.path.join(project_root, 'config', 'substrate_settings.json') def load_substrate_config(config_path=None): """ Load substrate settings from config file. Args: config_path: Path to JSON config. If None, uses config/substrate_settings.json in S2F root. Returns: dict: Config with 'substrates', 'default_substrate' """ path = config_path or _default_config_path() if not os.path.exists(path): raise FileNotFoundError( f"Substrate config not found at {path}. " "Create config/substrate_settings.json or pass config_path." ) with open(path, 'r') as f: return json.load(f) def resolve_substrate(name, config=None, config_path=None): """ Resolve substrate name to a canonical substrate key. Args: name: Substrate key (e.g. 'fibroblasts_PDMS', 'PDMS_10kPa') config: Pre-loaded config dict. If None, loads from config_path. config_path: Path to config file (used if config is None). Returns: str: Canonical substrate key """ if config is None: config = load_substrate_config(config_path) s = (name or '').strip() if not s: return config.get('default_substrate', 'fibroblasts_PDMS') substrates = config.get('substrates', {}) s_lower = s.lower() for key in substrates: if key.lower() == s_lower: return key for key in substrates: if s_lower.startswith(key.lower()) or key.lower().startswith(s_lower): return key return config.get('default_substrate', 'fibroblasts_PDMS') def get_settings_of_category(substrate_name, config=None, config_path=None): """ Get pixelsize and young's modulus for a substrate. Args: substrate_name: Substrate or folder name (case-insensitive) config: Pre-loaded config dict. If None, loads from config_path. config_path: Path to config file (used if config is None). Returns: dict: {'name': str, 'pixelsize': float, 'young': float} """ if config is None: config = load_substrate_config(config_path) substrate_key = resolve_substrate(substrate_name, config=config) substrates = config.get('substrates', {}) default = config.get('default_substrate', 'fibroblasts_PDMS') if substrate_key in substrates: return substrates[substrate_key].copy() default_settings = substrates.get(default, {'name': 'Fibroblasts on PDMS', 'pixelsize': 3.0769, 'young': 6000}) return default_settings.copy() def list_substrates(config=None, config_path=None): """ Return list of available substrate keys for user selection. Returns: list: Substrate keys """ if config is None: config = load_substrate_config(config_path) return list(config.get('substrates', {}).keys()) def compute_settings_normalization(config=None, config_path=None): """ Compute min-max normalization parameters from all substrates in config. Returns: dict: {'pixelsize': {'min', 'max'}, 'young': {'min', 'max'}} """ if config is None: config = load_substrate_config(config_path) substrates = config.get('substrates', {}) all_pixelsizes = [s['pixelsize'] for s in substrates.values()] all_youngs = [s['young'] for s in substrates.values()] if not all_pixelsizes or not all_youngs: pixelsize_min, pixelsize_max = 3.0769, 9.8138 young_min, young_max = 1000.0, 10000.0 else: pixelsize_min, pixelsize_max = min(all_pixelsizes), max(all_pixelsizes) young_min, young_max = min(all_youngs), max(all_youngs) return { 'pixelsize': {'min': pixelsize_min, 'max': pixelsize_max}, 'young': {'min': young_min, 'max': young_max} }